Professional CMake:

A Practical Guide

Learn to use CMake effectively with practical advice from a CMake co-maintainer. You can also have the author work directly with your team!

Professional CMake: A Practical Guide 19th Edition

Release Date: 16th August 2024

This is part of the change history for the book Professional CMake: A Practical Guide. The 19th Edition has been updated for the CMake 3.30 release and for Qt 6.7. The main new CMake feature covered in this edition is the new FetchContent behavior that avoids a sub-build. This is a potential big performance gain for large projects with many dependencies. The new translations APIs added in Qt 6.7 are another significant update for this edition.

The book has grown in size, and some parts are fairly verbose. In addition, the number one piece of reader feedback is for more examples. The main focus of this edition has been to begin addressing these two things. The first 18 chapters (parts 1 and 2 of the book) have been reviewed and reworked in this edition, and this work will continue in future editions for the remaining chapters.


Setup Project chapter:

  • Updated examples to use cmake -B build ... instead of manually making and changing to the build directory first.

Building Simple Targets chapter:

  • Added an example to the Executables section for add_executable() showing the effect of the EXCLUDE_FROM_ALL keyword and a motivating case for why you might want to use it.
  • Updated the wording and advice in the Linking Non-targets section. Policy CMP0060 is no longer mentioned, advice for plain library names now cross-references the chapter that talks about requiring target names for linking, and the link flags item advises against using them and to use the dedicated support for adding such flags instead (with cross-references to the relevant sections).
  • The Old-style CMake section now mentions policy CMP0023 when discussing what happens when mixing the keyword and non-keyword forms of target_link_libraries().
  • Updated examples to use cmake -B build ... instead of manually making and changing to the build directory first.

Basic Testing And Deployment chapter:

  • Added a brief mention of the CMAKE_TEST_ARGUMENTS variable when discussing how to run ctest via the test build target.
  • Updated examples to use cmake -B build ... instead of manually making and changing to the build directory first.

Variables chapter:

  • The String Handling and Lists sections were reworked to have less text, and to use more examples instead of formal command syntax for string() and list(). Related examples were also combined to reduce verbosity, and these sections now avoid repeating a lot of the command details that are already clearly explained in the official CMake reference documentation for these two commands.

Flow Control chapter:

  • Added an example demonstrating a common trap with how the if() command can treat an unquoted value as a string or a variable name, depending on whether a variable by that name exists. Guidelines were also added to help avoid running into problems related to this behavior.
  • Added a brief discussion and example for how the if(COMMAND) form can be useful for checking the availability of a command provided by a third party as a technology preview. Simple examples were also added for if(POLICY)and if(TARGET).
  • Mention that if(TEST) can only be used if policy CMP0064 is set to NEW.
  • Updated the example for if(IN_LIST) to also show the older, more verbose logic based on list(FIND) for when policy CMP0057 cannot be assumed to be NEW.
  • Updated the example in the Interrupting Loops subsection to use set(s "") instead of unset(s). This is more robust, since it doesn’t rely on there not being a cache variable named s.
  • The Recommended Practices section was updated to explicitly mention ensuring policy CMP0054 is set to NEW as part of setting the minimum CMake version.

Functions And Macros chapter:

  • Updated examples involving cmake_parse_arguments() to use lowercase arg as the variable prefix instead of other uppercase values like ARG. This makes it easier to visually spot any use of the resultant variables instead of them looking like cache variables or keywords.
  • Discussion and examples of tracking deferred commands using cmake_language(DEFER) via their ID have been removed. This is a very advanced topic that few projects need, and it made the rest of the material related to cmake_language(DEFER) seem more complicated. That part of the Other Ways Of Invoking CMake Code section now focuses on the more common uses of cmake_language(DEFER).
  • Added a paragraph to the Recommended Practices section warning against implementing overly complex keyword and argument parsing with cmake_parse_arguments(). The advice highlights that this is often a sign of a function or macro having too many responsibilities.

