CMake进阶
补充上篇博客没有提到的内容,方便更完善的构建C++程序。
链接库
以下是一段链接库的代码:
1 | target_link_libraries(hello_binary |
在CMake中,**PRIVATE
、PUBLIC
** 和 INTERFACE
是用来指定依赖库属性的关键词。它们用于控制库与目标(例如可执行文件或其他库)之间的链接方式和信息传递。
- **
PRIVATE
**:- 当你将一个库标记为
PRIVATE
时,这意味着依赖库仅在当前目标中使用,并且不会传递给由当前目标链接的其他目标。 - 在本例中,**
target_link_libraries(hello_binary PRIVATE hello_library)
** 表示只有hello_binary
可以访问hello_library
提供的功能,其他与hello_binary
无关的目标不会受到hello_library
的影响。
- 当你将一个库标记为
- **
PUBLIC
**:- 将库标记为
PUBLIC
时,这表示依赖库在当前目标中使用的同时,也会传递给由当前目标链接的其他目标。 - 在本例中,**
target_include_directories(hello_library PUBLIC ${PROJECT_SOURCE_DIR}/include)
** 会将hello_library
的头文件包含路径公开给所有链接到它的目标,这样其他目标也能使用hello_library
提供的头文件。
- 将库标记为
- **
INTERFACE
**:INTERFACE
类似于 **PUBLIC
**,但不会影响当前目标本身。它仅将依赖项传递给其他目标。- 如果你有一个中间库,它不会被链接到任何可执行文件,但你希望它的依赖项传递给其他库,那么可以使用 **
INTERFACE
**。
总结:
- 使用
PRIVATE
来限制库的使用范围为当前目标。 - 使用
PUBLIC
来将库的依赖传递给链接到当前目标的其他目标。 - 使用
INTERFACE
将依赖项传递给其他目标,而不影响当前目标。
在你的示例代码中,**target_include_directories(hello_library PUBLIC ${PROJECT_SOURCE_DIR}/include)
** 允许其他目标访问 hello_library
的头文件路径,而 target_link_libraries(hello_binary PRIVATE hello_library)
使 hello_binary
能够链接到 hello_library
的功能,但这些功能不会传递给其他目标。
设置宏定义
target_compile_definitions
是CMake中用于在目标(例如可执行文件、库等)编译过程中添加预处理宏定义的函数。预处理宏定义是在编译阶段进行文本替换的标识符,它可以影响代码的编译过程。
函数的基本语法如下:
1 | target_compile_definitions(target_name |
- **
target_name
**:目标的名称,可以是可执行文件、库等的名称。 PRIVATE
、INTERFACE
和PUBLIC
:这些关键词用于指定定义的可见性。PRIVATE
表示仅在当前目标内部可见,**INTERFACE
** 表示仅在与当前目标链接的目标中可见,**PUBLIC
** 表示在当前目标和链接到它的目标中都可见。definition1
,definition2
, …:要添加的预处理宏定义。
设置编译类型
1 | // Set a default build type if none was specified |
编译命令检查
方法一
1 | include(CheckCXXCompilerFlag) |
方法二
1 | // set the C++ standard to C++ 11 |
方法三
1 | // set the C++ standard to the appropriate standard for using auto |
CMake配置文件
configure_file
函数是CMake中的一个非常有用的工具,用于将源文件的内容复制到目标文件中,并在复制过程中进行文本替换。这对于生成配置文件、资源文件等非常有用。以下是**configure_file
** 函数的基本用法:
1 | configure_file(input_file output_file [@ONLY] [COPYONLY] |
- **
input_file
**:要作为源的输入文件路径。 - **
output_file
**:要生成的目标文件路径。 - **
@ONLY
:一个可选参数,当设置为这个值时,只有以@
**开头的变量会被替换。如果不设置这个参数,所有变量都会被替换。 - **
COPYONLY
**:一个可选参数,当设置为这个值时,只复制输入文件到输出文件,不进行变量替换。这在复制二进制文件等时很有用。
通常,您将在CMake中使用**configure_file
**来生成配置文件,其中一些变量的值在构建过程中被设置。
下面是一个示例,演示如何使用**configure_file
**生成一个配置文件:
假设你有一个名为 config.h.in
的输入文件,内容如下:
1 |
然后,在CMakeLists.txt中,您可以使用**configure_file
**来生成 config.h
配置文件:
1 | configure_file(config.h.in config.h) |
在上述示例中,CMake将 config.h.in
中的**@PROJECT_NAME@
、@PROJECT_VERSION_MAJOR@
和@PROJECT_VERSION_MINOR@
**等变量替换为实际的值,并生成了一个名为 config.h
的输出文件。您可以在代码中包含 **config.h
**,以使用正确的配置信息。
这是一个简单的示例,但是**configure_file
**函数非常有用,可以用于生成各种配置文件,包括用于在构建过程中动态生成项目信息的头文件、脚本文件等。
宏和函数的区别
在CMake中,宏(macros)和函数(functions)都是用于封装一系列操作的工具,但它们在使用和行为方面存在一些区别。以下是它们之间的主要区别:
- 参数传递方式:
- 宏(macros): 在调用宏时,参数会在调用点被展开,并将参数的值替换到宏的定义中。这意味着宏在展开时会完整地嵌入其调用点,包括所有参数的值和宏的代码。
- 函数(functions): 函数的参数是按值传递的,这意味着在函数内部使用参数时,您在函数定义中定义的参数名将作为变量使用,保存传递给函数的实际值。
- 变量作用域:
- 宏(macros): 宏的变量在调用宏时被展开到调用点,并且宏的定义中的所有变量都处于全局作用域。这意味着宏内部定义的变量可以影响宏调用点之外的代码。
- 函数(functions): 函数内部定义的变量在函数内部有效,不会影响函数调用点之外的代码。函数的参数和局部变量仅在函数内部可见。
- 返回值:
- 宏(macros): 宏没有返回值的概念。它们实际上是一组命令和逻辑的组合,宏的效果直接体现在宏的调用点。
- 函数(functions): 函数可以有返回值。您可以在函数内部使用
return()
命令来返回一个值,并在函数调用点使用该值。
- 定义方式:
- 宏(macros): 定义宏使用
macro
关键字。 - 函数(functions): 定义函数使用
function
关键字。
- 宏(macros): 定义宏使用
- 调用方式:
- 宏(macros): 调用宏使用宏的名称,后面跟上参数列表,参数之间使用空格分隔。
- 函数(functions): 调用函数使用函数的名称,后面跟上参数列表,参数之间使用分号
;
分隔。
综合起来,宏和函数在CMake中都用于封装代码块,但它们在参数传递、变量作用域、返回值等方面存在差异。您可以根据需要选择使用宏或函数来实现不同的封装和逻辑。
find_package、find_library和find_program的区别
find_package
、find_library
和 find_program
都是 CMake 中用于查找不同类型资源的命令,它们有不同的用途和区别:
find_package
:find_package
用于查找已安装的软件包或库,并导入其配置信息。它通常用于查找外部的第三方库或工具,并在项目中使用它们。**find_package
** 将检查指定的包是否安装在系统上,并在找到包时配置项目以使用该包。通过CONFIG
模式,它还可以导入该软件包的配置文件,设置编译选项、链接库等。find_library
:find_library
用于查找特定的库文件,并返回库文件的绝对路径。它主要用于查找并指定要链接到项目中的库,以便在编译和链接时使用。您可以指定库的名称以及查找路径,**find_library
** 将返回库文件的完整路径,供您在项目中使用。find_program
:find_program
用于查找可执行文件或命令。它允许您在CMake中查找特定的可执行文件,然后可以将找到的可执行文件与目标一起使用,或者执行自定义操作。例如,您可以使用find_program
来查找编译器、工具或其他外部命令。
综合起来,这些命令在CMake中都具有不同的用途,用于查找不同类型的资源。**find_package
** 用于查找库和工具,并导入其配置,**find_library
** 用于查找库文件的路径,而 find_program
则用于查找可执行文件的路径。根据您的需求,您可以选择适当的命令来在CMake项目中查找所需的资源。
option的使用
option
函数用于在CMake项目中创建用户可配置的选项。这些选项通常用于允许用户自定义项目的行为或功能开关。**option
** 函数接受三个参数:选项名称、选项描述和默认值。
其基本语法如下:
1 | option(<option_name> <option_description> <initial_value>) |
- **
<option_name>
**:选项的名称,这是用户在CMake配置中使用的标识符。 - **
<option_description>
**:选项的描述,通常是一段文本,用于说明选项的作用。 - **
<initial_value>
**:选项的默认值,可以是ON
或 **OFF
**,表示选项的初始状态。
例如,以下是一个使用 option
函数的示例:
1 | option(ENABLE_FEATURE_X "Enable feature X" ON) |
在这个示例中,我们定义了一个名为 ENABLE_FEATURE_X
的选项,它用于启用或禁用某个功能(feature X)。默认情况下,该选项的值为 **ON
**,表示功能是启用的。
要在项目中使用这个选项,您可以使用 if
语句来检查选项的值,例如:
1 | if (ENABLE_FEATURE_X) |
用户在CMake配置中可以使用 -D
选项来设置选项的值。例如,要禁用功能 X,可以执行以下命令:
1 | cmake -DENABLE_FEATURE_X:BOOL=OFF .. |
这将覆盖默认值,并在CMake配置中禁用功能 X。
总之,**option
** 函数允许您在CMake项目中创建用户可配置的选项,以便根据用户的需求来配置项目的行为。
使用FetchContent引入第三方库
FetchContent
是一个CMake模块,用于在CMake项目中下载和管理外部依赖项。它的目标是简化项目依赖项的管理,使得在项目中集成和使用第三方库更加方便。**FetchContent
** 模块提供了一组函数,其中最重要的函数是 FetchContent_Declare
和 **FetchContent_MakeAvailable
**。
以下是 FetchContent
相关函数的基本使用示例:
1 | cmakeCopy code |
上述示例中的关键步骤包括:
- 包含
FetchContent
模块:首先,使用include(FetchContent)
命令包含FetchContent
模块。 - 声明要下载的外部依赖项:使用
FetchContent_Declare
函数声明要下载的外部依赖项。您需要提供依赖项的名称以及其来源(通常是一个Git仓库的URL)。您还可以选择性地指定特定的标签或分支。 - 配置并下载外部依赖项:使用
FetchContent_GetProperties
函数检查依赖项是否已经被下载和配置。如果尚未下载,使用FetchContent_Populate
函数来下载和配置依赖项。这些函数会将依赖项的源代码下载到项目的构建目录中。 - 使用已下载的外部依赖项:在项目中使用已下载的依赖项,例如,通过链接到它们的库文件。
FetchContent
还提供了其他函数和选项,用于自定义依赖项的下载和配置过程。这使得您可以更灵活地管理和集成外部依赖项,而无需手动下载和管理它们。注意,**FetchContent
** 在CMake版本3.11及更高版本中可用。