Build Systems¶
When starting a new software project, picking a build system is one of
those early decisions that tends to stick around for the life of the
codebase. It’s not something you swap out casually later on—once
you’ve wired it into your workflow, your CI, your dependency
management, and your team’s habits, it’s there for good. That’s why a
good build system needs to handle about 90% of what your project will
ever need, right out of the gate. You want something flexible enough
to grow with you, but not so complex that it slows everyone down in
the early stages. Whether you’re using CMake, Bazel, Meson, or
something custom, the goal is the same: make builds predictable, fast,
and maintainable—because once the choice is made, you’re probably
going to be living with it for years.
Before modern build systems came along, GNU Make was the go-to tool
for just about everything; and while it was powerful, it wasn’t
exactly elegant when projects started getting big or
complicated. Developers would often hit the limits of what Make’s
syntax or dependency logic could handle, especially when dealing with
platform differences, code generation, or dynamic dependencies. The
result was a patchwork of shell scripts and ad-hoc helper tools that
filled in the gaps. You’d have a Makefile calling out to Bash, Perl,
or Python just to get something done that didn’t fit neatly into
Make’s rule system. Over time, these projects became mini build
ecosystems, with fragile interdependencies and a “don’t touch it if it
works” vibe. It wasn’t until more robust and expressive systems like
CMake, Meson, and Bazel showed up that teams could finally describe
complex builds without duct-taping a dozen scripts together.
Modern build systems have come a long way from the days of fragile
Makefile-and-script Frankenbuilds. Tools like CMake, Meson, and Bazel
have introduced real structure—dependency graphs, cross-platform
abstractions, and reproducible builds that make large projects far
more manageable. But the pendulum has swung a bit in the other
direction. Because these systems are so powerful (and sometimes so
rigid), developers can find themselves boxed in when they hit an edge
case the build system doesn’t natively support. Instead of quickly
hacking together a small external tool or helper script, they’ll often
spend hours wrestling with configuration syntax or custom macros just
to stay “within” the build system’s ecosystem. In a way, the
discipline and architecture that solved the chaos of the old Make days
have created a new kind of friction—where the fear of breaking the
build outweighs the freedom to just build something simple that gets
the job done.
Notes based on project usage¶
Add notes from project post-mortems. What worked and what didn’t
Team notes on build systems¶
Major features of Apache Maven Project Object Model (POM): The core of Maven is the pom.xml file, an XML document that contains all the project information, including dependencies, plugins, and build profiles. Dependency management: Maven automatically downloads and manages project dependencies and their transitive dependencies (the dependencies of your dependencies). This is done by specifying the required libraries in the pom.xml, which Maven then fetches from repositories like Maven Central. Standardized project structure: Maven enforces a uniform directory layout for projects, making it easy for developers to move between different projects and immediately know where to find source code (src/main/java), test code (src/test/java), and other resources. Build lifecycle: Maven defines a consistent sequence of build phases, such as validate, compile, test, package, verify, and install. When you run a command like mvn install, Maven executes all the preceding phases in the correct order. Plugins: Maven’s functionality can be extended through a robust plugin system. Plugins are collections of goals that execute in specific phases of the build lifecycle to perform tasks such as compiling code, running tests, or creating documentation. Repositories: Maven uses repositories to store and retrieve artifacts (built outputs) and dependencies. It first checks a local cache on your machine, and if a dependency isn’t found, it downloads it from a remote repository, such as the Central Repository. Build profiles: This feature allows you to customize the build for different environments, such as development, testing, or production. You can activate specific profiles from the command line to apply a particular set of configurations. Project information: Using the data in the pom.xml, Maven can generate reports and project documentation, including dependency lists, unit test reports, and cross-referenced source code. Maven vs. other popular build tools
...
Aspect-Oriented Programming (AOP) is a programming paradigm that complements Object-Oriented Programming (OOP) by providing a way to modularize cross-cutting concerns—aspects of a program that affect multiple modules or classes but don’t fit neatly into the object model.
Typical examples of cross-cutting concerns include:
Logging Security checks Transaction management Error handling Performance monitoring In OOP, these concerns often end up scattered across many classes and methods, leading to code duplication and reduced maintainability. AOP addresses this by encapsulating such behavior in reusable units called aspects.
...
Bazel Bazel is an open-source build and test tool developed by Google, similar to Make, Maven, and Gradle. It is designed to handle large, multi-language projects with a focus on speed and reproducibility. It achieves this through heavy caching, parallel execution, and hermetic builds. It uses BUILD files and a Python-like language called Starlark to define build targets.
...
Buck Buck is a build system developed and used by Facebook. It is very similar to Bazel in its design and goals, emphasizing high-performance, reproducible builds for large, multi-language codebases. It encourages the creation of small, reusable modules that can be built in parallel.
...
Cargo Cargo is the official build system and package manager for the Rust programming language. It handles a wide range of tasks, including downloading and managing dependencies (called “crates”), compiling code, running tests, and generating documentation. Project configuration is managed through a Cargo.toml file.
Cex.C - Comprehensively EXtended C Language Whereas not a build system, but an extended c compiler, that has a build system incorporated with it, like zig does.
CEX’s integrated build system, cexy$, eliminates the need for external tools like CMake, Make, Ninja, or Meson. Inspired by Zig-build and Tsoding’s nob.h, cexy$ adapts to your project’s complexity. Small projects can use a straightforward, configuration-driven mode, while larger or cross-platform projects can utilize low-level tools for fine-grained control over the build process.
...
CI/CD enhances quality by implementing a virtuous cycle of automation and rapid feedback that starts on the developer’s desktop. By integrating these practices early in the development lifecycle, developers and teams can catch and fix bugs when they are small and less costly, maintain a stable codebase, and deliver high-quality software with speed and confidence. See also Test Drive Development
How CI/CD starts on the developer’s desktop
1. Frequent, small commits:
...
Cmake CMake is a cross-platform, open-source build system generator. It uses configuration files named CMakeLists.txt to define the build process. From these files, it generates native build files for various environments, such as Makefiles for Unix-like systems or Visual Studio projects for Windows, allowing developers to use their preferred native build tools.
...
Conan is not a build system it is a package manager for C and C++. It functions as a front end and build system wrapper, orchestrating and managing dependencies for other build systems like CMake, MSBuild, and Makefiles. Here’s a breakdown of Conan’s role:
Agnostic to build systems: Conan is designed to work with any build system, and individual packages within Conan can use different build systems. This includes popular ones like CMake, Visual Studio (MSBuild), and Autotools, as well as proprietary or custom ones. A package manager: Conan’s primary role is to create and manage packages and their dependencies. It handles versioning, configurations, and the retrieval of binary dependencies. A build-system wrapper: Conan can call and execute the build process for you using the underlying build system. It provides a consistent layer between the developer and the build tools, which is especially useful for managing complex build processes like cross-compiling. Build integration files: When you use Conan to install dependencies, it generates integration files that are then read by your project’s native build system. This allows your build system to find and use the Conan-managed dependencies seamlessly.
Design by Contract (DbC), as defined by Bertrand Meyer in his book Object-Oriented Software Construction, is a systematic approach to software design based on the idea of a formal, verifiable “contract” between software components. This contract is specified using logical assertions embedded in the code itself, which define the obligations and benefits of interacting components. Core principles of DbC
DbC models the interaction between a “client” component that calls a service and a “supplier” component that provides it. The contract specifies the following conditions for a supplier’s method or procedure: ...