一步一莲花祈祷
- 0. 官方文档
- 1. 一些概念
- 2. CMakeLists进阶历程
- 3. cmake命令用法
- 4. 文档阅读方式
- 5. 知识点列表
- 构建、编译、链接
- 顶层、父层、子层CMakeLists.txt
- CMake_minimum_required()
- project()
- add_executable()
- add_library()
- cmake变量
- 变量的作用域
- cmake -Dxx=xx
- set()
- option()
- block()
- execute_process()
- ADD_DEFINITIONS()
- ADD_DEPENDENCIES()
- find_package()
- 注释
- 操作符优先级
- if条件语句
- while循环
- foreach循环遍历
- 命令(函数)
- 多核编译加速 -j4
- 编译目录build结构
- 编译模式
- 宏定义、编译定义、编译选项、cmake变量
- 什么是target
- 编译库文件
- 编译可执行文件
- 设置target属性
- get_target_property 和 set_target_properties
- 添加源文件
- 编译结果后缀
- INTERFACE库、OBJECT库、IMPORTED目标
- 6. CMake多文件项目示例
- 参考文献
0. 官方文档
首页:CMake: A Powerful Software Build System
开发文档:CMake Reference Documentation
1. 一些概念
-
CMake是构建C++代码的标准;
-
支持构建多平台的C++代码,如unix,windows,ios…
-
以linux系统为例,make使用Makefile来构建代码,cmake使用CMakeLists.txt来构建Makefile文件,因此,学习cmake就是学习如何编写 CMakeLists.txt !以及如何使用命令行工具cmake!(有哪些参数?如何使用?)
-
cmake允许每个源代码树具有多个构建树:
-
CMake支持许多流行的C++IDE系统以及命令行构建工具如:Visual Studio、Xcode、ninja、make和VSCode等;
-
cmake除了可以构建库文件和可执行文件外,CMake还允许在构建时运行任意cmd命令。
-
尽管CMake支持大写、小写和混合大小写命令,但小写命令是首选命;
-
CMake的发展历史:https://cmake.org/history/
-
Forum、FAQs、Support、Issue、Documentation
CMake Discourse Forum
CMake FAQs
Advanced Support
Issue Tracker
CMake Reference Documentation -
如何阅读文档?
(1)将不懂的指令或函数放入搜索框中搜索阅读。
(2)有哪些工具行命令?用法?:Command-Line Tools;如cmake、cpack。
(3)有哪些函数?用法?:cmake-commands(7);如set。
(4)有哪些变量?用法?:cmake-variables(7);如CMAKE_CXX_STANDARD。 -
只有包含main函数的xx.cpp才可以被cmake构建成可执行程序!其余的会被构建成库文件xx.so。
-
构建、编译、链接
构建 = 编译 + 链接
构建:Build,指生成可执行文件的全过程,包括预编译、编译、和链接等。
编译:Compile,将main.cpp, main.h
编译成目标文件main.o
;将xx.cpp, xx.h
编译成库文件xx.so
链接:Link,将库文件xx.so
链接到目标文件main.o
得到最终的可执行文件main.exe
-
构建可执行程序的过程
-
顶层CMakeLists.txt和子层CMakeLists.txt
cmake的对象是顶层CMakeLists.txt文件。因此要么在顶层CMakeLists.txt所在目录下执行cmake命令,要么cmake时使用相对路径。- 顶层CMakeLists.txt的作用
- 解决依赖问题:findpakage()
- 告知cmake要编译哪些cpp源文件
1)编译默认路径下的源文件,即与顶层CMakeLists.txt同级路径下的源文件。
2)编译通过add_subdirectory()指定的目录下的源文件。如,add_subdirectory(subdirectoryName)
- 告知cmake如何编译cpp源文件
1)将哪些cpp文件构建成可执行文件xx.exe:add_executable()
2)将哪些cpp文件编译成库文件xx.so:add_library(xxso_name xx1.cpp xx2.cpp)
,其中xx1.cpp和xx2.cpp可以是相互调用的关系也可以毫无关系。 - 告知cmake如何将库文件xx.so链接到目标文件main.o以得到最后的可执行文件main.exe
1)告知cmake该库的.h文件所在:target_include_directories()
2)告知cmake该库的.so文件所在:target_link_libraries()
- 其他
声明项目名称:project()
声明cmake版本要求:cmake_minimum_required()
C++版本要求:
CMAKE_CXX_STANDARD
CMAKE_CXX_STANDARD_REQUIRED
set()
- 子层CMakeLists.txt的作用
将同级目录中的xx.h, xx.cpp
编译成库文件libxx.so。
add_library(libxx )
- 顶层CMakeLists.txt的作用
-
在cmake中,
[[...]]
或[*[...]*]
表示换行符,其中*
表示某个任意字符。 -
在CMake中的路径分隔符总是应当使用
/
,因为CMake会对字符串中的\
转义,CMake在对接VS时会自动处理路径分隔符的替换问题。 -
在较复杂的项目中,我们可以在不同的子目录下使用多个CMakeLists.txt,在根目录下的CMakeLists.txt是最顶级的,例如可以使用add_subdirectory(source)命令,进入source文件夹,然后自动执行source目录下的CMakeLists.txt,执行完毕后返回上一级,还可以继续前往其它子目录执行相应的CMakeLists.txt。
2. CMakeLists进阶历程
2.1 创建一个最简单的CMake项目:1 xx.cpp + 1 CMakeLists.txt
-
使用场景:项目结构 = 1 xx.cpp + 1 CMakeLists.txt
-
目录结构(文件路径):CMakeLists.txt是否必要与写有main函数的cpp文件同一目录?不一定,但一般一个CMakeLists.txt与它负责的xx.cpp、xx.h文件会放在同一个目录下。如果没放在同一个目录,则在CMakeLists.txt中要写相对路径!
-
学习目标:语法、变量、命令
-
最小 CMakeLists.txt:至少包含3行代码(命令)
- 1)使用CMake_minimum_required() 指定最低CMake版本。
项目的最高层CMakeLists.txt都必须通过使用CMake_minimum_required() 来指定最低CMake版本。 - 2)使用 project() 设置项目名称、语言、版本号等。
每个项目都需要此调用,并且应在cmake_minimum_required()之后立即调用。 - 3)使用 add_executable() 命令告诉CMake将哪些
.cpp
构建成可执行程序。
注意:只有包含main函数的xx.cpp才可以被cmake编译成可执行程序!
- 1)使用CMake_minimum_required() 指定最低CMake版本。
-
如何编写项目的顶层CMakeLists.txt ?
例子:项目结构 = 1 xx.cpp + 1 CMakeLists.txt
目录结构(文件路径):CMakeLists.txt 与 xx.cpp 放在同一目录下。
CMakeLists.txt的写法如下:cmake_minimum_required(VERSION 3.10)#cmake的最低版本 project(Tutorial)#cmake项目名称 add_executable(Tutorial tutorial.cpp)#将源文件tutorial.cpp编译成可执行程序Tutorial
注意:只有包含main函数的xx.cpp才可以被cmake编译成可执行程序!add_executable的更多用法请参考:cmake-commands。
-
编译(构建)项目
写完CMakeLists.txt后如何用cmake命令构建项目?- 1)创建一个build目录:
mkdir Step1_build
cd Step1_build
- 2)根据CMakeLists.txt构建Makefile:
格式:cmake <顶层CMakeLists.txt的路径>
如:cmake ../Step1
其中../Step1
是项目最顶层CMakeLists.txt的所在路径。 - 3)编译:
格式:cmake --build <Makefile所在路径>
如:cmake --build .
或用make
命令进行编译。
其中.
是Makefile的所在目录
- 1)创建一个build目录:
2.2 指定 C++ 标准
- 学习目标:1. cmake变量;2. 使用set函数设置cmake变量;
- cmake中的变量:CMake中的系统变量如
CMAKE_CXX_STANDARD
和CMAKE_CXX _STANDARD_REQUIRED
可以用于指定构建项目所需的C++标准。
CMake机制中许多变量以CMAKE_
开头。 - 使用场景:某
.cpp
文件中使用了C++11的语法,CMakeLists.txt 中必须要指定构建程序时使用C++11标准。C++其他版本则同理。 - 涉及命令:
CMAKE_CXX_STANDARD
CMAKE_CXX_STANDARD_REQUIRED
set()
- CMakeLists.txt的写法:
在add_executable()之前添加代码:
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
- 编译方法同理。
2.3 显示编译版本号
- 使用场景:有时,在源代码中希望可以使用CMakelists.txt文件中定义的变量。例如,我们希望根据每次编译来作为项目版本的依据。
- 涉及命令
<PROJECT-NAME>_VERSION_MAJOR
<PROJECT-NAME>_VERSION_MINOR
configure_file()
target_include_directories()
- 其本质是.cpp文件与cmake变量之间的交互!
- 非重点,待续…
- 详细请参考:Exercise 3 - Adding a Version Number and Configured Header File
2.4 使用库来构建可执行程序
- 参考:Adding a Library
- 使用场景:一个项目,包含多个目录、子目录,源文件被放在不同的目录中,顶层的cpp代码引用了子层的cpp,或互引用。
- 说明:这种场景下,包含main函数的cpp所在目录即项目的顶层目录,顶层目录拥有一个CMakeLists.txt文件,称为顶层CMakeLists,其他层级的目录也拥有属于自己的CMakeLists.txt文件,称为子层CMakeLists,它们文件名相同但路径不同!功能上也略有不同。
- 子层CMakeLists在构建程序时会将该层级的cpp编译成库文件xx.o(linux平台),顶层CMakeLists负责将整个项目代码编译成最后的可执行程序,在顶层CMakeLists文件中,使用
add_subdirectory
命令将子目录添加到构建中,最后通过target_include_directories
和target_link_libraries
将库文件xx.o 链接(link) 到最后的可执行程序。 - 涉及命令:
add_library()
add_subdirectory()
target_include_directories()
target_link_libraries()
PROJECT_SOURCE_DIR
- 例子:
顶层目录: 1 CMakeLists.txt + 1 tutorial.cpp(入口函数main所在的cpp)
库目录(顶层目录的子目录):CMakeLists.txt + 2 xx.cpp + 2 xx.h
调用关系:顶层目录中的代码调用了库目录中的代码(函数)。
子层CMakeLists写法:
1)创建一个名为MathFunctions的库文件(编译结果应该是MathFunctions.o),库文件包含这些MathFunctions.cxx mysqrt.cxx源文件的代码。
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)
顶层CMakeLists写法:
1)添加要被编译成库的子目录(名称):
add_subdirectory(MathFunctions)
2)将库链接到可执行程序:
target_link_libraries(Tutorial PUBLIC MathFunctions)
3)使用这些库的必要依赖信息(指定库的头文件位置):
target_include_directories(Tutorial PUBLIC
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
如果链接的库比较多时,还可以这样写:
# 添加要被编译成库都的子目录
add_subdirectory(MathFunctions1)
add_subdirectory(MathFunctions2)
list(APPEND EXTRA_INCLUDES1 "${PROJECT_SOURCE_DIR}/MathFunctions1")
list(APPEND EXTRA_INCLUDES2 "${PROJECT_SOURCE_DIR}/MathFunctions2")
target_link_libraries(Tutorial PUBLIC MathFunctions1)
target_link_libraries(Tutorial PUBLIC MathFunctions2)
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES1}
${EXTRA_INCLUDES2}
)
- 完整的CMakeLists
顶层CMakeLists:
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
# TODO 2: Use add_subdirectory() to add MathFunctions to this project
add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
# TODO 3: Use target_link_libraries to link the library to our executable
target_link_libraries(Tutorial PUBLIC MathFunctions)
# TODO 4: Add MathFunctions to Tutorial's target_include_directories()
# Hint: ${PROJECT_SOURCE_DIR} is a path to the project source. AKA This folder!
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
子层CMakeLists:
# TODO 1: Add a library called MathFunctions with sources MathFunctions.cxx
# and mysqrt.cxx
# Hint: You will need the add_library command
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)
2.5 cmake自定义变量、编译定义
- 使用场景:在使用cmake编译代码时需要输入自定义选项来控制代码块的选择性编译,如
cmake -DTHIS_MYOPTION=ON
。 - 涉及命令
if()
option()
target_compile_definitions()
- 学习目标:
- 添加cmake自定义选项:
option(USE_MYOPTION "my diy option" ON)
什么是自定义选项?如cmake -DUSE_MYOPTION =ON
中的 -DUSE_MYOPTION就是自定义选项!其中选项的默认前缀为-D
- 添加编译定义:
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
什么是编译定义?原名词为 compile definition,可以理解为编译某块代码的使能,如 xx.cpp、xx.h 中:
#ifdef MY_ COMPILE_DEFINITION
#else
#endif
其中的 MY_ COMPILE_DEFINITION 就是编译定义(compile definition)!! - 条件编译语句:
如 xx.cpp、xx.h 中:
#ifdef USE_MYMATH
#else
#endif
- 添加cmake自定义选项:
- 用法、语法
- 添加自定义选项
对于cmake命令行工具来说是选项;对于CMakeLists.txt来说是变量!
1)在CMakeLists.txt中添加一个变量:option(USE_MYOPTION "my diy option" ON)
2)该变量USE_MYOPTION
的赋值可以通过命令行选项的形式输入,如cmake -DUSE_MYOPTION =OFF
3)然后就可以在同一个CMakeLists.txt中使用该变量(选项),如
if (USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
endif()
- 为库添加编译定义
在xx.cpp、xx.h中,除了可以用#define
来添加(声明)编译定义之外,还可以在CMakeLists.txt中用target_compile_definitions添加,如:
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
其中第一个参数的含义是要为哪个target添加,target可以是库(Library),也可以是可执行程序;第二个参数是作用域;第三个参数是编译定义的名称,并且"USE_MYMATH"与USE_MYMATH、-DUSE_MYMATH都会被认定为名称为USE_MYMATH !
- 添加自定义选项
2.6 库的使用依赖(usage requirement)
- usage requirement 表示使用某个库时必须要包含的依赖(如:库的源码路径等信息)。
- 解决的痛点:
在之前,任何想要链接到某个库如MathFunctions的target都需要被告知该库的源码路径信息,例如可执行程序Tutorial要链接库MathFunctions,在顶层CMakeLists.txt中必须包含这些代码:
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
其中${PROJECT_BINARY_DIR}
是可执行程序Tutorial的源码目录,${PROJECT_SOURCE_DIR}/MathFunctions
是库MathFunctions的源码目录;
为了节省这些不必要的源码路径声明,可以使用INTERFACE依赖项来实现!
- 用法
- 为库添加依赖项(usage requirement)
(1)为库添加 INTERFACE usage requirement
在该库的CMakeLists.txt中添加:
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
其中,CMAKE_CURRENT_SOURCE_DIR是该库所在的绝对路径。
这样就为该库指定了一个INTERFACE usage requirement。
INTERFACE usage requirement 表示调用者需要而库本身不必须得东西。
usage requirement 被称为“使用依赖”,而 INTERFACE 是其中一项。
(2)在调用层的 CMakeLists.txt 删除该库的依赖信息
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
target_include_directories(Tutorial PUBLIC
${EXTRA_INCLUDES}
)
或:
target_include_directories(Tutorial PUBLIC
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
(3)这样,在调用层的CMakeLists.txt仅这几行代码,需要就可以实现对某库的调用(链接):
add_subdirectory(MathFunctions)
target_link_libraries(Tutorial PUBLIC MathFunctions)
- 为库添加依赖项(usage requirement)
2.7 为库指定C++标准
- 使用场景:为不同的库指定不同的C++版本。
- 技术路线:使用接口库(interface library)。
- 用法
- (1)在顶层CMakeLists.txt中,删除全局配置:
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
- (2)创建一个接口库,并为接口库添加特性(指定C++版本)
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
- (3)将可执行程序、库、接口库链接到一起
target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
- (1)在顶层CMakeLists.txt中,删除全局配置:
2.8 添加生成器表达式
- 使用场景:生成时添加编译器警告标志。
- 条件:CMake 3.15以上。
- 非重点,略…
- 参考文献:Step 4: Adding Generator Expressions
2.9 Installing and Testing
- 使用场景:通常,仅仅构建一个可执行文件是不够的,它还应该是可安装的。使用CMake,我们可以使用 install 命令指定安装规则。在CMake中支持构建的本地安装通常很简单,只需指定安装位置以及要安装的目标和文件即可。
- 学习目标:安装可执行程序Tutorial 以及库 MathFunctions
- 涉及语法:install
- 条件:cmake3.15或以上版本;老版本的安装只支持
make install
- 关于安装路径
可以在CMakeLists.txt中用变量CMAKE_INSTALL_PREFIX
指定安装路径,也用--prefix
选项来指定,如:cmake --install . --prefix "/home/myuser/installdir"
, --prefix将会覆盖CMAKE_INSTALL_PREFIX的值! - 待续…
- 参考:Installing and Testing
2.10 Selecting Static or Shared Libraries
- 使用场景:如何构建非显式类型的库(如静态库)。
- 涉及语法
set
option
BUILD_SHARED_LIBS - 参考:Step 10: Selecting Static or Shared Libraries
2.11 Selecting Static or Shared Libraries
待续…
3. cmake命令用法
- 参考:cmake(1)
- 功能
- Generate a Project Buildsystem
cmake [<options>] -B <path-to-build> [-S <path-to-source>]
cmake [<options>] <path-to-source | path-to-existing-build>
- Build a Project
cmake --build <dir> [<options>] [-- <build-tool-options>]
- Install a Project
cmake --install <dir> [<options>]
- Open a Project
cmake --open <dir>
- Run a Script
cmake [-D <var>=<value>]... -P <cmake-script-file>
- Run a Command-Line Tool
cmake -E <command> [<options>]
- Run the Find-Package Tool
cmake --find-package [<options>]
- Run a Workflow Preset
cmake --workflow [<options>]
- View Help
cmake --help[-<topic>]
- Generate a Project Buildsystem
- 什么是Project Buildsystem
例如,Project Buildsystem可以是与make一起使用的Makefile文件,也可以是某IDE的项目文件,总之,它描述了如何使用构建工具(如make)从源代码编译成最终的可执行文件或可被调用的库文件。 - 如何Generate a Project Buildsystem
- 标准语法
cmake [<options>] -B <path-to-build> [-S <path-to-source>]
cmake [<options>] <path-to-source | path-to-existing-build>
解析:
cmake [可选选项] -B <编译路径> -S <源码路径>
其中:
[可选选项] :可在中查阅;cmake-options
<编译路径>:生成makefile、CMakeCache.txt 的路径,若不存在则会自动创建,支持绝对路径或相对路径。
<源码路径>:顶层 CMakeLists.txt文件所在的路径,支持绝对路径或相对路径。 - 用法1:源码路径、编译路径都指定
cmake -S src -B build
或cmake -S ./src -B ./build
cd ./build
make -j
- 用法2:只指定源码路径,编译路径缺省,
-S
缺省
编译路径缺省,默认当前目录为编译路径,并且选项-S
可以缺省!!
mkdir build
cd build
cmake ../src
- 用法3:在已存在的编译目录下重新编译(缓存文件CMakeCache.txt已存在)
针对缓存文件CMakeCache.txt已存在的情况
例如已知CMakeCache.txt的路径为./build
cd ./build
cmake .
- 用法4:缺省
-S -B
的情况下自动判定源码路径、编译路径
其中,cwd 表示当前目录 current working directory 。 - 构建Project Buildsystem后如何编译?如何安装?
如:
make
make install
- 标准语法
4. 文档阅读方式
授人以鱼不如授人以渔。
一开始cmake的目的只是单纯的为生成Makefile而生。
到如今的cmake强大到可以为不通的IDE生成项目文件,如Visual Studio、Xcode等。
根据不同的使用cmake目的来阅读不同的帮助文档,例如:
- (1)为了编译从网上(如github)下载的某个项目(source code package)。
请阅读这处:User Interaction Guide 或直接阅读命令行工具的文档 cmake(1) 、cmake-gui(1)。 - (2)为了在自己的C++应用程序中使用第三方库。
请阅读这处文档:Using Dependencies Guide - (3)为了创建一个cmake C++项目。
请阅读这处文档:CMake Tutorial 。
5. 知识点列表
构建、编译、链接
构建、编译、链接即 build、compile、link。
构建 = 预编译+编译 + 链接。
构建:Build,指生成可执行文件的全过程,主要包括预编译、编译、和链接等过程。
编译:Compile,将main函数.cpp
编译成目标文件main.o
;将xx.cpp, xx.h
编译成库文件xx.so
。
链接:Link,将依赖库libxx.so链接到xx.so;将库文件xx.so
、依赖库libxx.so
链接到目标文件main.o
得到最终的可执行文件main.exe
。
顶层、父层、子层CMakeLists.txt
父层CMakeLists.txt中的add_subdirectory()会告知cmake有哪些子层CMakeLists.txt。
父层CMakeLists.txt执行到add_subdirectory()时就会进入子层目录执行子层CMakeLists.txt??
顶层一定是父层,但父层不一定是顶层。
顶层CMakeLists.txt一般用于遍历、全局变量定义、全局环境配置等。
父层CMakeLists.txt一般用于构建可执行文件。
子层CMakeLists.txt一般用于构建库文件。
CMake_minimum_required()
指定cmake版本。
project()
指定项目名。适用于顶层目录或子目录。
add_executable()
指定要将哪些.cpp构建成可执行文件。
一般出现在顶层或父层CMakeLists.txt。
add_library()
指定要将哪些.cpp构建成库文件。
一般出现在子层CMakeLists.txt。
cmake变量
-
和其他语言一样,cmake可以看做是一种编程语言,他也有变量和函数。
-
所有变量的值只能是字符串;
-
当变量值不包括空格时,可以不使用双引号;
-
使用空格或者分号作为字符串的分隔符;
-
采用
${var}
形式来获取变量值; -
可以使用未定义的变量,如果使用了未定义的变量,那么它的值是一个空字符串;
-
变量的值可以包含换行,也可以包含引号,不过需要转义。
-
分类
- 一般变量
- 系统变量:系统变量通常以
CMAKE_
开头。作用域为全局。 - 自定义变量:作用域在定义时指定为当前CMakeCache.txt域或父CMakeCache.txt域。
- 系统变量:系统变量通常以
- 列表变量:若对一个一般变量赋了多个值,则这个变量是一个列表变量。作用域、用法与一般变量相同。
- 环境变量:作用域为全局且仅在cmake过程中有效,而非操作系统级环境变量。
- 缓存变量:上一次运行cmake时缓存变量的值会被缓存在CMakeCache.txt 中,所以当再次运行cmake时可以从中获取上一次的值,而不是重新去评估。作用域为全局。
- 一般变量
-
变量定义的格式
set(<variable> <value>... [PARENT_SCOPE])
-
变量的析构
set(variable) unset(variable)
-
定义一般变量(自定义变量)
# 作用域在当前CMakeLists.txt区域 set(varName "*.cpp") # 作用域在父CMakeLists.txt区域 set(varName "*.cpp" PARENT_SCOPE)
-
定义列表变量
# 作用域在当前CMakeLists.txt区域 set(varName "aaa.cpp" "bbb.cpp") # 作用域在父CMakeLists.txt区域 set(varName "aaa.cpp" "bbb.cpp" PARENT_SCOPE) # 等价于 set(varName "aaa.cpp;bbb.cpp" PARENT_SCOPE)
-
定义cmake环境变量
set(ENV{varName} "/opt/myDir")
引用:
$ENV{varName}
-
定义缓存变量
set(varName value... CACHE type ["description"] [FORCE])
BOOL类型的缓存变量除了可以使用set来定义外,还可以使用option:
option(varName helpString [initialValue]) # 等价于: set(varName initialValue CACHE BOOL helpString) # 不过上述两个命令定义缓存变量是有一点区别的,option()命令没有FORCE关键字。
其中:
CACHE
是固定参数,必填。"description"
记录变量的描述,可填。FORCE
表示每次运行都强制重新评估该变量的值,而不是从缓存CMakeCache.txt 中读取该变量值。如果缺省则会从缓存中取值。可填。type
表示变量类型。类型取值范围如下:- BOOL
布尔型。
ON、TRUE、1 或 OFF、FALSE、0 - FILEPATH
文件路径 类型,表示这个变量存放的是文件路径。 - STRING
字符串 类型。 - INTERNAL
内部缓存变量 类型。内部变量一般不会对用户可见。内部缓存变量默认是FORCE的。
- BOOL
-
使用场景
什么场景使用一般变量?什么场景使用缓存变量?
一般变量适用于变量的值相对固定,而且只在某一个很小的作用域生效的场景。
缓存变量适用于其可以随时更改,作用域作为全局的情况,经常在cmake中定义缓存变量,给一个默认值,如果用户想要更改缓存变量的值,可以通过cmake -D
的形式去更改。 -
变量值的修改
在CMakeLists.txt中可以通过set
函数来修改所有类型变量的值。
也可以在执行cmake命令时通过选项-DvarName=xxx
动态地修改变量的值,同样适用于所有类型的变量。 -
常用系统变量
cmake-variables.
cmake系统变量通常以CMAKE_
开头。
如CMAKE_CXX_STANDARD、CMAKE_CXX _STANDARD_REQUIRED用于指定C++标准。
变量的作用域
Cmake里面有三种作用域,全局层的,目录层的,函数层的。
- 全局层:cache变量,在整个项目范围之内可见,一般在用set定义变量的时候指定CACHE参数就能定义为cache的变量。
- 目录层:在当前目录CMakeLists.txt中定义,及在该文件中包含进来的(通过include或者macro引进的)其他的cmake源文件中定义的变量属于目录层这一级的作用域。
- 函数层:在命令函数中定义的变量,属于函数作用域内的变量。
全局层 < 目录层 < 函数层。
如果修改时通过set命令明确指定PARENT_SCOPE参数,修改的变量作用域就是在上一层作用域,而不是当前作用域。
cmake -Dxx=xx
可以动态地为所有类型的变量赋值。
如果该变量没在CMakeLists.txt中被定义不会报错但会有警告信息。
set()
set可以实现变量定义或赋值。
如果变量不存在,定义并初始化;如果变量已存在则仅赋值。
option()
定义、初始化BOOL类型的缓存变量。
block()
在任意位置产生新的作用域。
待续…
execute_process()
- 执行cmd命令序列,并且多个命令是通过管道方式执行,上一个命令的标准输出通过管道传递,作为下一个命令的标准输入。
- 命令可以大写EXECUTE_PROCESS。
- 命令格式
其中[]
表示可选、<>
表示必选、COMMAND
是固定参数。execute_process(COMMAND <cmd1> [<arguments>] [COMMAND <cmd2> [<arguments>]]... [WORKING_DIRECTORY <directory>] [TIMEOUT <seconds>] [RESULT_VARIABLE <variable>] [RESULTS_VARIABLE <variable>] [OUTPUT_VARIABLE <variable>] [ERROR_VARIABLE <variable>] [INPUT_FILE <file>] [OUTPUT_FILE <file>] [ERROR_FILE <file>] [OUTPUT_QUIET] [ERROR_QUIET] [COMMAND_ECHO <where>] [OUTPUT_STRIP_TRAILING_WHITESPACE] [ERROR_STRIP_TRAILING_WHITESPACE] [ENCODING <name>] [ECHO_OUTPUT_VARIABLE] [ECHO_ERROR_VARIABLE] [COMMAND_ERROR_IS_FATAL <ANY|LAST>])
- 例子
# 格式: execute_process(COMMAND <cmd1> [<arguments>] COMMAND <cmd2> [<arguments>] COMMAND <cmd2> [<arguments>] ) # 示例: EXECUTE_PROCESS(COMMAND ls -al COMMAND grep "cmake" COMMAND awk "{print $9}" ) # 等价于: ls -al | grep "cmake" | awk "{print $9}"
- 参数选项
- WORKING_DIRECTORY
指定命令的工作目录。 - TIMEOUT
命令的超时。 - RESULT_VARIABLE
管道最后一个命令的结果、返回码、描述字符串。 - RESULTS_VARIABLE
所有子命令执行结果存入该变量中,子结果以分号连接。
注意与RESULT_VARIABLE的区别!! - OUTPUT_VARIABLE、ERROR_VARIABLE
将标准输出和标准错误管道内容分别写入OUTPUT_VARIABLE和ERROR_VARIABLE指定的变量中,如果指定的是同一个变量,那么会按照执行的顺序合并输出。 - INPUT_FILE
指定文件作为第一个命令的标准输入 。 - OUTPUT_FILE
指定文件作为最后一个命令的标准输出。 - ERROR_FILE
所有命令运行错误结果存储的文件。 - OUTPUT_QUIET, ERROR_QUIET
忽略标准输出或者标准错误。 - COMMAND_ECHO
将运行的命令echo到STDOUT(标准输出)、STDERR(标准错误)、NONE,也可以通过设置变量CMAKE_EXECUTE_PROCESS_COMMAND_ECHO来控制默认值。 - ENCODING
指定命令执行的输出用何种方式解码,只在Windows下生效。 - ECHO_OUTPUT_VARIABLE, ECHO_ERROR_VARIABLE
将标准输出或标准错误复制一份到指定的变量(例如通过OUTPUT_VARIABLE指定的变量),而不是重定向。这意味着标准输出或者标准错误仍然会有效。 - COMMAND_ERROR_IS_FATAL
指定当遇到错误时的命令行为。
- WORKING_DIRECTORY
ADD_DEFINITIONS()
已被add_compile_definitions()、include_directories() 、add_compile_options() 代替。
添加编译定义(宏定义)、编译选项、cmake变量。
ADD_DEPENDENCIES()
ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)
如果两个targets有依赖关系,虽然可以通过target_link_xxx解决。add_dependencies 可以在编译当前target时,自动检查它所依赖的target是否已经编译,如果为否则先编译依赖的target,然后再编译当前target,最后 link depend target。若只有一个targets有依赖关系,一般选择使用 target_link_libraries。
find_package()
- 功能:搜索外部库,将结果赋值给cmake系统变量。
- 基本语法:
其中:find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE] [REQUIRED] [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...] [REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)] [GLOBAL] [NO_POLICY_SCOPE] [BYPASS_PROVIDER])
[version]
表示版本号;
[EXACT]
表示版本号精确匹配;
[QUIET]
表示禁止提示信息;
[REQUIRED]
表示如果包(库)没有被找到的话,cmake过程会终止,并输出警告信息;
[[COMPONENTS] [components...]
表示与该库相关的部件清单;
待续… - 搜索原理
cmake本身不提供任何搜索库的便捷方法,搜索库并给变量赋值的所有操作必须由该库提供的FindXXX.cmake
和XXXConfig.cmake
来完成。一般,库的作者通常会提供这两个文件,以方便使用者调用。 - 搜索模式
- Module模式
默认模式。
去CMAKE_MODULE_PATH
变量指定的路径下搜索FindXXX.cmake文件,并执行该文件从而找到XXX库。 - Config模式
如果Module模式未找到库,才会采取Config模式。
先去变量XXX_DIR
指定的路径搜索XXXConfig.cmake文件,执行该文件从而找到XXX库。XXX_DIR可以是操作系统级别的环境变量也可以是cmake级别的环境变量也可以是cmake变量。若找不到,再去/usr/local/lib/cmake/XXX/
中找。
例如:XXX库安装时没有安装到系统目录,因此无法自动找到XXXConfig.cmake,可以在CMakeLists.txt中定义一个XXX_DIR缓存变量:set(XXX_DIR /home/wjg/projects/XXX/build CACHE PATH "Directory that contains XXXConfig.cmake")
- Module模式
- 搜索结果
无论是Module模式还是Config模式,搜索结果都会被保存在XXX_INCLUDE_DIRS
和XXX_LIBRARIES
两个变量中。这两个变量在FindXXX.cmake或XXXConfig.cmake中被定义。 - 使用示例
以引用第三方库 BZip2 为例。project(helloworld) add_executable(helloworld hello.cpp) find_package (BZip2) if (BZIP2_FOUND) include_directories(${BZIP_INCLUDE_DIRS}) target_link_libraries (helloworld ${BZIP2_LIBRARIES}) endif (BZIP2_FOUND)
- xxx.cmake、cmake模块
为了能支持各种常见的库和包的搜索,CMake自带了很多模块,每个模块对应一个xxx.cmake。
可以使用命令cmake --help-module-list
来查看所有模块名称。
或者使用ls /usr/share/cmake-xx/Modules/
命令查看所有模块。
使用cmake --help-module Findxxx
查看Findxxx.cmake模块中有哪些可用变量。如:
注释
支持#
开头的单行注释,支持#[[
开头,]]
结尾的多行注释。
操作符优先级
-
优先级
() > 一元操作符 > 二元操作符 > 逻辑运算操作符类型 操作符名称 一元 EXISTS, COMMAND, DEFINED 二元 EQUAL, LESS, LESS_EQUAL, GREATER, GREATER_EQUAL, STREQUAL,STRLESS, STRLESS_EQUAL, STRGREATER, STRGREATER_EQUAL, VERSION_EQUAL, VERSION_LESS, VERSION_LESS_EQUAL, VERSION_GREATER, VERSION_GREATER_EQUAL, MATCHES 逻辑 NOT, AND, OR
if条件语句
if(表达式)
# 要执行的命令块
elseif(表达式2)
# 要执行的命令块
else(表达式)
# 要执行的命令块
endif(表达式)
while循环
while(表达式)
# 要执行的命令块
endwhile(表达式)
foreach循环遍历
#用法1
foreach(item "A" "B" "C")
message("${item}")
endforeach(item)
#用法2
foreach(循环变量 RANGE total)
# 要执行的命令块
endforeach(循环变量)
#用法3
foreach(循环变量 RANGE start stop [step])
# 要执行的命令块
endforeach(循环变量)
#用法4
# 支持对列表的遍历
set(list_var "1;2;3;4")
foreach(x IN LISTS list_var)
message("${x}")
endforeach(x)
命令(函数)
系统命令如set、message、if、while、foreach等。
cmake中的命令(command)本质上就是函数。
自定义函数命令:
function(<name> [<arg1> ...])
# 要执行的命令块
# 不需要返回值!
endfunction()
注意:如果不指定参数列表,则函数可以接受任意的参数,ARGC内置变量表明传人参数的个数,ARGV0, ARGV1, ARGV2, …内置变量可以获得对应传入的参数,ARGV内置变量可以获得整个参数列表。
例如:
function(foo x y z)
message("Calling function 'foo':")
message(" x = ${x}")
message(" y = ${y}")
message(" z = ${z}")
message("ARGC = ${ARGC} arg1 = ${ARGV0} arg2 = ${ARGV1} arg3 = ${ARGV2} all args = ${ARGV}")
endfunction(foo)
foo("1" "2" "3")
用宏macro来定义函数:
macro(foo [<arg1> ...])
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
endmacro(foo)
foo("1" "2" "3")
宏和函数的基本上是一样的,只是说函数命令有自己的作用域,宏命令的作用域和调用者的作用域一样。
多核编译加速 -j4
make -j4
cmake --build build -j4
编译目录build结构
默认情况下:
- 对于Linux,非常简单直观:可执行文件被放在bin;静态库(.a)和动态库(.so)都被放在lib。
- 对于windows(MSVC),非常繁琐:可执行文件(.exe)和动态库的主要部分(.dll)被放在bin;静态库(.lib)和动态库的辅助部分(.lib)被放在lib。
- 对于windows(Mingw),效果也类似:可执行文件(.exe)和动态库的主要部分(.dll)被放在bin;静态库(.a)和动态库的辅助部分(.dll.a)被放在lib。
自定义不同模式下,编译后的输出目录:
# 设置不同模式下,编译后的输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib")
编译模式
CMake支持四种编译模式:Debug、Release、RelWithDebInfo、MinSizeRel。
由系统变量 CMAKE_BUILD_TYPE
决定。
如:
cmake .. -DCMAKE_BUILD_TYPE=Release
宏定义、编译定义、编译选项、cmake变量
- 宏定义、编译定义表示同一个概念。只不过在
xx.cpp, xx.h
中被叫做宏定义(macro);在CMakeLists.txt
被叫做编译定义(ompile definitions)而已。 - 编译选项、cmake变量也表示同一个概念。只不过cmake把编程语言中的变量称为编译选项(compile options)而已。
- 宏定义
在CMakeLists.txt
或xx.cpp, xx.h
中定义,而在xx.cpp, xx.h
中使用!
例如:- 在
CMakeLists.txt
中定义宏USE_PYTHON2 :target_compile_definitions(targetName PRIVATE USE_PYTHON2)
- 或在
xx.cpp, xx.h
中定义宏:#define USE_PYTHON2
- 在
xx.cpp, xx.h
中使用该宏 :#ifdef USE_PYTHON2 //代码块1被编译 #else //代码块2被编译 #endif
- 在
- cmake变量
只在 CMakeLists.txt 中定义和使用!可通过cmake -Dxx=xx
来动态地修改变量的值!
例如:
待续…
什么是target
target 即 “构建目标”。
cmake中的target有以下几种:
- 可执行文件target(executable target)
- 静态库target(static library target)
- 动态库target(shared library target)
- 其他库:(1)由.o文件组成的OBJECT库;(2)由头文件组成的INTERFACE库(header-only)。
编译库文件
- 静态、动态库分别用关键字
STATIC | SHARED
来区分。 - add_library() 可缺省 STATIC | SHARED 参数,此时默认为STATIC全部生成静态库,但是也可以通过指定
BUILD_SHARED_LIBS
为真,修改默认值为SHARED全部生成动态库。 - 编译静态库
add_library(static_fun STATIC) target_sources(static_fun PRIVATE static_fun.cpp) #库的cpp文件 # 指明该库编译时和使用时都需要使用的头文件搜索路径: target_include_directories(static_fun PUBLIC ${PROJECT_SOURCE_DIR}/include)
- 编译动态库
add_library(shared_fun SHARED) target_sources(shared_fun PRIVATE shared_fun.cpp) #库的cpp文件 # 指明该库编译时和使用时都需要使用的头文件搜索路径: target_include_directories(shared_fun PUBLIC ${PROJECT_SOURCE_DIR}/include)
- 为target取别名
add_library(demo SHARED) add_library(Demo::demo ALIAS demo)
- 对以上代码中
PRIVATE
参数的解释请看下文。
编译可执行文件
add_executable(test)
target_sources(test PRIVATE test.cpp)
#或者:
add_executable(test PRIVATE test.cpp)
设置target属性
对于一个target,我们引入以下两个概念:
- build-requirements: 为了正确编译这个target我们需要的一切。
- usage-requirements: 其它的target为了正确使用当前target所需要的一切,会自动被它的使用方target获取,而build-requirements则不会传播给别的target。
target通常需要设置如下属性:
- 源文件
- 头文件搜索路径
- 链接的库
- 库搜索路径
- 宏定义(编译定义compile definitions)
- 编译选项(cmake变量、compile options)
- 链接选项
- 其它编译特点,例如指定C++标准
我们使用如下三个修饰符来指定这些属性的作用范围:
- PRIVATE: 私有的,表示只用于build-requirements,不用于usage-requirements.
- INTERFACE: 接口化的,只用于usage-requirements,不用于build-requirements.
- PUBLIC: 公开的,既用于usage-requirements,又用于build-requirements.
例如:
#编译一个动态库target
add_library(shared_fun SHARED)
#编译target之前需要设置一些必要的属性:
#(1)源文件
target_sources(<target> PRIVATE <source-file>...)
#(2)头文件搜索路径
target_include_directories(<target> PRIVATE <include-search-dir>...)
#(3)预处理的宏定义
target_compile_definitions(<target> PRIVATE <macro-definitions>...)
#(4)编译选项
target_compile_options(<target> PRIVATE <compile-option>...)
#(5)链接相关的库
target_link_libraries(<target> PRIVATE <dependency>...)
#(6)库搜索路径
target_link_directories(<target> PRIVATE <linker-search-dir>...)
#(7)链接选项
target_link_options(<target> PRIVATE <linker-option>...)
# (8)其它编译特点,例如指定C++标准
target_compile_features(<target> PRIVATE <feature>...)
这里的PRIVATE换成INTERFACE和PUBLIC均可。建议总是加上这些修饰符,虽然有时候省略也是合法的语法,但不是modern cmake推荐的用法。target_link_libraries 既支持链接到CMake构建的target,也支持连接到一个已经安装在某目录下的库。
除了使用target_xxx来设置target的属性外,还可以使用get_target_property
和set_target_properties
命令,请参考https://zhuanlan.zhihu.com/p/653279430。
get_target_property 和 set_target_properties
除了使用target_xxx来设置target的属性外,还可以使用get_target_property
和set_target_properties
命令,请参考https://zhuanlan.zhihu.com/p/653279430。
例如:
set_target_properties(demo PROPERTIES INTERFACE_INCLUDE_DIRECTORIES src/include)
set_target_properties(demo PROPERTIES INCLUDE_DIRECTORIES src/include)
# 分别等价于:
target_include_directories(demo INTERFACE src/include)
target_include_directories(demo PRIVATE src/include)
添加源文件
- 用法1
add_executable(targetXX PRIVATE main.cpp a.cpp b.cpp)
- 用法2
add_executable(targetXX) target_sources(targetXX PRIVATE main.cpp a.cpp b.cpp)
- 用法3
先将搜索结果存储到变量varXX,再添加到target。
这里其实没必要添加头文件,只是便于VS项目的生成,因为如果不添加头文件,则生成VS项目时会把头文件直接排除出去。file(GLOB varXX src/*.cpp include/*.h) target_sources(targetXX PRIVATE ${varXX})
- 用法4
使用关键字GLOB_RECURSE
递归查找源文件。file(GLOB_RECURSE CONFIGURE_DEPENDS varXX src/*.cpp include/*.h) target_sources(targetXX PRIVATE ${varXX})
- 用法5
使用aux_source_directory
命令自动搜集添加当前目录下的源文件。add_executable(targetXX) aux_source_directory(. varXX) aux_source_directory(./src varXX) target_sources(targetXX ${varXX})
编译结果后缀
请参考:https://cgold.readthedocs.io/en/latest/index.html
略…
INTERFACE库、OBJECT库、IMPORTED目标
请参考:https://cgold.readthedocs.io/en/latest/index.html
略…
6. CMake多文件项目示例
这里是一个多文件项目示例,包含多层级CMakeLists,这里暂不涉及对第三方库的依赖,不涉及一些编译的复杂逻辑,不涉及安装。项目在Linux平台上进行。
-
Demo项目的文件结构如下:
-
Demo项目包括:
- 单独生成可执行文件test1。
- 首先生成静态库static_fun,然后生成可执行文件test2,test2调用静态库static_fun。
- 首先生成动态库shared_fun,然后生成可执行文件test3,test3调用动态库shared_fun。
-
两个库的头文件分别为:
// static_fun.h void static_function(); // shared_fun.h void shared_function();
-
两个库的源文件依次为:
// static_fun.cpp #include "static_fun.h" #include <cstdio> void static_function(){ printf("this is static_function\n"); return; } // shared_fun.cpp #include "shared_fun.h" #include <cstdio> void shared_function(){ printf("this is shared_function\n"); return; }
-
三个可执行文件的源文件依次为:
// test1.cpp #include <cstdio> int main(){ printf("这是单文件测试\nhello,world\n"); return 0; } // test2.cpp #include "static_fun.h" #include <cstdio> int main(){ printf("这是main函数, 调用静态库测试\n"); static_function(); return 0; } // test3.cpp #include "shared_fun.h" #include <cstdio> int main(){ printf("这是main函数, 调用动态库测试\n"); shared_function(); return 0; }
-
项目根目录下的CMakeLists.txt如下:
# CMakeLists(0) cmake_minimum_required(VERSION 3.10) project(Demo VERSION 0.1) # 设置不同模式下,编译后的输出目录 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib") add_subdirectory(source)
-
各级子目录下的CMakeLists.txt依次为:
# CMakeLists(1) add_subdirectory(test1) add_subdirectory(test2) add_subdirectory(test3) # CMakeLists(2) add_executable(test1) target_sources(test1 PRIVATE test1.cpp) # CMakeLists(3) add_subdirectory(static_fun) add_executable(test2) target_sources(test2 PRIVATE test2.cpp) target_link_libraries(test2 PUBLIC static_fun) # CMakeLists(4) add_subdirectory(shared_fun) add_executable(test3) target_sources(test3 PRIVATE test3.cpp) target_link_libraries(test3 PUBLIC shared_fun) # CMakeLists(5) add_library(static_fun STATIC) target_sources(static_fun PRIVATE static_fun.cpp) target_include_directories(static_fun PUBLIC ${PROJECT_SOURCE_DIR}/include) # CMakeLists(6) add_library(shared_fun SHARED) target_sources(shared_fun PRIVATE shared_fun.cpp) target_include_directories(shared_fun PUBLIC ${PROJECT_SOURCE_DIR}/include)
-
生成构建系统,编译完成后,我们会得到两个库和三个可执行文件,分别存放在bin和lib目录:
参考文献
【0】https://cmake.org/cmake/help/latest/index.html
【1】https://blog.csdn.net/m0_38036750/article/details/131490392
【2】https://blog.csdn.net/FL1768317420/article/details/137001232
【3】https://preshing.com/20170522/learn-cmakes-scripting-language-in-15-minutes/
【4】https://zhuanlan.zhihu.com/p/653279430
【5】https://cgold.readthedocs.io/en/latest/index.html
【6】https://www.jianshu.com/p/1ec2b5602b03