GitHub - wzpan/cmake-demo: 《CMake入门实战》源码
CMake Reference Documentation — CMake 3.27.6 Documentation
Key Concepts — Mastering CMake
CMake 入门与进阶_.cmake文件_行稳方能走远的博客-CSDN博客
A CMake-based buildsystem is organized as a set of high-level logical targets. Each target corresponds to an executable or library, or is a custom target containing custom commands. Dependencies between the targets are expressed in the buildsystem to determine the build order and the rules for regeneration in response to change.
cmake编写:cmake 编写的过程实际上是编程的过程,不过你需要编写的是 CMakeLists.txt(确保每个目录一个,注意文件名大小写.),使用的是”cmake 语言和语法”。
CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的。符号 #
后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。
1,变量使用 ${} 方式取值,但是在 IF 控制语句中是直接使用变量名
2,指令(参数 1 参数 2...) 参数使用括弧括起,参数之间使用空格或分号分开。
指令是大小写无关的,参数和变量是大小写相关的。
编译项目:在当前目录执行 cmake .
,得到 Makefile 后再使用 make
命令编译。
执行 cmake . (注意命令后面的点号,代表本目录)后,你会发现,系统自动生成了:
CMakeFiles, CMakeCache.txt, cmake_install.cmake 等文件,并且生成了 Makefile.
现在不需要理会这些文件的作用,以后你也可以不去理会。最关键的是,它自动生成了 Makefile.
如果你需要看到 make 构建的详细过程,可以使用 make VERBOSE=1 或者 VERBOSE=1
make 命令来进行构建。
多个目录,多个源文件:
1. 在每个目录里创建 CMakeLists.txt 文件。在顶层目录的 CMakeLists.txt 里使用命令 add_subdirectory
指明本项目包含一个子目录 math,这样 math 目录下的 CMakeLists.txt 文件和源代码也会被处理 。 使用命令 target_link_libraries
指明可执行文件 main 需要连接一个名为 MathFunctions 的链接库 。
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo3)
# 查找当前目录下的所有源文件,并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 添加 math 子目录
add_subdirectory(math)
# 指定生成目标
add_executable(Demo main.cc)
# 添加链接库
target_link_libraries(Demo MathFunctions)
Add a subdirectory to the build.
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM])
这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除,比如,工程的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建(当然,你也可以通过定义依赖来解决此类问题)。
add_subdirectory(src bin) 了将 src 子目录加入工程,并指定编译输出(包含编译中间结果)路径为 bin 目录。如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在 build/src 目录(这个目录跟原有的 src 目录对应),指定 bin 目录后,相当于在编译时将 src 重命名为 bin,所有的中间结果和目标二进制都将存放在 bin 目录。
可以使用 CMake 的变量来指定可执行文件输出路径。例如,将可执行文件输出到 bin 目录:set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
可以使用 CMake 的变量来指定库文件输出路径。例如,将静态库输出到 lib 目录:set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
可以使用 CMake 的变量设置动态库输出路径(注意这个放在最后):
set_target_properties(dllname PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR/lib)
CMake 可以通过 set_target_properties 函数来指定一个文件的输出路径。这个函数用于设置目标属性,包括输出路径、编译选项等。
假设我们需要将一个名为 mylib.so 的共享库文件输出到 build/lib 目录下,可以使用以下代码:
add_library(mylib SHARED mylib.cpp)
# 指定mylib.so的输出路径
set_target_properties(mylib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
类似地,如果你想要将一个可执行文件 output.exe 输出到 build/bin 目录下,可以使用以下代码:
add_executable(output main.cpp)
# 指定output.exe的输出路径
set_target_properties(output PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
注意:如果你使用了多个target,你需要分别对每个target使用set_target_properties进行配置。
2. 子目录中的 CMakeLists.txt:使用命令 add_library
将 src 目录中的源文件编译为静态链接库。
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)
# 生成链接库
add_library (MathFunctions ${DIR_LIB_SRCS})
一 、add_library 介绍
使用该命令可以在Linux下生成(静态/动态)库so或者.a文件,Windows下就是dll与lib文件,它有两种命令格式:
1. Normal Libraries 普通库,add_library命令的格式为:
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[<source>...])
生成一个名为 < name > 的library,注意以下规则:
- < name > 应该保证在一个项目中的唯一性。
- 实际生成的 library文件名是基于平台的约定规则,比如 linux 下的 lib< name >.a, Windows下的 < name >.lib 等
- STATIC,SHARED,MODULE 用于指定创建的 library 类型。
- STATIC库:生成 obj 文件后,将其链接成静态库,用于链接到其他 targets。
- SHARED库:生成 obj 文件后,将其链接成动态库,用于运行时加载。
- MODULE库:不能链接到其它 targets,但是可以用 dlopen 之类的方法在运行时动态加载。
- 如果没有明确指定要生成的 library 的类型到底是 STATIC,SHARED 还是 MODULE。则查看 BUILD_SHARED_LIBS 变量,如果值为 ON,则默认是 SHARED,否则默认 STATIC。
- 对于 SHARED 和 MODULE 类型的库,POSITION_INDEPENDENT_CODE 属性自动置为ON。
- EXCLUDE_FROM_ALL:加了EXCLUDE_FROM_ALL 属性的 target 在默认编译的时候,不会被编译,如果要编译它们,需要手动编译。
- source: 构建库的文件
By default, the add_library() command defines a STATIC
library, unless a type is specified.
2. 对象库,生成 obj 文件对象,该对象库只编译源文件,但不链接。
add_library(<name> OBJECT [<source>...])
这种方式,只编译 source 列表的文件,但不将生成的目标文件打包或者链接为库,而是在其他 add_library() 或者 add_executable() 生成目标的时候,可以使用形如 $<TARGET_OBJECTS:name> 的表达式将对象库作为源引入。其中,name 是对象库的名称。格式如下:
add_library(... $<TARGET_OBJECTS:name> ...)
add_executable(... $<TARGET_OBJECTS:name> ...)
The OBJECT
library type defines a non-archival collection of object files resulting from compiling the given source files. The object files collection may be used as source inputs to other targets by using the syntax $<TARGET_OBJECTS:name>.
add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)
add_library(archiveExtras STATIC $<TARGET_OBJECTS:archive> extras.cpp)
add_executable(test_exe $<TARGET_OBJECTS:archive> test.cpp)
这种方式不常使用,因为静态库就是把文件直接打包,这种情况直接使用静态库就行。
二 、target_link_libraries 的介绍
指定链接给定目标和/或其依赖项时要使用的库。命名的 <tartget> 必须是由 add_executable() 或add_library() 之类的命令创建的。一般与 link_directories 连用(添加外部库的搜索路径 )
target_link_libraries 命令的格式:
target_link_libraries(<target> ... <item>... ...)
大家习惯通过 $<TARGET_OBJECTS:name> 引用 object 文件。其实 object library 与 静态库,动态库一样,都是可以设置 PUBLIC/PRIVATE 属性的,再通过 target_link_library() 命令链接。
三、target_include_directories
该命令用于为指定的目标(target)添加包含目录(include directories)。指定编译目标时需要搜索头文件的路径,以确保编译器可以找到所需的头文件,这对于创建 CMake 项目中的库和可执行文件非常有用,因为它们可能依赖于其他模块或库的头文件。
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
参数说明
- target:要添加包含目录的目标名称。
- SYSTEM:可选参数,指定被包含的目录是系统目录。这会告诉编译器将这些目录视为系统的标准头文件目录。
- BEFORE:可选参数,指定将包含目录添加到已有的包含目录之前,而不是默认的添加到后面。
- INTERFACE、PUBLIC、PRIVATE:指定包含目录的可见性级别。
- INTERFACE:表示包含目录将应用于目标及其使用该目标的其他目标。
- PUBLIC:表示包含目录将应用于目标本身和使用该目标的其他目标。
- PRIVATE:表示包含目录仅应用于目标本身。
- items:指定要添加的包含目录路径,可以是目录名、绝对路径或相对路径。
三、add_library 的实例
1. 文件结构:
2. 第一种 library 格式
cmake_minimum_required (VERSION 3.12.1)
project (Demo)
# 生成对象库文件
add_library(hello hello.cpp)
# 添加头文件目录
target_include_directories(hello PUBLIC ${CMAKE_SOURCE_DIR}/public)
# 生成可执行文件
add_executable(Demo main.cpp)
# 链接对象库
target_link_libraries(Demo hello)
3. 第二种 object 格式
cmake_minimum_required (VERSION 3.12.1)
project (Demo)
# 生成对象库文件,不链接
add_library(hello OBJECT hello.cpp)
# 添加头文件目录
target_include_directories(hello PUBLIC ${CMAKE_SOURCE_DIR}/public)
# 添加编译选项 -Wall
target_compile_options(hello PUBLIC -Wall)
# 生成可执行文件
add_executable(Demo main.cpp $<TARGET_OBJECTS:hello>)
# 添加头文件目录
target_include_directories(Demo PUBLIC ${CMAKE_SOURCE_DIR}/public)
大家习惯通过 $<TARGET_OBJECTS:name> 引用 object 文件,尤其 object 是多文件时。
也可以直接指定链接文件:
cmake_minimum_required (VERSION 3.12.1)
project (Demo)
# 生成对象库文件,不链接
add_library(hello OBJECT hello.cpp)
# 添加头文件目录
target_include_directories(hello PUBLIC ${CMAKE_SOURCE_DIR}/public)
# 添加编译选项 -Wall
target_compile_options(hello PUBLIC -Wall)
# 生成可执行文件
add_executable(Demo main.cpp)
# 链接对象库
target_link_libraries(Demo hello)
A-------------------------
add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)
add_library(archiveExtras STATIC $<TARGET_OBJECTS:archive> extras.cpp)
add_executable(test_exe $<TARGET_OBJECTS:archive> test.cpp)
B-------------------------
add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)
add_library(archiveExtras STATIC extras.cpp)
target_link_libraries(archiveExtras PUBLIC archive)
add_executable(test_exe test.cpp)
target_link_libraries(test_exe archive)
支持 gdb
让 CMake 支持 gdb 的设置也很容易,只需要指定 Debug 模式下开启 -g 选项:
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
之后可以直接对生成的程序使用 gdb 来调试。
自定义编译选项
CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。
例如,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON
,就使用该库定义的数学函数来进行运算。否则就调用标准库中的数学函数库。
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo4)
# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# 是否使用自己的 MathFunctions 库
option (USE_MYMATH "Use provided math implementation" ON)
# 是否加入 MathFunctions 库
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/math")
add_subdirectory (math)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# 查找当前目录下的所有源文件,并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo ${EXTRA_LIBS})
-
configure_file
命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。 option
命令添加了一个USE_MYMATH
选项,并且默认值为ON
。- 根据
USE_MYMATH
变量的值来决定是否使用我们自己编写的 MathFunctions 库。
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#ifdef USE_MYMATH
#include "math/MathFunctions.h"
#else
#include <math.h>
#endif
编写 config.h.in 文件
上面的程序值得注意的是引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH 的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件,内容如下:
#cmakedefine USE_MYMATH
这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件。
设置 USE_MYMATH
选项值为 ON
。此时 config.h 的内容为:
#define USE_MYMATH
设置 USE_MYMATH
选项值为 OFF
。此时 config.h 的内容为:
/* #undef USE_MYMATH */
cmake-commands(7)
内部构建与外部构建
内部构建(in-source build) 就是直接在源代码目录里构建。相信看到生成的临时文件比您的代码文件还要多的时候,估计这辈子你都不希望再使用内部构建。
外部构建(out-of-source build)就是让编译中间文件和源代码分离。
外部编译的过程如下:
- 首先,请清除工程目录中除代码原文件和 CmakeLists.txt 之外的所有中间文件,最关键的是CMakeCache.txt。
- 在工程目录中建立 build 目录,当然你也可以在任何地方建立 build 目录,不一定必须在工程目录中。
- 进入 build 目录,运行 cmake .. (注意,.. 代表父目录,因为父目录存在我们需要的 CMakeLists.txt,如果你在其他地方建立了 build 目录,需要运行 cmake <工程的全路径>),查看一下 build 目录,就会发现了生成了编译需要的 Makefile 以及其他的中间文件.
- 运行 make 构建工程,就会在当前目录(build 目录)中获得目标文件 xxxx。
进入 build 目录进行外部编译。
out-of-source 外部编译,一个最大的好处是,对于原有的工程没有任何影响,所有动作全部发生在编译目录。
指定编译器
可以使用 CMake 的变量来指定编译器。例如,使用 GCC 编译器:
set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)
指定编译选项
可以使用 CMake 的变量来指定编译选项。例如,指定编译器优化选项:
set(CMAKE_CXX_FLAGS_RELEASE \O3\)
cmake 项目
CMake的一个基本概念是项目既要有 source directory 又要有 binary directory。
source directory 是 CMakeLists.txt 文件所在的位置,项目的源文件和构建所需的所有其他文件都组织在该位置下。源目录通常使用 git、subversion 或类似工具进行版本控制。
binary directory 是创建 build 生成的所有内容的地方。它通常也被称为 build directory。CMake通常使用术语 build directory,但在开发人员中,术语 build directory 更常用。
cmake --build . 该命令的含义是:执行当前目录下的构建系统,生成构建目标。
作用: cmake --build .
效果与 make
等价,但可以自动识别适配当前平台的生成器,无需手动指定采用 mingw32-make 还是 Nmake,常用于跨平台脚本中。
注:其用于cmake ..
命令之后。
cmake 项目构建过程简述:
- 首先,使用命令行: ‘cmake <source tree>’,比如:cmake .. ,在构建目录(外部构建方式)下生成了项目文件 project files,官方文档中又叫 build tree/binary tree,这其中就包括,比如:Makefile,还有一些其他相关文件/目录/子目录;
- 其次,自然是对生成好的项目 (project files) 进行编译构建,使用到 'cmake --build .'
- 最后,--build后面的那个‘.’,指的是生成好的 build tree 的路径。 一般来说,如果明确知道系统中使用的是哪种构建器 (build generator),比如:Unix Makefiles,完全可以直接使用 make 进行项目构建。
对于这种 --build 的形式,多用于自动化脚本之中,或者IDE环境下。
注: <source tree> 指的是源文件+顶层 CMakeLists 所在的路径,cmake .. 假设了路径在上一层。通过 cmake . / cmake .. 命令创建 Makefile 文件后,一般使用 make 命令编译文件。这里的cmake --build . 就与 make 一样的效果。
为什么不直接 make,而是使用 cmake --build 形式的命令,主要是为了跨平台,使用这种形式后,不管你是使用的什么生成器,CMake 都能正确构建,否则如果使用的是 Ninja 或者其他生成器,那 make 就不生效了。
【Cmake】.cmake 文件是什么|有什么用?
.cmake 文件 是给 cmake 执行用的:cmake -P /path/of/xxx.cmake
Process the given cmake file as a script written in the CMake language. No configure or generate step is performed and the cache is not modified. If variables are defined using -D, this must be done before the -P argument.
.cmake 文件的作用
cmake 可以被 include,以达到复用其中函数的作用。
就像 CMakeList.txt 的库一样,加载后可以在 CMakeList.txt 中使用它的一些函数和定义?
Cmake 官方教程(翻译)-面圈网 (mianshigee.com)
Mordern CMake 简体中文版
Introduction · Modern CMake (modern-cmake-cn.github.io)
------------------------------------------------------------------------------------
CMake基础篇---用一个小型项目了解CMake及环境构建 来自 <学习C++:C++进阶(三)CMake基础篇---用一个小型项目了解CMake及环境构建>
我们平常在Linux上用命令编译链接文件时,可能会涉及以下几项:
add_library — CMake 3.22.0 Documentation
target_link_libraries — CMake 3.22.0 Documentation
一、add_library 介绍
add_library命令用来使用指定的源文件向工程中添加一个目标库,使用该命令可以在 Linux 下生成(静态/动态)库 so 或者 .a 文件,Windows下就是 dll 与 lib 文件,它有两种命令格式。
1.1 第一种格式 :
Normal Libraries,add_library 命令的格式为:
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
source1 [source2 ...])
生成一个名为 <name> 的 library,注意以下规则:
- <name> 应该保证在一个项目中的唯一性。
- 实际生成的 library 文件名是基于平台的约定规则,比如linux下的lib<name>.a, Windows下的<name>.lib等
- STATIC,SHARED,MODULE 用于指定创建的 library 类型。
- STATIC库:生成obj文件后,将其链接成静态库,用于链接到其他 targets。
- SHARED库:生成obj文件后,将其链接成动态库,用于运行时加载。
- MODULE库:不能链接到其他 targets,但是可以用 dlopen 之类的方法在运行时动态加载。
- 如果没有明确指定要生成的 library 的类型到底是 STATIC,SHARED 还是 MODULE。则查看 BUILD_SHARED_LIBS 变量,如果值为 ON,则默认是SHARED,否则默认 STATIC。
- 对于 SHARED 和 MODULE 类型的库,POSITION_INDEPENDENT_CODE 属性自动置为 ON。
- EXCLUDE_FROM_ALL:表明该 target 是否从默认构建 target 中排除。加了 EXCLUDE_FROM_ALL 属性的 target 在默认编译的时候,不会被编译,如果要编译它们,需要手动编译。
- source参数可以使用 generator表达式($ <…>)。构建库的源文件可以直接指定,也可以后续使用 target_sources() 指定。
generator 表达式 :Generator expressions are evaluated during build system generation to produce information specific to each build configuration.
逻辑表达式:
$<BOOL : …> : 如果…是true,则为1,否则为0.
$<AND:?[,?]… > : 如果所有?都是1,则为1,否则为0.
$<EQUAL:a,b > : 如果数字a等于数字b,则为1,否则为0.
信息表达式
$<C_COMPILER_VERSION> : C compiler的版本.
$<TARGET_FILE:tgt> : 名为tgt的主文件的全路径。
输出表达式
$<0:…> : 输出空字符串,忽略…内容。
$<1:…> : 输出…的内容。
$<JOIN:list, … > :将…内容加到list上。
1.2 第二种格式 :
生成 obj 文件对象库,该对象库只编译源文件,但不链接。
add_library(<name> OBJECT [<source>...])
这种形式类型固定为 OBJECT,以这种方式,只编译 source 列表的文件,但不将生成的目标文件打包或者链接为库,而是在其他 add_library() 或者add_executable() 生成目标的时候,可以使用形如 $<TARGET_OBJECTS:name> 的表达式将对象库作为源引入。其中,name 是对象库的名称。格式如下:
add_library(... $<TARGET_OBJECTS:name> ...)
add_executable(... $<TARGET_OBJECTS:name> ...)
使用方式:
add_library(<objlib> OBJECT [<source>...])
add_library(... $<TARGET_OBJECTS:objlib> ...)
add_executable(... $<TARGET_OBJECTS:objlib> ...)
具体示例:
add_library(test_library OBJECT a.cpp b.cpp c.cpp)
add_executable(test_app main.cpp $<TARGET_OBJECTS:test_library>)
add_library(anotherlib STATIC other.cpp $<TARGET_OBJECTS:test_library>)
这种方式不常使用,因为静态库就是把文件打包,这种情况直接使用静态库就行,了解即可。
1.3 接口库
add_library(<name> INTERFACE [IMPORTED [GLOBAL]])
生成一个接口库,这类库不编译任何文件,也不在磁盘上产生库文件。它有一些属性被设置,并且能够被安装和导出。通常,使用以下命令在接口目标上填充属性。
- set_property()
- target_link_library(INTERFACE)
- target_link_options(INTERFACE)
- target_include_directions(INTERFACE)
- target_compile_options(INTERFACE)
- target_compile_definitions(INTERFACE)
- target_sources(INTERFACE)
然后像其他目标一样被用作参数给 target_link_libraries()。
1.4 导入的库
add_library(<name> <SHARED|STATIC|MODULE|UNKNOWN> IMPORTED [GLOBAL])
用来导入已经存在的库,Cmake 也不会添加任何编译规则给它。
- 此类库的标志就是有 IMPORT 属性,导入的库的作用域为创建它的目录及更下级目录。但是如果有 GLOBE 属性,则作用域被拓展到全工程。
- 一个 IMPORTED target 表示一个已经存在的依赖,且不可变的存在,无法修改它。
- 导入的库的类型必须是 STATIC,SHARED,MODULE,UNKNOWN中的一种。对于 UNKNOW 类型,不需要知道类型就可使用的。从工程外部引入一个库,使用IMPORTED_LOCATION 属性确定库文件的在磁盘上的完整路径。
关于 imported library 的详细信息,是通过设置那些以 IMPORTED_ 和 INTERFACE_ 开头的属性来指定的。其中最重要的属性是:IMPORTED_LOCATION,这个属性用于指定磁盘上主库文件的位置。
举个例子:
add_library(PocoLib SHARED IMPORTED GLOBAL)
#It's important to specify the full path to the library you want to import
set_target_properties(PocoLib PROPERTIES IMPORTED_LOCATION "/usr/local/lib/Poco_1.7.2/lib/libPocoFoundation.so")
set_target_properties
set_target_properties(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)
1.5 别名库
add_library(<name> ALIAS <target>)
为给定 library 添加一个别名,后续可使用<name>来替代<target>。
二、target_link_libraries的介绍
该命令用于用于将目标与所需的库进行链接。它用于指定一个目标(例如可执行文件或库)需要依赖的其他库,以便在构建过程中正确地链接这些库。该命令有多种使用方法,但他们都符合下面的命令格式:
target_link_libraries(<target> ... <item>... ...)
target:要链接库的目标
target 必须是由 add_executable() 或 add_library() 等命令创建的,并且不能是 ALIAS 目标。对同一 target 的重复调用按照调用的顺序追加项。
链接的库必须是由项目中的add_library()创建或者作为导入库创建。如果是在项目中创建的,则会在构建系统中自动添加排序依赖项,以确保指定的库目标在被链接时是最新的。
注: 构建系统自动添加排序依赖项,指的是在构建项目时,当目标(例如可执行文件或库)依赖于其他目标(通常是库)时,构建系统会确保按正确的顺序构建这些目标,以满足依赖关系。比如:
add_library(mylib1...)
target_link_libraries(mylib2 PRIVATE mylib1)
add_executable(myapp...)
target_link_libraries(myapp PRIVATE mylib2)
add_library(mylib2...)
在这个例子中,myapp可执行文件依赖于mylib2库,而mylib2库又依赖于mylib1库。由于存在这种依赖关系,构建系统会自动添加构建规则,确保在构建myapp可执行文件之前,先构建mylib2库,而在构建mylib2库之前,先构建mylib1库。
最常使用方式
target_link_libraries(<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
在这个命令中,可以使用 PUBLIC、PRIVATE 和 INTERFACE 这三个作用域关键字来指定链接的依赖项和链接接口:
- PUBLIC 表示将指定的库或目标链接到目标,并将其作为链接接口的一部分。这意味着当其他目标链接到当前目标时,也会自动链接这些PUBLIC作用域的依赖项。这样的依赖项将被传递给依赖于当前目标的其他目标。
- PRIVATE表示将指定的库或目标链接到目标,但不将其作为链接接口的一部分。这意味着这些 PRIVATE 作用域的依赖项只会影响当前目标,而不会传递给依赖于当前目标的其他目标。
- INTERFACE 表示将指定的库链接到链接接口,而不进行实际的链接操作。这意味着这些 INTERFACE 作用域的依赖项不会对当前目标产生影响,但会被传递给依赖于当前目标的其他目标。这通常用于定义一组接口,以满足其他目标的特定要求。
不指定作用域
target_link_libraries(<target> <item>...)
根据这个命令的默认行为,库的依赖关系是传递的,即如果一个目标链接到了另一个目标,那么链接到这个目标的库也会出现在另一个目标的链接行中。
三、target_include_directories
该命令用于为指定的目标(target)添加包含目录(include directories)。指定编译目标时需要搜索头文件的路径,以确保编译器可以找到所需的头文件,这对于创建 CMake 项目中的库和可执行文件非常有用,因为它们可能依赖于其他模块或库的头文件。
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
- target:要添加包含目录的目标名称。
- SYSTEM:可选参数,指定被包含的目录是系统目录。这会告诉编译器将这些目录视为系统的标准头文件目录。
- BEFORE:可选参数,指定将包含目录添加到已有的包含目录之前,而不是默认的添加到后面。
- INTERFACE、PUBLIC、PRIVATE:指定包含目录的可见性级别。
- INTERFACE:表示包含目录将应用于目标及其使用该目标的其他目标。
- PUBLIC:表示包含目录将应用于目标本身和使用该目标的其他目标。
- PRIVATE:表示包含目录仅应用于目标本身。
- items:指定要添加的包含目录路径,可以是目录名、绝对路径或相对路径。
使用示例:
使用绝对路径:target_include_directories(target_name PRIVATE /path/to/directory) 这将为指定的目标(target_name)添加一个私有的包含目录,即只有该目标及其依赖项可以访问这个目录。
使用相对路径:target_include_directories(target_name PRIVATE relative/path/to/directory) 这将使用相对于当前 CMakeLists.txt 文件所在路径的相对路径来指定包含目录。
使用多个包含路径:target_include_directories(target_name PRIVATE directory1 directory2 ... ) 这种用法允许一次性指定多个包含目录。
四、include_directories
include_directories 用于指定包含头文件目录。include_directories 函数允许在 CMakeLists.txt 文件中指定项目的头文件目录,以便在编译过程中能够正确地找到和包含目录中的头文件。
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
- BEFORE | AFTER:可选参数,它们用于指定包含目录的添加顺序。如果使用AFTER修饰符,那么添加的目录将会放在已有包含目录的后面;如果使用BEFORE修饰符,那么添加的目录将会放在已有包含目录的前面。默认情况下,新的包含目录会放在已有目录的后面。
- SYSTEM:是一个可选的修饰符,用于指定所包含的目录是系统级别的目录。当使用SYSTEM修饰符时,编译器会将这些目录视为系统级别的头文件目录,这意味着编译器不会产生关于这些目录的警告信息。
注意事项
- include_directories 命令应该在 add_executable 或 add_library 之前使用,以确保编译器在构建过程中能够找到所需的头文件。
- include_directories 命令仅仅告诉编译器在哪些目录中查找头文件,并不会自动包含这些头文件。要包含头文件,需要使用 #include 预处理指令在代码中显式包含。
- 尽量避免使用 include_directories 命令的绝对路径。推荐使用相对于 CMakeLists.txt 文件的相对路径,以增加项目的可移植性。
- 在使用 include_directories 命令时,应确保指定的目录中包含正确的头文件。否则,编译器可能无法找到所需的头文件,导致编译错误。
推荐使用 target_include_directories 而不是 include_directories 原因如下:
- 更明确的作用域:include_directories命令是全局性的,它将全局添加的目录路径应用于整个项目。这可能导致潜在的命名冲突或不必要的搜索路径。而target_include_directories命令是针对特定目标(例如可执行文件或库)的,它可以将目录路径限定在特定的目标范围内,提供了更明确的作用域控制。
- 更好的可维护性:通过使用target_include_directories命令,可以将目录路径与特定的目标关联起来。这样,在添加、修改或删除目标时,与之相关的目录路径也会自动调整,保持代码的一致性和可维护性。而使用include_directories命令时,需要手动确保目录路径的一致性,增加了维护工作的复杂性。
- 更好的依赖管理:target_include_directories命令可以与target_link_libraries命令结合使用,以确保正确的头文件搜索路径传递给依赖的目标。这样,在构建库时,库的用户不需要手动添加依赖的头文件路径,而是通过依赖关系自动传递。这简化了项目的依赖管理。
- 跨平台兼容性:在某些情况下,使用target_include_directories命令可以提供更好的跨平台兼容性。例如,在使用一些编译器或构建工具链时,target_include_directories命令可以更好地处理特定的平台或编译器标志,以确保正确的头文件搜索路径。
CMake系列讲解 - 总目录(由浅入深,实例讲解) 来自 <https://codetheworld.blog.csdn.net/article/details/116940648>
Classic CMake Build Procedure (TM):
~/package $ mkdir build
~/package $ cd build
~/package/build $ cmake ..
~/package/build $ make [-j2]
Newer version of Cmake Build Procedure:
~/package $ cmake -S . -B build
~/package $ cmake --build build [-j2]
CMake菜谱(CMake Cookbook中文版) Introduction - 《CMake菜谱(CMake Cookbook中文版)》 - 书栈网 · BookStack