TL;DR: I'm happy with CMake + Ninja, even though the language is ugly and the speedup is not that impressive.
I'm not ashamed to admit it: I stayed away from CMake because the language seemed inelegant.
I am currently in the middle of a project moving from SCons to CMake and I am pleasantly surprised at the quick progress. The SCons project has about 3KLOC of SCons build scripts that do everything from generating code for language bindings to building 20-something external C and C++ libraries. My experience with this larger project has also convinced me to move over my JIRA time tracker, because of all the other things I should be doing, the most important thing is moving over the build system for no particular reason at all.
SCons served the larger project well when it started. Python was an easy language to learn, if not master and so most people could read the code and make little modifications when they needed to. As the project grew and things got more complicated to manage, we started mimicking other build systems to add features that SCons did not have. The most important feature we cribbed was the "use requirement" feature from Boost Build. In a nutshell, it allows you to attach "arbitrary" requirements to a target that you are consuming. So if you use SomeFunkyDll.dll in your project, you may want to require that the library clients define SOME_FUNKY_DLL when they consume it. Automatically doing this in SCons was a big pain in the rear. I don't need to tell you how we did it because it is not relevant but I wanted to give you an idea of the types of things we found important.
Another problem that the project started having is once we passed some critical mass of source code, the build seemed to slow down significantly. In the old days, SCons could be slow as molasses because it wanted to be correct over everything else. We tried everything in the "go fast" recommendation list but at best, a no-op build took a long time and this was resulting in too many sword fights.
Eventually, we got to the point where we thought that working on this project was becoming inefficient so we looked for alternatives. I had always had CMake in the back of my mind as I had actually written my own makefile generator for another project. The architectural separation of specification and the actual build actions allowed optimizations at different levels not unlike a compiler which allowed me to add many more features than I would have been able to had I had to also develop the build action logic.
I won't be talking about the larger project yet (maybe ever) but I will talk about my experience moving over Worklog Assistant's build system. Just so that there is a frame of reference:
- Number of targets in build (including third-party files): ~1K
- Lines of app code (excluding third-party files): ~20K
That is, it's not a huge project.
A post on build systems can go on forever, but the performance of the build for each of these activities is what actually matters to me when I'm doing my daily work:
- Clean build
- Modifying a core header file (aka a file that nearly everything depends on)
- Modifying any cpp file in the build
- Modifying an important, but not core header file
- For some reason this seems to be important when comparing build systems: building when nothing changed. I don't personally care about this metric.
The CMake language, other weirdness and leaky abstractionsMy initial aversion to CMake was likely due to the fact that it was clear to me there would be weird conventions and things that worked kind of differently depending on the backend chosen. I was not wrong in this assumption, but I was wrong in how much they would bother me: not much. Don't get me wrong, it's frustrating not completely understanding why changing an add_custom_target to add_custom_command makes things work "better" with generated files, but by and large, there are few such design bugs.
The biggest reaction any decent developer probably has when being introduced to CMake is the language. It just looks bad. However, while the language is not efficient by any means, nor is it beautiful, it is predictable and regular which makes it easy to learn. After the initial shock of the aesthetics of the language, you settle into it pretty easily. Thought: perhaps the language being intellectually unappealing encourages the developer to spend as little time in it as possible making him/her quite efficient. Did they learn this from Java? Hmm.
Different build systems
Third-party library support
A highly unscientific test with lots of numbers to look official
- scons -j8: A fairly well-optimized SCons build using many of the tips from the SCons wiki, run with 8 parallel jobs. Source lists are built dynamically, using a recursive glob + wildcard filter.
- cmake + ninja 1: An unoptimized CMake (22.214.171.124) build using the Ninja stable release from September 2013. Source lists are built with a preprocessing step and some elisp to make maintenance of the preprocessed file easier. See gist.
- cmake + ninja 2: Same as cmake + ninja 1, but configured with CMAKE_LINK_DEPENDS_NO_SHARED=ON
- cmake 3.0 + ninja: Same as cmake + ninja 1, but using cmake 3.0
- cmake + ninja latest: Same as cmake + ninja 1, but using Ninja from git
|Importance||scons -j8||cmake + ninja 1||cmake + ninja 2||cmake 3.0 + ninja||cmake + ninja latest||cmake 1 % speedup||cmake 2 % speedup||cmake 3.0 % speedup||cmake + ninja latest % speedup over scons|
|Touch core header file and rebuild||5%||165||178||174||180||175||-7.88%||-5.45%||-9.09%||-6.06%|
|Touch a source file and rebuild||50%||46||35||35||35||35||23.91%||23.91%||23.91%||23.91%|
|Touch an arbitrary non-core header file and rebuild||40%||60||68||65.5||66||65||-13.33%||-9.17%||-10.00%||-8.33%|
|Do-nothing and build||0%||10||0.5||0.5||0.2||0.44||95.00%||95.00%||98.00%||95.60%|
The weighted average is computed using the "importance" column. I think touching source files should perhaps have a higher importance than I've given it above. Do I really edit header files as often as source files? I don't have the answer to that so as a proxy, I just ran a simple script counting how many header files vs source files were touched in a 6 month period and applied the ratio as a weight. My gut feeling is that I probably spend more time editing source files than header files.