CMake Rants

CMake is a highly popular ‘meta-build’ system: it is a custom declarative syntax that is used to generate build scripts for all major OSs and compilers (e.g., VS solutions and projects). Designing such a system is a formidable task, but really not a very wise one to undertake in the first place.

I know there are only languages that people complain about and languages nobody uses. I know it’s bad manners to complain about stuff you get for free. I also know I’m working on windows and CMake scripts are generally authored by people who don’t care much about windows development.

And still I can’t help it. Here are a few particular irks.

Terminology

When CMake asks ‘where to build the binaries’ it isn’t talking about anything resembling a binary. It’s asking about the proper location for the outputs of its processing – i.e., solutions and projects (or other build scripts).

UI

This goes beyond a simple ‘designed by an engineer’ cliché. How long did it take you to figure out you need to repeatedly click the ‘configure’ button until all red lines are gone, then ‘Generate’?

Syntax

Not so good.

Clean/Rebuild

..is generally just a recommendation.

But what really makes CMake nearly unusable to me is –

CMake’s treatment of paths

In all generates build scripts, paths are absolute. In vcxproj’s – Output path, Additional Include Directories, PDB path, Linker input directories, custom build steps in ZERO_CHECK and ALL_BUILD, etc. etc. – are all absolute paths.

This makes CMake-generated projects almost useless: you cannot source control them or share them in any other way.

Turns out this has been known for quite some time. There’s a macro called CMAKE_USE_RELATIVE_PATHS but its documentation says:

May not work!… In general, it is not possible to move CMake generated makefiles to a different location regardless of the value of this variable.

It seems they tried to fix it for a while but gave up, and instead posted a FAQ which I don’t really understand:

CMake uses full paths because:

  1. configured header files may have full paths in them, and moving those files without re-configuring would cause upredictable behavior.
  2. because cmake supports out of source builds, if custom commands used relative paths to the source tree, they would not work when they are run in the build tree because the current directory would be incorrect.
  3. on Unix systems rpaths might be built into executables so they can find shared libraries at run time. If the build tree is moved old executables may use the old shared libraries, and not the new ones.

Can the build tree be copied or moved?

The short answer is NO. The reason is because full paths are used in CMake, see above. The main problem is that cmake would need to detect when the binary tree has been moved and rerun. Often when people want to move a binary tree it is so that they can distribute it to other users who may not have cmake in which case this would not work even if cmake would detect the move.

The workaround is to create a new build tree without copying or moving the old one.

The way I see it the real reasons for this sorry state are laid out in this 2009 discussion:

You should give up on CMAKE_USE_RELATIVE_PATHS , and we should deprecate it from CMake.  It just does not work, and frustrates people.

… It is really hard to make everything work with relative paths, and you don’t get that much out of it, except lots of maintenance issues and corner cases that do not work.

An alternative that I’m growing fond of

Is ‘Project from existing code’:

Download the sources you wish to use, and instead of invoking CMake on the root CMakeLists.txt, invoke ‘Project from existing’ code, select the source folder and follow the rest of the wizard instructions.

Today this approach worked for me perfectly on the first try (on this library that is admittedly simple in structure), but that was just lucky. It certainly isn’t perfect – but it is simple, and the generated project files do use relative paths. I’m beginning to think even when tweaks are needed, this is a better starting point for a usable project. Do tell me in the comments if your experience is different.


Edit:

It seems some words of clarification about my usage scenario are in order.

I wish to import an open source project to my build environment, and continue from there. With my build environment. Is that such an exceptional scenario? (It might be, judging by the comments below). I was under the impression that this is what CMake authors aimed for (why generate a VS project/solution otherwise?), and if I was creating an open source package that is how I would want others would use my code.

Moreover, except in the simplest of cases this is the only way to go: a CMake-generated project cannot possibly be the final say. All native build engines have their special knobs and handles that often must be tweaked. Did you ever, e.g., want to change the import library for a dll? Not really possible in CMake. Not to mention more advanced stuff – e.g., rebase it or make it ASLR.  I didn’t mention it above because I don’t consider this a CMake flaw – it’s just too much to ask of a portable build system. All you can expect from it is to set a portable common ground.

