I’ve recently been playing with CMake again, with much more success than last time. I still don’t love it, but:
- CMake is now very widely used in the C++ world, so supporting it can attract contributions.
- JetBrains’s CLion IDE supports CMake, and only CMake. I wanted to try its inspections and refactoring features, though these are rather disappointing so far.
- I wanted to try clang-tidy. But it needs a compile-commands.json file that they suggest you generate from CMake . However, I found the bear utility that does this for generic make builds, so I didn’t need CMake in the end for this.
I first tried it in the cmake branch of my little PrefixSuffix application because its build is rather simple.
I then finally allowed libsigc++ to have an additional CMake build system, at least in libsigc++-3.0, and Marcin Kolny got it done. This was possible now that libsigc++ doesn’t generate code from .m4 files. We wouldn’t have wanted to maintain two complicated build systems, but two simple build systems seems acceptable. I noticed many questions on StackExchange about actually using libsigc++ in CMake builds systems, so I added a hint about that to the documentation.
I also added a CMake build system to my Glom source code, to test something bigger, with many awkward dependencies such as Python, Boost, and PostgreSQL. It’s working well now, though I haven’t yet added all the tests to the build. This let me really try out CLion.
Minor Conclusions
I’m fairly pleased with the result. CMake doesn’t now feel massively more annoying than autotools, though it makes no attempt to do the dist and distcheck that I really need. I have now achieved a level of acceptance that means I wouldn’t mind much working on a project that uses CMake. I wouldn’t even need to hold my nose. The following complaints don’t seem to bother me quite so much any more:
I still find invoking CMake horribly obscure compared to ./configure. For instance
cmake -DCMAKE_INSTALL_PREFIX:PATH=/opt/something
seems ugly compared to
./configure --prefix=/opt/gnome
and I don’t see an equivalent for “./configure –help” to list all generic and project-specific build options. I also haven’t tried to replace mm-commons‘s wonderful –enable-warnings=fatal to easily turn on compiler warnings as errors, encouraging my code to be free of warnings and deprecated API while not troubling people who just want to build the project with the least difficulties.
I still cannot understand how it’s normal in the CMake world to create these awful little Find*() scripts for each library that you might depend on, instead of just using pkg-config, though CMake’s pkg-config support seems to work well now. And hoping that your own library’s Find*() script ends up in CMake itself does not seem scalable.
It’s also a little odd that I cannot specify the link directories (-L flags) per target. link_directories() seems to apply to all targets.
CMake’s syntax still seems like a toy language. Its quoting (none/implicit/unknown) and separating (spaces, not commas) and use of whitespace (occasionally significant) sometimes makes m4 feel almost friendly. The non-case-sensitivity of function names (but case-sensitivity of variable names) just leads to arbitrarily inconsistent code and unnecessary style differences between projects.
I don’t like the CMake tradition of having separate CMakeList.txt files in sub-directories, because this can lead to duplication and makes it harder to see the whole build system at once. I prefer non-recursive autotools, so I tried to use just one CMakeList.txt for my CMake projects too, which was only a little awkward.
Your first complaint led me to write a small CMake module* which computes common prefixes and paths. The inconsistent (across different projects) libdir handling in CMake was really giving me headaches before using that.
* https://gist.github.com/matze/7200903
Looks nice. However, doesn’t this overlap quite a lot with the built-in GNUInstallDirs module? https://cmake.org/cmake/help/v3.0/module/GNUInstallDirs.html
I’ve no idea why that functionality is in well-hidden optional module…
Good call! Had this lingering around for so long, I think I didn’t bother checking this. Looking at the code though … urgh (https://github.com/Kitware/CMake/blob/master/Modules/GNUInstallDirs.cmake).
For what it’s worth, I’ve been playing with the meson build system recently, which takes some inspiration from cmake apparently, but has a much nicer build description language in my opinion. Suffers some of the same shortcomings (ugly option syntax for non-standard options; no ‘make dist’), but those are pretty minor in the grand scheme of things.
I’ve been using CMake a bit recently, too, and I’ve written a few snippets I think you might find helpful:
First, for the cmake CLI, I have a script which provides an autotools-style API which invokes CMake: .
For adding C/C++ flags (such as warning flags), see . It’s an easy way to add C/C++ flags if the compiler supports them.
Using configure-cmake, it would be pretty easy to support a configuration option for fatal warnings, and with AddCompilerFlags it’s easy to add a bunch of extra warning flags. See for an example. It could probably use some improvements for C++, but it’s a good start.
I agree completely about the Find* insanity. That said, I’ve been working on putting together some handy modules for working with GNOME: . I’m not sure if any of these would be helpful for your project or not.
Trying the links again, as they seem to have been eaten:
configure-cmake: https://github.com/nemequ/configure-cmake/
AddCompilerFlags: https://github.com/quixdb/squash/blob/master/cmake/AddCompilerFlags.cmake
ExtraWarningFlags: https://github.com/quixdb/squash/blob/master/cmake/ExtraWarningFlags.cmake
gnome-cmake: https://github.com/nemequ/gnome-cmake
As for the missing “./configure –help”, there is the official ncurses based “ccmake” utility which allows you to interactively browse and set the configuration options available in a project.
Regarding target specific linker flags – you want to check out cmake’s target properties:
– https://cmake.org/cmake/help/v3.0/manual/cmake-properties.7.html#properties-on-targets
– https://cmake.org/cmake/help/v3.0/command/set_target_properties.html
You can invoke cmake with just:
cmake -DCMAKE_INSTALL_PREFIX=/opt/something
still long, but a bit shorter than you posted.
You can try
$ cmake -LAH
for a rough equivalent of `./configure –help`. Unfortunately, this runs cmake to get the list of variables used in the project.
I set CXXFLAGS=”-Werror” before running cmake on Travis in order to make warnings errors, but by default they are not so, in order not to annoy users.
CMake allows to specify include dirs and link libraries per target, e.g. first create an imported library target:
add_library(p::gmp INTERFACE IMPORTED)
set_property(TARGET p::gmp PROPERTY INTERFACE_INCLUDE_DIRECTORIES
${GMP_INCLUDE_DIR})
set_property(TARGET p::gmp PROPERTY INTERFACE_LINK_LIBRARIES
${GMP_LIBRARY})
and then just use for any of your targets:
target_link_libraries(my_library PUBLIC p::gmpxx)
This will only add the -L flags for your my_library target.
My experience with cmake has been that it feels a bit clunky at first, but then usually there is a solution that is clean and robust, it’s just not always obvious.
> I don’t see an equivalent for “./configure –help†to list all generic and project-specific build options.
There are `ccmake’ interactive cli interface and `cmake-gui’ qt-based gui. Doc strings are inserted via `set’ command: https://cmake.org/cmake/help/v3.0/command/set.html
>It’s also a little odd that I cannot specify the link directories (-L flags) per target. link_directories() seems to apply to all targets.
Have you tried this: https://cmake.org/cmake/help/v3.0/command/set_target_properties.html ?