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.XADD_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的共享库安装到
/lib目录; 将hello.h安装
/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的完整文件路径

浙公网安备 33010602011771号