CMakeLists.txt 编写规则
目录
1. 注释
1.1 注释行
1.2 注释块
2. CMakeLists.txt的编写
2.1 同意目录下的源文件
2.2 SET指令
2.3 file和aux_source_directory
2.4 包含头文件
2.5 生成动态库和静态库
2.6 链接库文件
2.7 message指令
2.8 移除操作
2.9 find_library和find_package
3. 常用的宏
4. 示例
参考:
CMake 保姆级教程(上)subingwen.cn/cmake/CMake-primer/#2-6-3-%E6%80%BB%E7%BB%93编辑https://link.zhihu.com/?target=https%3A//subingwen.cn/cmake/CMake-primer/%232-6-3-%25E6%2580%25BB%25E7%25BB%2593
cmake使用详细教程(日常使用这一篇就足够了)_cmake教程-CSDN博客blog.csdn.net/iuu77/article/details/129229361?ops_request_misc=%257B%2522request%255Fid%2522%253A%25223BDC4C1F-AC50-4EB2-99A3-1FD7204C3A17%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=3BDC4C1F-AC50-4EB2-99A3-1FD7204C3A17&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-2-129229361-null-null.142%5Ev100%5Epc_search_result_base8&utm_term=cmake&spm=1018.2226.3001.4187编辑https://link.zhihu.com/?target=https%3A//blog.csdn.net/iuu77/article/details/129229361%3Fops_request_misc%3D%25257B%252522request%25255Fid%252522%25253A%2525223BDC4C1F-AC50-4EB2-99A3-1FD7204C3A17%252522%25252C%252522scm%252522%25253A%25252220140713.130102334..%252522%25257D%26request_id%3D3BDC4C1F-AC50-4EB2-99A3-1FD7204C3A17%26biz_id%3D0%26utm_medium%3Ddistribute.pc_search_result.none-task-blog-2~all~top_positive~default-2-129229361-null-null.142%255Ev100%255Epc_search_result_base8%26utm_term%3Dcmake%26spm%3D1018.2226.3001.4187
需要注意的是CMakeLists.txt文件中的指令不区分大小写
1. 注释
1.1 注释行
CMake通过‘#’进行单行注释,比如
# Cmake版本至少为3.1
cmake_minimum_required(VERSION 3.1)
1.2 注释块
CMake通过‘if(FALSE)’进行块注释,比如
if(FALSE)# CMake版本至少为3.1# CMake版本至少为3.1# CMake版本至少为3.1
endif()
cmake_minimum_required(VERSION 3.1)
2. CMakeLists.txt的编写
2.1 同意目录下的源文件
如果只有一个源文件hello.cpp,内容如下:
#include <iostream>
using namespace std;int main()
{cout << "Hello World!" << endl;return 0;
}
那么CMakeLists.txt可以这样写:
cmake_minimum_required(VERSION 3.1)
project (LearnCMake LANGUAGES CXX)
add_executable(hello hello.cpp)
- cmake_minimum_required:指定使用的 cmake 的最低版本;
- project:定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可;
- 这里项目名称为 LearnCMake ,项目使用的语言是 C++,CXX表示C++
# PROJECT 指令的语法是:
project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME>[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]][DESCRIPTION <project-description-string>][HOMEPAGE_URL <url-string>][LANGUAGES <language-name>...])# 示例
project(GrpcServer VERSION 1.0.0 DESCRIPTION "A gRPC server implementation in C++" HOMEPAGE_URL "https://example.com/grpcserver" LANGUAGES CXX)
- add_executable:定义工程会生成一个可执行程序,可执行程序名为hello,源文件名称为hello.cpp
# 单个源文件
add_executable(可执行程序名 源文件名称)
# 多个源文件名(空格隔开)
add_executable(hello add.c div.c main.c mult.c sub.c)
# 多个源文件名(封号隔开)
add_executable(hello add.c;div.c;main.c;mult.c;sub.c)
我们一般需要单独创建一个build文件夹,用于存放
- 可执行文件:已经编译过的可执行文件,如Windows上的.exe文件,Linux和Mac上的二进制文件。
- 中间文件:编译过程中生成的中间文件,如目标文件(.o或.obj)、汇编文件(.s)等。
- 构建脚本:用于帮助构建项目的脚本文件,如Makefile、CMakeLists.txt的生成版本或Ninja文件等。
- 库文件:如果项目包含库代码的编译,那么还会生成静态库(.a)或动态库(.so、.dll)。
我们这里创建一个build文件夹,进入之后执行cmake并编译
mkdir build
cd ./build/
cmake ..
build文件夹内会生成以下文件:
执行make命令,使用makefile进行编译
make
生成可执行程序hello,并运行
./hello
2.2 SET指令
SET指令是 CMake 中用于定义和设置变量的命令,它允许你创建一个新的变量或者修改一个已存在的变量的值。
2.2.1 使用SET将多个源文件存储至指定变量
如果我们有五个源文件需要添加:
add_executable(hello add.c;div.c;main.c;mult.c;sub.c)
如果有多个源文件,我们可以定义一个变量,将文件名对应的字符串存储起来,在cmake里定义变量需要使用set
# SET 指令的语法是:
# [] 中的参数为可选项, 如不需要可以不写
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
我们使用SET指令将这五个文件名和自定义的变量联系起来,通过添加这个变量名即可同时调用这五个文件
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(hello ${SRC_LIST})
如果源文件特别多,一个个添加会特别慢,此时可以使用cmake中的函数存储这些源文件。
aux_source_directory(dir var)
他的作用是把dir目录中的所有源文件都储存在var变量中,然后需要用到源文件的地方用变量var来取代。
上述代码可以使用该函数改写为:(我们一般把.cpp文件放在src文件夹下,.h放在include文件夹下)
aux_source_directory(. SRC_LIST)
add_executable(hello ${SRC_LIST})
2.2.2 指定C++标准
C++标准对应有一宏叫做DCMAKE_CXX_STANDAR,我们可以通过SET指令指定我们使用的C++标准:
#增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
#增加-std=c++14
set(CMAKE_CXX_STANDARD 14)
#增加-std=c++17
set(CMAKE_CXX_STANDARD 17)
该宏一般与宏CMAKE_CXX_STANDARD_REQUIRED进行配合使用,该宏为ON时, 如果编译器不支持你指定的 C++ 标准(比如C++17),CMake 配置过程将会失败:
set(CMAKE_CXX_STANDARD_REQUIRED ON)
2.2.3 指定输出的路径
在CMake中指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH,可通过SET指令进行设置:
set(HOME /home/robin/Linux/Sort)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
- 如果子目录bin不存在,会自动生成
2.3 file和aux_source_directory
这两个指令都可以用于搜索指定目录下的文件,后者用于查找指定目录下的所有源文件,我们在2.1中使用过,这里不再做介绍。
2.3.1 file
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
在 CMake 中,file(GLOB ...) 和 file(GLOB_RECURSE ...) 命令用于从指定的目录中搜索匹配特定模式的文件,并将这些文件的列表存储在一个变量中。
- GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
- GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。
如果我们有一个目录如下:
project/
├── CMakeLists.txt
├── src/
│ ├── main.cpp
│ └── utils/
│ └── helper.cpp
如果我们像包含src/目录下的所有.cpp文件,只需:
# 使用 GLOB 只搜索 src 目录下的文件(不递归),并存储至变量SRC_FILES
file(GLOB SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") # 或者使用 GLOB_RECURSE 递归搜索 src 目录及其子目录中的文件
file(GLOB_RECURSE SRC_FILES_RECURSIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") # 然后你可以使用这些变量来添加可执行文件或库
add_executable(MyExecutable ${SRC_FILES_RECURSIVE})
- 宏 CMAKE_CURRENT_SOURCE_DIR 是 CMake 提供的一个内置变量,用于表示当前正在处理的CMakeLists.txt文件所在的源代码目录的路径
- 如果不同文件夹下有不同的CMakeLists.txt文件,那么CMAKE_CURRENT_SOURCE_DIR 随着CMakeLists.txt文件所处的路径变化而变化。
2.4 包含头文件
2.4.1 头文件均在一个目录下
在CMake中通过命令 include_directories 设置要包含的目录。
# 在指定 dir目录下寻找头文件
include_directories ( dir )
如果我们有一个目录如下:
$ tree
.
├── build
├── CMakeLists.txt
├── include
│ └── head.h
└── src├── add.cpp├── div.cpp├── main.cpp├── mult.cpp└── sub.cpp
我们可以使用如下指令包含头文件head.h
include_directories(${PROJECT_SOURCE_DIR}/include)
- 宏 PROJECT_SOURCE_DIR 是 CMake 在处理project()命令时自动设置的变量,它指向包含最近一次调用project()命令定义的项目的源目录
该目录下的CMakeLists.txt文件这样写:
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project (LearnCMake LANGUAGES CXX)include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(hello ${SRC_LIST})
2.4.2 头文件源文件分离,并包含多个文件夹
假设我们有如下目录结构:
LEARN_CMAKE
├── CMakeLists.txt
│ ├── inc_dir1
│ │ └── myfunc1.h
│ ├── inc_dir2
│ │ └── myfunc2.h
│ ├── main_dir
│ │ └── hello.cpp
│ ├── src_dir1
│ │ └── myfunc1.cpp
│ └── src_dir2
│ └── myfunc2.cpp
该目录下的CMakeLists.txt文件这样写:
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 项目名称
project (LearnCMake LANGUAGES CXX)# 头文件
include_directories(${PROJECT_SOURCE_DIR}/inc_dir1 ${PROJECT_SOURCE_DIR}/inc_dir2)# 获取源文件
file(GLOB SRC_LIST1 ${CMAKE_CURRENT_SOURCE_DIR}/src_dir1/*.cpp)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src_dir2 SRC_LIST2)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/main_dir MAIN_DIR)# 生成可执行文件
add_executable(hello ${SRC_LIST1} ${SRC_LIST2} ${MAIN_DIR})
2.5 生成动态库和静态库
- 动态库在编译时并不会将库的代码直接复制到最终的可执行文件中,而是在程序运行时动态地加载库。这种机制有助于节省磁盘空间和内存,因为多个程序可以共享同一个动态库的副本。
- 当我们提到动态库的“后者”时,如果是指与动态库相对的概念,那么通常指的是静态库。静态库在编译时会被完整地复制到最终的可执行文件中,因此每个使用静态库的程序都会有一个库的副本。
有些时候我们编写的源代码并不需要将他们编译生成可执行程序,而是生成一些静态库或动态库提供给第三方使用,下面来讲解在cmake中生成这两类库文件的方法。
2.5.1 静态库
静态库的生成需使用如下指令:
add_library(库名称 STATIC 源文件1 [源文件2] ...)
- 库名称:创建的静态库的名字。在构建成功后,这个名字会出现在输出目录中。
- STATIC:这个关键字指示你想要创建一个静态库。
- 源文件1 [源文件2] ...:这些是构成静态库的源文件列表。你可以列出多个源文件,CMake 会将它们编译并打包进静态库中。
我们一般只需要指定出库的名字和STATIC关键字就可以,另外两部分在生成该文件的时候会自动填充。
假设我们有如下目录结构:
$ tree
.
├── build
├── CMakeLists.txt
├── include # include目录下存放头文件
│ └── head.h
├── lib # lib目录下存放生成的库
└── src # src目录下存放源文件├── add.cpp├── div.cpp├── main.cpp├── mult.cpp└── sub.cpp
如果我们要将其生成为静态库,该目录下的CMakeLists.txt文件这样写:
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project (LearnCMake LANGUAGES CXX)include_directories(${PROJECT_SOURCE_DIR}/include)file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_library(hello STATIC ${SRC_LIST})
这样最终就会生成对应的静态库文件 hello.a或者hello.lib
2.5.2 动态库
动态库的指令和静态库类似,只不过关键字STATIC需改为SHARED
add_library(库名称 SHARED 源文件1 [源文件2] ...)
编写对应的CMakeLists.txt文件,最后生成对应的动态库文件hello.so或者hello.dll
2.5.3 指定库输出路径
我们使用宏 LIBRARY_OUTPUT_PATH 来指定静态/动态库的输出路径。而对于动态库,因为动态库有可执行权限,所以不仅可以像可执行程序一样使用宏 EXECUTABLE_OUTPUT_PATH 指定输出路径,而且可以使用宏 LIBRARY_OUTPUT_PATH;但静态库默认不具有可执行权限,所以静态库只可以用宏 LIBRARY_OUTPUT_PATH 来指定输出路径。
假设我们有如下目录结构:
$ tree
.
├── build
├── CMakeLists.txt
├── include # include目录下存放头文件
│ └── head.h
├── lib # lib目录下存放生成的库
└── src # src目录下存放源文件├── add.cpp├── div.cpp├── main.cpp├── mult.cpp└── sub.cpp
如果我们要将其生成为库,并指定输出路径,该目录下的CMakeLists.txt文件这样写:
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project (LearnCMake LANGUAGES CXX)
# 包含头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
# 查找源文件
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成动态库
add_library(hello SHARED ${SRC_LIST})
# 生成静态库
add_library(hello STATIC ${SRC_LIST})
2.6 链接库文件
如果lib目录下已经存在库文件,我们应该如何用lib下的静态/动态库。
假设我们有如下目录结构:
$ tree
.
├── build
├── CMakeLists.txt
├── bin $ bin目录下存放项目生成的可执行文件
├── include # include目录下存放头文件
│ └── head.h
├── lib # lib目录下存放生成的库├── libhello.a└── libhello.so
└── src # src目录下存放源文件├── add.cpp├── div.cpp├── main.cpp├── mult.cpp└── sub.cpp
2.6.1 链接静态库
在cmake中,链接静态库的命令如下:
link_libraries(<static lib> [<static lib>...])
- 静态库名称可以是全名 libxxx.a,也可以是掐头(lib)去尾(.a)之后的名字 xxx
如果该静态库不是系统提供的(自己制作或者使用第三方提供的静态库)可能出现静态库找不到的情况,此时可以将静态库的路径也指定出来:
link_directories(<lib path>)
该目录下的CMakeLists.txt文件这样写:
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project (LearnCMake LANGUAGES CXX)
# 包含头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
# 查找源文件
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 查找库文件
# 第一个参数:变量,用于存储查找到的库文件 第二个参数:要查找的库文件 第三个参考:指定目录下查找
find_library(HELLO_LIB libhello.a ${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(${HELLO_LIB})
# 生成可执行程序
add_executable(hello ${SRC_LIST})
find_library函数用于查找指定目录下的库文件,并将其存储至指定变量中。
2.6.2 链接动态库
在cmake中链接动态库的命令如下:
target_link_libraries(<target> <PRIVATE|PUBLIC|INTERFACE> <item>... [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
- target:指定要加载的库的文件的名字
- 该文件可能是一个源文件
- 该文件可能是一个动态库/静态库文件
- 该文件可能是一个可执行文件
- PRIVATE/PUBLIC/INTERFACE:动态库的访问权限,默认为PUBLIC
- 如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可。
- 动态库的链接具有传递性,如果动态库 A 链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中定义的方法。
- PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。
- PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库
- INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。
动态库的链接和静态库是完全不同的
1. 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
2. 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存。因此,在cmake中指定要链接的动态库的时候,应该将 命令写到生成了可执行文件之后。
该目录下的CMakeLists.txt文件这样写:
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project (LearnCMake LANGUAGES CXX)
# 包含头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
# 查找源文件
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
# 包含动态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 查找库文件
# 第一个参数:变量,用于存储查找到的库文件 第二个参数:要查找的库文件 第三个参考:指定目录下查找
find_library(HELLO_LIB libhello.so ${PROJECT_SOURCE_DIR}/lib)
# 生成可执行程序
add_executable(hello ${SRC_LIST})
# 链接的动态库
target_link_libraries(hello pthread ${HELLO_LIB})
其中,pthread 和 libhello都是可执行程序hello需要链接的动态库名称。
2.6.3 target_link_libraries 和 link_libraries 的区别
- target_link_libraries 是更现代和推荐的用法,适合精细控制和管理项目的库依赖。
- link_libraries 适合简单的项目或早期的 CMake 版本,但在复杂的项目中会带来管理上的困难
现在一般推荐使用target_link_libraries
a. target_link_libraries
仅对指定的目标(如可执行文件或库)在编译时需要链接哪些库生效。
target_link_libraries(<target> <LIBRARIES>)
比如
target_link_libraries(MyExecutable PRIVATE mylib)
优点:
- 可以指定链接库的作用范围(PRIVATE、PUBLIC、INTERFACE),从而控制库的可见性和传递性。
- 可以单独为每个目标配置链接库,不会影响其他目标。
- 支持接口库(interface libraries)和导出库(exported libraries)。
b. link_libraries
对整个目录内的所有目标生效.
缺点:
- 由于是全局的,无法控制库的作用范围(PRIVATE、PUBLIC 等),所有目标都会使用这些库。
- 缺乏模块化,不适合复杂的项目结构或多目标项目。
2.7 message指令
message命令用于在配置和生成过程中向用户输出信息。基本语法如下:
message([SEND_ERROR | FATAL_ERROR | WARNING | AUTH_WARNING | STATUS | DEPRECATION_WARNING] "message-text")
- SEND_ERROR:将消息作为错误输出,并继续执行(但通常会停止在产生错误的命令)。
- FATAL_ERROR:将消息作为错误输出,并立即停止处理 CMakeLists.txt 文件。
- WARNING:将消息作为警告输出。
- AUTH_WARNING:输出一个授权警告(通常用于策略警告)。
- STATUS:输出普通状态信息(这是默认行为,如果不指定类型)。
- DEPRECATION_WARNING:输出一个弃用警告。
举例:
if(UNIX) message(STATUS "Building for UNIX-like system.")
elseif(WIN32) message(STATUS "Building for Windows.")
else() message(WARNING "Unknown platform. Proceeding with caution.")
endif()
2.8 移除操作
若我们通过file或者aux_source_directory对某一个目录进行搜索,但搜索结果中并不是全部的源文件都是我们需要的,这时候我们需要通过将我们不需要的源文件移除。可以使用list命令:
list(REMOVE_ITEM <list> <value1> [<value2> ...])
- <list> 是要操作的列表的名称。
- <value1>, <value2>, ... 是希望从列表中移除的元素
举例:
# 假设我有一个列表包含以下元素
set(my_list "a" "b" "c" "d" "e")
# 删除b和d
list(REMOVE_ITEM my_list "b" "d")
假设我们有如下目录结构:
$ tree
.
├── build
├── CMakeLists.txt
├── include # include目录下存放头文件
│ └── head.h
├── lib # lib目录下存放生成的库
└── src # src目录下存放源文件├── add.cpp├── div.cpp├── main.cpp├── mult.cpp└── sub.cpp
若源文件不包含sub.cpp,该目录的CMakeLists.txt文件这样写:
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project (LearnCMake LANGUAGES CXX)
# 包含头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
# 查找源文件
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
# 移除前日志
message(STATUS "message: ${SRC_LIST }")
# 移除 main.cpp
list(REMOVE_ITEM SRC_LIST ${PROJECT_SOURCE_DIR}/sub.cpp)
# 移除后日志
message(STATUS "message: ${SRC_LIST }")
# 生成可执行文件
add_executable(hello ${SRC_LIST})
2.9 find_library和find_package
find_library用于查找指定目录下的指定文件,并将其存储至变量中。
# 第一个参数:变量,用于存储查找到的库文件 第二个参数:要查找的库文件 第三个参考:指定目录下查找
find_library(HELLO_LIB libhello.so ${PROJECT_SOURCE_DIR}/lib)
find_package根据系统环境找到指定的软件包或库,并将其相关信息(如库的路径、头文件的路径等)提供给项目使用。
find_package(<PackageName> [version] [REQUIRED] [COMPONENTS components...])
- <PackageName>: 需要查找的软件包的名称,例如 Boost、OpenCV 等。
- [version]: 可选,指定软件包的版本号。如果需要特定版本,可以指定版本号,例如 1.70.0。
- [REQUIRED]: 可选,表示该包是必需的。如果 CMake 找不到该包,它会停止并报错。
- [COMPONENTS components...]: 可选,指定要查找的组件或模块。例如,某些库可能有多个子模块(如 Boost 中的 system、filesystem 模块),你可以通过此选项指定只查找需要的部分。
举例:
find_package(Boost 1.70 REQUIRED COMPONENTS system filesystem)
查找 Boost 库的版本 1.70,并确保 system 和 filesystem 两个组件被找到。如果找不到这些组件,CMake 将报错并停止构建。
3. 常用的宏
宏 | 功能 |
---|---|
PROJECT_NAME | 用函数project(demo)指定的项目名称 |
PROJECT_SOURCE_DIR | 一般为工程的根目录,指向包含最近一次调用project()命令定义的项目的源目录 |
PROJECT_BINARY_DIR | 执行cmake命令的目录,一般为build |
CMAKE_CURRENT_SOURCE_DIR | 当前处理的CMakeLists.txt文件所在目录 |
CMAKE_CURRENT_BINARY_DIR | 当前处理的二进制输出目录,也就是 CMake 将生成构建文件(例如 Makefile 或其他生成器文件)的目录 |
CMAKE_CURRENT_LIST_DIR | CMakeLists.txt的完整路径 |
CMAKE_CURRENT_LIST_LINE | 当前所在行 |
EXECUTABLE_OUTPUT_PATH | 重新定义目标二进制可执行文件的存放位置 |
LIBRARY_OUTPUT_PATH | 重新定义目标链接库文件的存放位置 |
CMAKE_CXX_STANDARD | 指定C++标准 |
CMAKE_CXX_STANDARD_REQUIRED | 如果编译不符合指定C++标准,编译 |
4. 示例
这是我们之前在linux上编译grpc通信代码用到的CMakeLists.txt文件,现在可以分析每条指令的作用是什么:
# 指定cmake版本最低为3.1
cmake_minimum_required(VERSION 3.1)
# 项目名称为GrpcServer,编程语言C++
project(GrpcServer LANGUAGES CXX)
# 将 CMAKE_CURRENT_SOURCE_DIR 和 CMAKE_CURRENT_BINARY_DIR 自动添加到包含路径中,这些目录下的头文件被自动包含
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# C++版本17
set(CMAKE_CXX_STANDARD 17)
# 如果不满足C++17,强制编译失败
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找Threads 库,如果找不到该包,编译停止并报错
find_package(Threads REQUIRED)
# 使用模块查找的规则,优先使用 CMake 自带的 FindProtobuf.cmake 文件,而不是使用配置模式(即通过 protobuf-config.cmake 文件进行查找)
set(protobuf_MODULE_COMPATIBLE TRUE)
# 查找protobuf库,并要求找到的是基于CONFIG模式的配置文件(protobuf-config.cmake),且找不到会报错
find_package(Protobuf CONFIG REQUIRED)
# 输出protobuf版本号
message(STATUS "Using protobuf ${Protobuf_VERSION}")
# 将protobuf的核心库libprotobuf赋值给变量_PROTOBUF_LIBPROTOBUF
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
# 将grpc的反射库赋值给_REFLECTION
set(_REFLECTION gRPC::grpc++_reflection)
# Find gRPC installation
# Looks for gRPCConfig.cmake file installed by gRPC's cmake installation.
find_package(gRPC CONFIG REQUIRED)
message(STATUS "Using gRPC ${gRPC_VERSION}")set(_GRPC_GRPCPP gRPC::grpc++)
# 添加可执行文件和源文件
file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB PBSOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)
# 生成可执行程序GrpcServer
add_executable(GrpcServer ${SOURCES}${PBSOURCES})
# 链接库动态库文件
target_link_libraries(GrpcServer${_REFLECTION}${_GRPC_GRPCPP}${_PROTOBUF_LIBPROTOBUF})
相关文章:

CMakeLists.txt 编写规则
目录 1. 注释 1.1 注释行 1.2 注释块 2. CMakeLists.txt的编写 2.1 同意目录下的源文件 2.2 SET指令 2.3 file和aux_source_directory 2.4 包含头文件 2.5 生成动态库和静态库 2.6 链接库文件 2.7 message指令 2.8 移除操作 2.9 find_library和find_package 3. 常…...

Javascript算法——二分查找
1.数组 1.1二分查找 1.搜索索引 开闭matters!!![left,right]与[left,right) /*** param {number[]} nums* param {number} target* return {number}*/ var search function(nums, target) {let left0;let rightnums.length-1;//[left,rig…...
node-sass/vendor/linux-x64-72 : Error: EACCES: permission denied, mkdir
npm i --unsafe-perm node-sassgithub解决问题...

uniapp-uniapp + vue3 + pinia 搭建uniapp模板
使用技术 ⚡️uni-app, Vue3, Vite, pnpm 📦 组件自动化引入 🍍 使用 Pinia 的状态管理 🎨 tailwindcss - 高性能且极具灵活性的即时原子化 CSS 引擎 😃 各种图标集为你所用 🔥 使用 新的 <script setup> …...

深度学习的一些数学基础
数学基础 万丈高楼平地起 怎么说呢,学的数二对于这些东西还是太陌生了,而且当时学的只会做题,不知道怎么使用/(ㄒoㄒ)/~~ 所以记下来一些不太清楚的前置知识点,主要来自《艾伯特深度学习》,书中内容很多,…...

自由学习记录(13)
服务端常见的“资源” 在服务端,常见的“资源”指的是服务端提供给客户端访问、使用、处理或操作的各种数据和功能。根据不同类型的服务和应用场景,服务端的资源种类可以非常广泛。以下是一些常见的服务端资源类型: 1. 文件和静态资源 网页…...

低代码可视化-uniapp海报可视化设计-代码生成
在uni-app中,海报生成器通常是通过集成特定的插件或组件来实现的,这些插件或组件提供了生成海报所需的功能和灵活性。我们采用了lime-painter海报组件。lime-painter是一款canvas海报组件,可以更轻松地生成海报。它支持通过JSON及Template的方…...

一次使用LD_DEBUG定位问题的经历
在实际工作中,当遇到段错误,我们会很容易的想到这是非法访问内存导致的,比如访问了已经释放的内存,访问数据越界,尝试写没有写权限的内存等。使用gdb进行调试,查看出异常的调用栈,往往可以定位到…...
数据库安全:如何进行数据库安全审计?
数据库安全:如何进行数据库安全审计? 数据库安全审计是保障数据库安全的重要手段之一,可以帮助企业及时发现潜在的安全风险并采取相应的措施。以下是进行数据库安全审计的步骤和方法: 一、确定审计目标 在进行数据库安全审计之前,首先需要确定审计的目标。这可能包括以…...
【Python】基础语法错误和异常
在Python中,语法错误和异常是两个常见的问题。下面对它们进行简要介绍。 1.语法错误 (Syntax Error) 语法错误是指代码的语法不符合Python的语言规则。当Python解释器读取程序代码时,如果发现语法不正确,就会抛出语法错误。这种错误通常在代…...
获取每个页面的元素,并写入json
获取每个页面的元素,并写入json 想法:如何去记住每个页面的元素,如何实现不同页面的导航,如何从主页面遍历每一个页面的每一个元素 1.创建数据结构存储 2.树状图正好是我们想要的结构体:创建树状图结构体 3.记录每个页…...
【ShuQiHere】深入解析数字电路中的锁存器与触发器
深入解析数字电路中的锁存器与触发器 🤖🔌 在数字电路设计中,**锁存器(Latch)和触发器(Flip-Flop)**是实现时序逻辑的基本元件。它们能够存储状态,是构建复杂数字系统的关键。本文将…...

【学习AI-相关路程-mnist手写数字分类-python-硬件:jetson orin NX-自我学习AI-基础知识铺垫-遇到问题(1) 】
【学习AI-相关路程-mnist手写数字分类-python-硬件:jetson orin NX-自我学习AI-基础知识铺垫-遇到问题(1) 】 1、前言2、先行了解(1)学习基础知识-了解jetson orin nx 设备(2)学习python&AI…...

数据轻松上云——Mbox边缘计算网关
随着工业4.0时代的到来,工厂数字化转型已成为提升生产效率、优化资源配置、增强企业竞争力的关键。我们凭借其先进的边缘计算网关与云平台技术,为工厂提供了高效、稳定的数据采集与上云解决方案。本文将为您介绍Mbox边缘计算网关如何配合明达云平台&…...
ifftshift函数
ifftshift 原理 将频域数据移回时域的函数。它通常与 fftshift 配合使用,后者用于将时域数据移动到频域中心。 而ifftshift所作的事正好相反,将频谱恢复到能量集中在两端(或四个角)上,接着就可以做逆傅里叶变换了 具…...

vue3 + ts + element-plus 二次封装 el-dialog
实现效果: 组件代码:注意 style 不能为 scoped <template><el-dialog class"my-dialog" v-model"isVisible" :show-close"false" :close-on-click-modal"false" :modal"false"modal-class&…...

MySQL9.0安装教程zip手动安装(Windows)
本章教程,主要介绍如何在Windows上安装MySQL9.0,通过zip方式进行手动安装。 一、下载MySQL压缩包 下载地址:https://downloads.mysql.com/archives/community/ 二、解压MySQL压缩包 将下载好的压缩包,进行解压缩,并且将…...

如何在浏览器中查看格式化的 HTML?
问题描述 我需要这个 HTML 页面在我的浏览器中显示格式化后的信息。我只是将文件存储在本地驱动器上。我需要将文件上传到服务器才能将其作为 HTML 查看,还是可以在本地查看?如在屏幕截图中看到的,它当前显示相同的 HTML 代码。我尝试搜索&am…...

浅谈计算机存储体系和CPU缓存命中
一、计算机存储 一般关于计算机存储体系分为三层 ①三级缓存/寄存器 大多数寄存器只有四字节到八字节,只用于读取很小的数据;三级缓存是为了方便CPU读取内存中数据而存在的 ②内存————数据结构就是在内存中管理数据 ③硬盘————数据库/文件就…...
ES操作:linux命令
查询数据库所有索引 没有密码 curl -X GET "http://localhost:9200/_cat/indices?v" 有密码 curl -u elastic:my_password -X GET "http://localhost:9200/_cat/indices?v" 删除索引 curl-X DELETE "http://localhost:9200/index_XXX" 不…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...