原文来自:CMake 保姆级教程
视频来自B站:CMake 保姆级教程C/C++
1、快速操作:
原文来自:在 VScode 中使用 CMake 快速创建cpp工程
首先创建一个 C/C++ 工程文件夹 CALC,用 VSCode 打开,目录结构如下:
-
使用快捷键
Ctrl+Shift+P
打开vscode 的控制面板。输入CMake:quick start
,或选择如下:
-
输入项目名称:CALC
-
选择创建 C++ 项目
-
选择创建可执行文件
-
最后直接 ok
-
添加对应的加减乘除文件
1、add.c
#include <stdio.h> #include "head.h" int add(int a, int b) { return a+b; }
2、sub.c
#include <stdio.h> #include "head.h" // 你好 int subtract(int a, int b) { return a-b; }
3、mult.c
#include <stdio.h> #include "head.h" int multiply(int a, int b) { return a*b; }
4、div.c
#include <stdio.h> #include "head.h" double divide(int a, int b) { return (double)a/b; }
5、head.h
#ifndef _HEAD_H #define _HEAD_H // 加法 int add(int a, int b); // 减法 int subtract(int a, int b); // 乘法 int multiply(int a, int b); // 除法 double divide(int a, int b); #endif
6、main.c
#include <stdio.h> #include "head.h" int main() { int a = 20; int b = 12; printf("a = %d, b = %d\n", a, b); printf("a + b = %d\n", add(a, b)); printf("a - b = %d\n", subtract(a, b)); printf("a * b = %d\n", multiply(a, b)); printf("a / b = %f\n", divide(a, b)); return 0; }
-
修改 CMakeLists.txt 中的可执行文件
cmake_minimum_required(VERSION 3.0.0) project(CALC VERSION 0.1.0 LANGUAGES C CXX) add_executable(CALC main.c add.c sub.c mult.c div.c)
-
直接运行即可得到以下结果:
2、CMake 的使用
1、注释
注释行
CMake
使用 #
进行行注释
:
# 这是一个 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0.0)
注释块
CMake
使用 #[[ ]]
形式进行块注释
:
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)
2、案例引入
准备工作
为了方便测试,在我本地电脑准备了这么几个测试文件
-
add.c
#include <stdio.h> #include "head.h" int add(int a, int b) { return a+b; }
-
sub.c
#include <stdio.h> #include "head.h" // 你好 int subtract(int a, int b) { return a-b; }
-
mult.c
#include <stdio.h> #include "head.h" int multiply(int a, int b) { return a*b; }
-
div.c
#include <stdio.h> #include "head.h" double divide(int a, int b) { return (double)a/b; }
-
head.h
#ifndef _HEAD_H #define _HEAD_H // 加法 int add(int a, int b); // 减法 int subtract(int a, int b); // 乘法 int multiply(int a, int b); // 除法 double divide(int a, int b); #endif
-
main.c
#include <stdio.h> #include "head.h" int main() { int a = 20; int b = 12; printf("a = %d, b = %d\n", a, b); printf("a + b = %d\n", add(a, b)); printf("a - b = %d\n", subtract(a, b)); printf("a * b = %d\n", multiply(a, b)); printf("a / b = %f\n", divide(a, b)); return 0; }
上述文件的目录结构如下:
$ tree
.
├── add.c
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c
添加 CMakeLists.txt
文件
# 指定使用的 cmake 的最低版本
cmake_minimum_required(VERSION 3.0.0)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 定义工程会生成一个可执行程序
# 格式:add_executable(可执行程序名 源文件名称),也可用“;”分隔
add_executable(CALC main.c add.c sub.c mult.c div.c)
执行CMake
命令
# cmake 命令原型
$ cmake CMakeLists.txt文件所在路径
$ tree
.
├── add.c
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c
0 directories, 7 files
$ cmake .
$ make
最终可执行程序 CALC 就被编译出来了(这个名字是在CMakeLists.txt
中指定的)。
规范管理项目生成的目录和文件
$ mkdir build
$ cd build
$ cmake ..
现在 cmake
命令是在 build
目录中执行的,但是 CMakeLists.txt
文件是 build
目录的上一级目录中,所以 cmake
命令后指定的路径为..
,即当前目录的上一级目录。
tips:
- 若终端路径与 CMakeLists.txt 文件所在路径一致,则执行
cmake .
; - 若终端路径为 CMakeLists.txt 文件所在路径的下一级,则执行
cmake ..
;
最终效果:
$tree -L 1
.
├── add.c
├── calc
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile
├── mult.c
└── sub.c
1 directory, 11 files
3、CMake 语法
1、定义变量(set)
# SET 指令的语法是:
# [] 中的参数为可选项, 如不需要可以不写
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
其中,VAR
为变量名,VALUE
为变量值
简化上面的 CMakeLists.txt 的部分内容:
set(SRC_LIST add.c div.c main.c mult.c sub.c)
add_executable(CALC ${SRC_LIST})
2、指定C++标准
法1:在 CMakeLists.txt 中通过 set 命令指定
#增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
#增加-std=c++14
set(CMAKE_CXX_STANDARD 14)
#增加-std=c++17
set(CMAKE_CXX_STANDARD 17)
法2:在执行 cmake 命令的时候指定出这个宏的值
#增加-std=c++11
cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=11
#增加-std=c++14
cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=14
#增加-std=c++17
cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=17
3、指定输出的路径
在CMake中指定可执行程序输出的路径,也对应一个宏,叫做 EXECUTABLE_OUTPUT_PATH
,它的值还是通过 set
命令进行设置:
set(HOME /home/robin/Linux/Sort)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
- 第一行:定义一个变量用于存储一个绝对路径;
- 第二行:将拼接好的路径值设置给
EXECUTABLE_OUTPUT_PATH
宏,如果这个路径中的子目录不存在,会自动生成,无需自己手动创建。
示例:
cmake_minimum_required(VERSION 3.0)
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 指定可执行文件的输出目录
# ${CMAKE_CURRENT_SOURCE_DIR}为该 CMakeLists.txt 所在的目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build/bin)
# 定义可执行文件
add_executable(calc main.c add.c sub.c mult.c div.c)
4、搜索文件
如果一个项目里边的源文件很多,在编写 CMakeLists.txt
文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦也不现实。所以,在CMake中为我们提供了搜索文件的命令,可以使用 aux_source_directory
命令或者 file
命令。
法1:aux_source_directory
命令格式为:
aux_source_directory(< dir > < variable >)
dir
:要搜索的目录;variable
:将从dir
目录下搜索到的源文件列表存储到该变量中。
举例:
cmake_minimum_required(VERSION 3.0)
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 包含头文件的路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build/bin)
# 定义可执行文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src src_path)
add_executable(calc ${src_path})
tips:如果报错找不到头文件,记得将头文件的路径添加到 CMakeLists.txt 中,如上面的第7行。
法2:file
命令格式为:
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
GLOB
: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。GLOB_RECURSE
:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。
举例:
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
示例:
cmake_minimum_required(VERSION 3.0)
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 包含头文件的路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build/bin)
# 定义可执行文件
file(GLOB src_path ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
add_executable(calc ${src_path})
tips:这里为了方便寻找路径,CMake 提供了两个命令:PROJECT_SOURCE_DIR
和 CMAKE_CURRENT_SOURCE_DIR
。
1、PROJECT_SOURCE_DIR
PROJECT_SOURCE_DIR 是指顶层CMakeLists.txt文件所在的目录。它通常表示整个项目的源代码的根目录。无论你在CMake构建过程中运行哪个CMakeLists.txt文件,PROJECT_SOURCE_DIR 都始终指向顶层的源目录。
举个例子,如果你的项目结构如下:
/path/to/project/
├── CMakeLists.txt
├── src/
│ ├── CMakeLists.txt
│ └── main.cpp
└── include/
└── CMakeLists.txt
在 /path/to/project/CMakeLists.txt
中和 /path/to/project/src/CMakeLists.txt
中,PROJECT_SOURCE_DIR
都会指向 /path/to/project
。
2、CMAKE_CURRENT_SOURCE_DIR
CMAKE_CURRENT_SOURCE_DIR
是指当前处理的 CMakeLists.txt
文件所在的目录。每当 CMake
处理一个 CMakeLists.txt
文件时,CMAKE_CURRENT_SOURCE_DIR
会更新为这个文件所在的目录。
使用同样的项目结构,在 /path/to/project/CMakeLists.txt
中,CMAKE_CURRENT_SOURCE_DIR
会是 /path/to/project
,而在 /path/to/project/src/CMakeLists.txt
中,CMAKE_CURRENT_SOURCE_DIR
会是 /path/to/project/src
。
总结
PROJECT_SOURCE_DIR
始终指向项目的顶层源目录。CMAKE_CURRENT_SOURCE_DIR
指向当前处理的CMakeLists.txt
文件所在的目录。
这两个变量在配置项目路径、引用文件和组织CMake逻辑时非常有用,特别是在处理具有多个子目录的复杂项目时。
示例
假设你在顶层CMakeLists.txt文件中写:
message("PROJECT_SOURCE_DIR: ${PROJECT_SOURCE_DIR}")
message("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
并在 /path/to/project/src/CMakeLists.txt
文件中写:
message("PROJECT_SOURCE_DIR: ${PROJECT_SOURCE_DIR}")
message("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
然后运行 CMake
配置,你将看到类似如下的输出:
PROJECT_SOURCE_DIR: /path/to/project
CMAKE_CURRENT_SOURCE_DIR: /path/to/project
PROJECT_SOURCE_DIR: /path/to/project
CMAKE_CURRENT_SOURCE_DIR: /path/to/project/src
这清晰地展示了这两个变量在不同上下文中的值。
5、包含头文件
在编译项目源文件的时候,很多时候都需要将源文件对应的头文件路径指定出来,这样才能保证在编译过程中编译器能够找到这些头文件,并顺利通过编译。在 CMake 中设置要包含的目录也很简单,通过一个命令就可以搞定了,他就是 include_directories
:
include_directories(headpath)
举例说明,有源文件若干,其目录结构如下:
$ tree
.
├── build
├── CMakeLists.txt
├── include
│ └── head.h
└── src
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp
3 directories, 7 files
CMakeLists.txt
文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 包含头文件的路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build/bin)
# 定义可执行文件
file(GLOB src_path ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
add_executable(calc ${src_path})
其中,第七行指定就是头文件的路径,PROJECT_SOURCE_DIR
宏对应的值就是我们在使用 cmake 命令时,后面紧跟的目录,一般是工程的根目录。
6、制作动态库或静态库
满足用户自定义第三方库的需要:静态库和动态库。
制作静态库
命令格式:
add_library(库名称 STATIC 源文件1 [源文件2] ...)
在Linux中,静态库名字分为三部分: l i b + 库名字 + . a lib + 库名字 + .a lib+库名字+.a,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。(windows 中为 l i b + 库名字 + . l i b lib + 库名字 + .lib lib+库名字+.lib )
下面有一个目录(将 main.c 从 src 文件夹移出),需要将 src
目录中的源文件编译成静态库,然后再使用:
$tree -L 2
.
├── build
│ ├── bin
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ └── Makefile
├── CMakeLists.txt
├── include
│ └── head.h
├── main.c
└── src
├── add.c
├── div.c
├── mult.c
└── sub.c
5 directories, 10 files
根据上面的目录结构,可以这样编写CMakeLists.txt
文件:
cmake_minimum_required(VERSION 3.0)
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 包含头文件的路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 定义可执行文件
file(GLOB src_path ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
# 制作静态库
add_library(calc_static_lib STATIC ${src_path})
这样最终就会生成对应的静态库文件 libcalc.a
。
$tree -L 2
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── libcalc_static_lib.a
│ └── Makefile
├── CMakeLists.txt
├── include
│ └── head.h
├── main.c
└── src
├── add.c
├── div.c
├── mult.c
└── sub.c
4 directories, 11 files
动态库
在cmake中,如果要制作动态库,需要使用的命令如下:
add_library(库名称 SHARED 源文件1 [源文件2] ...)
在Linux中,动态库名字分为三部分:lib
+库名字
+.so
,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。(windows 中为
l
i
b
+
库名字
+
.
d
l
l
lib+库名字+.dll
lib+库名字+.dll )
在Windows中虽然库名和Linux格式不同,但也只需指定出名字即可。
根据上面的目录结构,可以这样编写CMakeLists.txt
文件:
cmake_minimum_required(VERSION 3.0)
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 包含头文件的路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 定义可执行文件
file(GLOB src_path ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
# 制作动态库
add_library(calc_shared_lib SHARED ${src_path})
这样最终就会生成对应的动态库文件libcalc.so
。
tips:生成所需的库文件需要包含头文件内容。
$ tree -L 2
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── libcalc_shared_lib.so
│ └── Makefile
├── CMakeLists.txt
├── include
│ └── head.h
├── main.c
└── src
├── add.c
├── div.c
├── mult.c
└── sub.c
4 directories, 11 files
指定库文件的输出路径
命令格式:
set(LIBRARY_OUTPUT_PATH 文件存放位置)
示例:
cmake_minimum_required(VERSION 3.0)
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 包含头文件的路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 定义可执行文件
file(GLOB src_path ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
# 设置动态库的存放位置,注意是:LIBRARY_OUTPUT_PATH
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 制作动态库
add_library(calc_shared_lib SHARED ${src_path})
$ tree -L 2
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ └── Makefile
├── CMakeLists.txt
├── include
│ └── head.h
├── lib
│ └── libcalc_shared_lib.so
├── main.c
└── src
├── add.c
├── div.c
├── mult.c
└── sub.c
5 directories, 11 files
tips:
- 静态库和动态库可以同时设置,但不能同名;
- 可通过 set_target_properties 来设置他们的名称相同。
7、包含库文件
链接静态库
在cmake中,链接静态库的命令如下:
link_libraries(<static lib> [<static lib>...])
- 参数 1:指定出要链接的静态库的名字,可以是全名 libxxx.a,也可以是掐头(lib)去尾(.a)之后的名字 xxx
- 参数 2 − N 2-N 2−N:要链接的其它静态库的名字
$ tree -L 2
.
├── build
│ ├── bin
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ └── Makefile
├── CMakeLists.txt
├── include
│ └── head.h
├── lib
│ ├── libcalc_shared_lib.so
│ └── libcalc_static_lib.a
└── src
└── main.c
6 directories, 8 files
示例:
cmake_minimum_required(VERSION 3.0)
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 包含头文件的路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build/bin)
#
file(GLOB src_path ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
# 设置库文件的输出地址
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 包含静态库路径
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(calc_static_lib)
# 定义可执行文件
add_executable(calc ${src_path})
tips:在生成静态库后,选择删去了函数源文件(只保留了main.c),链接静态库时报错:找不到四个加减乘除函数源文件,但此时因为已经生成了静态库,所以不需要了,将制作静态库的语句注释即可。
链接动态库
在cmake中链接动态库的命令如下:
target_link_libraries(
<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
-
target
:指定要加载动态库的文件的名字-
该文件可能是一个源文件
-
该文件可能是一个动态库文件
-
该文件可能是一个可执行文件
-
-
PRIVATE|PUBLIC|INTERFACE
:动态库的访问权限,默认为PUBLIC
,一般情况下选取public即可。-
如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可。
-
动态库的链接具有传递性,如果动态库 A 链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中定义的方法。
target_link_libraries(A B C) target_link_libraries(D A)
- PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用
- PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库
- INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。
-
示例:
cmake_minimum_required(VERSION 3.0)
project(CALC VERSION 0.1.0 LANGUAGES C CXX)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 包含头文件的路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 设置可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build/bin)
# 定义可执行文件
file(GLOB src_path ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
# 设置库文件的输出地址
# set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 指定要链接的动态库的路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 制作动态库
# add_library(calc_shared_lib SHARED ${src_path})
# 生成可执行文件
add_executable(calc ${src_path})
# 链接动态库
target_link_libraries(calc calc_shared_lib)
tips:与静态库同理,如果函数源文件被删除了,需要注释掉 cmake 中创建动态库的相关语句。
链接动态库和链接静态库的区别
动态库的链接和静态库是完全不同的:
- 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
- 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存。
8、日志
命令格式:
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
- (无) :重要消息
- STATUS :非重要消息
- WARNING:CMake 警告, 会继续执行
- AUTHOR_WARNING:CMake 警告 (dev), 会继续执行
- SEND_ERROR:CMake 错误, 继续执行,但是会跳过生成的步骤
- FATAL_ERROR:CMake 错误, 终止所有处理过程
示例:
# message 消息
# 重要消息
message("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
# 非重要消息
message(STATUS "111xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
# 错误消息,立即终止
# message(FATAL_ERROR "222xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
9、变量操作
追加
法1:set
命令格式:
set(变量名1 ${变量名1} ${变量名2} ...)
示例:
cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
set(SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}")
法2:list
命令格式:
list(APPEND <list> [<element> ...])
APPEND
表示进行数据追加,后边的参数和 set
就一样。
cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}")
list 还可以实现字符串移除、字符串反转、字符串排序等
10、宏定义
在进行程序测试的时候,我们可以在代码中添加一些宏定义,通过这些宏来控制这些代码是否生效,如下所示:
#include <stdio.h>
#define NUMBER 3
int main()
{
int a = 10;
#ifdef DEBUG
printf("我是一个程序猿, 我不会爬树...\n");
#endif
for(int i=0; i<NUMBER; ++i)
{
printf("hello, GCC!!!\n");
}
return 0;
}
在程序的第七行对 DEBUG
宏进行了判断,如果该宏被定义了,那么第八行就会进行日志输出,如果没有定义这个宏,第八行就相当于被注释掉了,因此最终无法看到日志输入出(上述代码中并没有定义这个宏)。
为了让测试更灵活,我们可以不在代码中定义这个宏,而是在测试的时候去把它定义出来,其中一种方式就是在 gcc/g++ 命令中去指定,如下:
$ gcc test.c -DDEBUG -o app
在 gcc/g++ 命令中通过参数 -D 指定出要定义的宏的名字,这样就相当于在代码中定义了一个宏,其名字为DEBUG
。
在 CMake
中我们也可以做类似的事情,对应的命令叫做 add_definitions
:
add_definitions(-D宏名称)
针对于上面的源文件编写一个CMakeLists.txt,内容如下:
cmake_minimum_required(VERSION 3.0)
project(TEST)
# 自定义 DEBUG 宏
add_definitions(-DDEBUG)
add_executable(app ./test.c)
通过这种方式,上述代码中的第八行日志就能够被输出出来了。
11、嵌套的CMake
有如下目录结构:
$ tree
.
├── build
├── calc
│ ├── add.cpp
│ ├── CMakeLists.txt
│ ├── div.cpp
│ ├── mult.cpp
│ └── sub.cpp
├── CMakeLists.txt
├── include
│ ├── calc.h
│ └── sort.h
├── sort
│ ├── CMakeLists.txt
│ ├── insert.cpp
│ └── select.cpp
├── test1
│ ├── calc.cpp
│ └── CMakeLists.txt
└── test2
├── CMakeLists.txt
└── sort.cpp
6 directories, 15 files
添加子目录
命令格式:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
source_dir
:指定了CMakeLists.txt 源文件和代码文件的位置,其实就是指定子目录binary_dir
:指定了输出文件的路径,一般不需要指定,忽略即可。EXCLUDE_FROM_ALL
:在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外。用户必须显式构建在子路径下的目标。
示例
根目录中的 CMakeLists.txt 文件内容如下:定义全局变量和添加子目录
cmake_minimum_required(VERSION 3.0)
project(test)
# 定义变量
# 静态库生成的路径
set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 测试程序生成的路径
set(EXEC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# 头文件目录
set(HEAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 静态库的名字
set(CALC_LIB calc)
set(SORT_LIB sort)
# 可执行程序的名字
set(APP_NAME_1 test1)
set(APP_NAME_2 test2)
# 添加子目录
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)
calc 目录中的 CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${CALC_LIB} STATIC ${SRC})
sort 目录中的 CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})
test1 目录中的 CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
link_directories(${LIB_PATH})
link_libraries(${CALC_LIB})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
add_executable(${APP_NAME_1} ${SRC})
test2 目录中的 CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(SORTTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
link_directories(${LIB_PATH})
add_executable(${APP_NAME_2} ${SRC})
target_link_libraries(${APP_NAME_2} ${SORT_LIB})
静态库链接静态库
比如:sort中链接calc静态库
cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
# 格式如下:直接添加要链接的静态库即可
link_libraries(${CALC_LIB})
# 如果是第三方的库,则需要指出路径
link_directories(${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})
静态库链接动态库
1、首先要将需链接的库设为动态库:
cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${CALC_LIB} SHARED ${SRC})
2、在静态库中链接该动态库:
cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
# 设置动态库的地址
link_directories(${LIB_PATH})
# 静态库链接动态库
add_library(${SORT_LIB} STATIC ${SRC})
# d库链接动态库
# add_library(${SORT_LIB} SHARED ${SRC})
# 链接动态库
target_link_libraries(${SORTLIB} ${CALCLIB})