
Joseph Sibony
reading time:
On 31st August 2020, CMake celebrated its 20th Birthday. CMake has taken the software world by storm. It is estimated to be used by at least 50% of all C++ projects as their build system. CMake versions after 3.0 are called Modern CMake (analogous to C++11 and afterward being known as ‘modern’ C++) and this document gives some tips and tricks to use modern CMake. Learn more about what is CMake. Also, to better understand CMake and its history, and for a comparison with Make check out our CMake vs. Make comparison.
The most important tip: use modern CMake. If your project is still using CMake versions below 2.6, spend some time and effort to move to the newer versions. The thumb rule is to use the version of CMake that came after your compiler version.
CMake is no longer just about C++. CMake is constantly growing support for more languages and as on CMake 3.8, has added support for C# and CUDA. It already supports languages C/C++, Java, Objective C/C++, Swift and Fortran.
This is more of a caution than a tip. Don’t manually append -std=C++11 to CMAKE_CXX_FLAGS. This practice is vestiges of old school CMake. For modern CMake use CXX_STANDARD and CXX_STANDARD_REQUIRED flags instead.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

Prevent in-source builds by explicitly disallowing it in your top level CMakelists.txt. In-source builds pollute the source directory with build related artifacts. You can use
if ( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} )
message( FATAL_ERROR “In-source builds not allowed! Create a build directory and run CMake from there. ” )
endif()
While using the recommended out of source builds in CMake, use the documented -S and -B flags to specify the source and build directory. Older versions used undocumented -H and -B flags to specify the same.
cmake -S . -B build -G “Visual Studio 16 2019” à supported by CMake 3.13+, recommended approach. cmake -H. -Bbuild -G “MSYS Makefiles” à For older CMake versions. No space between the -B flag and folder name!
Presets are a way to specify a collection of CMake options using a file. For complex projects that support multiple platforms, it is always a good idea to provide (also as a user to use) CMake preset files. Presets make sure that multiple configurations across different compiler tool chains and packages are consistent. Preset files are loaded and used by passing the -C flag and is a best practice to provide them in cmake/presets folder in the source directory.
It is always a best practice to run linter against the codebase to report issues that the compiler misses. A central place to run linter – as against the IDE of programmer – is to integrate linting with CMake. For C/C++ based projects, CMake supports clang-tidy natively starting from version 3.7.2. Enable treating warnings as errors in the CI builds to keep technical debts in check.
Formatting code is akin to religion where developers have strong opinions. But it is always a good practice, more so when multiple developers are involved, to enforce a consistent formatting style. For C++ projects clang-format can easily be integrated to CMake.
Whether you decide to use CTest or Google test framework, it is always a good idea to make sure your tests are executed as a part of the continuous integration process. It is even possible as a post build step (POST_BUILD) to run the tests in the compilation time too.
It is not just old school to use include_directories but using the alternatives target_include_directores and target_link_libraries will enforce the design of your project. It is for a good reason that CMake has PRIVATE, PUBLIC and INTERFACE keywords, using them correctly is the key to maintain unidirectional layering of components in your project.
Custom commands can be created in CMake using both macros and functions. Macros introduce no extra scope to the variables but functions do. Macros are useful for wrapping commands that have output parameters and in all other cases use functions.
CMake natively supports graphviz of dependencies. The output dot file can easily be viewed by using programs like ZGRViewer.
Give the same attention that you give to production code to CMakeLists.txt too. Make sure it is liberally commented and all the modern CMake best practices are followed.
Here is a nice table describing the use of PRIVATE, PUBLIC and INTERFACE compiler dependencies mean in CMake
| Dependency | Description |
| PRIVATE | Needed by me, but not by my dependents |
| PUBLIC | Needed both by me and my dependents |
| INTERFACE | Not needed by me, but my dependents |
Findpackage is a CMake or user supplied search script that knows how to look for a package. The modern way for a package author is to supply a <package>Config.cmake. If your package is installed, PackageConfig can provide its details to CMake.
During a CMake build, pass –parallel flag with a number of parallel jobs as an option. Remember that not all build generators support –parallel option for a faster build. This option is supported by CMake 3.12 onwards. When using distributed compilation solutions to accelerate your CMake build, such as Incredibuild, you’ll want to set the –parallel flag to a very large number, such as 300, instructing CMake to execute as much as 300 tasks to execute concurrently, which will allow Incredibuild to distribute up to 300 tasks to remote idle cores – resulting in a much faster compilation.
This is more of an advice than a tip. CMake is heavily battle-tested and if you have decided to use or migrate to CMake, don’t give up! For a newbie, CMake can be intimidating and frustrating. CMake is open source and the community is very helpful. The reference documentation is very nicely written for CMake. Just don’t give up, help is at hand.
CMake is a great build system generator supported on all major platforms. Visual Studio 17 natively supports CMake and Qt is growing CMake support from Qt6.0. This shows how successful CMake is! The above tips and tricks should guide you in your endeavor. Best of luck working with CMake!

Table of Contents
Shorten your builds
Incredibuild empowers your teams to be productive and focus on innovating.
Incredibuild empowers your teams to be productive and focus on innovating.
| Cookie | Duration | Description |
|---|---|---|
| cookielawinfo-checkbox-analytics | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics". |
| cookielawinfo-checkbox-functional | 11 months | The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional". |
| cookielawinfo-checkbox-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
| cookielawinfo-checkbox-others | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other. |
| cookielawinfo-checkbox-performance | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance". |
| viewed_cookie_policy | 11 months | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |