Intro
I do a lot of thinking about game engines - perhaps too much. Part of it is because it relates to my day job, but also, I just really enjoy working on problems that come with creating little engines. I don’t usually share what I am thinking outside of stream since I am not a fan of short form social media (i.e. Bluesky, Mastodon, etc.). However, I want to try out posting my ideas/thoughts on this forum as I work on my own little engine.
I happen to want to take a fresh look at how I design my engines, so for this first little post, I am going to ramble a bit on my chosen language and build system for game engines.
The Language
I like to learn different programming languages. Each language has its own unique design and way of designing programs, so I often find myself writing a little renderer in different languages to see how the language might change my approach to programming.
However, I really like C. Of all the languages I have used over the years (C++, Rust, Odin, Zig, Go), C remains my favorite language and I always find myself returning to it. The spec is simple. The language is simple. The build systems are atrocious. I can program without feeling like the language is in my way. So, for this little engine, I am going to use C - or more specifically C99.
There’s also the decision of which compiler’s I want to support. For now, I am going to support the main compilers - GCC, Clang, and Microsoft’s Visual Compiler. There is a part of me that wants to just use GCC so I can have my own little defer macro, but getting GCC on Windows is…interesting.
The Build System
The build system ecosystem in C (and C++) is probably the worst of any language I have ever learned. Here are the tools people like to use (in various combinations):
- Makefiles
- NMake (Microsoft Makefiles)
- Ninja
- Autotools
- CMake
- Meson
- Bazel
- Build2
- vcpkg
- Conan
- …and probably many more.
There are many build tools in this ecosystem, and honestly, they all have their own flavor of suck.
Makefiles, NMake, Ninja, and Build2 are your actual “build tools” - the programmer provides a set of targets/recipes, and invoke the compiler with the correct flags.
Unfortunately, Makefiles and co. are written in the most obtuse languages possible, so folks created build tool generators so you don’t have to write them! That’s where CMake, Autotools, Meson, and Bazel come in. The programmer now writes a set of targets/rules in the generator’s language, which will then generate the build tool, which then invokes the compiler. Easy peasy, right? (/s)
Okay, but any massive project is going to have dependencies, which probably have their own dependencies, and none of the build generators are particularly good at resolving nested dependencies (they can do it, but it might get complex). This is where package managers like Conan and vcpkg come in! They’ll handle all of your deps for you - you need to just write some config files specific to the package manager.
Let’s see, we have:
- A C Compiler
- A build tool language
- A build generator language
- A package manager language
Sheesh, just to start programming, I need to use (learn) 4 languages! It’s no wonder C++ is so bloody hard to learn.
takes deep breadth
I (mostly) learned C by watching Casey Muratori work on Handmade Hero, and his approach to build his game is with a batch script. The script is like 10 lines code. The source is organized around a “unity” build, where all source files are compiled into a single translation unit (as opposed to the traditional one translation unit per source file), so compilation is dead simple.
To this day, a batch script + unity builds are the most elegant approach to compiling code I’ve come across. They are inherently simple - the script only cares about the unity source file. The downside is that you have to compile all of your code at once, but honestly, compiling my entire codebase in C is often faster than incremental builds in most other languages.
If I were to use a batch (or in my case, a bash script), a user (me) only needs the compiler to get started. Bash is supported on just about every platform, and if you have Git on Windows, you have a bash interpreter. Building the code base is simply:
# assuming a compiler is already installed
sh bin/init_local.sh # setup basic local filters
sh bin/build_all.sh # build the codebase
as opposed to:
# assuming a compiler is already installed
pacman -S cmake ninja git # install deps
sh bin/bootstrap_vcpkg.sh # fetch vcpkg if not installed
cmake --preset debug # generate build files for debug preset
cmake --build build-debug/ # build the codebase
My approach to writing build scripts has always been cumbersome when I try to add in multiple platforms and compilers, so I always end up just using CMake/Ninja.
However, I just so happened to stumble across Mr4th’s (the creator of 4coder - one of my favorite editors) gitea, where he was rebuilding his personal code base. He had written a set of bash scripts allowing his build system to support multiple platforms/compilers through the use of filters. It was the missing piece I needed for my own set of bash scripts, so I decided to test out his code with my codebase and it worked perfectly!
Going forward, I think I am going to keep using his scripts as part of my build system. If it continues to work out, I might write another post on it!
Conclusion
I had planned to talk about a few more topics in this post, but I think it is long enough. I might go into more detail on the build system in my next post, once I’ve had a few more days to play around with it or maybe the general architecture/goals I am aiming towards.
I’m curious to know if anyone is interested in my ramblings and might want more of them or have any game engine specific topics you might want me to discuss (although rendering architecture is where most of my experience is).
Until next time,
Enlynn