Building a build system - Part 0 - Intro
In the fall of 2017, I had the opportunity to help a friend working at the University of Sherbrooke rebuild a course from the ground up. This course, IGL 601, had unfortunately devolved over the years into something that barely had anything to do with modern software development. Which is really bad for a course titled Software Development Techniques and Tools. Having realised that himself, His Holiness, the Computer Science Department’s director, gave us his blessing to kill it with fire and rebuild something better out of its ashes. What fun it was to build a curriculum where students would be introduced to modern software development techniques and tools such as source control, quality assurance, build systems and finding plausible excuses to avoid pointless “Agile” meetings!
One thing that I was quick to realize is that many people know next to nothing about build systems. If you ask them how a build system works, they’ll respond that you simply have to press
F5 on the keyboard and let Visual Studio do its magic. For others, typing
make at the terminal is their build system. I’m not saying that to lord over them with my superior intellect and knowledge (not this time anyway), I also genuinely knew next to nothing about build systems and their inner workings at the beginning of this undertaking. Therefore, to make myself pass as a build (engineer/master/emperor/another royal title of your choosing), I had some work to do! Thus, I set out to answer the question: how could I implement a build system from scratch?
First, some non-goals:
- Creating a production grade build system
- Creating yet another scripting language to specify build actions and dependencies
- Creating a language-agnostic build system
- Manage dependencies a la Conan
With that out of the way, what are we actually going to build?
Concretely, our goal will be to have a tool that take a source code directory as an entry and spit out an executable. The directory should have the following (flat) form:
project │ main.cpp │ └─folder1 │ │ file1.cpp │ │ file1.h │ │ ... │ └─folder2 | │ file2.cpp | │ file2.h │ │ ... | └─ ...
The way the build system is going to work is by creating a library out of the contents of a given folder and then linking all the created libraries together with an entry point to produce an application. Consequently, it’ll only work on projects with a folder depth of at most one.
Also of note, the build system will only work with C/C++ projects.
Furthermore, I’ll be showing some code snippets of how we could go about implementing the ideas I’ll be presenting. I’ll make no guarantee that the snippets have ever met a compiler. Integrate them to your homework/personal project/company’s codebase at your own risk and peril. If you blindly trust code you found on the Internet then may God have mercy on your soul (because your employer certainly won’t!).
Lastly, our weapon of choice for this adventure will be modern C++. Because if we, valorous knights that we are, aim to slay the build system dragon, we better use some motherloving surface-to-air missiles.
Coming up next: Compilation is a problem, graphs are the cure.
This article is Part 1 in a 2-Part Series.
- Part 1 - This Article
- Part 2 - Building a build system - Part 1 - Dependency graph