Metric Panda Games

One pixel at a time.

Bye Bye CMake

Rival Fortress Update #30

In the past months working on Rival Fortress I found myself having to spend an unreasonable amount of time fussing with CMake build scripts.

Don’t get me wrong, I like CMake.

I think it’s an excellent tool if you have to manage complex build systems that have to work on multiple platforms and multiple toolchains. It abstracts away details of the build tool with a nice DSL that truly makes builds easier.

Unfortunately it can also make you waste a lot of time, especially if you are trying to do something that’s outside the standard usage pattern.

My bad experiences with CMake


Back in December I wrote about how to make CMake behave nicely with NASM. I remember wasting quite some time digging through CMake’s source trying to figure out a solution on how to have CMake detect NASM correctly. This has since been fixed in recent versions of CMake.

Custom preprocessor

When I implemented the engine’s Meta reflection system I had to hack together a reasonably complicated solution in order to have my custom preprocessor run before the normal build, while also tracking generated files. Something that really should be trivial to implement.


Recently I had to dig into CMake’s source once again, trying to figure out how to make it recognize Clang/C2 with Microsoft Codegen. I wasted a couple of days trying to get it pass the correct build flags, and stop it from detecting MSVC instead of Clang. In the end I ended up with forking the CMake repo and change the hard-coded scripts to make it do what I needed.

CMakeLists.txt spam

This is more of a pet peeve of mine, but I really hate the proliferation CMakeLists.txt files. I know that I can easily stuff all my build directives into one “master” CMakeLists.txt, but CMake best practices dictate that each logical build unit should be governed by its own CMakeLists.txt. I find this makes builds hard to modify as you have to jump back and forth between multiple files just to figure out what’s going on.

Back to the basics

Yesterday I nuked CMake and rewrote the build system in good ol’ GNU Make.

I’m now down to one Makefile of a couple hundred lines of code, instead of seven CMakeLists.txt.

The build system is able to:

  • output five build targets:
    • Meta reflection preprocessor
    • Asset packer
    • Game server
    • Game client
    • Game DLL for hot reloading
  • native compile on Linux and Mac using Clang and on Windows using Clang-cl
  • cross compile to Windows and Mac (from Linux) for 64 and 32 bits
  • generate HTML game documentation from Markdown files in the /docs folders
  • pack assets when out of date or modified
  • build and runs the preprocessor, correctly tracking generated files
  • assemble .asm files using NASM