So I would expect CMake to generate a native build package (say, VS solution) in a way that would make it possible to forget it was generated by CMake.   Due to all the native knobs and handles this is an inherently hard user story to implement – but CMake fails much, much earlier. I’d consider using relative paths a must-have, and I don’t see why portability makes this task any harder than, say MSBuild authors’ task of using relative paths.

Advertisements
This entry was posted in C++. Bookmark the permalink.

15 Responses to CMake Rants

  1. Mark Final says:

    Good article. Addresses many of the struggles I had with CMake, among others.

    But yes, I’m responsible for one of those new standards as mentioned above, for trying to gradually improve on existing build systems. 1) Make it open source for everyone to use and improve, 2) base it on a real programming language so it can be debugged and profiled by developers, 3) make it fast, 4) make it straightforward to use and understand, 5) make it work for as many projects as possible, 6) provide lots of examples, 7) make it extensible, 8) provide project generation so everyone can use their favourite IDE.

    It’s still work in progress, but http://buildamation.com is where to go to try it out.

    • Ofek Shilon says:

      Thanks. It’s actually not my choice to make – this rant relates to importing an open source github project to our environment, and they chose CMake (as most do).

  2. Andreas Weis says:

    Allow me to counter-rant a bit. :)

    Terminology – This is only partly true. The generated files contain information about where the resulting binaries will be placed. Most of this information can be parametrized through CMake. There are some limitations (placing binaries outside the build tree is usually a bad idea), but claiming that CMake simply does not offer control about this is a bit unfair.

    UI – If you have to configure more than once before you can generate, I would consider that a bug in the CMake script. It is a bit sad that CMake allows this in the first place, but there is absolutely no reason to let lazy build system maintainers get away with it.

    Syntax – Yes, it’s not nice. As with all languages that have crappy syntax, this is mostly due to historical reasons. Not sure if this is reason enough to throw the whole thing out the window. But yeah, improvements would be nice. Suggestions are welcome.

    Clean/Rebuild – Is surprisingly hard. This is one of those issues that seem trivial, but somehow it’s still a huge mess in all of the existing tools. Not sure why, but I don’t feel very confident that I could do better if I were to start from scratch.

    Paths – My answer would be, don’t distribute generated projects. Just check in the CMake source and have your users run CMake on their own. I consider anything that falls out of the CMake build process before the install step potentially non-portable. I see why this might not be acceptable for people. In particular, it forces your users to gain some proficiency with CMake themselves if they want to build your project (which is good for CMake, but maybe not good for your project).
    I guess in the end this is a trade-off. Changing CMake to generate portable project files is not easy and introduces a ton of issues which don’t have obvious answers (the question of how to deal with external dependencies in such a case seems unsolvable to me at first glance). Because of this, the critical mass of users required to give this feature enough weight to be considered for implementation is very high. I am doubtful whether you can reach this critical mass, given that this probably only becomes relevant in a somewhat narrow use case.

    Project From Existing Code – Glad that it works for you. I personally switched to CMake because I needed a unified build that works across multiple platforms and build tools. I cursed at CMake many times for its numerous quirks and challenging learning curve. However, to this day I simply have not found any other tool that solves this problem.

    • Ofek Shilon says:

      Thanks. You actually seem to agree with (most of?) my points, but claim – perhaps rightfully – that that’s the best we currently have. Can’t argue with that.
      Specifically regarding paths: I wish to import an open source project to my build environment, and continue from there. With my build environment. Is that such an exceptional scenario? (It might be, judging by the comments here). I was under the impression that this is what CMake authors aimed for (why generate a VS project/solution otherwise), and if I was creating an open source package that is how I was hoping others would use my code. Undoubtedly this is an extremely hard user story to implement – each build environment has many non-portable knobs and switches – but CMake fails much, much earlier. I’d consider using relative paths a must-have, and I don’t see why portability makes this task any harder than, say MSBuild authors’ task of using relative paths.

  3. Anonymous says:

    “This makes CMake-generated projects almost useless: you cannot source control them or share them in any other way.” – CMake-generated stuff is not supposed to be source-controlled, but to reside in the build directory with all the build files. This makes it a bit misleading, but you should treat generated VS projects as generated Makefiles – they exist only to run “make” on them (or, to run Visual Studio in this case). It is Visual Studios’ problem that build instructions an project information for IDE are merged.

    I agree that CMake is far from ideal, but it is the only existing IDE-independent build system that is capable of really large C++ projects with loads of nontrivial dependencies and build rules.

    • Ofek Shilon says:

      Mostly rephrasing my reply @Andreas Weis (it was over a minute ago!! How come you didn’t read it?):

      Except in the simplest of cases, a CMake-generated project cannot possibly be the final say – as all native build engines have their special tweaks that often must be tweaked. Did you ever, e.g., want to change the import library for a dll? Not really possible in CMake (http://stackoverflow.com/questions/34575066/how-to-prevent-cmake-from-issuing-implib). Not to mention more advanced stuff – e.g., rebase it or make it ASLR.

      So I – and I suspect many others – wish to import a CMake generated project to my build environment, and continue from there. With my build environment. The multitude of native tweaks makes this a hard user story to implement – but CMake fails much, much earlier. I’d consider using relative paths a must-have, and I don’t see why portability makes this task any harder than, say MSBuild authors’ task of using relative paths.

  4. markjamesabraham says:

    Seeking to store generated artefacts in source control is a classic anti-pattern. In your case, it’s guaranteed useless to anybody else, even you on a different machine. Store only the CMakeLists.txt and run cmake when you start to build the oroject

    • Ofek Shilon says:

      Thanks. It seems my scenario isn’t the obviously default scenario (see the previous replies) – I’ll edit the post to reflect that.

  5. CMake is great whenever you have to deal with a highly heterogenous development environment. It is perfect for open source development and for library vendors. On the other hand, for developers that work in a corporate environment where the product is only ever compiled in-house, the cross platform focus of CMake is less applicable. It is not uncommon for
    development environments to be strictly controlled and homogeneous, particularly so when technologies such as Docker or some other form of build environment virtualisation has been adopted. In this environment, CMake has less to offer, and there may exist alternatives that do what you want with fewer drawbacks. CMake is a good tool that does a particular job very well. The issues come when we apply the Peter Principle and start to use it to do jobs for which it was not designed. As always, the person who knows your own problem best is you. So, YMMV.

  6. petermtate says:

    I have just gone through the same pain, integrating a CMake open source project into our internal Visual Studio based build system. CMake is the worst cross platform build system, except for all the other ones.

    I ended up taking a different approach, run CMake, run the build (since the build generated some additional source files), run a script to convert the full paths to relative paths (even more fun since CMake sometimes used windows style back slashes, and sometimes unix style forward slashes). Strip out custom build steps (which would detect changes and re-run CMake again!). And then delete *.tlog files which Visual Studio uses to figure out that the generated source files should be deleted when you run ‘Clean’ on the build.

    I was a huge pain to figure this all out. I sympathize with these library developers, however, cross platform builds are a really hard problem, and I can understand that making it easier for you and me to fork their library is not huge on their priority list.

  7. Dave McMordie says:

    Thanks for articulating the frustrations we all share with CMake!

    CMake is 99% useful, which makes it 100% a frustrating waste of time for new projects, and just something we have to deal with for stuff on github. The biggest issue is this relative path thing. Instead of preaching to the user community and telling us we should just regenerate projects when we move code, they should probably listen and acknowledge this is a critical feature which was missed early on in the design and is now going to be very costly to fix.

    A project generator where a simple path rename breaks the build is kind of hopeless…

    • Ofek Shilon says:

      @Dave thanks. I’m afraid CMake’s current momentum makes it unlikely that any new cross platform build system would replace it, so our best hope is to get someone in kitware to rethink the path issue priority. Do you think that’s possible?

      • Dave McMordie says:

        I was actually wondering how hard it would be to fix; I spent some time digging into this today. It seems to be bound up in some cases with the windows path length limit. We have asked one of our developers to dig deeper and provide an estimate.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s