There are many build systems available for software development, and if your software is still using the venerable Make system, it can certainly pay to investigate an upgrade. Kitware’s cross-platform CMake system is one such framework, and it offers a considerable improvement. At its base level, CMake offers a single approach to building your software on nearly any Linux/Unix platform, including embedded systems, Windows, and OS X. The build system relies on a single file in each source directory along with a set of module files that are used to guide package and dependency discovery during the configuration phase.
CMake has its own syntax which is similar to shell scripting, but unfortunately, still slightly different. Nonetheless, it is fairly easy to learn the syntax and begin putting together a basic build system that can be easily extended and modularized. At its core, CMake allows the developer to simply specify compiler options, directory locations, source and include files, library dependencies, build type, and platform-specific options for any given build. A basic CMake install comes with dozens of pre-built modules designed to find required (or optional) dependencies with a simple command, e.g. find_package (MPI). These module files can also be supplemented or overridden by adding a tailored file under the source/cmake/Modules directory. This feature allows a developer to finely tune the search for dependencies based on specifics such as version number, unorthodox install locations, or other special requirements of a given build.
In addition to the find_package command, CMake also provides a find_path command, which can be very helpful in locating individual files nearly anywhere on the system. Each compiled component of the build (library or executable) is a “target” in CMake terminology, and during the configuration stage of the build, CMake will automatically detect source file dependencies within each target. This automatic detection, when combined with developer-defined dependencies between targets, greatly simplifies parallel builds, which can considerably shorten overall compile times.
Beyond improving the portability of your code by automatically finding dependencies and compiler flags, CMake also offers a number of additional benefits. Chief among these is the seamless integration of CTest to provide a framework for both regression and unit testing. Unit testing is the practice of including small, quick tests that ensure individual components or modules of code provide the expected results. In contrast, regression testing usually involves comparing the results of the entire code run under specific scenarios with pre-computed solutions.
In either case, only a few additional lines to the CMakeLists.txt file are required to add a set of unit or regression tests that can be wrapped directly into the build. These tests can be in the form of scripts or involve additional compiled targets for each individual test. For both scenarios, the tests can be launched directly after the build is complete by running a “make test” command in the build directory.
Once regression and/or unit tests have been incorporated into the new build system, CMake provides an excellent platform to support automated builds. Automated builds are an important part of continuous integration used in modern software development, and are generally managed through a server such as Jenkins or Hudson. The automated builds may be configured to occur at regular intervals (daily, weekly, etc.) or upon any new source control commit. Once configured, the automated build system will attempt to build the latest version of the software and test it against the tests defined within CMake. If the code fails in either the build or testing portion, it will be detected by the automated build system, thereby keeping errors from being introduced to the source without notice. Build results are reported in a web-based dashboard interface which allows both managers and fellow developers to quickly and easily check on the status of the code base.
In summary, CMake provides an excellent approach to automated dependency and platform discovery, which makes the whole build process easier and faster for users. Beyond improving the user experience, however, CMake provides a platform to improve best practices for software development such as enabling unit and regression testing and continuous integration. When all these factors are taken into account, converting your build system to CMake becomes an obvious choice for updating your coding practices and improving your software development standards. If you have questions about how this approach may work for you, feel free to reach out.