关于CMake的一些用法
关于CMake的一些用法
以下纪录了我使用CMake期间遇到的各种问题和基本函数用法
1 在VScode中使用CMake
安装CMake
下载链接:Download CMake
尽量选择Latest Release版本,比较稳定。
如图中红框所示,下载后缀为.msi的安装文件,然后直接安装。

验证安装成功:cmake --version
安装MinGW
VScode的C++环境配置 - Blog of Mr.Juan (ljy0109.github.io)
VSCode中配置CMake
选择编译工具链


使用CMake编译

2 make命令无法识别
make : 无法将“make”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包 括路径,请确保路径正确,然后再试一次。

这是因为make命令实际上是调用mingw64/bin文件夹中的mingw32-make.exe文件。无法识别的原因是命令必须与文件名一致。
解决方法:复制一份mingw32-make.exe文件,并重命名为make.exe

3 eigen3/Eigen/Core: No such file or directory
下载链接:Eigen

1 | |

这个问题出在项目的CMakeLists.txt文件编写中。具体原因是包含eigen3库的路径有问题。
eigen3没有库,只有头文件,所以cmake只需要include头文件的路径就好了。
错误示范:
1 | |
正确示范:
1 | |
上述错误示范中有两处错误:
-
路径错误。观测.cpp文件中include头文件的路径:
#include<eigen3/Eigen/Core>。所以在CMakeLists.txt文件包含库时,不需要精确到目标库所在的那个文件夹。- 举个例子:eigen3库所在路径
E:/Code/VSCode/library/eigen3,文件夹eigen3存放eigen3库,文件夹eigen3在文件夹library内。那么在CMakeLists.txt包含时,只用指定到文件夹library就行了。#include<eigen3/Eigen/Core>命令会接着文件夹library向后搜索
- 举个例子:eigen3库所在路径
-
include_directories()错误。在 CMake 中,include_directories()函数用于将指定的目录添加到项目的头文件搜索路径中。但是,include_directories()函数需要传递一个有效的目录路径作为参数。在这种情况下,EIGEN3_INCLUDE_DIR是一个变量,存储了 Eigen3 库的头文件目录路径。现在来看一下这两种用法的区别:
include_directories(${EIGEN3_INCLUDE_DIR}):这是正确的用法。${EIGEN3_INCLUDE_DIR}是一个变量,用于存储 Eigen3 库的头文件目录路径。通过使用${EIGEN3_INCLUDE_DIR},可以将该变量的值(即 Eigen3 库的头文件目录路径)传递给include_directories()函数,从而将 Eigen3 库的头文件目录添加到项目的头文件搜索路径中。include_directories(EIGEN3_INCLUDE_DIR):这是错误的用法。在这种情况下,EIGEN3_INCLUDE_DIR并不是一个变量,而是一个字符串。因此,传递给include_directories()函数的参数实际上是一个字符串"EIGEN3_INCLUDE_DIR",而不是变量EIGEN3_INCLUDE_DIR的值。这样做会导致编译器无法正确解析路径,因为它实际上会在搜索路径中添加一个名为"EIGEN3_INCLUDE_DIR"的字符串,而不是指定的实际路径。
因此,正确的做法是使用
${EIGEN3_INCLUDE_DIR},而不是直接使用字符串"EIGEN3_INCLUDE_DIR"。这样可以确保将变量的值传递给include_directories()函数,从而正确地将 Eigen3 库的头文件目录添加到项目的头文件搜索路径中。
4 在VScode中调试CMake的程序
vscode下cmake工程环境配置以及调试配置(c++)_vscode cmake 调试-CSDN博客
在VScode中的运行与调试区域点击链接生成launch.json文件。

