Until this week I was not familiar with qmake and CMake because I am a fairly satisfied user of autotools. I have not forgotten how strange it was when I first learned it, when there was no decent documentation, but things are much better now. I feel at home with it and I like how other systems and distros expect the standard “configure;make all install” steps. I am definitely biased against the use of other build systems.
However, these days I have to deal with code that uses qmake and CMake and I can’t just be stubbornly ignorant. So I tried an experiment. I created branches of a little (but real-world) project that uses Qt and an extra library via pkg-config: qlom with qmake, and qlom with cmake. The master branch uses non-recursive autotools.
I was not impressed. Please do add comments to correct me. I’d particular like patches that make my errors crystal clear and prove that it’s all much better than I think. I am capable of admitting error and changing my mind when appropriate.
Bad Documentation. Weird Syntax
Just like autotools, neither were easy to get started with. The documentation is fragmented, unclear, and incomplete. I found qmake easier than CMake, but that’s probably just because qmake does much less. Nevertheless, CMake has a real problem with documentation and in general, as a widely-used open-source project, it deserves a better infrastructure.
They both suffer from one major problem shared by autotools: They have evolved over enough time that Google will happily return out-of-date examples and documentation, making the syntax seem more varied than it really is. I wish that autotools could force me not to use deprecated syntax. I don’t know if qmake and CMake can.
As with autotools there’s a heavy dose of arbitrariness:
- File names: autotools’s configure.ac and Makefile.am seem more logical than SomeProject.pro (qmake) or CMakeLists.txt (CMake)
- Syntax: m4 (used by autotools) is a crappy language, but at least it’s a mature one that’s used outside of autotools. I understand the wish to have no dependencies, but “invent a programming language” is always a bad design decision.
And autotools don’t require anything but make when building from a tarball. qmake and CMake seem to require that they are installed even when building from a tarball. I would have liked to see Python used because it’s easily available on all machines that would create source tarballs.
CMake’s non-case-sensitive function names and keywords are personally annoying to me, because I like code to have a consistent style.
No Respect For pkg-config
I am dismayed that CMake expects people to write code for “Find” modules for each library they might use, while having poor support for the generic pkg-config solution. A project’s list of dependencies is data, not an excuse to write code. More code means more problems.
In fact, after several days of trying, I still can’t figure out how to use pkg-config with cmake, so my glom_cmake branch is not finished yet.
Most libraries provide the simple .pc files these days. The autotools PKG_CHECK_MODULES() macro makes it very easy to check for several dependencies at once. CMake feels too much like the bad old 90s when we suffered fragile copy-paste-hacked config scripts, custom m4, macros and verbose build files with separate CFLAGS and LIBS variables for each dependency, and inconsistent release-version and API-version checking.
I understand that pkg-config is not popular on Windows or MacOS, and that those are awkward platforms to work with, but:
- The real solution to poor dependency information on Windows and MacOS is to encourage the use of pkg-config, so it can fix the problem there like it fixed the problem for Linux. That’s entirely possible for the cross-platform libraries that are typically open source. And pkg-config would be easy to port.
- The platform-specific libraries on Windows and MacOs don’t need any cross-platform support in the build system.
CMake’s use of scripts just spreads the chaos to Linux instead of keeping things simple on Linux and improving the other platforms too.
No Configure Stage
With qmake, I really miss having a proper configure stage, with the resulting config.h so my code can use ifdefs. I like seeing a list of documented build options from configure –help, which neither qmake or cmake offer.
CMake can generate a config.h, at least. However,
- There are no default checks, so you must specify the common stuff always, making the build file more verbose.
- You can’t just generate the config.h.in, as you can with autoheader when using autotools. There’s no reason to specify the defines in both CMakeFiles.txt/configure.ac and config.h.in.
- Arbitrarily, the config.h.in uses “#cmakedefine SOMETHING 1” instead of #undef SOMETHING as in autotools (now deprecated, unnecessary) config.h.in files. For anything other than booleans, there’s an undocumented syntax such as #cmakedefine “@PACKAGE_NAME@”.
Install is an Afterthought
Both CMake and qmake require explicit extra syntax to actually install the built executable. With autotools you get this for free when using the regular bin_PROGRAMS to specify the executable name, though libraries do need more explicit instructions to install the correct headers in the correct place.
- With qmake, this typically means that paths are hard-coded in the build files. That way lies pain.
- With CMake, it’s easier, but you have to do “make -DCMAKE_INSTALL_PREFIX=/opt/mystuff” instead of autotool’s simpler ./configure –prefix=/opt/mystuff. autotools lists that option via “configure –help”.
Also, qmake and CMake have no make distcheck for making source tarball releases.
This makes sense slightly because CMake (and maybe qmake) aim to support Windows, Mac, and Linux equally, and Windows has no real convention for installation of built-from-source binaries. CMake (and maybe qmake) generate convenient Visual Studio and X-Code project files, for instance. But the end results it that Linux and Linux conventions are not fully supported, so they feel like major regressions compared to autotools.
(note: removed my nonsense with the TODO about the location of .o files.)
In summary, I think:
- qmake would only be used by Trolltech (now called Qt Development Frameworks) for their own projects, out of habit, or by users of Qt who are so unaware of other systems that they would just do whatever Qt’s developers recommend.
- CMake isn’t simpler, so it doesn’t solve autotool’s steep learning curve. It makes many thinks worse than autotools and its documentation is disconcertingly vague and incomplete. It’s cross-platform dependency checking adds complication without solving the real problem. The best CMake experience is probably with a large monolithic project rather than a large dependency tree of modules. But I like modularity and reuse.
I understand the wish to support Windows and Mac-specific build systems such as Visual Studio and X-Code. I just don’t like how qmake and cmake do that, and for me it’s not worth making things worse on Linux. Still, I’ll have to use these in some existing projects, so I’m glad that I’m more familiar with them now.