阅读更多
1 tutorial
1.1 step1: A Basic Starting Point
tutorial.cxx
内容如下:
1 |
|
CMakeLists.txt
内容如下:
cmake_minimum_required
:用于指定cmake
的最小版本,避免出现兼容性问题(包含了高级版本的特性,但是实际的cmake
版本较小)project
:用于设置项目名称,并存储到CMAKE_PROJECT_NAME
变量中,cmake
中的一些环境变量会以这里指定的项目名称作为前缀,例如PROJECT_SOURCE_DIR
、<PROJECT-NAME>_SOURCE_DIR
PROJECT_BINARY_DIR
、<PROJECT-NAME>_BINARY_DIR
set
:用于设置一些变量add_executable
:添加目标可执行文件
1 | cmake_minimum_required(VERSION 3.10) |
此时文件结构如下:
1 | . |
测试:
1 | mkdir build |
1.2 step2: Adding a Library and Adding Usage Requirements for a Library
接下来,我们用自己实现的求开方的函数替换标准库中的实现。创建MathFunctions
子目录,并在该子目录添加MathFunctions.h
以及mysqrt.cxx
、CMakeLists.txt
三个文件
MathFunctions/MathFunctions.h
内容如下:
1 | double mysqrt(double x); |
MathFunctions/mysqrt.cxx
内容如下:
1 |
|
MathFunctions/CMakeLists.txt
内容如下:
1 | add_library(MathFunctions mysqrt.cxx) |
添加TutorialConfig.h.in
文件,内容如下:
1 |
修改tutorial.cxx
文件,内容如下:
1 |
|
修改CMakeLists.txt
文件,内容如下:
option
:用于添加cmake
选项,可以通过-D<OPTION-NAME>=ON/OFF
参数来选择打开或者关闭该选项- 例如
cmake .. -DUSE_MYMATH=OFF
- 例如
configure_file
:一般用于根据cmake选项动态生成头文件if statement
:控制流add_subdirectory
:用于将子目录添加到构建任务中list
:容器相关的操作target_link_libraries
:指定库文件target_include_directories
:指定头文件搜索路径
1 | cmake_minimum_required(VERSION 3.10) |
此时目录结构如下:
1 | . |
测试:
1 | # 使用的是自定义的sqrt函数 |
1.3 step3: Installing
现在,我们要安装make
后产生的二进制、库文件、头文件
在step2
的基础上,修改MathFunctions/CMakeLists.txt
文件,追加如下内容:
- 其中这里指定了两个相对路径
lib
、include
。前缀由cmake
变量CMAKE_INSTALL_PREFIX
确定,默认值为/usr/local
1 | # add the install targets |
在step2
的基础上,修改CMakeLists.txt
文件,追加如下内容:
1 | # add the install targets |
测试:
1 | # 使用默认的安装路径 |
1.4 step4: Testing
接下来,增加测试功能。在step2
的基础上,修改CMakeLists.txt
文件,追加如下内容:
add_test
:用于增加测试,其中NAME
指定的是测试用例的名称,RUN
指定的是测试的命令function
:用于定义一个方法set_tests_properties
:用于设置测试项的属性,这里指定了测试结果的通配符
1 | enable_testing() |
测试:
1 | mkdir build |
1.5 step5: Adding System Introspection
同一个库,在不同平台上的实现可能不同,例如A平台有方法funcA
,而B平台没有funcA
,因此我们需要有一种机制来检测这种差异
接下来,增加测试功能。在step2
的基础上,修改MathFunctions/CMakeLists.txt
文件,追加如下内容:
include
:加载cmake
模块,这里加载了CheckSymbolExists
模块,该模块用于检测指定文件中的指定符号是否存在
1 | include(CheckSymbolExists) |
修改MathFunctions/mysqrt.cxx
文件,内容如下:
1 |
|
测试:
1 | mkdir build |
2 target
cmake
可以使用add_executable
、add_library
或add_custom_target
等命令来定义目标target
。与变量不同,目标在每个作用域都可见,且可以使用get_property
和set_property
获取或设置其属性
3 variables
3.1 Frequently-Used Variables
参考(cmake-variables):
CMAKE_BINARY_DIR
、PROJECT_BINARY_DIR
、<PROJECT-NAME>_BINARY_DIR
:指的是工程编译发生的目录。在递归处理子项目时,该变量不会发生改变- 若指定了
-B
参数,即-B
参数指定的目录 - 若没有指定
-B
参数,即执行cmake
命令时所在的目录
- 若指定了
CMAKE_SOURCE_DIR
、PROJECT_SOURCE_DIR
、<PROJECT-NAME>_SOURCE_DIR
:指的是工程顶层目录。在递归处理子项目时,该变量不会发生改变CMAKE_CURRENT_SOURCE_DIR
:指的是当前处理的cmake
项目的工程目录,一般来说是CMakeLists.txt
所在目录。在递归处理子项目时,该变量会发生改变- Difference between CMAKE_CURRENT_SOURCE_DIR and CMAKE_CURRENT_LIST_DIR
- 若通过
include(src/CMakeLists.txt)
引入子项目,假设工程根目录是project
,那么该子项目在处理的时候,CMAKE_CURRENT_SOURCE_DIR
是project
,而CMAKE_CURRENT_LIST_DIR
是project/src
CMAKE_CURRENT_BINARY_DIR
:指的是工程编译结果存放的目标目录。在递归处理子项目时,该变量会发生改变。可以通过set
命令或ADD_SUBDIRECTORY(src bin)
改变这个变量的值,但是set(EXECUTABLE_OUTPUT_PATH <new_paht>)
并不改变这个变量的值,只会影响最终的保存路径CMAKE_CURRENT_LIST_DIR
:指的是当前处理的CMakeLists.txt
所在目录。在递归处理子项目时,该变量会发生改变CMAKE_CURRENT_LIST_FILE
:指的是当前处理的CMakeLists.txt
的路径。在递归处理子项目时,该变量会发生改变CMAKE_MODULE_PATH
:include()
、find_package()
命令的模块搜索路径EXECUTABLE_OUTPUT_PATH
、LIBRARY_OUTPUT_PATH
:定义最终编译结果的二进制执行文件和库文件的存放目录PROJECT_NAME
:指的是通过set
设置的PROJECT
的名称CMAKE_INCLUDE_PATH
、CMAKE_LIBRARY_PATH
:这两个是系统变量而不是cmake
变量,需要在bash
中用export
设置CMAKE_MAJOR_VERSION
、CMAKE_MINOR_VERSION
、CMAKE_PATCH_VERSION
:主版本号、次版本号,补丁版本号,2.4.6
中的2
、4
、6
CMAKE_SYSTEM
:系统名称,比如Linux-2.6.22
CMAKE_SYSTEM_NAME
:不包含版本的系统名,比如Linux
CMAKE_SYSTEM_VERSION
:系统版本,比如2.6.22
CMAKE_SYSTEM_PROCESSOR
:处理器名称,比如i686
UNIX
:在所有的类UNIX
平台为TRUE
,包括OS X
和cygwin
WIN32
:在所有的win32
平台为TRUE
,包括cygwin
ENV{NAME}
:环境变量,通过set(ENV{NAME} value)
设置,通过$ENV{NAME}
引用CMAKE_INSTALL_PREFIX
:安装路径前缀
3.2 BUILD_SHARED_LIBS
该参数用于控制add_library
指令,在缺省类型参数的情况下,生成静态还是动态库
4 property
4.1 INCLUDE_DIRECTORIES
去哪找头文件.h
,-I(GCC)
include_directories
:该方法会在全局维度添加include
的搜索路径。这些搜索路径会被添加到所有target
中去(包括所有sub target
),会追加到所有target
的INCLUDE_DIRECTORIES
属性中去target_include_directories
:该方法为指定target
添加include
的搜索路径,会追加到该target
的INCLUDE_DIRECTORIES
属性中去
如何查看全局维度以及target维度的INCLUDE_DIRECTORIES
属性值
1 | get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) |
4.2 LINK_DIRECTORIES
去哪找库文件.so/.dll/.lib/.dylib/...
,-L(GCC)
4.3 LINK_LIBRARIES
需要链接的库文件的名字:-l(GCC)
5 command
5.1 message
用于打印信息
格式:message([<mode>] "message text" ...)
合法的mode
包括
FATAL_ERROR
:致命错误,终止构建SEND_ERROR
:继续构建,终止generation
WARNING
:告警信息,继续构建AUTHOR_WARNING
:告警信息,继续构建DEPRECATION
:当CMAKE_ERROR_DEPRECATED
或CMAKE_WARN_DEPRECATED
参数启用时,若使用了deprecated
的特性,会打印error
或者warn
信息NOTICE
:通过stderr
打印的信息STATUS
:用户最应该关注的信息VERBOSE
:项目构建者需要关注的信息DEBUG
:项目调试需要关注的信息TRACE
:最低级别的信息
5.2 set
set
用于设置:cmake
变量或环境变量
格式:
cmake
变量:set(<variable> <value>... [PARENT_SCOPE])
- 环境变量:
set(ENV{<variable>} [<value>]
变量如何引用:
cmake
变量:${<variable>}
- 环境变量:
$ENV{<variable>}
5.3 option
option
用于设置构建选项
格式:option(<variable> "<help_text>" [value])
- 其中
value
的可选值就是ON
和OFF
,其中OFF
是默认值
5.4 file
file
用于文件操作
格式:
file(READ <filename> <out-var> [...])
file({WRITE | APPEND} <filename> <content>...)
操作类型说明:
READ
:读取文件到变量中WRITE
:覆盖写,文件不存在就创建APPEND
:追加写,文件不存在就创建
5.5 add_executable
add_executable
用于添加可执行文件,示例如下:
1 | add_executable(<name> [WIN32] [MACOSX_BUNDLE] |
5.6 add_library
add_library
用于生成库文件,格式和示例如下:
<name>
:target
名称- 第二个参数用于指定库文件类型,可以省略,由
BUILD_SHARED_LIBS
变量控制STATIC
:静态库SHARED
:动态库
1 | add_library(<name> [STATIC | SHARED | MODULE] |
5.7 set_target_properties
为指定target
设置属性
1 | set_target_properties(xxx PROPERTIES |
5.8 target_compile_options
为指定target
设置编译参数
1 | target_compile_options(xxx PUBLIC "-O3") |
5.9 Link Libraries
5.9.1 link_libraries
Link libraries to all targets added later.
1 | link_libraries([item1 [item2 [...]]] |
5.9.2 target_link_libraries
Specify libraries or flags to use when linking a given target and/or its dependents. Usage requirements from linked library targets will be propagated. Usage requirements of a target’s dependencies affect compilation of its own sources.
Each <item>
may be:
- A library target name
- A full path to a library file
- A plain library name, like
thread
,dl
- A link flag
1 | target_link_libraries(<target> ... <item>... ...) |
5.9.2.1 Automatic Inclusion of Header File Paths in CMake with target_link_libraries
When using the target_link_libraries
command in CMake to link a target (such as a library), the related header file paths may automatically be included. This is because modern CMake manages projects based on the concept of “targets,” which allows targets to own and propagate attributes used for building and usage, such as include directories, definitions, compile options, etc.
This behavior is primarily achieved through “usage requirements.” When you set INTERFACE
, PUBLIC
, or PRIVATE
properties for a target, these properties affect both the target itself and any other targets that link to it:
PRIVATE
: These properties are only visible to the target that defines them and do not propagate to other targets that depend on this target.PUBLIC
: These properties apply to both the target that defines them and automatically propagate to any other targets that depend on this target.INTERFACE
: These properties do not apply to the target that defines them but do propagate to any other targets that depend on this target.
When a library specifies its header file paths in its CMake configuration file using the target_include_directories
with the PUBLIC
or INTERFACE
keywords, these paths automatically become part of the include paths for targets that depend on this library. Therefore, when you link to such a library using target_link_libraries
, you also implicitly gain access to these include paths.
This design greatly simplifies dependency management within projects, allowing maintainers to avoid explicitly specifying include paths, compiler, and linker configurations repeatedly. This is also one of the recommended best practices in modern CMake.
5.10 Link Directories
5.10.1 link_directories
Adds the paths in which the linker should search for libraries. Adds the paths in which the linker should search for libraries. Relative paths given to this command are interpreted as relative to the current source directory. The command will apply only to targets created after it is called.
By default the directories specified are appended onto the current list of directories. This default behavior can be changed by setting CMAKE_LINK_DIRECTORIES_BEFORE
to ON
. By using AFTER
or BEFORE
explicitly, you can select between appending and prepending, independent of the default.
1 | link_directories([AFTER|BEFORE] directory1 [directory2 ...]) |
See the below example, only /pathA/lib
will be added to the library search path.
1 | link_directories(/pathA/lib) |
5.10.2 target_link_directories
Add link directories to a target. Specifies the paths in which the linker should search for libraries when linking a given target. Each item can be an absolute or relative path, with the latter being interpreted as relative to the current source directory. These items will be added to the link command.
By using AFTER
or BEFORE
explicitly, you can select between appending and prepending, independent of the default.
1 | target_link_directories(<target> [BEFORE] |
5.11 Include Directories
5.11.1 include_directories
Add the given directories to those the compiler uses to search for include files. Relative paths are interpreted as relative to the current source directory.
The include directories are added to the INCLUDE_DIRECTORIES
directory property for the current CMakeLists
file. They are also added to the INCLUDE_DIRECTORIES
target property for each target in the current CMakeLists
file. The target property values are the ones used by the generators.
By default the directories specified are appended onto the current list of directories. This default behavior can be changed by setting CMAKE_INCLUDE_DIRECTORIES_BEFORE
to ON
. By using AFTER
or BEFORE
explicitly, you can select between appending and prepending, independent of the default.
1 | include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...]) |
5.11.2 target_include_directories
The INTERFACE
, PUBLIC
and PRIVATE
keywords are required to specify the scope of the following arguments. PRIVATE
and PUBLIC
items will populate the INCLUDE_DIRECTORIES
property of <target>
. PUBLIC
and INTERFACE
items will populate the INTERFACE_INCLUDE_DIRECTORIES
property of <target>
. The following arguments specify include directories.
1 | target_include_directories(<target> [SYSTEM] [AFTER|BEFORE] |
5.12 add_subdirectory
add_subdirectory
:用于引入一个cmake
子项目
source_dir
:子项目路径,该路径下必须包含CMakeLists.txt
文件。且必须是子目录,不能是外层目录binary_dir
:二进制路径,生成的可执行文件或者库文件的放置路径EXCLUDE_FROM_ALL
:当指定该参数时,构建父项目时,若无明确依赖子项目(例如通过target_link_libraries
添加依赖),那么子项目不会被自动构建。若有明确依赖,那么仍然会构建
1 | add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM]) |
5.13 include
include
:用于引入一个cmake
子项目。例如include(src/merger/CMakeLists.txt)
include
和add_subdirectory
这两个命令都可以用来引入一个cmake
子项目,它们的区别在于:
add_subdirectory
:该子项目会作为一个独立的cmake
项目进行处理。所有CURRENT
相关的变量都会进行切换。此外,CMakeLists.txt
文件中涉及的所有相对路径,其base
路径也会切换成add_subdirectory
指定的目录include
:该子项目不会作为一个独立的cmake
项目进行处理。只有CMAKE_CURRENT_LIST_DIR
、CMAKE_CURRENT_LIST_FILE
这两个CURRENT
变量会进行切换,而CMAKE_CURRENT_BINARY_DIR
和CMAKE_CURRENT_SOURCE_DIR
不会进行切换。此外,CMakeLists.txt
文件中涉及的所有相对路径,其base
路径保持不变
5.14 find_package
本小节转载摘录自Cmake之深入理解find_package()的用法
为了方便我们在项目中引入外部依赖包,cmake
官方为我们预定义了许多寻找依赖包的Module
,他们存储在path_to_your_cmake/share/cmake-<version>/Modules
目录下(例如/usr/local/lib/cmake-3.21.2-linux-x86_64/share/cmake-3.21/Modules
)。每个以Find<LibaryName>.cmake
命名的文件都可以帮我们找到一个包。注意,find_package(<LibaryName>)
与Find<LibaryName>.cmake
中的<LibaryName>
部分,大小写必须完全一致
我们以curl
库为例,假设我们项目需要引入这个库,从网站中请求网页到本地,我们看到官方已经定义好了FindCURL.cmake
。所以我们在CMakeLists.txt
中可以直接用find_pakcage
进行引用
对于系统预定义的Find<LibaryName>.cmake
模块,使用方法如下,每一个模块都会定义以下几个变量(这些信息会在Find<LibaryName>.cmake
文件的最上方注释中说明)。注意,这些变量命名只是规范,命名中<LibaryName>
部分是全部大写还是包含大小写完全由Find<LibaryName>.cmake
文件决定。一般来说是大写的,例如FindDemo.cmake
中定义的变量名为DEMO_FOUND
<LibaryName>_FOUND
<LibaryName>_INCLUDE_DIR
<LibaryName>_LIBRARY
:该模块通过add_library
定义的名称<LibaryName>_STATIC_LIB
1 | find_package(CURL) |
你可以通过<LibaryName>_FOUND
来判断模块是否被找到,如果没有找到,按照工程的需要关闭某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。如果<LibaryName>_FOUND
为真,则将<LibaryName>_INCLUDE_DIR
加入INCLUDE_DIRECTORIES
5.14.1 Add Non-Official Library
通过find_package
引入非官方的库,该方式只对支持cmake编译安装的库有效
一般来说说,对于需要引入的三方库xxx
,步骤通常如下
1 | git clone https://github.com/xxx.git |
假设此时我们需要引入glog
库来进行日志的记录,我们在Module
目录下并没有找到FindGlog.cmake
。所以我们需要自行安装glog
库,再进行引用
1 | git clone https://github.com/google/glog.git |
此时我们便可以通过与引入curl
库一样的方式引入glog
库了
1 | find_package(GLOG) |
5.14.2 Module Mode & Config Mode
通过上文我们了解了通过cmake
引入依赖库的基本用法。知其然也要知其所以然,find_package
对我们来说是一个黑盒子,那么它是具体通过什么方式来查找到我们依赖的库文件的路径的呢。到这里我们就不得不聊到find_package
的两种模式,一种是Module
模式,也就是我们引入curl
库的方式。另一种叫做Config
模式,也就是引入glog
库的模式。下面我们来详细介绍着两种方式的运行机制
在Module
模式中,cmake
需要找到一个叫做Find<LibraryName>.cmake
的文件。这个文件负责找到库所在的路径,为我们的项目引入头文件路径和库文件路径。cmake
搜索这个文件的路径有两个,一个是上文提到的cmake
安装目录下的share/cmake-<version>/Modules
目录(例如/usr/local/lib/cmake-3.21.2-linux-x86_64/share/cmake-3.21/Modules
),另一个使我们指定的CMAKE_MODULE_PATH
的所在目录
如果Module
模式搜索失败,没有找到对应的Find<LibraryName>.cmake
文件,则转入Config
模式进行搜索。它主要通过<LibraryName>Config.cmake
或<lower-case-package-name>-config.cmake
这两个文件来引入我们需要的库。以我们刚刚安装的glog
库为例,在我们安装之后,它在/usr/local/lib/cmake/glog/
目录下生成了glog-config.cmake
文件,而/usr/local/lib/cmake/glog/
正是find_package
函数的搜索路径之一
5.14.3 Create Customized Find<LibraryName>.cmake
假设我们编写了一个新的函数库,我们希望别的项目可以通过find_package
对它进行引用我们应该怎么办呢。
我们在当前目录下新建一个ModuleMode
的文件夹,在里面我们编写一个计算两个整数之和的一个简单的函数库。库函数以手工编写Makefile
的方式进行安装,库文件安装在/usr/lib
目录下,头文件放在/usr/include
目录下。其中的Makefile
文件如下:
1 | # 1、准备工作,编译方式、目标文件名、依赖库路径的定义。 |
编译安装:
1 | make |
接下来我们回到我们的cmake
项目中来,在cmake
文件夹下新建一个FindAdd.cmake
的文件。我们的目标是找到库的头文件所在目录和共享库文件的所在位置
1 | # 在指定目录下寻找头文件和动态库文件的位置,可以指定多个目标路径 |
这时我们便可以像引用curl
一样引入我们自定义的库了,在CMakeLists.txt
添加
1 | # 将项目目录下的cmake文件夹加入到CMAKE_MODULE_PATH中,让find_pakcage能够找到我们自定义的函数库 |
5.15 find_library
find_library
用于查找库文件,示例如下:
- 所有指定的可能的名字中,只要有一个匹配上了,那么查找过程就终止了
CMAKE_FIND_LIBRARY_SUFFIXES
可用于控制优先查找静态库还是优先查找动态库set(CMAKE_FIND_LIBRARY_SUFFIXES ".so;.a")
:动态库优先,默认值set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so")
:静态库优先
1 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".so;.a") |
5.16 find_path
find_path
用于查找包含给定文件的目录,示例如下:
- 所有指定的可能的名字中,只要有一个匹配上了,那么查找过程就终止了
1 | # NAMES 后可接多个可能的别名,结果保存到变量 BRPC_INCLUDE_PATH 中 |
5.17 aux_source_directory
Find all source files in a directory
5.18 PUBLIC vs. PRIVATE
In CMake, PUBLIC and PRIVATE are used to specify the visibility of target properties and dependencies. Here’s what they mean:
PUBLIC: A property or dependency marked as PUBLIC is visible to all targets that depend on the current target. This means that the property or dependency will be propagated to any targets that link against the current target.
- For example, suppose you have a library target called “foo” that depends on another library called “bar”. If you mark the dependency on “bar” as PUBLIC, any target that links against “foo” will also link against “bar”.
PRIVATE: A property or dependency marked as PRIVATE is only visible to the current target. This means that the property or dependency will not be propagated to any targets that depend on the current target.
- For example, suppose you have a library target called “foo” that uses a header file called “bar.h”. If you mark the header file as PRIVATE, any targets that depend on “foo” will not be able to access “bar.h”.
To summarize, PUBLIC properties and dependencies are visible to all targets that depend on the current target, while PRIVATE properties and dependencies are only visible to the current target.
示例如下:
1 | . |
1 | # CMakeLists.txt |
如果将target_link_libraries(libfoo PUBLIC libbar)
中的PUBLIC
改成PRIVATE
,那么编译将会无法通过,因为main
没有显式依赖libbar
,会找不到头文件bar.h
6 Tips
6.1 Command Line
cmake --help
Generators
,默认使用Unix Makefiles
build
cmake <path-to-source>
:当前目录作为<build_path>
cmake -S <path-to-source>
:当前目录作为<build_path>
cmake -B <build_path>
:当前目录作为<path-to-source>
cmake -B <build_path> <path-to-source>
cmake -B <build_path> -S <path-to-source>
cmake --build <build_path>
:等效于在<build_path>
中执行make
命令cmake --build <build_path> -j $(( (cores=$(nproc))>1?cores/2:1 ))
:等效于在<build_path>
中执行make -j $(( (cores=$(nproc))>1?cores/2:1 ))
命令
cmake --install <build_path>
:等效于在<build_path>
中执行make install
命令
6.2 Print
6.2.1 Print All Variables
1 | get_cmake_property(_variableNames VARIABLES) |
6.2.2 Print All Envs
1 | execute_process(COMMAND "${CMAKE_COMMAND}" "-E" "environment") |
6.2.3 Print All Compile Command
TheseWhen using the default generator Unix Makefiles
, the following three methods are equivalent:
cmake -B <build_path> -DCMAKE_VERBOSE_MAKEFILE=ON
make VERBOSE=1
cmake --build <build_path> -- VERBOSE=1
6.3 Specify Compiler
6.3.1 Command
1 | cmake -DCMAKE_CXX_COMPILER=/usr/local/bin/g++ -DCMAKE_C_COMPILER=/usr/local/bin/gcc .. |
6.3.2 CMakeLists.txt
1 | set(CMAKE_C_COMPILER "/path/to/gcc") |
6.4 Add Compile Options
6.4.1 Command
1 | cmake -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -O3" -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -O3" .. |
6.4.2 CMakeLists.txt
示例如下:
1 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall -fopt-info-vec") |
CMAKE_BUILD_TYPE
的所有可选值包括
Debug
Release
RelWithDebInfo
MinSizeRel
6.5 Specify Macro Definition
6.5.1 Command
1 | cmake -B build -DUSE_XXX |
6.5.2 CMakeLists.txt
1 | add_definitions(-DUSE_XXX -DVALUE_YYY=5) |
6.6 Build Type
1 | # If you want to build for debug (including source information, i.e. -g) when compiling, use |
CMAKE_BUILD_TYPE
的所有可选值包括
Debug
Release
RelWithDebInfo
MinSizeRel
6.7 Include All Source File
6.7.1 file
1 | # Search for all .cpp and .h files in the current directory |
If your project has a more complex structure and you wish to recursively search all subdirectories for files, you can replace the file(GLOB ...) command with
file(GLOB_RECURSE …)`:
1 | file(GLOB_RECURSE MY_PROJECT_SOURCES "*.cpp") |
6.7.2 aux_source_directory
如果同一个目录下有多个源文件,那么在使用add_executable
命令的时候,如果要一个个填写,那么将会非常麻烦,并且后续维护的代价也很大
1 | add_executable(Demo main.cxx opt1.cxx opt2.cxx) |
我们可以使用aux_source_directory(<dir> <variable>)
)命令,该命令可以扫描一个目录下得所有源文件,并将文件列表赋值给一个变量
1 | # 查找当前目录下的所有源文件 |
6.8 Library
6.8.1 Build Static Library By Default
Add following config to project’s root CMakeLists.txt
, then all sub modules (imported via add_subdirectory
) will be built in static way.
1 | set(BUILD_SHARED_LIBS FALSE) |
6.8.2 Import Library From Unified Thirdparty Directory
Suppose you have put all libraries in ${THIRDPARTY_DIR}/lib
, then you can use the following config to import it.
1 | add_library(protobuf STATIC IMPORTED) |
6.8.3 Priority: Static Libraries vs. Dynamic Libraries
6.8.3.1 find_package
The factors influencing the choice between static and dynamic libraries by the find_package
command may include:
- The provided CMake configuration files by the package: Some packages offer their own CMake configuration files, such as
<PackageName>Config.cmake
(Located at/usr/local/lib/cmake
). These configuration files may explicitly specify the use of static or dynamic libraries, or make the selection based on the value of theBUILD_SHARED_LIBS
variable (maybe).- For boost, it offers a variable named
Boost_USE_STATIC_LIBS
(Defined at/usr/local/lib/cmake/Boost-1.84.0/BoostConfig.cmake
) to control whether to use static version or dynamic version.
- For boost, it offers a variable named
- Default behavior: Certain packages may use specific library types based on conventions or default settings. For instance, if a package typically provides dynamic libraries and does not have explicit configuration options to choose static libraries, the
find_package
command might default to using dynamic libraries.
Example:
1 | set(Boost_USE_STATIC_LIBS ON) |
6.8.3.2 find_library
To control whether find_library
should prefer static libraries or dynamic libraries, you typically set the CMAKE_FIND_LIBRARY_SUFFIXES
variable. This variable specifies the suffixes that find_library
searches for when looking for libraries.
Here’s how you can control find_library
to prioritize static libraries or dynamic libraries:
Prioritize static libraries:
1 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so") |
Prioritize dynamic libraries:
1 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".so;.a") |
6.9 compile_commands.json
6.9.1 Manually Generate compile_commands.json
cmake
指定参数-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
即可。构建完成后,会在构建目录生成compile_commands.json
,里面包含了每个源文件的编译命令
6.9.2 Auto generate compile_commands.json and copy to project source root
参考Copy compile_commands.json to project root folder
1 | add_custom_target( |
6.10 How to uninstall
After installation, there will be a install_manifest.txt
recording all the installed files. So we can perform uninstallation by this file.
1 | xargs rm < install_manifest.txt |
6.11 Ignore -Werror
1 | cmake --compile-no-warning-as-error -DWERROR=0 ... |
7 Install
We can get binary distributions from Get the Software:
1 | wget https://github.com/Kitware/CMake/releases/download/v3.21.2/cmake-3.21.2-linux-x86_64.tar.gz |