(CMake Series) Part 7 - Common Variables, Functions, and Modules
After using CMake for quite some time, I have recorded a lot of knowledge in my notebook. Actually, this knowledge should have been introduced at the beginning of this series, as it includes some fundamental aspects. Here, I will provide a brief summary.
During Configuration
Generating Configuration Files
1 | configure_file("${PROJECT_SOURCE_DIR}/include/config.h.in" |
For example, you can pass the version set in the CMake project
command to your program using this method:
1 | // config.h.in |
By using two @
symbols, you can transfer variables from CMake to the program you need to compile.
Guard Against Accidental Builds in Source Directory
To prevent building and modifying in the source directory, you can error out if a build is accidentally started in the current directory, avoiding contamination of the source code:
1 | set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) |
Finding Third-party Libraries
This requires the use of CMAKE_FIND_ROOT_PATH
and CMAKE_PREFIX_PATH
, as CMake will look for libraries in the system’s default locations. If you need to use libraries located elsewhere, you can add them to this variable.
Also, for individual libraries, you can use these two variables:
<PackageName>_ROOT
: To specify the path of headers, libraries, and executables;<PackageName>_DIR
: To specify the path to the library’s CMake file;
During Compilation
Compilers
If there are multiple compilers or versions available in the system, you can set the following two variables for C and C++ compilers:
1 | CMAKE_C_COMPILER=/path/to/gcc |
FLAGS
To customize build configurations, you can also set the following variables. Most of the time, you don’t need to configure them, as CMake will automatically configure based on the environment and other variables:
1 | CMAKE_C_FLAGS= |
Features
If you need the above FLAGS
to configure -std=c++17
, that’s also unnecessary. You can set other variables to achieve this purpose, such as globally:
1 | set(CMAKE_CXX_STANDARD 17) |
And a more recommended local approach:
1 | add_library(myTarget lib.cpp) |
Additionally, if you want -fPIC
, there’s a specific variable for that: CMAKE_POSITION_INDEPENDENT_CODE
, which, of course, is also recommended to be set locally:
1 | set_target_properties(myTarget PROPERTIES POSITION_INDEPENDENT_CODE ON) |
Preprocessor Definitions
1 | add_compile_definitions(-DTEST) # Globally |
Build Types
The commonly used compilation categories CMAKE_BUILD_TYPE
are Release
and Debug
. Due to different compilation environments, this value will also be constrained. For reference, you can also have RelWithDebInfo
and MinSizeRel
.
This variable determines whether the compilation will be optimized and include debugging information. Do not release your company’s private programs in Debug mode, as the code will be unoptimized, performance will be poorer, and it could leak source code.
Dynamic and Static Libraries
You need to use the BUILD_SHARED_LIBS
variable, which is often used in option
to allow users to configure. This variable controls whether add_library(myLib ...)
will generate a static or shared library.
Perhaps you might wonder why there is no BUILD_STATIC_LIBS
; by default, it is static
, which is equivalent to BUILD_SHARED_LIBS=OFF
.
Moreover, there’s a little trick if you need to compile both dynamic and static libraries at the same time:
1 | add_library(myLib STATIC lib.cpp) |
Useful Modules
ExternalProject doesn’t need much introduction, as it was specially covered in 【CMake Series】(3) ExternalProject Practice.
Note: These modules need to be included before they can be used.
CMakePrintHelpers
Very suitable for debugging, cmake_print_variables
helps you print the values of variables and cmake_print_properties
can print some properties of targets.
Below is an example of printing out the include
paths:
1 | cmake_print_properties(TARGETS foo bar PROPERTIES |
WriteCompilerDetectionHeader
Sometimes, to write cross-platform code, we need to determine if a compiler supports certain features. CMake provides this module, which helps you generate a predefined header file, listing all the supported compiler features:
1 | write_compiler_detection_header( |
Here are more compiler features:
- cxx_constexpr
- cxx_deleted_functions
- cxx_extern_templates
- cxx_variadic_templates
- cxx_noexcept
- cxx_final
- cxx_override
FeatureSummary
This module is suitable at the end of project initialization, to print out some summary information:
1 | feature_summary(WHAT ALL) |
Then, you can add more descriptions to this summary:
1 | set_package_properties(LibXml2 PROPERTIES |
This way, you can see more summary information.
Installation
This is very straightforward, yet very common. To avoid confusion for beginners, here’s a tip:
Simply set CMAKE_INSTALL_PREFIX
, and when you execute the installation command, the corresponding files will be installed in the designated directory.
Conclusion
Above, I’ve briefly listed what our team commonly uses, also chosen by me after screening, because so many features cannot possibly be listed in one article. For details, you still need to refer to the official CMake documentation. Additionally, most examples in the text are also from the CMake official documentation.
First published on GitHub issues: https://github.com/xizhibei/blog/issues/140, welcome to Star and Watch
This article is licensed BY-NC-SAAuthor: 习之北 (@xizhibei)
Original link: https://blog.xizhibei.me/en/2020/06/02/cmake-7-common-var-func-and-modules/