Word count: 7897, approximately 40 minutes of reading time
Comprehensive Guide to CMakeLists (Part 1)

0. CMake Application Examples
Previously, we organized content on using CMake to introduce third-party libraries (header file directories, library directories, library files). However, the content organized here is not complete. Therefore, we need to further organize the usage of CMake for future engineers to reference and query.
cmake-template
├── CMakeLists.txt
└── build
└── include
└── src
└── main.cpp
1. Basic CMakeLists Examples (15)
# Set the minimum required version of CMake
cmake_minimum_required(VERSION 3.15)
# Set the project name and version
project(MyProject VERSION 1.0 DESCRIPTION "An example project with CMake")
# Options, can be defined via -D on the command line
option(USE_CUSTOM_LIBRARY "Use a custom library" ON)
# Define conditional preprocessor macros
if(USE_CUSTOM_LIBRARY)
add_definitions(-DUSE_CUSTOM_LIB)
endif()
# Find external dependencies
find_package(Threads REQUIRED)
# Specify the search path for header files
include_directories(${PROJECT_SOURCE_DIR}/include)
# Specify the search path for library files
link_directories(${PROJECT_SOURCE_DIR}/libs)
# Add subdirectories, which should also have their own CMakeLists.txt
add_subdirectory(src)
add_subdirectory(libs)
# Add an executable
add_executable(myExecutable src/main.cpp)
# Add a static library
add_library(myStaticLib STATIC src/myStaticLib.cpp)
# Add a shared library
add_library(mySharedLib SHARED src/mySharedLib.cpp)
# Set properties for static and shared libraries
set_target_properties(myStaticLib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/static_lib"
)
set_target_properties(mySharedLib PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/shared_lib"
VERSION ${PROJECT_VERSION}
)
# Set properties for the executable
set_target_properties(myExecutable PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
OUTPUT_NAME "myExecutableFinal"
)
# Specify linked libraries
target_link_libraries(myExecutable PRIVATE myStaticLib mySharedLib Threads::Threads)
# Installation rules
install(TARGETS myExecutable myStaticLib mySharedLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static
)
# Include directories
target_include_directories(myExecutable PRIVATE ${PROJECT_SOURCE_DIR}/include)
# Custom commands and dependencies
add_custom_target(run
COMMAND myExecutable
)
add_dependencies(run myExecutable)
# Macro definition for printing messages
macro(print_details)
message("Project details: ${PROJECT_NAME} version ${PROJECT_VERSION}")
endmacro()
print_details()
# File operation example
file(GLOB_RECURSE MY_SOURCES "src/*.cpp")
target_sources(myExecutable PRIVATE ${MY_SOURCES})
# Generate configuration file
configure_file(config.h.in ${CMAKE_BINARY_DIR}/config.h)
# Source file directory search
aux_source_directory(. DIR_SRCS)
add_executable(myProgram ${DIR_SRCS})
# Add directory, introduce other projects
add_subdirectory(external)
# Custom target, does not produce output files
add_custom_target(CustomCommand ALL
COMMAND echo "Executing a custom command"
COMMENT "This is a custom build step"
)
# File copy command
file(COPY ${CMAKE_SOURCE_DIR}/data.txt DESTINATION ${CMAKE_BINARY_DIR})
1.1 cmake_minimum_required
Specifies the minimum version of CMake required. It ensures that features from a specific version or higher of CMake can be utilized and guarantees that features that may cause undefined behavior in older versions of CMake are not misused.
Basic syntax:
cmake_minimum_required(VERSION major.minor[.patch][.tweak] [FATAL_ERROR])
-
• The
VERSION
keyword is followed by the required minimum version number. -
•
FATAL_ERROR
is an optional parameter that is required in older versions of CMake. If the user is using a version older than the specifiedVERSION
, CMake will report a fatal error and stop processing. In newer versions of CMake, by default, if the version does not meet the requirements, CMake will report an error and stop.
1.2 project
Declares the project name and version, and optionally specifies supported programming languages. This command is usually located at the top of the CMakeLists.txt
file, immediately following the cmake_minimum_required
command.
Basic syntax:
project(<PROJECT-NAME> [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
[DESCRIPTION <project-description-string>]
[HOMEPAGE_URL <url-string>]
[LANGUAGES <language-name>...])
-
•
<PROJECT-NAME>
is your project name. -
• The
VERSION
keyword can be followed by the project version number. -
•
DESCRIPTION
can add a project description. -
•
HOMEPAGE_URL
can specify the URL of the project homepage. -
• The
LANGUAGES
keyword can be followed by a series of programming language names, such as C, CXX (for C++), CUDA, Fortran, etc. If no language is specified, CMake defaults to enabling C and CXX.
1.3 $
The dollar sign $
in CMake is used with parentheses, in the form ${...}
, to reference the value of variables. The most common usage is in relation to the project, for example:
Basic syntax:
# Set project name and version
project(MyProject VERSION 1.2.3 LANGUAGES CXX)
set(MY_VARIABLE "Hello, World")
message("Project name: ${PROJECT_NAME}")
message("Project version: ${PROJECT_VERSION}")
message(${MY_VARIABLE})
${MY_VARIABLE} will be replaced by the value of the variable MY_VARIABLE. The project
command will create some variables, such as PROJECT_NAME
, PROJECT_VERSION
, etc., which can then be referenced.
1.4 set
In CMake, the set
command is used to define and modify variables. This command is very flexible and is one of the most frequently used commands in CMake scripts, as it can be used to store various types of data, such as strings, file paths, lists (i.e., string arrays), boolean values, etc.
Basic syntax:
set(<variable> <value> [CACHE <type> <docstring> [FORCE]])
<variable>
is the name of the variable you want to set. <value>
is the value assigned to the variable. It can be one or more values (if multiple values, they will be treated as a list). The CACHE
option is used to store the variable in CMake’s cache, which is useful for configurable options, as they remain unchanged across different builds unless modified by the user or project scripts. The <type>
specifies the type of the variable in the cache, such as STRING, BOOL, etc. The <docstring>
is a description of the variable, which usually appears in the CMake GUI. The FORCE
option is used to force overwrite the value in the cache, even if the user has already set that value.
In CMake, the set
command is used to set the value of a variable. Its basic syntax is as follows:
set(my_variable "Hello, World!")
-
1. Set a list variable:
set(my_list 1 2 3 4 5)
-
2. Set a cache variable:
set(my_cached_variable Release CACHE STRING "Set C++ Compiler Flags" FORCE) # Release or Debug
# Unset cache entry
unset(<variable> CACHE)
The CMakeCache.txt
file is used to store cache entries, which is generated during the first build, and subsequent builds will not create this file. When referencing cache entries, it will look for this file and return the value.
When using the set()
command, by default, if the cache entry does not exist in the CMakeCache.txt
file, it will create the cache entry and write it to the CMakeCache.txt
file. If the cache entry exists in the CMakeCache.txt
file, the set()
command will be ignored and will not overwrite the existing cache entry. However, if we want to force overwrite the existing cache entry, we can use the FORCE
option.
-
3. Force set a cache variable:
set(my_cached_variable "Another value" CACHE STRING "Description of my_cached_variable" FORCE)
The above is the basic syntax and usage examples of the set
command. You can set different types of variables according to specific needs, such as regular variables, list variables, or cache variables.
1.5 option
In CMake, the option
command is used to define a boolean type option, which can be used to control certain behaviors or features during the compilation process. Basic syntax:
Basic syntax:
option(<option_variable> "<help_string>" [initial_value])
Where:
-
•
<option_variable>
is the name of the variable for the option to be defined. -
•
"<help_string>"
is a description of the option, which will be displayed in the CMake GUI or command line help information. -
•
[initial_value]
is the initial value of the option, optional, default isOFF
.
Below is an example of defining a switch option that indicates whether to enable a certain feature:
option(ENABLE_FEATURE "Enable some feature" ON)
1.6 add_definitions
In CMake, the add_definitions
command is used to add predefined macro definitions to the compiler. These macro definitions will take effect when compiling source files. Basic syntax:
add_definitions(-D<macro1> [-D<macro2>] ...)
Where:
-
•
-D<macro>
indicates the macro to be defined, with-D
followed by the name of the macro. If the macro needs to have a value, it can be separated from the macro name using an equal sign (=
).
Below are some examples of using the add_definitions
command:
-
1. Define a simple macro:
add_definitions(-DENABLE_LOGGING)
-
2. Define a macro with a value:
add_definitions(-DDEBUG_LEVEL=2)
-
3. Define multiple macros:
add_definitions(-DENABLE_FEATURE_A -DENABLE_FEATURE_B)
Using add_definitions
allows you to pass predefined macros to the compiler, which will take effect when compiling source files. It is important to note that macros defined using add_definitions
will apply to all source files in the project.
1.7 find_package
When developing a project using CMake, the find_package()
command is very important as it helps you find and use external libraries, simplifying the configuration and management of libraries. Suppose you are developing a project and suddenly need to use an external library, such as Boost, SQLite, or OpenCV. Manually setting the paths for these libraries, header files, and linking library files is a lot of work, especially in cross-platform development. Fortunately, CMake provides the find_package()
command, which can automatically find these libraries and configure the relevant paths.
Basic syntax
find_package(<package_name> [version] [EXACT] [QUIET] [MODULE] [REQUIRED]
[COMPONENTS [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])
Parameter description
-
•
<package_name>
: The name of the package to find. -
•
version
: An optional parameter used to specify the version of the package. -
•
EXACT
: An optional parameter that requires the version of the package found to match the specified version exactly. -
•
QUIET
: An optional parameter that suppresses error messages if the package cannot be found. -
•
MODULE
: An optional parameter indicating that the package to be found is a module file rather than a package. -
•
REQUIRED
: An optional parameter that generates an error message and stops the configuration process if the package cannot be found. -
•
COMPONENTS [components...]
: An optional parameter used to specify components of the package. -
•
OPTIONAL_COMPONENTS components...
: An optional parameter used to specify optional components of the package. -
•
NO_POLICY_SCOPE
: An optional parameter indicating that CMake should not modify CMake policies.
Usage examples
-
1. Find the Boost library, requiring at least version 1.70 (version >= 1.70):
find_package(Boost 1.70 REQUIRED)
This line of code tells CMake, “Help me find the Boost library, at least version 1.70, and report an error if it cannot be found.” If CMake successfully finds Boost, it will automatically set some variables, such as:
Then, you can pass this information to CMake to handle the compilation process. For example, if you have a target
my_target
(such as the executable or library you want to compile), you can tell CMake thatmy_target
needs to use Boost:# Tell CMake that my_target needs to use Boost's header files target_include_directories(my_target PRIVATE ${Boost_INCLUDE_DIRS}) # Tell CMake that my_target needs to link the Boost library target_link_libraries(my_target PRIVATE ${Boost_LIBRARIES})
This code means:
target_include_directories()
: Add the path to Boost’s header files to the compiler’s search path, ensuring that the compiler can find Boost’s header files during compilation.target_link_libraries()
: Link the Boost library tomy_target
, ensuring that the generated program can correctly link to the Boost library.
-
•
Boost_INCLUDE_DIRS
: Path to Boost’s header files -
•
Boost_LIBRARIES
: Path and name of Boost’s library files
2. Find the OpenSSL package, requiring it to be found:
find_package(OpenSSL REQUIRED)
3. Find the MPI package, specifying the C++ component:
find_package(MPI REQUIRED COMPONENTS CXX)
4. Find the HDF5 package, but do not require it to be found:
find_package(HDF5 QUIET)
if(HDF5_FOUND)
message(STATUS "HDF5 found: ${HDF5_VERSION}")
else()
message(STATUS "HDF5 not found")
endif()
5. Find the PythonInterp package, specifying the version, and print a warning if it cannot be found:
find_package(PythonInterp 3.6 REQUIRED)
if(NOT PythonInterp_FOUND)
message(WARNING "Python 3.6 or later is required!")
endif()
6. Find the CUDA package, specifying the version and specific components:
find_package(CUDA 11.0 REQUIRED COMPONENTS Runtime)
Custom path for finding dependent libraries
In actual development, some libraries are not installed in the system’s default paths (such as /usr/lib
or /usr/local/lib
), which causes find_package()
to be unable to find them. In this case, you need to tell CMake where to look for these libraries.
Default search order for find_package()
-
1. User-specified hint paths: If the
HINTS
orPATHS
parameters are used infind_package()
, these paths will be prioritized. -
2. Globally configured paths: CMake will prioritize searching globally specified paths:
-
•
CMAKE_PREFIX_PATH
: This is a global variable that can be configured via the command line or in CMakeLists.txt. -
• Environment variables specific to library support. For example: Boost supports the
BOOST_ROOT
environment variable. OpenCV supports theOpenCV_DIR
environment variable.
3. Library’s own configuration file paths: If the library is built using CMake, it usually comes with <PackageName>Config.cmake
or <lowercasename>-config.cmake
files.
4. Standard system paths: If none of the above paths are found, CMake will search the system’s default library paths:
-
• Linux:
/usr/lib
,/usr/local/lib
-
• macOS:
/opt/local/lib
,/usr/local/lib
-
• Windows:
C:/Program Files/
,C:/Program Files (x86)/
If no suitable library is found in the above paths, CMake will report an error and stop the configuration process.
How to solve
Simply tell CMake the custom paths:
-
1. Use HINTS or PATHS to provide path hints:
find_package(MyLib REQUIRED HINTS /custom/path/to/mylib)
-
•
HINTS
provides additional search paths to CMake, which have a higher priority than the system default paths. -
•
PATHS
is used to specify explicit paths, which have a higher priority.
2. Set the CMAKE_PREFIX_PATH variable:
set(CMAKE_PREFIX_PATH "/custom/path/to/mylib")
find_package(MyLib REQUIRED)
Summary
-
• Using
find_library()
andfind_path()
can help you find specific library files and header files, especially when the library does not provide aConfig.cmake
file. -
find_library()
is used to find library files, whilefind_path()
is used to find header files.
Using package managers
Using package managers (such as vcpkg and Conan) can make it easier to manage external dependencies, automatically downloading, installing, and configuring libraries, simplifying the development process. With package managers, you can avoid manually handling library installations and path configurations.
-
1. vcpkg: A cross-platform package manager that simplifies library installation and management.
vcpkg install opencv
Set the vcpkg toolchain file in CMake:
set(CMAKE_TOOLCHAIN_FILE "path_to_vcpkg/scripts/buildsystems/vcpkg.cmake") find_package(OpenCV REQUIRED) target_link_libraries(my_target PRIVATE ${OpenCV_LIBS})
-
2. Conan: Another popular C++ package manager.
pip install conan conan install boost/1.70.0@
Use Conan in CMake files:
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup() target_link_libraries(my_target PRIVATE Boost::Boost)
-
3. Using Git submodules: Add external dependency libraries as Git submodules to the project, automatically downloading and building them.
git submodule add <https://github.com/boostorg/boost.git> external/boost git submodule update --init
Introduce submodules in CMake:
add_subdirectory(external/boost) target_link_libraries(my_target PRIVATE boost)
Through these methods, you can manage external library dependencies in your project more efficiently, reducing the complexity of manual configurations.
1.8 include_directories
The include_directories
command is used to add header file search paths to a CMake project, allowing you to reference header files directly using relative paths or filenames during the compilation process. Below is the basic syntax and detailed usage examples, using OpenCV and Boost libraries as examples:
Basic syntax:
include_directories([AFTER|BEFORE] [SYSTEM] directory1 [directory2 ...])
Where:
-
•
AFTER
andBEFORE
are optional parameters used to specify the order of adding directories, defaulting toAFTER
, meaning added after other include directories. -
•
SYSTEM
is an optional parameter used to mark the header files in these directories as system header files, which the compiler may ignore warning messages from. -
•
directory1
,directory2
, … are the header file search paths to be added.
Add the header file search path for OpenCV:
# Find the OpenCV package
find_package(OpenCV REQUIRED)
set (INCLUDE_PATH D:\\ProgramFiles\\Qt\\qt5_7_lib_shared_64)
include_directories(${INCLUDE_PATH}/include)
# Add external library header directories
include_directories(/path/to/my_library/include)
# Add OpenCV's header file search path
include_directories(${OpenCV_INCLUDE_DIRS})
In these two examples, we first used find_package
to find the OpenCV package and obtain its relevant information. Then, by using include_directories
, we added their header file search paths to the project. This setup allows us to directly use the functionalities provided by the OpenCV library in the source code without specifying the full path or filename.
find_package is used to find and configure external packages in CMake projects. When you call find_package(OpenCV REQUIRED), CMake searches for the installed OpenCV package in the system and sets relevant variables, including OpenCV_INCLUDE_DIRS and OpenCV_LIBS.
• The
OpenCV_INCLUDE_DIRS
variable contains the search paths for OpenCV header files.• The
OpenCV_LIBS
variable contains the paths and names of OpenCV library files. Therefore, when you use${OpenCV_INCLUDE_DIRS}
in CMakeLists.txt, you are actually telling CMake to look for the location of OpenCV header files, so that the compiler can find the corresponding header files during compilation.
1.9 link_libraries
The link_libraries
command is used to link libraries to target files. This command has been marked as deprecated in CMake version 3.13, and it is recommended to use the target_link_libraries
command instead. However, I will provide the basic syntax and a simple usage example of link_libraries
.
Basic syntax:
link_libraries(library1 [library2 ...])
Where:
-
•
library1
,library2
, … are the names of the libraries to be linked.
Detailed usage example:
# Link a library to an executable
link_libraries(my_library)
In this example, we use the link_libraries
command to link a library named my_library
to the executable. Although link_libraries
can accomplish this task, its usage is relatively simple and not flexible enough. Therefore, it is recommended to use the more modern target_link_libraries
command instead, especially in large projects.
-
•
link_libraries
indicates adding the search path for third-party lib library files. If the project needs to use a certain third-party library’s lib file during compilation,link_libraries
can be used. -
• The
target_link_libraries
command specifies which lib library file is needed by the subproject. (The lib library file must be found in the search path.)
1.10 target_link_libraries
The target_link_libraries
command is used to link target files (such as executables, library files) to one or more libraries. This is the recommended way to use in CMake. Below is the basic syntax and detailed usage examples:
Basic syntax:
target_link_libraries(target_name library1 [library2 ...])
Where:
-
•
target_name
is the name of the target file, which can be an executable file, library file, etc. -
•
library1
,library2
, … are the names of the libraries to be linked to the target file.
-
1. Basic usage
# Add source files
add_executable(my_program main.cpp)
# Find and link external libraries (using OpenCV as an example)
find_package(OpenCV REQUIRED)
target_link_libraries(my_program ${OpenCV_LIBS})
In this example, we first use add_executable
to add an executable file named my_program
. Then, we use find_package
to find and configure the OpenCV package and obtain its relevant information. Finally, we use target_link_libraries
to link the OpenCV library to the my_program
executable file.
The target_link_libraries
command is used to link the target with one or more libraries, which can be static libraries, dynamic libraries, or executable files.
Basic syntax:
target_link_libraries(target_name
item1
item2
...
)
-
•
target_name
: Target name, which can be an executable file, static library, or dynamic library. -
•
item1
,item2
, …: Names of the libraries to be linked, which can be the names of libraries, paths, or variables.
-
1. Link a static library:
# Assume there is a static library named my_library
add_library(my_library STATIC ${LIBRARY_SOURCES})
# Link the executable my_executable with the static library my_library
target_link_libraries(my_executable my_library)
-
1. Link a dynamic library:
# Assume there is a dynamic library named my_dynamic_library
add_library(my_dynamic_library SHARED ${DYNAMIC_LIBRARY_SOURCES})
# Link the executable my_executable with the dynamic library my_dynamic_library
target_link_libraries(my_executable my_dynamic_library)
-
1. Link both static and dynamic libraries:
# Link the executable my_executable with both static library my_library and dynamic library my_dynamic_library
target_link_libraries(my_executable my_library my_dynamic_library)
In the above examples, the target_link_libraries
command links the specified target (my_executable
) with the given libraries (my_library
, my_dynamic_library
). This allows the target to access the functions and variables defined in the libraries.
It is important to note that when linking dynamic libraries, you also need to ensure that the dynamic libraries can be correctly found at runtime, which can be done by setting environment variables like LD_LIBRARY_PATH
or placing the dynamic libraries in appropriate paths at runtime.
export LD_LIBRARY_PATH=/mnt/xxx/usr/lib/
1.11 include
The include
command is used to include other CMake script files in a CMake script so that the variables, functions, and commands defined in the included file can be used in the current script.
Basic syntax:
include(filename)
-
•
filename
: The path of the file to be included. It can be a relative or absolute path.
Usage example:
Suppose your project structure is as follows:
project/
│
├── CMakeLists.txt
└── cmake/
└── protobuf-generate.cmake
You want to include the protobuf-generate.cmake
file in the main project’s CMakeLists.txt
, you can do it like this:
# project/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(MyProject)
# Include other CMake script files
include(cmake/protobuf-generate.cmake)
# Other project configuration and build logic
Then, in the included cmake/protobuf-generate.cmake
file, you can define your generation logic:
# project/cmake/protobuf-generate.cmake
# Define generation logic
# Here you can define variables, functions, add targets, etc.
# For example, define a variable
set(PROTOBUF_FILES
${CMAKE_CURRENT_SOURCE_DIR}/protobuf/file1.proto
${CMAKE_CURRENT_SOURCE_DIR}/protobuf/file2.proto
)
# For example, add a custom command and target to generate protobuf files
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated_protobuf_files.cpp
COMMAND protoc ${PROTOBUF_FILES} --cpp_out=${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${PROTOBUF_FILES}
COMMENT "Generating Protobuf files"
)
add_custom_target(generate_protobuf_files
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated_protobuf_files.cpp
)
By using the include
command, you can include other CMake script files in the main project’s CMakeLists file, making your project structure clearer and allowing you to reuse existing code logic.
1.12 add_executable
The add_executable
command is used to create an executable target, which compiles source files into an executable file. Below is the basic syntax and detailed usage examples:
Basic syntax:
add_executable(target_name source1 [source2 ...])
Where:
-
•
target_name
is the name of the executable file. -
•
source1
,source2
, … are the source files to be compiled into the executable file.
Detailed usage example:
Suppose we have a simple project that includes two source files main.cpp
and functions.cpp
, and we want to compile them into an executable file.
# Add executable target
add_executable(my_program main.cpp functions.cpp)
1.13 add_library
The add_library
command is used to add a new library (static library, dynamic library, or module library) to a CMake project.
Basic syntax:
add_library(<lib_name> [STATIC | SHARED | MODULE | OBJECT]
[EXCLUDE_FROM_ALL]
source1.cpp source2.cpp ...)
-
•
<lib_name>
: The name of the library. -
•
STATIC
: Generates a static library (default). -
•
SHARED
: Generates a dynamic library. -
•
MODULE
: Generates a module library. -
•
OBJECT
: Generates an object library. -
•
EXCLUDE_FROM_ALL
: Indicates that the library will not be depended on by the default build target.
Usage examples:
# Add source files
set(SOURCES source1.cpp source2.cpp)
# Generate static library
add_library(my_static_lib STATIC ${SOURCES})
# Generate dynamic library
add_library(my_dynamic_lib SHARED ${SOURCES})
# Generate module library
add_library(my_module_lib MODULE ${SOURCES})
# Generate object library
add_library(my_object_lib OBJECT ${SOURCES})
In the above examples, the add_library
command is used to generate a static library my_static_lib
, a dynamic library my_dynamic_lib
, a module library my_module_lib
, and an object library my_object_lib
, specifying the corresponding source file lists.
The type of these libraries is determined by the optional parameters, defaulting to STATIC
, which means generating a static library. If you need to generate a dynamic library, module library, or object library, simply specify the type accordingly in the command.
1.14 target_include_directories
The target_include_directories
command is used to add include directories to a specific target so that header files can be found during compilation.
Basic syntax:
target_include_directories(target_name [SYSTEM] [BEFORE]
PRIVATE|PUBLIC|INTERFACE
[items1...]
[items2...]
...)
-
•
target_name
: The name of the target to which include directories are to be added. -
•
SYSTEM
: An optional parameter that specifies whether the included directories are treated as system header file directories. -
•
BEFORE
: An optional parameter that specifies that the include directories should be added to the beginning of the target’s header file search path. -
•
PRIVATE|PUBLIC|INTERFACE
: Specifies the scope of the include directories. -
•
items1
,items2
, …: The include directories to be added, which can be directory paths, variables, generator expressions, etc.
Usage example:
Suppose you have a library target named my_library
, and you want to add the include
directory to the include path of that target.
target_include_directories(my_library PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
In this example, my_library
is the target name, and PUBLIC
indicates that the include path will be added to the public interface of my_library
so that other targets can access it. ${CMAKE_CURRENT_SOURCE_DIR}/include
is the include directory path to be added.
You can also add multiple directories:
target_include_directories(my_library PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${SOME_OTHER_DIR}/include
)
In this example, in addition to ${CMAKE_CURRENT_SOURCE_DIR}/include
, ${SOME_OTHER_DIR}/include
is also added to the public interface of my_library
. This target_include_directories
has transitive properties; if a library is built and then other executable files call that library, using target_include_directories
with PUBLIC
allows access to the corresponding include paths.
1.15 install
The install
command is used to install the generated files (executables, library files, header files, etc.) to specified locations. This command can be used to install your software into the system, making it convenient for other users to use your software.
Basic syntax:
install(TARGETS target_name
[EXPORT <export-name>]
[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
[INCLUDES DESTINATION [<dir> ...]]
)
install(FILES files...
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
)
install(DIRECTORY dirs...
[DESTINATION <dir>]
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
)
install(EXPORT <export-name>
DESTINATION <dir>
[NAMESPACE <namespace>]
[FILE <filename>]
)
-
TARGETS
: Install targets, which can be executables, library files, etc. This option is used to specify which targets to install, which can be created usingadd_executable
,add_library
, oradd_custom_target
. It tells CMake which build targets to install.
-
•
EXPORT <export-name>
: Exports the target for use in other projects so that other projects can reference these targets. -
•
ARCHIVE
: Specifies that the install target is a static library archive file (.a file). -
•
LIBRARY
: Specifies that the install target is a shared library file (dynamic library, .so file). -
•
RUNTIME
: Specifies that the install target is an executable file. -
•
OBJECTS
: Specifies that the install target is an object library file (.o file). -
•
FRAMEWORK
: Specifies that the install target is a framework file on macOS. -
•
BUNDLE
: Specifies that the install target is a bundle file on macOS. -
•
DESTINATION <dir>
: Specifies the target path for the install target. -
•
PERMISSIONS permissions...
: Specifies the permissions for the install target. -
•
CONFIGURATIONS [Debug|Release|...]
: Specifies that the target should only be installed under the specified build configuration. -
•
COMPONENT <component>
: Specifies the component of the install target. -
•
OPTIONAL
: Specifies whether the install target is optional. -
•
INCLUDES DESTINATION [<dir> ...]
: Specifies the header files for the install target.
2. FILES
: List of files to install. This option is used to specify the list of files to install. You can list each file to be installed and then use install(FILES ...)
to install them to the specified location.
-
•
DESTINATION <dir>
: Specifies the target path for installation. -
•
PERMISSIONS permissions...
: Specifies the permissions for the install files. -
•
CONFIGURATIONS [Debug|Release|...]
: Specifies that the files should only be installed under the specified build configuration. -
•
COMPONENT <component>
: Specifies the component of the install files. -
•
OPTIONAL
: Specifies whether the install files are optional.
3. DIRECTORY
: List of directories to install. This option is used to specify the list of directories to install. It will recursively copy all files and subdirectories in the directory.
-
•
DESTINATION <dir>
: Specifies the target path for installation. -
•
FILE_PERMISSIONS permissions...
: Specifies the permissions for the install files. -
•
DIRECTORY_PERMISSIONS permissions...
: Specifies the permissions for the install directories. -
•
USE_SOURCE_PERMISSIONS
: Use the permissions of the source files. -
•
CONFIGURATIONS [Debug|Release|...]
: Specifies that the directories should only be installed under the specified build configuration. -
•
COMPONENT <component>
: Specifies the component of the install directories. -
•
OPTIONAL
: Specifies whether the install directories are optional.
4. EXPORT
: Exports targets for use in other projects. This option is used to export targets for use in other projects so that other projects can reference these targets.
-
•
DESTINATION <dir>
: Specifies the target path for the exported targets. -
•
NAMESPACE <namespace>
: Specifies the namespace for the exported targets. -
•
FILE <filename>
: Specifies the filename for the exported targets.
Usage examples:
Install an executable:
install(TARGETS my_executable DESTINATION bin)
Install a library file:
install(TARGETS my_library DESTINATION lib)
Install a header file:
install(FILES my_header.h DESTINATION include)
Install an entire directory:
install(DIRECTORY my_directory DESTINATION share/my_project)
Export targets:
install(EXPORT my_targets DESTINATION lib/my_project)
These examples demonstrate how to install executable files, library files, header files, entire directories, and how to export targets to other projects.
1.16 Efficient CMake Configuration: Conditional Judgments, Loops, and String Operations
As projects become more complex, CMake needs to handle various situations more intelligently. By learning conditional judgments, loops, and string operations, you can make CMake more flexible and efficient!
1.16.1 Conditional Judgments
CMake acts like a smart decision-maker, able to make choices based on different conditions. For example, suppose you are compiling a cross-platform project that requires linking different libraries on Windows, additional configurations on macOS, and none on Linux. CMake can easily solve these problems through conditional judgments.
For example, suppose we need to use different libraries on different platforms:
if(WIN32)
# Use specific libraries on Windows
set(MY_LIBRARY "C:/libs/windows_lib.lib")
# Print debug information
message("Using library for Windows")
elseif(APPLE)
# Use other libraries on macOS
set(MY_LIBRARY "/usr/local/lib/mac_lib.dylib")
message("Using library for macOS")
else()
# Use another library on Linux
set(MY_LIBRARY "/usr/lib/linux_lib.so")
message("Using library for Linux")
endif()
# Finally link the library
target_link_libraries(my_project ${MY_LIBRARY})
Explanation: On Windows: If on Windows, CMake will set MY_LIBRARY
to the Windows-specific library path. On macOS: If on macOS, CMake will set it to the macOS version of the library. On Linux: If on Linux, CMake will choose the library for Linux. In this way, regardless of which operating system you are on, CMake can automatically select the correct library without requiring you to manually change the code each time.
1.16.2 Loops: foreach and while
-
1. foreach loop: The
foreach
loop is particularly suitable for iterating over a list. For example, suppose you have a bunch of source files and want to perform some operations on each file, such as printing the filename or setting compilation options.
set(SOURCES main.cpp utils.cpp helper.cpp)
foreach(SRC ${SOURCES})
message("Compiling source file: ${SRC}")
# More operations can be done here, such as adding each source file to the target
target_sources(hello PRIVATE ${SRC})
endforeach()
Understanding: Here, foreach
takes each filename from SOURCES
one by one, executing message()
to print the filename each time. You can also perform other operations in the loop, such as setting compilation options for each file or adding these files to the target. foreach
is suitable when you have a bunch of files, options, or other content that needs to be processed one by one.
-
2. while loop: The
while
loop executes repeatedly based on a condition until the condition is no longer met. In simple terms, you set a “threshold” and check this condition each time until it is satisfied.
set(COUNT 1)
while(COUNT LESS 5)
message("Current count: ${COUNT}")
math(EXPR COUNT "${COUNT} + 1")
endwhile()
Understanding: In this example, while
first checks if COUNT
is less than 5. If it is, it prints it and then increments COUNT
by 1. It continues checking until COUNT
reaches 5 and stops. This is a continuous loop until you meet a certain condition (in this case, COUNT
being less than 5). while
is suitable when you need to do something repeatedly based on certain conditions until they are no longer met.
1.17 String Operations: Concatenation, Splitting, and Searching
-
1. String Concatenation (STRING(CONCAT …)): Concatenation means combining two or more strings into one larger string.
set(DIR "/home/user/project/")
set(FILE_NAME "main.cpp")
# Concatenate strings
set(FULL_PATH "${DIR}${FILE_NAME}")
message("Full path: ${FULL_PATH}")
Explanation: We concatenate the path and filename, ultimately forming /home/user/project/main.cpp
. The method used for concatenation is to insert the two strings together using ${}
, and CMake will automatically concatenate them.
-
1. String Splitting (STRING(REGEX REPLACE …)): Sometimes, we need to extract a part from a long string.
set(PATH "/home/user/project/main.cpp")
# Get the filename
string(REGEX REPLACE ".*/" "" FILE_NAME ${PATH})
message("File name: ${FILE_NAME}")
Explanation: Here, we used string(REGEX REPLACE ...)
to remove everything in the path, leaving only the filename part. The regular expression ^.*/
means “match all characters from the beginning to the last /
“. REPLACE "^.*/" ""
means to replace the matched string (the path part) with an empty string, thus easily obtaining the filename main.cpp
from the path.
-
1. String Searching (STRING(FIND …)): Sometimes, you need to search for a substring within a string.
set(PATH "/home/user/project/")
# Search for a substring in the string
string(FIND ${PATH} "user" POSITION)
if(POSITION GREATER -1)
message("Found 'user' in the path at position: ${POSITION}")
else()
message("'user' not found in the path.")
endif()
Explanation: string(FIND ...)
returns the position of the substring in the string; if not found, it returns -1. In this example, CMake searches for the occurrence of user
in the path, returning position 5, indicating it starts from the 6th character. You can use this return value to determine whether a substring was found and then perform corresponding actions.
2. CMake Variables
Below are some commonly used CMake variables and their corresponding content, along with usage examples in CMakeLists.txt
and output:
Variable Name | Content | Usage and Output in CMakeLists.txt | Output Result |
CMAKE_BINARY_DIR | The top-level binary directory of the project currently being built | message("CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") |
CMAKE_BINARY_DIR: /path/to/build |
PROJECT_BINARY_DIR | Same as CMAKE_BINARY_DIR | message("PROJECT_BINARY_DIR: ${PROJECT_BINARY_DIR}") |
PROJECT_BINARY_DIR: /path/to/build |
_BINARY_DIR | Same as CMAKE_BINARY_DIR | message("_BINARY_DIR: ${_BINARY_DIR}") |
_BINARY_DIR: /path/to/build |
CMAKE_SOURCE_DIR | The top-level source code directory of the project currently being configured | message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}") |
CMAKE_SOURCE_DIR: /path/to/source |
PROJECT_SOURCE_DIR | Same as CMAKE_SOURCE_DIR | message("PROJECT_SOURCE_DIR: ${PROJECT_SOURCE_DIR}") |
PROJECT_SOURCE_DIR: /path/to/source |
_SOURCE_DIR | Same as CMAKE_SOURCE_DIR | message("_SOURCE_DIR: ${_SOURCE_DIR}") |
_SOURCE_DIR: /path/to/source |
CMAKE_CURRENT_SOURCE_DIR | The source code directory where the currently processed CMakeLists.txt is located | message("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}") |
CMAKE_CURRENT_SOURCE_DIR: /path/to/source/current |
CMAKE_CURRENT_BINARY_DIR | The build directory where the currently processed CMakeLists.txt is located | message("CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}") |
CMAKE_CURRENT_BINARY_DIR: /path/to/build/current |
CMAKE_CURRENT_LIST_FILE | The full path of the currently processed CMakeLists.txt file | message("CMAKE_CURRENT_LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}") |
CMAKE_CURRENT_LIST_FILE: /path/to/source/CMakeLists.txt |
CMAKE_CURRENT_LIST_LINE | The line number of the current line in CMakeLists.txt | message("CMAKE_CURRENT_LIST_LINE: ${CMAKE_CURRENT_LIST_LINE}") |
CMAKE_CURRENT_LIST_LINE: 10 |
CMAKE_MODULE_PATH | Additional paths for CMake to find module files | message("CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}") |
CMAKE_MODULE_PATH: /path/to/cmake/modules |
EXECUTABLE_OUTPUT_PATH | Sets the output path for generated executable files | message("EXECUTABLE_OUTPUT_PATH: ${EXECUTABLE_OUTPUT_PATH}") |
EXECUTABLE_OUTPUT_PATH: /path/to/build/bin |
PROJECT_NAME | The name of the current project | message("PROJECT_NAME: ${PROJECT_NAME}") |
PROJECT_NAME: MyProject |
CMAKE_BUILD_TYPE | The current build type (Release, Debug, etc.) | message("CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") |
CMAKE_BUILD_TYPE: Release |
CMAKE_CXX_FLAGS | C++ compiler options | message("CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") |
CMAKE_CXX_FLAGS: -std=c++11 -Wall |
CMAKE_CXX_FLAGS_RELEASE | C++ compiler options for Release build type | message("CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") |
CMAKE_CXX_FLAGS_RELEASE: -O3 |
CMAKE_MAJOR_VERSION | The major version number of CMake | message("CMAKE_MAJOR_VERSION: ${CMAKE_MAJOR_VERSION}") |
CMAKE_MAJOR_VERSION: 3 |
CMAKE_SYSTEM | The type of the current operating system | message("CMAKE_SYSTEM: ${CMAKE_SYSTEM}") |
CMAKE_SYSTEM: Linux |
CMAKE_SYSTEM_VERSION | The version of the current operating system | message("CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}") |
CMAKE_SYSTEM_VERSION: 18.04 |
CMAKE_SYSTEM_PROCESSOR | The architecture of the current system’s processor | message("CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") |
CMAKE_SYSTEM_PROCESSOR: x86_64 |
LIBRARY_OUTPUT_PATH | Sets the output path for generated library files | message("LIBRARY_OUTPUT_PATH: ${LIBRARY_OUTPUT_PATH}") |
LIBRARY_OUTPUT_PATH: /path/to/build/lib |
CMAKE_SIZEOF_VOID_P | The size of a void pointer (in bytes), which tells us whether the CPU is 32-bit or 64-bit | message("CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}") |
CMAKE_SIZEOF_VOID_P: 8 |
CMAKE_HOST_SYSTEM_PROCESSOR | The architecture of the host system’s processor | message("CMAKE_HOST_SYSTEM_PROCESSOR: ${CMAKE_HOST_SYSTEM_PROCESSOR}") |
CMAKE_HOST_SYSTEM_PROCESSOR: x86_64 |
👉 Follow us to discover more interesting content on autonomous driving/embodied intelligence/GitHub! 🚀