将生成的launch.json文件按照下列代码更改即可进行程序调试。需要安装cmake Tool插件
1 | |
5 undefined reference to 'omp_get_thread_num'
undefined reference to omp_get_thread_num'_undefined reference toomp_set_num_threads-CSDN博客
undefined reference to ‘omp_set_num_threads’ - CSDN文库
错误表示:编译器无法找到OpenMP库中的函数 omp_get_thread_num
需要修改CMakeLists.txt 文件,增加:
1 | |
基本函数用法
CMakeLists.txt的基本组成部分
1 | |
set()
set() 是 CMake 中用于设置变量的命令。它可以用于创建新变量、修改已存在变量的值,以及执行其他与变量相关的操作。
基本语法如下:
1 | |
其中 variable 是要设置的变量的名称,value 是要给变量赋予的值。value 可以是一个字符串、一个列表,也可以是其他 CMake 支持的数据类型。
示例:
1 | |
这行代码将一个名为 MY_VARIABLE 的变量设置为字符串 "Hello, world!"。
如果要设置的值包含空格或其他特殊字符,可以将值放在引号中以避免意外解析错误。
另外,set() 命令还可以用于执行其他操作,例如向列表变量添加元素、从环境变量中获取值等。以下是一些示例:
1 | |
在 CMake 中,变量的作用域是全局的,因此可以在项目的任何位置访问和修改变量。但是,建议将变量的作用域限制在最小范围内,以避免潜在的命名冲突和混乱。
find_package()
find_package() 是 CMake 中用于查找并加载特定软件包的模块的命令。它用于在系统中查找指定软件包的安装位置,并将其包含路径、库文件路径等信息导入到 CMake 构建系统中,以便于后续的项目构建和链接。
基本语法如下:
1 | |
其中:
package_name是要查找的软件包的名称。version是可选的,用于指定软件包的版本。EXACT表示要求查找到的软件包版本必须与指定的版本完全匹配。QUIET表示即使未找到软件包也不会产生错误。REQUIRED表示必须找到指定的软件包,否则将产生错误。COMPONENTS用于指定要查找的特定组件或模块。OPTIONAL_COMPONENTS用于指定可选的组件。NO_POLICY_SCOPE表示在查找软件包时不要修改全局策略。
示例:
1 | |
这行代码用于查找并加载 OpenCV 软件包,并将其相关信息导入到 CMake 构建系统中。在这个示例中,REQUIRED 表示必须找到 OpenCV 软件包,否则会产生错误。
find_package() 命令通常与 include_directories()、target_link_libraries() 等命令一起使用,以便于在项目中使用找到的软件包。例如:
1 | |
这样可以将 OpenCV 软件包的包含路径和库文件链接到项目中。
include_directories()
include_directories 是 CMake 中用于添加包含目录的命令。它用于将指定目录添加到编译器的包含路径中,以便编译器可以找到并包含这些目录中的头文件。
基本语法如下:
1 | |
其中 directory1 directory2 ... 是您要添加到包含路径的目录列表。
示例:
1 | |
这些命令将 include 和 src 目录添加到编译器的包含路径中。这样,在编译时,编译器就可以在这些目录中找到头文件,并将它们包含到源代码中。
需要注意的是,include_directories 命令会将指定的目录添加到所有目标的包含路径中,包括通过 add_executable 或 add_library 定义的可执行文件和库文件。如果您只想为特定目标添加包含路径,可以使用 target_include_directories 命令。
引用变量${}
在 CMake 中,${} 是用于引用变量的语法。它允许您在 CMake 脚本中引用变量的值,并将其插入到代码中。
基本用法如下:
1 | |
其中 variable_name 是您要引用的变量的名称。${} 语法将被替换为变量的实际值。
示例:
1 | |
在这个示例中,${MY_VARIABLE} 被替换为变量 MY_VARIABLE 的值,即 "Hello, world!"。message() 函数会将该值打印到 CMake 输出中。
${} 语法还允许进行一些高级操作,例如字符串连接、数学运算、路径操作等。以下是一些常见的用法:
-
字符串连接:
1
2
3set(STRING1 "Hello")
set(STRING2 "world!")
message("${STRING1}, ${STRING2}") -
数学运算:
1
2
3
4set(NUMBER1 10)
set(NUMBER2 20)
math(EXPR RESULT "${NUMBER1} + ${NUMBER2}")
message("The result is ${RESULT}") -
路径操作:
1
2
3set(PATH "/usr/local/include")
get_filename_component(DIRECTORY_NAME "${PATH}" DIRECTORY)
message("The directory name is ${DIRECTORY_NAME}")
add_executable()
add_executable 是 CMake 中用于定义可执行文件的命令。它的作用是将源文件编译成一个可执行文件。
基本语法如下:
1 | |
其中:
target_name是要生成的可执行文件的名称。source1.cpp source2.cpp ...是要编译的源文件列表。
示例:
1 | |
这行命令会将 main.cpp 和 functions.cpp 这两个源文件编译成一个名为 my_program 的可执行文件。
还可以使用变量来指定源文件列表,例如:
1 | |
target_link_libraries()
target_link_libraries 是 CMake 中用于将目标(例如可执行文件或库)与特定的库文件链接起来的命令。它的作用是将指定的库文件与目标进行链接,使得目标可以使用这些库中提供的函数和功能。
基本语法如下:
1 | |
其中:
target_name是要链接库的目标的名称,通常是通过add_executable或add_library命令定义的。library1 library2 ...是要链接的库文件的名称,可以是库文件的名称(例如mylib)或者是完整的路径(例如/path/to/mylib.lib)。
示例:
1 | |
这行命令会将名为 my_lib 的库文件链接到 my_program 可执行文件中。
可以链接多个库文件,例如:
1 | |
这样可以将多个库文件链接到同一个目标中。
另外,还可以链接系统提供的库,例如:
1 | |
这行命令会链接 POSIX 线程库(pthread)到 my_program 可执行文件中,使得 my_program 可以使用 POSIX 线程库中提供的函数和功能。
动态链接与静态链接
target_link_libraries 可以用于进行静态链接或动态链接,具体取决于所链接的库文件是静态库还是动态库。
- 静态链接: 如果链接的是静态库文件(
.a或.lib),则会进行静态链接。在静态链接时,库文件的内容会被复制到最终生成的可执行文件中,因此可执行文件独立于外部库文件。 - 动态链接: 如果链接的是动态库文件(
.so或.dll),则会进行动态链接。在动态链接时,可执行文件会保留对动态库的引用,而实际的库文件会在运行时加载到内存中。因此,动态链接库文件通常不会包含在最终的可执行文件中。
默认情况下,CMake 会自动根据库文件的后缀来判断是静态链接还是动态链接。例如,.a 或 .lib 后缀的库文件通常被视为静态库,.so 或 .dll 后缀的库文件通常被视为动态库。也可以通过显式指定 STATIC 或 SHARED 参数来指定链接类型,例如:
1 | |
在上述示例中,PRIVATE 关键字用于指定链接的范围,my_static_lib 和 my_shared_lib 分别是静态库和动态库的名称。