In the field of embedded Linux development, including BSP development and audio-video development, working with CMake is absolutely essential. This requires every embedded engineer to be very familiar with commonly used CMake commands.In this issue, let us consolidate some CMake commands that must be mastered in the workplace. For those who are new to the job or are currently interning, when encountering an incomprehensible CMakeLists.txt file, you can refer to this article to look up the corresponding commands, consult and learn. With more usage and exposure, you will gradually become proficient.
▍1 Overview of CMake
CMake is a cross-platform project build tool. Regarding project builds, we are also familiar with the Makefile built using the make command. Nowadays, most IDE software integrates make;
For example: nmake in VS, GNU make in Linux, qmake in Qt, etc.
If you write a Makefile by hand, you will find that Makefiles usually depend on the current platform, lack compatibility, and the workload of writing Makefiles is relatively large. It is also easy to make mistakes when resolving dependencies, making it difficult to write.
In this article, you will learn about the commonly used CMake commands that often appear in the CMakeLists.txt files in the project root directory, first-level subdirectories, and second-level subdirectories, as well as the usage and functions of these commands.
▍2 Common Command OverviewBased on the author’s practical work experience, the frequently used CMake commands and brief descriptions are provided according to the directory structure. The purpose is to give a quick overview of the commands. It’s okay if you don’t understand them at first; I will explain each command in detail later.2.1 Project Root Directory2.1.1 Set Project Name and Version
cmake_minimum_required(VERSION 3.10.0)project(myProject)
2.1.2 Set C++ Standard
set(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED True)
2.1.3 Standardize Installation Path Variables
include(GNUInstallDirs)
Perhaps you do not understand what “standardized installation path variables” are here; this will be explained later. The same applies to other commands.2.1.4 Define Version File Path
set(VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/VERSION")
2.1.5 Add src Subdirectory for Building
add_subdirectory(src)
2.1.6 Add Sample Build Switch (default off)
option(BUILD_SAMPLES "Build samples" OFF)
2.1.7 Set Toolchain
set(CMAKE_TOOLCHAIN_FILE "path/to/toolchain-file.cmake")
2.2 First-Level Subdirectory src/CMakeLists.txtThe first-level subdirectory is generally the parent directory of multiple application module folders. The CMakeLists.txt in this directory is shared by multiple modules.2.2.1 Define Module Name Variable
# Module Name
set(MODULE_NAME "VideoStreamProxy")
2.2.2 Output Version Related Status Information Header
message(STATUS "***************Version*****************")
2.2.3 Add myCameraProxy Subdirectory for Building
add_subdirectory(myCameraProxyServer)
2.2.4 Set Output Directory
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
2.2.5 Install Target
install(TARGETS my_executable my_library DESTINATION bin)
2.2.6 Add Custom Target
add_custom_target(my_custom_target ALL DEPENDS${CMAKE_BINARY_DIR}/output.txt)
2.2.7 Add Library to List
list(APPEND ${LIBRARY}"${PROJECT_SOURCE_DIR}/../lib/libaisc_stream.so")
2.2.8 Add Source Files
target_sources(aisc_test PUBLIC ${${SOURCES}})
2.3 Second-Level Subdirectory src/myCameraProxy/CMakeLists.txtThe second-level subdirectory generally has multiple entries, and the CMakeLists.txt in this directory is independently used for each application module, personalizing the compilation of the source files and header files in this path.2.3.1 Define myCameraProxy Project and Version
project(myCameraProxy VERSION ${VERSION})
2.3.2 Collect Source Files from Specified Directory into Variable
aux_source_directory(${PROJECT_ROOT_DIR}/src/myCameraProxyServer SRCS)
2.3.3 Specify Library File Search Directory
link_directories( ${CMAKE_SOURCE_DIR}/dependency/myTestServer/${PLATFORM}/lib ${CMAKE_SOURCE_DIR}/dependency/myOSAL/${PLATFORM}/lib …… )
2.3.4 Add Executable File
add_executable(${PROJECT_NAME} ${SRCS})
2.3.5 Set Compile and Link Options
set_compile_and_link_options(${PROJECT_NAME})
2.3.6 Set Header File Directory
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/dependency/myOSAL/${PLATFORM}/include/myOSAL/osal)
2.3.7 Set Library Search Directory
target_link_directories(${PROJECT_NAME} PRIVATE $ENV{QNX_TARGET}/aarch64le/io-sock/lib)
2.3.8 Conditional Statements
if(${PLATFORM} STREQUAL "platform0")……elseif(${PLATFORM} STREQUAL "platform1")……endif()
2.3.9 Set Linked Libraries
target_link_libraries(${PROJECT_NAME} PRIVATE ${proxy_server_lib})
2.3.10 Install Target to Standard Directory
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
2.3.11 Compile to Generate Shared Variable
add_library(ais SHARED ${AIS_SRCS})
2.3.12 Find Dependencies
find_package(Boost REQUIRED)include_directories(${Boost_INCLUDE_DIRS})target_link_libraries(my_executable ${Boost_LIBRARIES})
▍3 Detailed Explanation of Common Commands3.1 include
include(GNUInstallDirs)
<span><span>include(GNUInstallDirs)</span></span> is a core module in CMake used for standardizing installation paths, which automatically defines a set of directory variables that conform to the GNU software installation standards (GNU Coding Standards), allowing your project installation paths to align with system default rules, enhancing cross-platform compatibility and user experience.
# There is common.cmake in the project root directory (storing common compilation options)
include(${CMAKE_SOURCE_DIR}/cmake/common.cmake)# Import common configuration
The built-in CMake command <span><span>include()</span></span> can also read and execute specified CMake script files (with .cmake suffix or CMake scripts without suffix), importing variables, functions, macros, configuration logic, etc. from the script into the current CMakeLists.txt, achieving configuration reuse and logical separation.3.2 set
Syntax:<span><span>set(variable_name variable_value1 [variable_value2 ...] [PARENT_SCOPE])</span></span>
- Variable values can be a single string, multiple values (forming a list), paths, etc.;
- If not added
<span><span>PARENT_SCOPE</span></span>, the variable’s scope is the current script (such as the current<span><span>CMakeLists.txt</span></span><span><span> or </span></span><code><span><span>include</span></span><span><span> script);</span></span> - If added
<span><span>PARENT_SCOPE</span></span>, the variable will be passed up to the parent script (for example, calling a subdirectory’s<span><span>set</span></span><span><span> in the main project </span></span><code><span><span>CMakeLists.txt</span></span><span><span>, adding this parameter allows the main project to access that variable).</span></span>
# Define variable VERSION_FILE, value is the path of the VERSION file in the current directory
set(VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/VERSION")# Later can reference this path through ${VERSION_FILE} (such as reading file content, passing to other commands)
3.3 option
Core: Define a boolean cache option (switch)
Details: A built-in CMake command used to create a boolean cache variable (option switch) that users can configure via the command line (<span><span>-Doption_name=ON/OFF</span></span>) or CMake GUI interface, usually used to control compilation features (such as whether to enable debug mode, whether to compile a certain module).
Syntax:<span><span>option(option_name "Description" default_value)</span></span>
- Option name: Custom option identifier (such as
<span><span>BUILD_TESTS</span></span>); - Description: Explanation of the option (will be displayed in CMake GUI for user understanding);
- Default value: The initial state of the option, can only be
<span><span>ON</span></span>or<span><span>OFF</span></span>(boolean value).
Core Logic:
- The defined option will be stored in
<span><span>CMakeCache.txt</span></span>, globally effective and retains state during project build (unless manually modified); - Users can modify the value through
<span><span>-Doption_name=ON</span></span>(command line) or check GUI options without modifying CMake scripts; - Usually used in conjunction with
<span><span>if()</span></span><span><span> conditional statements to execute different build logic based on the option value (such as compiling test code, enabling functional modules).</span></span>
Example:
- Define an option for “whether to compile test programs”:
option(BUILD_TESTS "Whether to build test executable" OFF)
if(BUILD_TESTS) add_executable(test_app test.cpp)endif()
- Define an option for “whether to enable debug logs”:
option(ENABLE_DEBUG_LOG "Enable debug log output during compilation" ON)
if(ENABLE_DEBUG_LOG) target_compile_definitions(${PROJECT_NAME} PRIVATE DEBUG_LOG)endif()
3.4 list
Core: Operate on CMake list variables (add, delete, modify, query)
Details: A built-in CMake command used to perform various operations on list-type variables (such as adding elements, deleting elements, finding elements, sorting, etc.). Lists in CMake are represented as “space-separated strings” and are a core tool for managing collections of multiple elements (such as source lists, path lists).
Syntax:<span><span>list(operation_type list_variable_name [parameter1 parameter2 ...])</span></span>Common operation types and descriptions:
<span><span>APPEND</span></span>: Add elements to the end of the list;<span><span>PREPEND</span></span>: Add elements to the beginning of the list;<span><span>REMOVE_ITEM</span></span>: Remove specified elements from the list;<span><span>FIND</span></span>: Find the index position of an element in the list;<span><span>LENGTH</span></span>: Get the number of elements in the list;<span><span>GET</span></span>: Extract the element at the specified index from the list;<span><span>SORT</span></span>: Sort the elements of the list;<span><span>JOIN</span></span>: Concatenate list elements into a string (with specified separator);<span><span>SEPARATE_ARGUMENTS</span></span>: Split a string into a list (by space / quotes).
Core Logic:
- List variables are essentially space-separated strings, and the
<span><span>list</span></span>command identifies the action to be performed through the operation type; - All operations directly modify the original list variable (no need to reassign);
- Indexing starts from 0 and supports negative indexing (e.g.,
<span><span>-1</span></span>represents the last element).
Example:
- Add a new file to the source list:
set(SRCS main.cpp utils.cpp)
list(APPEND SRCS network.cpp) # SRCS becomes: main.cpp utils.cpp network.cpp
- Remove specified elements from the list:
list(REMOVE_ITEM SRCS utils.cpp) # SRCS becomes: main.cpp network.cpp
- Find element position and extract:
list(FIND SRCS network.cpp INDEX) # INDEX stores the element index (here it is 1)
list(GET SRCS 0 FIRST_FILE) # FIRST_FILE stores the first element (main.cpp)
- Concatenate list into a string:
list(JOIN SRCS ";" SRCS_STR) # SRCS_STR becomes: main.cpp;network.cpp
Key Functions:
- Dynamic management of collections: Flexibly add, delete, and modify list elements (such as adding source files based on conditions);
- Data processing: Implement list searching, sorting, concatenation, etc., adapting to complex configuration logic;
- Batch operations: Use in conjunction with loops (
<span><span>foreach</span></span>) to iterate over list elements, simplifying repetitive operations.
Points to Note:
- List elements cannot contain spaces (otherwise they will be split into multiple elements), and must be enclosed in quotes (e.g.,
<span><span>"file with space.cpp"</span></span>); - Operation types are case-insensitive (e.g.,
<span><span>APPEND</span></span>is equivalent to<span><span>append</span></span>); - Be cautious with empty list operations (e.g.,
<span><span>GET</span></span>on an empty list will throw an error; check length first).
3.5 add_subdirectory
Core: Add source files to the target (dynamically extend the source list)
Details: A built-in CMake command used to add source files to an already defined build target (executable/library), supporting dynamic supplementation of source code after the target is created. This is more flexible than directly listing source code in <span><span>add_executable</span></span><span><span>/</span></span><code><span><span>add_library</span></span><span><span>, especially suitable for scenarios where source code is added conditionally or modularly.</span></span>
Syntax:<span><span>target_sources(target_name scope source_file1 [source_file2 ...])</span></span>
- Target name: The target defined by
<span><span>add_executable</span></span>or<span><span>add_library</span></span>(e.g.,<span><span>aisc_test</span></span>); - Scope (controls visibility):
<span><span>PUBLIC</span></span>: Source files participate in the current target compilation and are visible to modules that depend on this target (commonly used for library targets);<span><span>PRIVATE</span></span>: Source files are only used in the current target compilation and are not visible externally;<span><span>INTERFACE</span></span>: Source files do not participate in the current target compilation, only provided to modules that depend on this target (rarely used);- Source files: Can directly specify file paths or reference variables (e.g.,
<span><span>${SOURCES}</span></span>), supporting nested variables (e.g.,<span><span>${${SOURCES}}</span></span><span><span> references the variable value stored in the variable name).</span></span>
Core Logic:
- Must first create the target using
<span><span>add_executable</span></span>/<span><span>add_library</span></span>, then use<span><span>target_sources</span></span>to supplement the source code; - The added source files will be merged with the initial source code of the target and will participate in the compilation and linking;
- Nested variables
<span><span>${${SOURCES}}</span></span>means first resolving the value of the<span><span>SOURCES</span></span>variable (assuming it is<span><span>MY_SRC_LIST</span></span>), then referencing the corresponding source list of<span><span>${MY_SRC_LIST}</span></span>to achieve dynamic variable name referencing.
Example:
# First create the target
add_executable(aisc_test main.cpp)
# Define nested variable (assuming SOURCES stores variable name MY_SRC_LIST)
set(MY_SRC_LIST "test_case.cpp utils.cpp")
set(SOURCES "MY_SRC_LIST")
# Add source code referenced by nested variable to the target
target_sources(aisc_test PUBLIC ${${SOURCES}})
Finally, <span><span>aisc_test</span></span> will include <span><span>main.cpp</span></span>, <span><span>test_case.cpp</span></span>, and <span><span>utils.cpp</span></span> three source files.
Key Functions:
- Dynamic extension of source code: Source code can be added conditionally (such as by platform or feature switch) after the target is created, without modifying the initial
<span><span>add_executable</span></span>command; - Modular management: Split the source code of different modules into multiple
<span><span>target_sources</span></span>calls for easier maintenance; - Nested variable support: Achieve dynamic variable name referencing through
<span><span>${${variable_name}}</span></span>, adapting to complex source list management logic.
Points to Note:
- The target must be defined first: Must call
<span><span>target_sources</span></span>after<span><span>add_executable</span></span>or<span><span>add_library</span></span>; - Nested variable validity: Ensure that the variable pointed to by
<span><span>${SOURCES}</span></span>exists and is valid; otherwise, it will lead to source addition failure; - Scope selection: Executable targets usually use
<span><span>PRIVATE</span></span>(no need to expose source code externally), while library targets choose based on interface requirements between<span><span>PUBLIC</span></span>/<span><span>PRIVATE</span></span>.
3.6 add_library
Core: Compile to generate shared library target
Details: A built-in CMake command used to compile and link specified source files into a shared library (dynamic library, such as Linux <span><span>.so</span></span>, Windows <span><span>.dll</span></span>, macOS <span><span>.dylib</span></span>), generating a library target named <span><span>ais</span></span>, which can be linked by other targets.
Syntax:<span><span>add_library(library_name library_type source_file1 [source_file2 ...])</span></span>
- Library name: Here it is
<span><span>ais</span></span>, as the identifier of the library target, which can be used for linking and configuring properties later; - Library type:
<span><span>SHARED</span></span>indicates generating a shared library (dynamic link library), other types include<span><span>STATIC</span></span>(static library),<span><span>MODULE</span></span>(module library); - Source files: Referenced through
<span><span>${AIS_SRCS}</span></span>variable (must be defined in advance, such as through<span><span>aux_source_directory</span></span>or manually listed).
Core Logic:
- Receives library name, type, and source file list, generating corresponding library build rules;
- During the compilation phase, the source files are compiled into object files, and during the linking phase, the dynamic link library file is generated according to the shared library format;
- The generated library target can be linked by other executable targets or library targets through
<span><span>target_link_libraries</span></span>, achieving code reuse.
Example:
# Collect source files into AIS_SRCS variable
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src AIS_SRCS)
# Generate a shared library named ais
add_library(ais SHARED ${AIS_SRCS})
Key Functions:
- Code reuse: Encapsulate common functionalities into shared libraries for multiple executable programs or other libraries to link, reducing code redundancy;
- Dynamic linking: Shared libraries are loaded at runtime, allowing independent updates of library files without recompiling dependent programs;
- Modular development: Split the project into multiple library modules, reducing coupling and facilitating maintenance and expansion.
Points to Note:
- Source file variables must be defined in advance: Ensure that
<span><span>${AIS_SRCS}</span></span>contains valid source paths; otherwise, it will lead to library compilation failure; - Library type selection:
<span><span>SHARED</span></span>must consider platform compatibility (e.g., Windows requires exporting symbols); if static linking is needed, use<span><span>STATIC</span></span>; - Library name uniqueness: Library target names in the same project cannot be duplicated to avoid conflicts.
3.7 add_subdirectory
Core: Recursively build subdirectories (execute their CMakeLists.txt)
Details: A built-in CMake command used to include specified subdirectories in the current project’s build process, achieving project management by directory/module splitting.
Syntax:<span><span>add_subdirectory(subdirectory_path [optional_output_directory])</span></span>
Core Logic:
- CMake will enter the specified subdirectory, read the
<span><span>CMakeLists.txt</span></span>file in that directory; - Execute the build logic in the subdirectory script (such as compiling source code, generating targets, defining dependencies, setting properties, etc.);
- The build results of the subdirectory (executable files, libraries, variables, etc.) will be integrated into the main project, supporting cross-directory dependency references (e.g., the main project links libraries generated by the subdirectory).
Example:
<span><span>add_subdirectory(src)</span></span>: Build the<span><span>src</span></span>subdirectory under the project root directory;<span><span>add_subdirectory(myCameraProxyServer)</span></span>: Build the my<span><span>CameraProxyServer</span></span>functional submodule.
Key Functions:
- Split project structure: Place different functional modules (such as core logic, example code, dependency libraries) in independent subdirectories for easier maintenance;
- Support nested builds: Subdirectories can also call
<span><span>add_subdirectory</span></span>, adapting to complex project hierarchies; - Isolate configurations: Each subdirectory’s CMake configuration is independent, interacting with the main project only through specified interfaces (such as generated targets, exported variables), reducing coupling.
3.8 installCore: Define installation rules (deploy build products/files)Details: A built-in CMake command used to specify installation operations after the project build is complete (such as <span><span>make install</span></span>, <span><span>cmake --install .</span></span>) according to deployment rules, copying target files, header files, configuration files, etc. to specified directories according to specifications, facilitating project usage, deployment, or distribution.Syntax:
# Install build targets (executable files/libraries)
install(TARGETS target_name1 [target_name2 ...] [RUNTIME DESTINATION directory] [LIBRARY DESTINATION directory] [ARCHIVE DESTINATION directory] [PUBLIC_HEADER DESTINATION directory] [CONFIGURATIONS Debug|Release] [COMPONENT component_name])
# Install single/multiple files
install(FILES file_path1 [file_path2 ...] DESTINATION directory [PERMISSIONS permissions] [COMPONENT component_name])
# Install directory (including contents)
install(DIRECTORY directory_path1 [directory_path2 ...] DESTINATION directory [FILES_MATCHING PATTERN matching_rule] [COMPONENT component_name])
# Install script (execute CMake script)
install(SCRIPT script_path [COMPONENT component_name])
Core Logic:
- Define rules according to installation object types (targets, files, directories, scripts), specifying “source objects” and “target installation directories”;
- Support splitting installation logic by build type (Debug/Release), components (such as runtime, development libraries);
- When executing the installation command, CMake will automatically copy files according to the rules and can set file permissions (such as readable, executable);
- Combining with the standard directory variables of the
<span><span>GNUInstallDirs</span></span>module (such as<span><span>CMAKE_INSTALL_BINDIR</span></span>), it can adapt to cross-platform installation specifications.
3.9 aux_source_directory
Core: Collect source files from the directory into a variable (non-recursive) Details: A built-in CMake command used to automatically scan all source files in the specified directory (default matches common source suffixes such as <span><span>.c</span></span>/<span><span>.cpp</span></span>, etc.), consolidating the file path list and assigning it to the specified variable, which can then be directly used as a source list for compiling targets, simplifying source listing operations.
Syntax:<span><span>aux_source_directory(directory_path output_variable_name)</span></span>
Core Logic:
- Only scans the specified directory itself, not recursively traversing the source code in subdirectories;
- Automatically matches files in the directory that conform to source suffixes without manually specifying individual files;
- The variable stores the file path list, which can be directly passed to
<span><span>add_executable</span></span>/<span><span>add_library</span></span>and other compilation commands; - After adding/removing source files in the directory, you need to re-execute
<span><span>cmake ..</span></span>to update the variable value.
Example:
- Collect source files from the
<span><span>src</span></span>folder in the current directory into the<span><span>SRCS</span></span>variable:
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRCS)
- Collect IPC platform VSM module source files into the
<span><span>SRCS</span></span>variable:
aux_source_directory(${IPC_PLATFORM}/VSM/src SRCS)
3.10 link_directories
Core: Specify global library file search directories
Details: A built-in CMake command used to set global library file search paths during the linking phase, allowing CMake to automatically look for the required library files (such as <span><span>.so</span></span>/<span><span>.a</span></span>/<span><span>.lib</span></span>, etc.) in these directories when linking targets, without manually specifying the full path of the libraries.
Syntax:<span><span>link_directories(directory_path1 [directory_path2 ...])</span></span>
Core Logic:
- The search directories set by this command are globally effective, and all subsequent
<span><span>target_link_libraries</span></span>commands will prioritize looking for library files in these directories; - Directory paths support absolute paths (such as paths concatenated based on
<span><span>CMAKE_SOURCE_DIR</span></span>), relative paths, or paths concatenated with environment variables (such as adapting to different platform dependency library paths); - Only affects the linking phase and does not impact the compilation phase’s header file search (header file searches need to use
<span><span>include_directories</span></span>or<span><span>target_include_directories</span></span>).
Example:
- Specify multiple dependency library search directories (adapting to different platforms):
link_directories( ${CMAKE_SOURCE_DIR}/dependency/myTestServer/${PLATFORM}/lib ${CMAKE_SOURCE_DIR}/dependency/myOSAL/${PLATFORM}/lib $ENV{QNX_TARGET}/aarch64le/io-sock/lib)
Key Functions:
- Simplify linking configuration: No need to write the full path of library files in
<span><span>target_link_libraries</span></span>, just specify the library name; - Adapt to multiple platforms: By concatenating platform variables (such as
<span><span>${PLATFORM}</span></span>), you can uniformly configure library paths for different architectures/systems; - Batch effectiveness: Set multiple directories at once, which can be reused in all subsequent target links, reducing repetitive configuration. Points to Note:
- Global effectiveness risk: May lead to library file conflicts (e.g., libraries with the same name in different directories), it is recommended to prioritize using
<span><span>target_link_directories</span></span>(specifying search directories by target for more precision); - Path validity: Ensure that the specified directories exist during linking; otherwise, it will lead to library lookup failures;
- Cross-platform compatibility: The library directory structure may differ across different systems, and conditions (such as
<span><span>if(WIN32)</span></span>/<span><span>if(LINUX)</span></span>) need to be adapted.
3.11 add_executable
Core: Compile source code to generate executable targets
Details: A built-in CMake command used to compile and link specified source files into an executable program, generating the corresponding build target (the target name is usually used as the executable file name), which is the core command for building executable programs.
Syntax:<span><span>add_executable(target_name source_file1 [source_file2 ...] [EXCLUDE_FROM_ALL])</span></span>
- Target name: The name of the executable program (also the target identifier in CMake, which can be used to configure dependencies, properties, etc. later);
- Source files: Can be listed individually (e.g.,
<span><span>main.cpp utils.cpp</span></span>), or referenced through variables (e.g.,<span><span>${SRCS}</span></span>, where the variable stores multiple source file path lists); - Optional parameter
<span><span>EXCLUDE_FROM_ALL</span></span>: This executable target will not be built by default and must be specified manually (e.g.,<span><span>make target_name</span></span>).
Core Logic:
- Receives a list of source files (single file or multiple files referenced by variables) and associates them with the specified target name;
- CMake will generate corresponding build rules for the specified platform (such as Makefile, Visual Studio project) based on project configurations (such as compilation options, linking options, dependency libraries);
- During the compilation phase: The source files (.c/.cpp, etc.) are compiled into object files (.o/.obj);
- During the linking phase: The object files are linked with dependency libraries (specified through
<span><span>target_link_libraries</span></span>), ultimately generating the executable program (such as ELF files under Linux, .exe files under Windows).
Example:
- Directly list source files to generate an executable program:
add_executable(MyApp main.cpp network.cpp utils.cpp)
- Reference source files through variables (in conjunction with
<span><span>aux_source_directory</span></span>to collect source code):
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRCS) add_executable(${PROJECT_NAME} ${SRCS}) # Use project name as target name
Key Functions:
- Define executable targets: This is the core entry for building executable programs, associating source code with target names;
- Carry forward subsequent configurations: After generating the target, you can use
<span><span>target_include_directories</span></span>(to add header file directories),<span><span>target_link_libraries</span></span>(to link dependency libraries),<span><span>set_compile_options</span></span>(to set compilation options), etc., to supplement configurations for this target; - Support multiple targets: The
<span><span>add_executable</span></span>command can be called multiple times in one CMakeLists.txt to generate multiple independent executable programs. Points to Note: - Source file validity: Ensure that the specified source file paths are correct (relative paths are based on the directory where the current CMakeLists.txt is located, or use absolute paths);
- Target name uniqueness: Executable target names and library target names in the same project (including subdirectories) cannot be duplicated;
- Dependency order: Source file variables (such as
<span><span>${SRCS}</span></span>) or listed source files must be defined before calling this command; if linking dependency libraries, it must be called after<span><span>add_executable</span></span>;
3.12 target_include_directories
Core: Specify header file search directories for targets
Details: A built-in CMake command used to set dedicated header file search directories for specific build targets (executable programs, libraries), allowing the target to find header files referenced by <span><span>#include</span></span> during the compilation phase, supporting control of header file visibility by dependency scope (PUBLIC/PRIVATE/INTERFACE).
Syntax:<span><span>target_include_directories(target_name scope directory_path1 [directory_path2 ...])</span></span>
- Target name: The build target defined by
<span><span>add_executable</span></span>or<span><span>add_library</span></span>(e.g.,<span><span>${PROJECT_NAME}</span></span>); - Scope (required, controls visibility):
<span><span>PRIVATE</span></span>: Only available during the compilation of the current target, other modules depending on this target cannot access it;<span><span>PUBLIC</span></span>: Available to both the current target and other modules depending on this target;<span><span>INTERFACE</span></span>: Only available to other modules depending on this target, the current target itself does not use it;- Directory paths: Support absolute paths (such as based on
<span><span>CMAKE_SOURCE_DIR</span></span>concatenation), relative paths, or paths referenced by variables.
Core Logic:
- Only affects the specified target, does not impact other targets (compared to
<span><span>include_directories</span></span>which is globally effective, this is more precise); - When compiling this target, CMake will automatically add the specified directory to the header file search path, allowing direct
<span><span>#include</span><span> "xxx.h"</span></span>(no need to write absolute paths); - Control visibility by scope, avoiding unnecessary header file exposure and reducing dependency coupling.
Example:
- Add a private header file directory for the current target (only used by itself):
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/dependency/myOSAL/${PLATFORM}/include/myOSAL/osal )
- Add a public header file directory for the library target (available to both itself and dependencies):
target_include_directories(MyLib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include # Public header file directory PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src # Private implementation header file directory )
3.13 target_link_directories
Core: Specify dedicated library search directories for targets
Details: A built-in CMake command used to set dedicated library file search directories for specific build targets (executable programs, libraries), only that target will look for the required library files in these directories during the linking phase (such as <span><span>.so</span></span>/<span><span>.a</span></span>/<span><span>.lib</span></span>, etc.), compared to <span><span>link_directories</span></span> which is more precise, avoiding global path conflicts.
Syntax:<span><span>target_link_directories(target_name scope directory_path1 [directory_path2 ...])</span></span>
- Target name: The build target defined by
<span><span>add_executable</span></span>or<span><span>add_library</span></span>(e.g.,<span><span>${PROJECT_NAME}</span></span>); - Scope (controls dependency passing):
<span><span>PRIVATE</span></span>: Only used by the current target when linking, other modules depending on this target do not inherit;<span><span>PUBLIC</span></span>: Both the current target and all modules depending on this target will use this directory;<span><span>INTERFACE</span></span>: Only modules depending on this target will use this directory, the current target itself does not use it;- Directory paths: Support absolute paths, variable concatenation paths (such as
<span><span>${PLATFORM}</span></span>adapting to different architectures) or environment variable paths (such as<span><span>$ENV{QNX_TARGET}</span></span>).
Core Logic:
- Only affects the specified target, does not impact the library search paths of other targets, providing stronger isolation;
- During the linking phase, CMake will prioritize looking for library files in the directories specified by this command when linking libraries referenced by
<span><span>target_link_libraries</span></span>, without needing to write the full library path; - Control path passing by scope, avoiding unnecessary library search directories being inherited by dependent modules, reducing the risk of conflicts with libraries of the same name.
Example:
- Add a private library search directory for the current target (only used by itself):
target_link_directories(${PROJECT_NAME} PRIVATE $ENV{QNX_TARGET}/aarch64le/io-sock/lib)
- For the library target, add public + private library directories (public paths for dependencies to use):
target_link_directories(MyLib PUBLIC ${CMAKE_SOURCE_DIR}/dependency/public/lib # Paths for dependencies to inherit PRIVATE ${CMAKE_SOURCE_DIR}/dependency/private/lib # Only used by itself)
Key Functions:
- Precise isolation: Split library search directories by target, solving the path conflict issues caused by
<span><span>link_directories</span></span>being globally effective; - Control dependency passing: Clearly define path passing scope through
<span><span>PRIVATE/PUBLIC/INTERFACE</span></span>, adapting to the interface exposure needs of library targets; - Simplify linking configuration: No need to write the full library path in
<span><span>target_link_libraries</span></span>, just specify the library name; - Adapt to complex scenarios: Support platform-specific paths, environment variable paths, meeting the linking needs of multiple systems/architectures. Points to Note:
- The target must be defined: Must call this command after
<span><span>add_executable</span></span>or<span><span>add_library</span></span>; - Scope selection: Executable targets usually use
<span><span>PRIVATE</span></span>(no need to pass to other modules), while library targets use<span><span>PUBLIC/INTERFACE</span></span>when providing interfaces externally; - Path validity: Ensure that the specified directory exists during linking; otherwise, it will lead to library lookup failures;
- Priority: The directories specified by this command have a higher priority than the global directories set by
<span><span>link_directories</span></span>, and library searches will prioritize matching them.
3.14 target_link_libraries
Core: Specify dedicated library search directories for targets
Details: A built-in CMake command used to configure dedicated library file search paths for specific build targets (executable programs, libraries), only that target will look for the required library files in the specified directories during the linking phase (such as <span><span>.so</span></span>/<span><span>.a</span></span>/<span><span>.lib</span></span>, etc.), compared to the globally effective <span><span>link_directories</span></span>, it has stronger target isolation, avoiding path conflicts.
Syntax:<span><span>target_link_directories(target_name scope directory_path1 [directory_path2 ...])</span></span>
- Target name: Must be a build target defined by
<span><span>add_executable</span></span>or<span><span>add_library</span></span>(e.g.,<span><span>${PROJECT_NAME}</span></span>, custom library name); - Scope (controls path passing rules):
<span><span>PRIVATE</span></span>: Only used by the current target when linking, other modules depending on this target do not inherit this search path;<span><span>PUBLIC</span></span>: The current target and all modules depending on this target will use this directory;<span><span>INTERFACE</span></span>: Only modules depending on this target will use this directory, the current target itself does not take effect;- Directory paths: Support absolute paths, variable concatenation paths (such as
<span><span>${PLATFORM}</span></span>adapting to different architectures), environment variable paths (such as<span><span>$ENV{QNX_TARGET}</span></span>), or relative paths (based on the directory where the current<span><span>CMakeLists.txt</span></span>is located).
Core Logic:
- The paths only affect the specified target, not the library search rules of other targets in the project, providing stronger isolation;
- During the linking phase, CMake will prioritize looking for library files in the directories specified by this command when linking libraries referenced by
<span><span>target_link_libraries</span></span>, without needing to write the full library path; - Control path passing precisely by scope, avoiding unnecessary library search directories being inherited by dependent modules, reducing the risk of conflicts with libraries of the same name.
Example:
- Add a private library directory for the current executable target (only used by itself):
target_link_directories(${PROJECT_NAME} PRIVATE $ENV{QNX_TARGET}/aarch64le/io-sock/lib)
- For custom library targets, configure public + private library directories (public paths for dependencies to use):
target_link_directories(MyCoreLib PUBLIC ${CMAKE_SOURCE_DIR}/dependency/public/lib # Paths for dependencies to inherit PRIVATE ${CMAKE_SOURCE_DIR}/dependency/private/lib # Only used by itself)
Key Functions:
- Precise isolation: Split library search directories by target, solving the path conflict issues caused by
<span><span>link_directories</span></span>being globally effective; - Control dependency passing: Clearly define path passing scope through
<span><span>PRIVATE/PUBLIC/INTERFACE</span></span>, adapting to the interface exposure needs of library targets; - Simplify linking configuration: No need to write the full library path in
<span><span>target_link_libraries</span></span>, just specify the library name; - Adapt to complex scenarios: Support platform-specific paths, environment variable paths, meeting the linking needs of multiple systems/architectures. Points to Note:
- The target must be defined: Must call this command after
<span><span>add_executable</span></span>or<span><span>add_library</span></span>; - Scope selection: Executable targets usually use
<span><span>PRIVATE</span></span>(no need to pass to other modules), while library targets use<span><span>PUBLIC/INTERFACE</span></span>when providing interfaces externally; - Path validity: Ensure that the specified directory exists during linking; otherwise, it will lead to library lookup failures;
- Priority: The directories specified by this command have a higher priority than the global directories set by
<span><span>link_directories</span></span>, and library searches will prioritize matching them.
3.15 set_compile_and_link_options
Core: Set compilation and linking options for project targets
Details: Call the custom CMake function <span><span>set_compile_and_link_options</span></span> and pass the current project name <span><span>${PROJECT_NAME}</span></span> (i.e., target name, such as the previous <span><span>myCameraProxy</span></span>) as a parameter. The core purpose of this function is to batch set compilation and linking options for targets (this function must be defined in the CMake script in advance).
Common Functions:
- Compilation options: Enable warnings (such as
<span><span>-Wall</span></span>), specify C++ standards (such as<span><span>-std=c++11</span></span>), optimization levels (such as<span><span>-O2</span></span>), macro definitions (such as<span><span>-DDEBUG</span></span>), etc.; - Linking options: Set library linking methods (static/dynamic), linker flags (such as
<span><span>-ldl</span></span>to link dynamic library loading libraries), dependency library paths, etc. By encapsulating them into functions, multiple targets’ compilation and linking rules can be managed uniformly, avoiding repetitive configurations and enhancing script maintainability.