CMake实用知识点之一
1 Q&A
-
Q : 在C++代码工程中,有**.h.in后缀的文件,有什么用?里面有这样的代码:#cmakedefine CROSS_COMPILE又是有什么作用?
-
A :
在C++代码工程中,带有 .h.in 后缀的文件通常是 CMake 配置文件模板,用于生成配置头文件。这些文件通常包含一些占位符或宏定义,这些占位符或宏定义会在配置过程中被替换为实际的值。
-
.h.in 文件的用途
- 模板文件:.h.in 文件是模板文件,其中包含占位符或预定义的宏。这些占位符将在 CMake 配置过程中被替换为实际的值,从而生成实际的头文件(通常是 .h 文件)。
- 配置时间定义:这些文件允许在 CMake 配置时根据当前的配置选项、系统属性等生成特定的宏定义。这使得生成的头文件能够反映配置过程中的条件和选项。
-
cmakedefine 的作用
- 宏替换:#cmakedefine 是 CMake 特定的指令,用于在配置过程中生成特定的宏定义。CMake 会根据 CMakeLists.txt 中的 configure_file 命令将这些指令替换为实际的宏定义或注释掉。
-
示例解释
假设有一个 config.h.in 文件:
/* config.h.in - template for config.h */ #cmakedefine CROSS_COMPILE #cmakedefine HAVE_LIBJPEG #cmakedefine VERSION "@PROJECT_VERSION@"
在 CMakeLists.txt 中,你可能有以下配置:
# 设置一些选项或检查一些条件 set(CROSS_COMPILE 1) set(HAVE_LIBJPEG 1) set(PROJECT_VERSION "1.0.0") # 生成 config.h 文件 configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY )
在 configure_file 命令执行后,CMake 会将 config.h.in 中的 #cmakedefine 指令替换为相应的宏定义,并替换占位符为实际的值,生成的 config.h 文件可能是这样的
/* config.h - generated from config.h.in by CMake */ #define CROSS_COMPILE 1 #define HAVE_LIBJPEG 1 #define VERSION "1.0.0"
-
更详细的解释
-
#cmakedefine
:在配置过程中,如果 CROSS_COMPILE 被定义了(即在 CMakeLists.txt 中调用了 set(CROSS_COMPILE 1)),那么 #cmakedefine CROSS_COMPILE 将被替换为 #define CROSS_COMPILE 1。如果 CROSS_COMPILE 没有被定义,则 #cmakedefine CROSS_COMPILE 行会被注释掉。 -
@PROJECT_VERSION@:在 configure_file 中,@PROJECT_VERSION@ 会被替换为实际的项目版本字符串 1.0.0。
-
-
配置示例
假设有以下 CMakeLists.txt 文件:
cmake_minimum_required(VERSION 3.10) project(MyProject VERSION 1.0.0) # 设置一些选项 option(CROSS_COMPILE "Enable cross compiling" ON) option(HAVE_LIBJPEG "Enable JPEG support" ON) # 生成 config.h 文件 configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY )
配置并生成项目后,CMake 会将这些指令替换为实际的宏定义,从而生成最终的 config.h 文件。
-
总结
带有 .h.in 后缀的文件是 CMake 用来生成配置头文件的模板文件。它们允许根据项目配置和系统属性生成特定的宏定义。#cmakedefine 指令用于在配置过程中生成这些宏定义,并根据条件将其替换为实际的值或注释掉。通过这种方式,CMake 可以灵活地管理配置时间的条件和选项,从而生成合适的头文件
-
-
Q:
CMAKE_FIND_ROOT_PATH
有什么用? -
A:
在交叉编译中,只有设置了该宏,才可以让
gdb
自动到该目录下去寻找相关的g++
或g--
,之后只需要CAMKE_BUILD_TYPE
和CMAKE_CXX_FLAGS_RELEASE
就可实现同一份代码不同的平台下的编译;CMAKE_FIND_ROOT_PATH
是一个 CMake 变量,用于设置根路径(root paths),这些路径将被用于搜索依赖项、库、头文件和可执行文件。它通常用于交叉编译环境中,帮助指定搜索路径的优先级和位置。-
主要用途
- 交叉编译:在交叉编译环境中,目标平台的库和头文件通常位于不同的目录中。使用 CMAKE_FIND_ROOT_PATH 可以帮助 CMake 在这些特定路径中查找依赖项,而不是在主机系统的默认路径中查找。
- 控制查找顺序:通过配置 CMAKE_FIND_ROOT_PATH_MODE_* 变量(如 CMAKE_FIND_ROOT_PATH_MODE_LIBRARY,CMAKE_FIND_ROOT_PATH_MODE_INCLUDE,CMAKE_FIND_ROOT_PATH_MODE_PROGRAM),可以控制在查找库、头文件和可执行文件时是否使用 CMAKE_FIND_ROOT_PATH 变量。
-
使用方法
设置 CMAKE_FIND_ROOT_PATH
可以在命令行或 CMakeLists.txt 文件中设置 CMAKE_FIND_ROOT_PATH:
cmake -DCMAKE_FIND_ROOT_PATH=/path/to/sysroot ..
或者在 CMakeLists.txt 文件中:
set(CMAKE_FIND_ROOT_PATH /path/to/sysroot)
-
控制查找模式
可以通过设置 CMAKE_FIND_ROOT_PATH_MODE_* 变量来控制查找行为。这些变量的值可以是 ONLY,NEVER,或 BOTH。
ONLY
:仅在 CMAKE_FIND_ROOT_PATH 中查找。NEVER
:不在 CMAKE_FIND_ROOT_PATH 中查找,只在默认路径中查找。BOTH
:在 CMAKE_FIND_ROOT_PATH 和默认路径中查找。
-
示例
# 仅在 CMAKE_FIND_ROOT_PATH 中查找库 set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # 在 CMAKE_FIND_ROOT_PATH 和默认路径中查找头文件 set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) # 不在 CMAKE_FIND_ROOT_PATH 中查找可执行文件,只在默认路径中查找 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-
-
Q:如何在交叉编译时指定编译器呢?
-
A:
设置如下的宏变量可以指定交叉编译时所要使用的编译器:
CMAKE_C_COMPILER
: 指定C编译器CMAKE_CXX_COMPILER
: 指定C++编译器
而对于交叉编译,尤其重要的是指定平台,以ARM为例,需要指定如下:
CMAKE_SYSTEM_NAME
: set(CMAKE_SYSTEM_NAME Linux)CMAKE_SYSTEM_PROCESSOER
: set(CMAKE_SYSTEM_PROCESSOR arm)
2 Knowledge point
-
**.cmake
: 称为工具链文件 -
CMAKE_CXX_COMPILER_ARG1
:是 CMake 的一个内部变量,用于指定传递给 C++ 编译器的第一个命令行参数。在大多数情况下,不会直接使用或设置这个变量。它主要用于特殊情况下,尤其是涉及某些编译器或工具链时,CMake 可能需要为编译器命令行添加特定的参数。
通常情况下,CMAKE_CXX_COMPILER_ARG1 不是用户直接配置的变量。更可能会通过工具链文件或高级构建脚本来间接设置它,如下示例:
-
示例
假设你需要为你的 C++ 编译器添加一个特殊的参数,例如 -foo,你可以在你的工具链文件(toolchain.cmake)中设置这个变量:
# toolchain.cmake,当前工具链的名字 # 设置 C++ 编译器 set(CMAKE_CXX_COMPILER /usr/bin/g++) # 设置第一个参数 set(CMAKE_CXX_COMPILER_ARG1 -foo) # 该变量是在工具链中所使用的
然后在命令行中使用该工具链文件:
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake ..
-
注意事项
- 慎重使用:CMAKE_CXX_COMPILER_ARG1 是一个内部变量,通常仅用于特定的高级配置或工具链文件。直接修改或使用它可能会导致意想不到的行为或构建错误。
- 工具链文件:在工具链文件中设置该变量是最常见的用法,因为工具链文件通常用于配置交叉编译环境或其他复杂的编译设置。
- 调试和验证:如果你必须使用这个变量,确保通过 CMake 输出(例如,cmake --trace 或检查生成的构建文件)验证编译器命令行参数的正确性。
-
结论:
CMAKE_CXX_COMPILER_ARG1 是一个用于传递给 C++ 编译器的第一个命令行参数的内部变量。它主要用于特殊配置或工具链文件中,帮助确保编译器以特定方式运行。对于大多数用户和常规项目配置,不需要直接使用或修改这个变量。如果确实需要,请谨慎操作,并在工具链文件中进行适当的设置。
-
-
条件判断语句
if (condition) ... else() ... endif()
-
set解释
SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE)
-
设置构建类型:
SET(CMAKE_BUILD_TYPE Release ...) 这部分代码将CMAKE_BUILD_TYPE变量设置为Release。这意味着当项目被构建时,默认会生成优化过的二进制文件,以便在生产环境中使用,而不是包含调试信息的版本。 -
CACHE STRING
:
表明CMAKE_BUILD_TYPE变量被添加到CMake的缓存中,并且它的类型是字符串。这意味着一旦设置,它将在CMake的缓存中保留其值,直到显式更改。这对于在多次运行CMake时保持一致的构建配置非常有用。 -
FORCE
:
关键字表示即使CMAKE_BUILD_TYPE变量已经在CMake缓存中有了值,也会强制使用这里指定的新值(即Release)。这在你想要确保构建类型总是被设置为特定值,而不受之前CMake运行的影响时非常有用。
-
-
set_target_properties
:设置目标的一些属性来改变它们构建的方式,如设置输出的名称,对于动态库,还可以用来指定动态库版本和API版本。我能够使用任何你想要的属性/值对,并且在随后的代码中调用get_target_property
后get_target
命令取出属性的值。 -
add_library
: 用于生成动态库或静态库-
静态库:静态库没有版本号
目标是
hello_static
, 源代码是${LIBHELLO_SRC}
add_library(hello_static STATIC ${LIBHELLO_SRC})
-
动态库:按照规定,动态库包含版本号
-
VERSION
:指代动态库版本, -
SOVERSION
:指代API版本 -
示例:
# 要注意到这里的使用带s的set_target_properties, 那么参数中也要使用properties,是一一对应关系 set_target_properties(hello PROPERTIES VERSION 1.2 SOVERSION 1)
-
-
-
get_target_property
: 获取某一个目标文件的属性信息,如下所示# 将当前的目标文件hello_static,生成时修改为自己想要的名字hello,这里的关键key是OUTPUT_NAME # 还有就是,原先的hello_static名字对应库文件或可执行文件会被CMake清理,而只保留一个hello对应的可执行文件 set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello") # 获取目标文件hello_static的输出文件名,这里的OUTPUT_VALUE是一个变量 get_target_property(OUTPUT_VALUE hello_static OUTPUT_NAME)
-
install
:需要将libhello.a, libhello.so.x以及hello.h安装到系统目录,才能真正让其他人开发使用- 将hello的共享库安装到
<prefix>/lib
目录; - 将
hello.h
安装到<prefix>/include/hello
目录;
示例代码如下所示,注意这里是
TARGETS
带S
,表示有多个目标文件,目标文件和输出文件是两个不同的概念,注意体会。还有就是FILES
是指非可执行文件,就是普通的源代码。而DESTINATION
后边跟着的是目的地文件夹。-
LIBRARY
:表示so动态库文件; -
ARCHIVE
: 表示lib静态库文件;install(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) install(FILES hello.h DESTINATION include/hello)
- 将hello的共享库安装到
-
可以指定so生成的位置,有两种方法
- 在主工程文件CMakeLists.txt中修改
add_subdirectory(lib)
指令来指定一个编译输出位置; - 或者在
lib/CMakeLists.txt
中添加set(LIBRARY_OUTPUT_PATH <路径>)
来指定一个新的位置;
- 在主工程文件CMakeLists.txt中修改
-
整体的
CMakeLists.txt
如下所示:SET (LIBHELLO_SRC hello.cpp) # SET (LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) # 添加动态库,关键词为shared,不需要写全称libhello.so, # 只需要填写hello即可,cmake系统会自动为你生成 libhello.X ADD_LIBRARY (hello SHARED ${LIBHELLO_SRC}) # 添加静态库,关键词为static,ADD_LIBRARY (hello STATIC ${LIBHELLO_SRC}) # 仍然用hello作为target名时,是不能成功创建所需的静态库的, # 因为hello作为一个target是不能重名的, 故把上面的hello修改为hello_static # 同理,我不需要写全libhello_static.a # 只需要填写hello即可,cmake系统会自动为我生成 libhello_static.X ADD_LIBRARY (hello_static STATIC ${LIBHELLO_SRC}) # 按照一般的习惯,静态库名字跟动态库名字应该是一致的,只是扩展名不同; # 即:静态库名为 libhello.a; 动态库名为libhello.so ; # 所以,希望 "hello_static" 在输出时,不是"hello_static",而是以"hello"的名字显示,故设置如下: SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello") GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME) MESSAGE (STATUS "This is the hello_static OUTPUT_NAME: " ${OUTPUT_VALUE}) # cmake在构建一个新的target时,会尝试清理掉其他使用这个名字的库, # 因此,在构建libhello.a时,就会清理掉libhello.so. # 为了回避这个问题,比如再次使用SET_TARGET_PROPERTIES定义 CLEAN_DIRECT_OUTPUT属性。 SET_TARGET_PROPERTIES (hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES (hello PROPERTIES CLEAN_DIRECT_OUTPUT 1) # 按照规则,动态库是应该包含一个版本号的, # VERSION指代动态库版本,SOVERSION指代API版本。 SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1) # 我们需要将libhello.a, libhello.so.x以及hello.h安装到系统目录,才能真正让其他人开发使用, # 在本例中我们将hello的共享库安装到<prefix>/lib目录; # 将hello.h安装<prefix>/include/hello目录。 INSTALL (TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) INSTALL (FILES hello.h DESTINATION include/hello)
-
set_target_properties
有哪些key?是 CMake 中的一个命令,用于设置特定目标(例如可执行文件、库等)的属性。以下是一些常见的 key 属性及其含义:
4 + 2 + 2 + 2 + 1
-
ARCHIVE_OUTPUT_DIRECTORY
:指定目标的归档文件(静态库)的输出目录。 -
LIBRARY_OUTPUT_DIRECTORY
: 指定目标的库文件(共享库、模块库等)的输出目录。 -
RUNTIME_OUTPUT_DIRECTORY
: 指定目标的运行时文件(可执行文件)的输出目录。 -
OUTPUT_NAME
:指定目标生成的文件的名称,而不是使用默认的目标名称。 -
VERSION
:设置目标的版本号(通常用于共享库)。 -
SOVERSION
:设置共享库的接口版本号。 -
COMPILE_FLAGS
:设置编译器标志(flags)。如:-std=c++0x
是指C++11
,是针对编译器而言的设置,是控制编译的不同行为的开关 -
COMPILE_DEFINITIONS
: 为目标添加编译器定义(如宏定义) -
LINK_FLAGS
:设置链接器标志(flags)。比如LINK_FLAGS "-Wl,-zdefs"
-Wl
:告诉编译器将后面的选项传递给链接器,是GCC和Clang常用前缀,用于将选项传递给链接器-z defs
:表示连接器在链接时检查所有符号,确保所有符号在链接时得到解析。若有任何未定义的符号,链接过程将失败并报告错误
-
INCLUDE_DIRECTORIES
: 为目标添加包含目录。 -
INSTALL_RPATH
: 设置目标在安装后的运行时搜索库的路径(即 RPATH)。 -
CLEAR_DIRECT_OUTPUT
: 设置为 1 时,CMake 会在生成新的目标输出文件之前清除以前生成的同名文件。这对于避免冲突和确保生成的文件是最新的非常有用。
以下是一个使用 set_target_properties 的简单示例:
add_executable(myapp main.cpp) set_target_properties(myapp PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" OUTPUT_NAME "my_application" VERSION "1.0.0" SOVERSION "1" COMPILE_FLAGS "-Wall" )
- 详细介绍
-
COMPILE_DEFINITIONS
: 为目标添加编译器定义(如宏定义),就是在开始编译时,源代码中添加相应的宏。这样在源代码中,就可以使用预处理命令#ifdef #endif
,来选择性的打开某些我们关心的代码,示例如下:以下是一个简单的示例 CMakeLists.txt 文件,展示了如何使用 COMPILE_DEFINITIONS 属性:
cmake_minimum_required(VERSION 3.0) project(MyProject) # 添加一个可执行文件 add_executable(myapp main.cpp) # 设置编译器定义 target_compile_definitions(myapp PRIVATE DEBUG_MODE # 定义一个名为 DEBUG_MODE 的宏 VERSION_NUMBER=123 # 定义一个名为 VERSION_NUMBER 的宏,并设定其值为 123 USE_FEATURE_X # 定义一个名为 USE_FEATURE_X 的宏,没有赋值,相当于 #define USE_FEATURE_X ) # 另一种设置方式,直接使用 set_target_properties,注意它们之间使用分号隔开 # set_target_properties(myapp PROPERTIES # # 就是对应Visual Studio中项目属性页中配置属性/C/C++/预处理器中的预处理器定义,由于可以有很多,所有使用S结尾 # COMPILE_DEFINITIONS "DEBUG_MODE;VERSION_NUMBER=123;USE_FEATURE_X" # )
-
INSTALL_RPATH
: 设置目标在安装后的运行时搜索库的路径(即 RPATH)。用途: 当我安装一个目标(例如共享库或可执行文件)时,
RPATH
用于指定该目标在运行时如何找到它所依赖的共享库。假设有一个目标 myapp,它依赖于某些安装在特定目录(例如 /usr/local/lib)中的库。我就可以使用
INSTALL_RPATH
属性来指定安装后的运行路径add_executable(myapp main.cpp) set_target_properties(myapp PROPERTIES INSTALL_RPATH "/usr/local/lib" )
-
-
-
CMAKE_INSTALL_PREFIX
:宏展开之后为/usr/local
,该目录通常存放本地安装的软件和文件
-
INSTALL
: 该函数用于指定项目安装的目标和安装路径,其中-
RUNTIME
:指定可执行文件的安装路径。与其他关键字(如LIBRARY
和ARCHIVE
)一起使用,可以分别为可执行文件、共享库和静态库指定安装路径。- 若value指定为绝对路径,就会用使用到
/usr/local
- 若value指定为相对路径,就会使用了
${CMAKE_INSTALL_PREFIX}
, 即安装在/usr/local
下
- 若value指定为绝对路径,就会用使用到
-
LIBRARY
:指定共享库文件安装路径。 -
ARCHIVE
:指定静态库指定安装路径。 -
EXPORT
: 导出目标及其属性,以便其它项目能够使用find_package
导入这些目标,注意这里是导入库了,库中可以有很多静态库或动态库以上三种二进制文件都是目标文件,所以统一用
TARGETS
来表示
INSTALL 中的 EXPORT 属性
-
作用: 导出目标及其属性,以便其他项目能够使用 find_package 导入这些目标。
-
用途: 使项目的构建配置可以被其他项目重用,简化依赖管理和集成过程。
-
使用示例
假设我有一个库项目,并希望其它项目能够使用这个库,可以这样设置
cmake_minimum_required(VERSION 3.10) project(MyLibraryProject) # 创建一个库 add_library(mylib mylib.cpp) # 定义安装目标 install(TARGETS mylib EXPORT MyLibraryTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin INCLUDES DESTINATION include ) # 安装头文件 install(FILES mylib.h DESTINATION include) # 安装导出文件 install(EXPORT MyLibraryTargets FILE MyLibraryTargets.cmake DESTINATION lib/cmake/MyLibrary )
在这个示例中:
add_library(mylib mylib.cpp)
: 创建了一个库 mylib。install(TARGETS mylib ... EXPORT MyLibraryTargets ...)
: 指定安装目标,并将mylib
导出为 MyLibraryTargets。install(EXPORT MyLibraryTargets ...)
: 安装导出文件MyLibraryTargets.cmake
,这个文件包含了mylib
的安装信息。
生成导出文件
生成的
MyLibraryTargets.cmake
文件包含了关于 mylib 的安装路径和其他相关信息,使得其他项目可以通过 find_package 轻松导入 mylib。在其他项目中使用导出的目标
假设有一个新的项目想要使用导出的 mylib 库,可以这样写:
cmake_minimum_required(VERSION 3.10) project(MyAppProject) # 查找安装的库,注意:安装的库和链接导入的库是两个不同的概念,安装的库通过`install(... export MyLib)`来完成,并生成对象的MyLib.cmake文件,find_package通过该文件找到安装的库名,并找到该库名下的静态库文件 find_package(MyLibrary REQUIRED PATHS /path/to/installed/lib/cmake/MyLibrary) # 创建可执行文件,并链接导入的库 add_executable(myapp main.cpp) target_link_libraries(myapp PRIVATE mylib)
在这个示例中:
find_package(MyLibrary REQUIRED PATHS /path/to/installed/lib/cmake/MyLibrary)
: 查找并导入 MyLibrary。target_link_libraries(myapp PRIVATE mylib)
: 链接导入的 mylib 库。
优势:
- 简化依赖管理: 通过导出和导入目标,可以轻松管理项目之间的依赖关系,而不需要手动指定库的路径和其他细节。
- 提高可重用性: 项目的构建配置可以被其他项目重用,减少重复工作。
- 增强集成性: 使用 EXPORT 属性,可以更好地集成多个项目,特别是当这些项目需要共享库或其他目标时。
总结:
INSTALL 命令中的 EXPORT 属性用于导出目标的安装信息。
导出的信息可以生成一个 CMake 脚本文件,使其他项目可以通过
find_package
导入这些目标。这种机制简化了依赖管理,提高了项目的可重用性和集成性
-
使用 RUNTIME 关键字的示例
假设我有一个可执行文件 myapp 和一个库 mylib,我希望在安装过程中将它们分别安装到不同的目录中,如下所示:
cmake_minimum_required(VERSION 3.10)
project(MyProject)
# 创建库
add_library(mylib SHARED mylib.cpp)
# 创建可执行文件
add_executable(myapp main.cpp)
# 链接库
target_link_libraries(myapp PRIVATE mylib)
# 安装库文件
install(TARGETS mylib
LIBRARY DESTINATION lib
)
# 安装可执行文件
install(TARGETS myapp
RUNTIME DESTINATION bin
)
-
文件说明:
RUNTIME
: 指定安装可执行文件(如 .exe、.bin、.dll 文件)的路径。主要用于安装可运行的二进制文件。LIBRARY
: 指定安装动态库(如 .so 文件)的路径。主要用于安装动态链接库。ARCHIVE
: 指定安装静态库(如 .a 文件)的路径。主要用于安装静态链接库。
以下是一个综合的例子:
cmake_minimum_required(VERSION 3.10) project(MyProject) # 创建静态库 add_library(mylib_static STATIC mylib_static.cpp) # 创建动态库 add_library(mylib_shared SHARED mylib_shared.cpp) # 创建可执行文件 add_executable(myapp main.cpp) # 链接动态库 target_link_libraries(myapp PRIVATE mylib_shared) # 安装静态库 install(TARGETS mylib_static ARCHIVE DESTINATION lib/static ) # 安装动态库 install(TARGETS mylib_shared LIBRARY DESTINATION lib/shared ) # 安装可执行文件 install(TARGETS myapp RUNTIME DESTINATION bin )
-
$ENV{...}
在 CMake 中,
$ENV{...}
用于访问环境变量。通过这种方式,我可以在 CMake 脚本中使用操作系统环境变量的值。例如,$ENV{ASDF} 将访问名为 ASDF 的环境变量的值。-
使用示例
假设我有一个环境变量 ASDF,并希望在 CMake 脚本中使用它的值,可以这样做:设置环境变量,在命令行中设置环境变量 ASDF,如下所示:
export ASDF=/path/to/some/directory
-
在 CMake 脚本中使用环境变量
在我的 CMakeLists.txt 文件中,可以使用 $ENV{ASDF} 来访问 ASDF 环境变量的值:cmake_minimum_required(VERSION 3.10) project(MyProject) # 获取环境变量 ASDF 的值 set(MY_ASDF_PATH $ENV{ASDF}) # 打印环境变量的值 message(STATUS "ASDF is set to ${MY_ASDF_PATH}") # 使用环境变量的值 include_directories(${MY_ASDF_PATH}/include) link_directories(${MY_ASDF_PATH}/lib) add_executable(myapp main.cpp) target_link_libraries(myapp mylib)
在这个示例中:
set(MY_ASDF_PATH $ENV{ASDF}) 将环境变量 ASDF 的值赋给 CMake 变量 MY_ASDF_PATH。 message(STATUS "ASDF is set to ${MY_ASDF_PATH}") 打印环境变量 ASDF 的值。 include_directories(${MY_ASDF_PATH}/include) 使用环境变量 ASDF 的值指定包含目录。 link_directories(${MY_ASDF_PATH}/lib) 使用环境变量 ASDF 的值指定库目录。
适用场景
- 配置依赖路径: 例如,您可以将第三方库的路径设置为环境变量,并在 CMake 脚本中引用它,以便在不同的开发环境中灵活配置。
- 条件编译: 根据环境变量的值来启用或禁用某些功能或编译选项。
- 跨平台构建: 使用环境变量来处理不同平台上的路径或设置差异。
注意事项
确保在运行 CMake 之前环境变量已被设置。
如果环境变量未设置或为空,CMake 中对应的变量也将为空,可能会导致构建错误。因此,可以使用 CMake 的 if 语句来检查环境变量是否设置。
if(NOT DEFINED ENV{ASDF}) message(FATAL_ERROR "Environment variable ASDF is not set") endif()
通过这种方式,您可以在构建过程中灵活地使用和管理环境变量,从而更好地适应各种构建需求和环境。
-
-
INCLUDE_DIRECTORIES
:在 CMake 中,
INCLUDE_DIRECTORIES
指令用于指定编译器在编译源文件时搜索头文件的目录。INCLUDE_DIRECTORIES
还可以使用BEFORE
关键字来控制包含目录的优先级。多个
BEFORE
,后面指定的BEFORE
目录的优先级更高使用示例
include_directories(BEFORE ${PROJ_SOURCE_DIR}/src/)
解释
-
${PROJ_SOURCE_DIR}/src/
: 这是一个包含目录的路径,它通常是项目源代码目录的子目录。在这个示例中,它表示在 ${PROJ_SOURCE_DIR} 下的 src 目录。 -
BEFORE
: 这个关键字用于确保指定的包含目录在包含搜索路径列表的前面(即具有更高的优先级)。
含义
通过使用 BEFORE 关键字,您可以确保 ${PROJ_SOURCE_DIR}/src/ 目录中的头文件比其他任何通过 include_directories 指定的目录中的头文件更优先被找到和使用。
详细解释:
-
优先级控制:
当编译器查找头文件时,它会按照包含目录的顺序进行搜索。使用 BEFORE 关键字,可以将 ${PROJ_SOURCE_DIR}/src/ 放在搜索路径的前面,确保该目录中的头文件首先被找到。
-
避免命名冲突:
如果项目中有多个包含目录,且这些目录中有同名的头文件,使用 BEFORE 可以确保指定目录中的头文件优先被使用,避免不必要的命名冲突。
-
示例场景
假设我的项目结构如下:
MyProject/ ├── CMakeLists.txt ├── src/ │ └── myheader.h └── include/ └── myheader.h
在 CMakeLists.txt 中:
cmake_minimum_required(VERSION 3.10) project(MyProject) # 指定源文件 add_executable(myapp src/main.cpp) # 指定包含目录,使用 BEFORE 确保 src 目录优先 include_directories(BEFORE ${PROJ_SOURCE_DIR}/src/) include_directories(${PROJ_SOURCE_DIR}/include/)
在这个例子中,如果 src/main.cpp 文件包含了 #include "myheader.h",那么编译器会首先在 src/ 目录中查找 myheader.h,因为 src/ 目录被指定为优先目录。只有在 src/ 目录中找不到 myheader.h 时,编译器才会去 include/ 目录中查找。
通过这种方式,可以更精细地控制包含目录的搜索顺序,以满足项目的特定需求。
-
-
PROJ_SOURCE_DIR
: 表示当前工程的根目录,获取方式为${PROJ_SOURCE_DIR}
-
$<TARGET_FILE:ABC>
: 是生成器表达式,用于获取可执行文件ABC的完整文件路径