关于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 to
omp_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
分别是静态库和动态库的名称。