Properties chapter:

  • Added examples to the General Property Commands section showing calls to set_property() and get_property() for the various property types. Examples were also added showing how the inheriting behavior of get_property() works for target and directory properties, including how it interacts with set_property().
  • Added an example to the Global Properties section for get_cmake_property().
  • Added an example to the Directory Properties section to demonstrate the pros and cons of set_property(DIRECTORY) versus set_directory_properties(). An example was also added for get_directory_property().
  • Added examples to the Target Properties section for set_target_properties() and get_target_property(). An explanation was also added for an important difference in behavior between get_property(TARGET) and get_target_property() for an unset property.
  • Added an example to the Source Properties section for set_source_files_properties(), showing how to handle calls in a different directory scope. This section also now highlights behavior differences between get_property(SOURCE) and get_source_file_property() for an unset property.
  • Added an example to the Cache Variable Properties section showing all cache variable properties being set by their more typical commands.
  • Added an example to the Test Properties section, showing how to manipulate test properties across different directories.

Generator Expressions chapter:

  • Mention that $<CONFIG:...> is a case-insensitive comparison.
  • Added examples for $<TARGET_PROPERTY:...> and $<TARGET_FILE_NAME:...> to the Target Details section.
  • Added an example to the List Expressions section showing a few commonly used sub-expressions for $<LIST:...>.
  • Added an example to the Utility Expressions section for $<COMMA>. The section was also updated for the new $<QUOTE> generator expression added in CMake 3.30.

Policies chapter:

  • Added an example to the Policy Control section showing how to use CMAKE_POLICY_DEFAULT_CMPxxxx variables to enable the new FetchContent direct population behavior globally.

Debugging And Diagnostics chapter:

  • Added an example to the Tracing Variable Access section showing how to define a command to receive variable_watch() events.
  • Added mention of Perfetto to the list of tools for viewing profiling information mentioned in the Recommended Practices section.

Build Type chapter:

  • Updated an example in the Common Errors section that shows wrong use of CMAKE_BUILD_TYPE to set a compiler definition, and added a second example showing how to do it correctly using a $<CONFIG:...> generator expression.
  • Updated the examples and text in the Custom Build Types section to no longer show enforcing the configuration to be one of a predefined set of values. Developers should be free to add their own new build types.
  • Updated examples to use cmake -B build ... instead of manually making and changing to the build directory first.

Compiler And Linker Essentials chapter:

  • Updated an example in the Linker Flags section to use set_property(TARGET) instead of set_target_properties() so that it can use the APPEND keyword to add to the existing linker options, not replace them.
  • Added a discussion and example to the Linking Libraries subsection explaining the behavior of PRIVATE with static and object libraries.
  • Slightly reordered parts of the Linker Options subsection to improve the flow.
  • Added an example and additional discussion to the Header Search Paths subsection for the interaction between generator expressions and the use of relative paths with target_include_directories().
  • Added a target_compile_definitions() example to the Compiler Defines subsection.
  • Added an example to the Compiler Options section to highlight how some things can safely be set conditionally using if() logic, but others must use generator expressions.
  • Added a number of examples to the Directory Properties And Command section to highlight the different behavior of properties and commands regarding their effect depending on where they are set or called (before or after a target is created). An example was also added to show the usefulness of generator expressions for setting language-specific options with add_compile_options().
  • The discussion of the debug, optimized, and general keywords for link_libraries() in the Directory Properties And Command section was significantly reduced, and the DEBUG_CONFIGURATIONS global property is no longer mentioned. These are very old features that projects should no longer need or use.
  • The System Header Search Paths subsection contains an example that uses FetchContent. Previous versions had a call to FetchContent_MakeAvailable() with no argument. This has now been corrected to include the missing anotherdep argument.
  • A paragraph was added to the Runtime Library Selection subsection for the new VS_USE_DEBUG_LIBRARIES target property and CMAKE_VS_USE_DEBUG_LIBRARIES variable added in CMake 3.30.

