When I was writing this blog four years ago I wanted to write cross-platform C code that was simple to build. At the time I settled on a dual approach of using a make file for non-windows and a set of Visual Studio 2010 projects and solution files for Windows building. I’ve never liked the dual approach but at the time I did not have anything better.
These days I use CMake as my build system. To be technically correct CMake is not a build system, it is a builder of build systems. This is what makes it great. Rather than being locked into a particular system, you write all your build files and dependencies in a language separate from the build system. CMake then will generate the build files for your chosen build system.
Something that CMake is particularly good at is building “out-of-source” builds. Something that Visual Studio is awful at by default. The standard method of Visual Studio is to scatter all the build artefacts throughout your source tree. This is particularly annoying for source control (you end up with tons of rules in .gitignore to avoid committing binaries and temporary files), and also makes it hard to have a clean build as you never know for sure what left overs may be lurking around affecting the build. With CMake you can just delete your build tree at anytime and generate a new one. You can also have many build trees in parallel. For example you may wish to build using Visual Studio and MinGW.
By default CMake will generate files with the most obvious builder it can find on your system. So if you are running Windows and have Visual Studio it will use that. If you are on Linux it will probably choose gcc and make. However you can tell it to generate a specific build system, and there is a large list of supported ones. In particular for Linux and MacOS I typically like it to build using Ninja which is lightning fast compared to make. Occasionally I get it to generate Xcode projects for MacOS.
Something else I particularly like about CMake is the concept of Toolchain files that can be specified on the command line using the (not especially elegant) syntax of:
It will this run this file first before attempting to locate the compilers for your system. With this you can setup cross-compilers or other custom settings you want without cluttering up your in source CMake files. Also you don’t want to put hard coded paths and settings into your actual project that you are planning to share.
So for example I like to keep my CMakeLists.txt files as simple and clean as possible and put all my custom stuff in my dev environment outside of the source trees. So if someone builds one of my projects it should always work with their system as it will just use the defaults. For example if they are using Visual Studio it will build it using fairly standard Visual Studio settings. I don’t actually like Visual Studio default builds, in particular I don’t like its dependent on its own C runtimes that you then have to distribute. So I use a toolchain file that builds using WinDDK 7.1 which links to MSVCRT.DLL. I also have other toolchain files for building on all the other platforms I want to do, including MinGW, Linux, MacOS, and even a Big-Endian mips Linux system!
I have made a sample “Hello World” project that can be built with CMake. A zip file containing the source is here
Assuming you have CMake installed and in the path then simply open a terminal at the root of the sample directory and run the following
cmake -H. -Bbuild -DCMAKE_INSTALL_PREFIX=bin cmake --build build --target install
This will create the build system and build the binaries and place them in the subdirectory bin
The first command generates the build system for your platform. In Windows this will probably be Visual Studio, in Linux this will probably be gcc and make. The parameter -H. says “source file starts here”. -Bbuild says “put the build system into the subfolder build“. The -CMAKE_INSTALL_PREFIX=bin says “When building with --target install, make the directory the subfolder bin”
The second command says “build using the build system in subdirectory build and place the executables in the install location specified before (bin)”
It is highly recommended to always perform an “out-of-source” build. You can however build “in-source” with the following simpler commands:
cmake . cmake --build .
This will however places the builds files all over the place in the directory and is generally considered messy. However if you are just wanting to get the binaries build with the least fuss, and are not planning to actually to use the source files for coding then this is the quickest way. In this example we did not specify the install location. The binaries will be built in the source tree and not copied to the install location.
I have since converted all my previous projects to using CMake. I will soon be publishing a newer version of CryptLib that uses CMake (and also has the new AES and AES-CTR cipher in it).