Reproducible Systems Development

My Systems Development Environment

A structured development setup for building, testing, debugging, and documenting C, C++, command-line tools, reusable libraries, and increasingly deeper systems software. The environment is designed to behave consistently across Windows, WSL, and Linux while keeping the Unix toolchain central.

Windows Host WSL Linux GCC Make CMake GDB Valgrind Sanitizers
systems-environment
$ gcc --version gcc 15.2.0 $ make clean && make Build completed successfully. $ make test PASS: all tests $ valgrind --leak-check=full ./build/app 0 bytes definitely lost $ git status working tree clean

Environment Architecture

The setup separates the host operating system, Unix development environment, compiler toolchain, and project workflow while keeping source files accessible across platforms.

01

Windows Host

Provides the primary desktop environment, file management, browser, visual development tools, native applications, and access to Windows-based compilers when required.

Primary workstation host
02

WSL Integration

Provides a Linux command-line environment directly within Windows, allowing Unix tools, GCC, Make, CMake, GDB, Valgrind, Git, and shell automation to remain central.

Daily systems workspace
03

Linux Toolchain

Supplies the Unix process model, shell utilities, compiler ecosystem, debugging tools, system interfaces, permissions, file semantics, and POSIX-oriented development behavior.

Target systems environment
04

Reproducible Workflow

Standard commands, project structures, warnings, tests, sanitizers, documentation, and Git history make each project rebuildable and inspectable.

Applied across repositories

Core Toolchain

Tools are grouped by their role in compilation, correctness, debugging, automation, and project maintenance.

Compilation & Build

GCC C and C++ compilation with strict warnings
Make Fast, repeatable project builds and targets
CMake Portable builds for larger C++ libraries

Debugging & Correctness

GDB Breakpoints, stack inspection, and execution tracing
Valgrind Leak detection and invalid memory access analysis
Sanitizers Address, undefined-behavior, and runtime diagnostics

Workflow & Automation

Git & GitHub Version history, milestones, and project publishing
Shell Tools Navigation, scripting, pipelines, and automation
Python Support Test utilities, scripts, reports, and automation

Environment Setup

A practical sequence for establishing the Unix-oriented development environment and validating the core toolchain.

Setup Sequence

Each layer is installed and verified before project development begins.

01

Enable WSL

Install a Linux distribution and confirm that the shell, filesystem, package manager, and Windows integration work.

02

Install the Build Toolchain

Install GCC, G++, Make, CMake, GDB, Git, and essential development packages.

03

Add Diagnostic Tools

Install Valgrind and validate compiler sanitizer support.

04

Configure Git

Set identity, authentication, branch conventions, and repository remotes.

05

Verify the Environment

Compile a warning-clean program, run tests, use GDB, and check the executable with Valgrind.

Installation & Verification Commands

Representative commands for a Debian or Ubuntu-based WSL distribution.

install-toolchain.sh shell
# Update package information sudo apt update # Install compilers and build tools sudo apt install build-essential gcc g++ make cmake # Install debugging and source-control tools sudo apt install gdb valgrind git # Verify installed tools gcc --version g++ --version make --version cmake --version gdb --version valgrind --version git --version
git-config.sh shell
git config --global user.name "Bruno Fonkeng" git config --global user.email "[email protected]" git config --global init.defaultBranch main git config --global core.autocrlf input

Standard Project Structure

Systems projects use a consistent directory layout so source code, headers, tests, documentation, examples, and generated files remain clearly separated.

Repository Layout

A representative structure used across modular C and C++ projects.

project/ ├── README.md ├── LICENSE ├── Makefile ├── CMakeLists.txt ├── include/ │ └── project.h ├── src/ │ ├── main.c │ └── project.c ├── tests/ │ └── test_project.c ├── examples/ ├── docs/ ├── build/ └── bin/ # build/ and bin/ contain generated outputs

Why This Structure Matters

The layout reinforces separation of concerns and makes each repository easier to build, test, review, and extend.

Public Interfaces

Header files define reusable contracts without exposing unnecessary implementation details.

Implementation Separation

Source files organize behavior into focused modules rather than one large program.

Dedicated Testing

Tests are independent from demonstration programs and can validate functions directly.

Generated Output Isolation

Object files, executables, and temporary artifacts remain outside source directories.

Learning Documentation

Notes, mistakes, design decisions, examples, and verified commands remain part of the repository.

Build & Validation Commands

Standard commands keep compilation strict, testing repeatable, and diagnostic behavior visible.

C Compilation

Warning-driven compilation is used as a first line of defense against incorrect or non-portable code.

compile-c.sh gcc
gcc \ -Wall \ -Wextra \ -Wpedantic \ -std=c11 \ -g \ src/main.c \ src/project.c \ -Iinclude \ -o build/app
compile-with-sanitizers.sh gcc
gcc \ -Wall \ -Wextra \ -Wpedantic \ -std=c11 \ -g \ -fsanitize=address,undefined \ -fno-omit-frame-pointer \ src/main.c \ -o build/app_san

Make & CMake Workflow

Small C projects use Make, while larger C++ libraries use out-of-source CMake builds.

make-workflow.sh make
make clean make make test make run
cmake-workflow.sh cmake
rm -rf build cmake -S . -B build cmake --build build ctest --test-dir build --output-on-failure

Daily Engineering Workflow

Every project follows a repeatable progression from implementation to verification and documentation.

01

Implement

Add one focused behavior through a clear interface and keep the change small enough to reason about.

02

Compile

Build with strict warnings enabled and treat unexpected warnings as defects requiring investigation.

03

Test

Verify normal behavior, boundary values, invalid input, failure paths, and reusable library functions.

04

Diagnose

Use GDB, sanitizers, and Valgrind to inspect crashes, ownership, leaks, undefined behavior, and incorrect state.

05

Document & Commit

Record commands, architecture, decisions, tests, lessons, and a meaningful Git milestone.

Debugging & Memory Diagnostics

Correctness is investigated with complementary tools rather than relying on print statements alone.

GDB

Used to pause execution, inspect stack frames, examine variables, trace control flow, and identify the exact point at which state becomes incorrect.

gdb-session gdb
gdb ./build/app break main run next print variable backtrace

Valgrind

Used to identify leaked allocations, invalid reads and writes, use of uninitialized values, and incorrect memory-management behavior.

memory-check shell
valgrind \ --leak-check=full \ --show-leak-kinds=all \ --track-origins=yes \ ./build/app

Compiler Sanitizers

Used during development to detect out-of-bounds access, use-after-free errors, undefined behavior, and other runtime violations with source-level reports.

sanitizer-run shell
ASAN_OPTIONS=detect_leaks=1 \ ./build/app_san

Reproducibility Principles

The environment is designed so that another developer—or future me—can understand how a project was built, tested, and verified.

01

Commands Are Preserved

Compilation, test, debugging, and execution commands are stored in Makefiles, scripts, and documentation.

02

Generated Files Are Separate

Build outputs do not mix with source files, making clean rebuilds predictable.

03

Tests Define Expected Behavior

Verified behavior is captured in test cases rather than remaining only in memory or manual observations.

04

Milestones Are Versioned

Meaningful Git commits preserve working stages, design changes, and technical progress.

Tool versions may change as the environment evolves. The enduring standard is not a specific version number, but a warning-clean, testable, diagnosable, documented, and reproducible workflow.

An Environment Designed for Deeper Systems Work

This setup supports the current C and C++ foundation while providing a path toward concurrency, operating systems, performance engineering, virtual machines, compilers, and runtime infrastructure.