Language Requirements chapter:

  • The <LANG>_STANDARD part of the Setting The Language Standard Directly section was rewritten to use tables to show which CMake versions support which language versions. The previous editions had become hard to read as more standards and versions were added, and the new tables are much easier to digest at a glance. Details for the HIP language are now also included in the tables.
  • The Requirements For C++20 Modules section was updated to make clear that an error is generated in the consuming project rather than the exporting project if a cxx_std_20 compile feature or higher isn’t set on an exported target.

Advanced Linking chapter:

  • CMake 3.30 added some improvements around combinations of link features. A brief note was added about this to the Custom Features subsection.

Custom Tasks chapter:

  • Added a brief paragraph to the end of the Commands That Generate Files section, mentioning the relevance of the CMP0118 and CMP0163 policies for generated files.

Working With Files chapter:

  • CMake 3.30 added support for a TLS_VERSION keyword for file(UPLOAD) and file(DOWNLOAD), and an associated CMAKE_TLS_VERIFY environment variable and CMake variable. The Downloading And Uploading section has been updated accordingly.

Libraries chapter:

  • The last paragraph in the Additional Complications With C++20 Modules subsection was updated to highlight that any files a module interface file depends on must also be installed, such as the algo_export.h header of the preceding example.

Toolchains And Cross Compiling chapter:

  • Updated the Compiler Checks section to make clear that the CMAKE_TRY_COMPILE_TARGET_TYPE variable should only be set in a toolchain file, not by a project.

Build And Test Mode chapter:

  • Updated examples to use cmake -B build ... instead of manually making and changing to the build directory first.

Installing chapter:

  • In the Runtime Dependency Sets subsection, the install(RUNTIME_DEPENDENCY_SET) example for Windows contained a robustness issue. The POST_EXCLUDE_REGEXES regular expression assumed paths only contained forward slashes (/). But CMake doesn’t guarantee this, and in fact sometimes it produces paths with mixed path separators. The example has been updated to handle both forward or backslashes as path separators. A paragraph was also added after the example to highlight this behavior.

Package Generators chapter:

  • The default behavior of the CPACK_WIX_INSTALL_SCOPE variable was reverted late in the CMake 3.29 release series (3.29.5). The default was changed again in CMake 3.30, but only for the newly supported WIX 4 version. Issues were also found when updating packages installed with an older cpack version if the install scope was changed, requiring projects to manually alert their users to the problem. These things are now discussed at the end of the WIX section.

FetchContent chapter:

  • In the opening paragraph, added 3.30 as one of the CMake versions with significant improvements for FetchContent.
  • The note at the end of the Basic Usage section was updated to highlight that the old manual population pattern is now officially deprecated as of CMake 3.30.
  • A new Avoiding Sub-builds For Population section has been added. CMake 3.30 gained the ability to avoid using a separate sub-build to fetch each dependency. It can now use a much more efficient method that directly executes as part of the main project. Performance gains for large projects with many dependencies can be substantial for Windows and Xcode builds. The Recommended Practices section was also updated with a recommendation to use this new behavior.
  • The Developer Overrides section was reordered slightly to improve the logical flow.

Presets chapter:

  • CMake 3.30 added presets schema version 9. This has been added to the corresponding version table in the High Level Structure section.

Working With Qt chapter:

  • Some Qt-provided translation commands for CMake have been in technology preview since early in the Qt 6 timeframe. Qt 6.7 made them official, but at the same time deprecated them in favour of better replacements. Examples throughout this chapter have been updated to use Qt 6.7 as their version for consistency with the newer APIs.
  • The Translations section was substantially reworked to reflect the new form of the qt_add_translations() command, and the associated changes to the qt_standard_project_setup() command. The old form is still discussed, but in less detail.
  • The Qt Deployment Tools subsection was updated to mention the new DEPLOY_TOOL_OPTIONS keyword for qt_generate_deploy_app_script(), which was added in Qt 6.7.
  • The Deploying Translation Files subsection and the Recommended Practices section were also updated to reflect the new API added in Qt 6.7.