赞
踩
系列文章目录
CMake是一个用于构建、测试和打包软件的跨平台构建工具。 它通过生成平台特定的构建文件(如Makefile、Visual Studio项目文件等),来协助开发者管理项目的构建过程。
CMake的作用和重要性:
1. 跨平台性: CMake可以在不同的操作系统和编译器之间进行无缝切换。它支持多种平台,如Windows、Mac、Linux等,而且可以使用不同的构建系统,比如Make、Ninja、Visual Studio等。
2. 自动化构建流程: CMake可以自动化处理构建过程的繁琐任务。通过定义CMakeLists.txt文件,开发者可以指定源代码文件的位置、依赖库的链接方式、编译选项等,从而简化了项目的构建和管理。
3. 灵活性和可定制性: CMake提供了丰富的命令和选项,使得开发者可以根据项目的需求进行灵活的配置和定制。开发者可以定义自己的构建选项、生成不同类型的目标文件(如可执行文件、库文件、模块等),以及设置自己的编译规则等。
4. 多工程管理: CMake可以用于管理复杂的多工程项目。通过使用CMake的模块化机制,开发者可以将项目分为多个模块,并定义它们之间的依赖关系。这样可以提高代码的可维护性和复用性。
5. 良好的社区支持: CMake是一个开源项目,有着庞大的用户社区。这意味着开发者可以轻松地找到相关的文档、教程、示例代码和解决方案。此外,CMake还有许多第三方的扩展库和工具,可以进一步增强其功能和易用性。
学习CMake对于以下场景是必要的:
1. 跨平台开发: 在开发跨平台应用或库时,不同操作系统和编译器的构建方式存在差异。学习CMake可以帮助开发者编写通用的构建脚本,使代码可以在不同平台上进行编译和运行。
2. 多工程项目: 在开发大型项目时,通常会划分为多个模块或库。使用CMake可以管理这些模块之间的依赖关系,并确保它们以正确的顺序进行构建和链接。
3. 第三方库的使用: 许多软件项目需要使用第三方库。学习CMake可以帮助开发者正确地配置和链接这些库,以便项目能够使用它们的功能。
4. 编译选项的管理: 不同的编译器和操作系统有不同的编译选项和标志。学习CMake可以帮助开发者定义和管理这些选项,以确保代码在不同环境下能够正确地编译和运行。
5. 自动化构建流程: 手动进行构建和编译往往是繁琐的。学习CMake可以帮助开发者定义构建流程,并自动处理构建相关的任务,如编译、链接、测试和打包等。
6. 项目的可维护性: 使用CMake可以将项目的构建过程与代码分离,使构建逻辑更加清晰和可维护。这样,开发者可以更方便地修改、扩展和维护项目。
CMake是一个开源、跨平台的编译、测试和打包工具,它使用比较简单的语言描述编译、安装的过程,输出Makefile或者project文件,再去执行构建。大多数IDE都集成CMake,相比于makefile,CMake不需要依赖当前编译的平台,并且工作量相对较少,依赖关系更少出错。
CMake过程如下所示,其先生成Makefile文件再生成目标文件:
工作原理:
CMake通过读取项目中的CMakeLists.txt文件来配置和生成构建脚本。这个脚本可以根据不同的平台、编译器和构建选项来生成所需的构建系统文件,如Makefile、Visual Studio解决方案、Xcode项目等。然后,可以使用生成的构建系统文件来编译、链接和打包项目。
基本组成部分如下:
CMakeLists.txt文件:
CMakeLists.txt文件是CMake的配置文件,用于描述项目的构建过程。它可以包含一系列的指令和命令,用于设置项目的编译参数、依赖关系、源文件列表等信息。
CMake提供了一系列的命令和变量,用于在CMakeLists.txt文件中配置项目的构建过程。这些命令可以用于设置项目的名称、版本号、编译选项、链接库、查找依赖等。
模块和宏:
CMake还提供了一些预定义的模块和宏,用于简化项目的配置过程。这些模块和宏可以用于查找特定的库、设置环境变量、定义函数等。
构建系统文件:
CMake根据CMakeLists.txt文件生成的构建系统文件用于实际的编译、链接和打包操作。这些文件可根据不同的平台和编译器生成,如Makefile、Visual Studio解决方案、Xcode项目等。
CMakeLists.txt文件是CMake的配置文件,用于描述项目的构建过程。它具有以下的结构和语法:
最小结构:
一个最小的CMakeLists.txt文件包含以下两个部分:
cmake_minimum_required(VERSION <version>)
project(<project_name>)
cmake_minimum_required命令:
这个命令用于指定需要的CMake的最低版本,语法为:
cmake_minimum_required(VERSION <version>)
project命令:
这个命令用于定义项目的名称,语法为:
project(<project_name>)
设置变量:
可以使用set命令来设置变量,语法为:
set(<variable_name> <value>)
添加子目录:
如果项目包含子目录,可以使用add_subdirectory命令来添加子目录,语法为:
add_subdirectory(<subdirectory>)
设置编译类型和选项:
可以使用set命令设置编译类型和选项,如:
set(CMAKE_BUILD_TYPE <build_type>)
set(CMAKE_CXX_FLAGS <flags>)
添加源文件:
可以使用add_executable或add_library命令来添加源文件,语法为:
add_executable(<executable_name> <source_files>)
add_library(<library_name> <source_files>)
链接库:
可以使用target_link_libraries命令来链接库,语法为:
target_link_libraries(<target_name> <library_name>)
定义函数和变量:
可以使用function和set命令来定义函数和变量,语法为:
function(<function_name> [ARG1 [ARG2 ...]])
...c
endfunction()
set(<variable_name> <value>)
条件语句:
可以使用if和else命令来进行条件判断,语法为:
if(<condition>)
...
elseif(<condition>)
...
else()
...
endif()
循环语句:
可以使用foreach和while命令来进行循环操作,语法为:
foreach(<variable> [IN [LISTS] [ITEMS] ...])
...
endforeach()
while(<condition>)
...
endwhile()
指定c++使用标准:
设置全局变量CMAKE_CXX_STANDARD:
#增加-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时使用-DCMAKE_CXX_STANDARD=xx选项
#增加-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
搜索文件:
使用aux_source_directory 命令可以查找某个路径下的所有源文(.c 或 .cpp):
#aux_source_directory(搜索路径 存储变量名)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
#CMAKE_CURRENT_SOURCE_DIR 全局变量表示CMakeLists.txt所在路径
使用file搜索指定文件
#file(GLOB/GLOB_RECURSE 存储变量名 搜索路径及文件类型)
#GLOB: 仅在指定目录下搜索
#GLOB_RECURSE:递归搜索
file(GLOB SRC ${PROJECT_SOURCE_DIR}/src/*.cpp)
# PROJECT_SOURCE_DIR 是 cmake命令后跟的路径
制作库文件:
add_library(库名称 STATIC/SHARED 源文件 [源文件2] ...)
# STATIC:静态库 SHARED:动态库
指定库输出路径
set(LIBRARY_OUTPUT_PATH 输出路径)
链接库文件:
link_directories(库路径)
# 注意是路径而不是库名
link_libraries(库名1 库名2)
# 系统静态库不需要指定路径 自定义静态库需要
target_link_libraries(目标文件 访问权限 库名1 访问权限 库名2)
# 系统静态库不需要指定路径 自定义静态库需要 同上
# 访问权限
# PUBLIC:库具有传递性,由目标文件链接得到的目标文件仍可以使用库文件
# PRIVATE:不具有传递性,目标文件可以使用库,但其传递的目标文件不能使用该库文件
# INTERFACE:非传递性,并且目标文件仅有库文件函数名(即只知道接口但无法得到具体内容)
# target_link_libraries要在目标文件生成命令之后使用
include_directories(头文件路径)
# 1.set
set(目标变量名 ${变量名1} ${变量名2} ...)
# 2.list添加到末尾
list(APPEND 待操作变量 ${变量名}/"字符串" ...)
# 3.移除某个元素
list(REMOVE_ITEM 待操作变量 ${变量名}/"字符串" ...)
# 4.获取子串个数
list(LENGTH 待操作变量 输出变量)
# 5.获取指定索引的元素
list(GET 待操作变量 索引1 索引2 ... 输出变量)
#索引从0开始编号,也可以是负数,-1表示列表的最后一个元素
#索引超过列表的长度会报错
# 6.用指定连接符将列表中的元素连接起来组成一个字符串
list (JOIN 待操作变量 连接符 输出变量)
<list>:当前操作的列表
<glue>:指定的连接符(字符串)
<output variable>:新创建的变量,存储返回的字符串
# 7.查找列表是否存在指定的元素,若果未找到,返回-1
list(FIND 待操作变量 搜索元素 输出变量)
# 8.在指定的位置插入若干元素
list(INSERT 待操作变量 索引 元素1 元素2 ...)
# 9.将元素插入到列表的0索引位置
list (PREPEND 待操作变量 元素 ...)
# 10.将列表中最后元素移除
list (POP_BACK 待操作变量 输出变量 ...)
# 11.将列表中第一个元素移除
list (POP_FRONT 待操作变量 输出变量 ...)
# 12.将指定索引的元素从列表中移除
list (REMOVE_AT 待操作变量 索引1 索引2 ...)
# 13.移除列表中的重复元素
list (REMOVE_DUPLICATES 待操作变量)
# 14.列表翻转
list(REVERSE 待操作变量)
# 15.列表排序
list (SORT 待操作变量 [COMPARE <compare>] [CASE <case>] [ORDER <order>])
#[[COMPARE:指定排序方法。有如下几种值可选:
STRING:按照字母顺序进行排序,默认
FILE_BASENAME:如果是一系列路径名,会使用basename进行排序
NATURAL:使用自然数顺序排序
CASE:指明是否大小写敏感。
SENSITIVE:默认
INSENSITIVE:按照大小写不敏感方式进行排序
ORDER:指定升降序。是升序
ASCENDING:默认
DESCENDING:降序
]]
使用#进行单行注释,#[[ ]]进行多行注释
#这是一个CMake单行注释
#[[
这是一个
多行注释
]]
message(消息等级 "消息内容" ...)
#[[
(无) :重要消息
STATUS :非重要消息
WARNING:警告, 会继续执行
AUTHOR_WARNING:警告 (dev), 会继续执行
SEND_ERROR:错误, 会继续执行,跳过生成的步骤
FATAL_ERROR:错误, 终止
]]
########################################################
string(ASCII 27 Esc)
set(R "${Esc}[31;1m")
set(E "${Esc}[0m")
message("${R}红色内容${E} 默认颜色")
在CMake中,可以使用add_definitions
命令来定义宏。该命令将在所有目标中添加编译选项,以定义宏。
下面是一个示例,展示如何在CMake中定义宏:
# 定义一个宏
add_definitions(-DDEBUG)
# 定义一个带有数值的宏
add_definitions(-DVERSION=1)
# 定义一个带有字符串值的宏
add_definitions(-DMESSAGE="Hello, World!")
在上面的示例中,add_definitions
命令用于定义不同的宏。通过在命令中使用-D
选项,可以指定要定义的宏的名称、数值或字符串值。这些宏在编译时将被替换为相应的值。
为了在代码中使用这些宏,可以在源文件中使用#ifdef
语句来判断宏是否被定义,以及使用宏的值:
#ifdef DEBUG
// 宏已定义,执行相应的代码
#endif
#ifdef VERSION
// 使用宏的值
int version = VERSION;
#endif
#ifdef MESSAGE
// 使用宏的值
std::cout << MESSAGE << std::endl;
#endif
在上面的示例中,#ifdef
语句用于检查宏是否被定义。如果宏已定义,则执行相应的代码块。
CMakeLists.txt文件是CMake工具的配置文件,它的主要作用是描述项目的构建过程和依赖关系,并生成适合不同平台和编译器的构建系统。
具体来说,CMakeLists.txt文件的作用包括:
1. 定义项目的名称和版本号: 通过project命令,可以指定项目的名称和版本号,方便项目的标识和管理。
2. 设置编译选项和参数: 通过set命令,可以设置编译器的选项和参数,例如编译类型、编译器标志等。
3. 添加源代码目录和文件: 通过add_subdirectory和add_executable命令,可以将子目录和源代码文件添加到项目中,告诉CMake如何构建项目。
4. 链接库和依赖项: 通过target_link_libraries命令,可以指定项目所需的链接库和依赖项,确保项目能够正确编译和运行。
5. 定义宏和函数: 通过define_property和function命令,可以定义宏和函数,方便在CMakeLists.txt文件中重复使用代码逻辑。
6. 条件判断和循环控制: 通过if、elseif、else和foreach命令,可以在CMakeLists.txt文件中进行条件判断和循环控制,使构建过程更加灵活和可定制。
常用命令:
命令 | 描述 |
---|---|
cmake_minimum_required(VERSION) | 指定CMake的最低版本要求。 |
project(name) | 指定项目的名称。 |
add_executable(name source_files) | 添加一个可执行文件,并指定源文件。 |
add_library(name source_files) | 添加一个库文件,并指定源文件。 |
target_link_libraries(target libraries) | 指定目标文件需要链接的库文件。 |
include_directories(directory) | 指定头文件的搜索路径。 |
link_directories(directory) | 指定库文件的搜索路径。 |
set(variable value) | 设置变量的值。 |
find_package(package) | 查找指定的第三方库。 |
aux_source_directory(directory variable) | 查找某个路径下的所有源文(.c 或 .cpp) |
include(sources.cmake) | 为了保持CMakeLists.txt文件的清晰和可维护性,我们可以将源文件列表放在一个单独的文件中,然后在CMakeLists.txt文件中包含它。 |
常用变量:
变量 | 描述 |
---|---|
CMAKE_SOURCE_DIR | 项目根目录的路径。 |
CMAKE_BINARY_DIR | 工程编译时存放二进制文件的目录。 |
CMAKE_CXX_COMPILER | C++编译器的路径。 |
CMAKE_C_COMPILER | C语言编译器的路径。 |
CMAKE_CURRENT_SOURCE_DIR | 当前源文件所在的目录。 |
CMAKE_CURRENT_BINARY_DIR | 当前编译文件所在的目录。 |
CMAKE_INCLUDE_PATH | 头文件搜索路径。 |
CMAKE_LIBRARY_PATH | 库文件搜索路径。 |
CMAKE_INSTALL_PREFIX | 安装目录的路径。 |
CMAKE_CURRENT_SOURCE_DIR | 全局变量表示CMakeLists.txt所在路径。 |
EXECUTABLE_OUTPUT_PATH | 生成的可执行文件的输出路径 |
LIBRARY_OUTPUT_PATH | 指定生成库输出路径 |
CMAKE_CXX_STANDARD | 指定c++使用标准 |
CMAKE_CXX_FLAGS | 用于设置C++编译器的编译选项。 |
CMAKE_INCLUDE_DIRECTORIES_BEFORE | 将添加的头文件搜索路径放在已有路径的前面。 |
CMAKE_INCLUDE_DIRECTORIES_ AFTER | 将添加的头文件搜索路径放在已有路径的后面。 |
CMAKE_MODULE_PATH | 定义自己的 cmake模块所在的路径 |
BUILD_SHARED_LIBS | 控制库的默认编译方式 |
DCMAKE_BUILD_TYPE | 置构建类型,类型选项有Debug/Release。当使用GDB调试工程时要使用Debug选项。该变量可以通过命令行:cmake DCMAKE_BUILD_TYPE=Release或指令SET(CMAKE_BUILD_TYPE [type])设置。 |
在CMake中,string 指令提供了一系列的操作来处理字符串。这些操作包括但不限于比较、替换、连接、长度计算等等。
string(ASCII <number> [<number> ...] <output_variable>)
string(HEX <string> <output_variable>)
message([<mode>] "message text" ...)函数的<mode>参数可以是以下之一:
参数 | 使用场景 | 底层原理 | 优点 | 缺点 |
---|---|---|---|---|
(none) | 当你想输出一条普通的状态消息,但不希望给它指定任何特殊的模式时 | 输出的信息会被发送到CMake的状态消息流。 | 简单易用,不需要指定模式。 | 不推荐使用,因为它的行为可能会在未来的CMake版本中改变。 |
STATUS | 当你想输出一条状态消息,例如进度信息或配置信息时。 | 输出的信息会被发送到CMake的状态消息流。 | 明确表示这是一条状态消息,易于理解。 | 在图形界面中,这些消息可能会被重定向到其他地方,不一定能被用户看到。 |
WARNING | 当你想输出一条警告消息,例如某个选项已被弃用或某个操作可能会失败时。 | 输出的信息会被发送到CMake的警告消息流。 | 明确表示这是一条警告消息,可以引起用户的注意。 | 过多的警告消息可能会让用户感到困扰,忽视真正重要的警告。 |
AUTHOR_WARNING | 当你是项目的开发者,并且你想输出一条只有在开发模式下才会显示的警告消息时。 | 输出的信息会被发送到CMake的 警告消息流,但只有CMAKE_SUPPRESS_ DEVELOPER_WARNINGS变量为FALSE时才会产生警告。 | 可以避免在用户模式下显示不必要的警告。 | 如果CMAKE_ SUPPRESS_ DEVELOPER_ WARNINGS变量 被设置为TRUE, 这些警告会被忽略。 |
SEND_ERROR | 当你遇到一个错误,但你希望CMake继续处理剩下的命令时。 | 输出的信息会被发送到CMake的错误消息流,但不会立即停止CMake的处理过程。 | 可以在发生错误时继续执行CMake的处理过程。 | 由于CMake的处理过程没有立即停止,可能会导致更多的错误。 |
FATAL_ERROR | 当你遇到一个严重的错误,你希望立即停止CMake的处理过程时。 | 输出的信息会被发送到CMake的错误消息流,并立即停止CMake的处理过程。 | 可以在发生严重错误时立即停止CMake的处理过程,防止错误的扩散。 | 一旦使用,CMake的处理过程会立即停止,无法执行任何后续的命令。 |
cmake_minimum_required(VERSION 3.10)
project(MyProject)
add_subdirectory(utils)
add_executable(MyApp main.cpp helper.cpp)
target_link_libraries(MyApp MyLibrary)
include_directories(include)
add_library(MyLibrary utils.cpp helper.cpp)
这个示例告诉CMake最低需要版本3.10,并且定义了一个名为MyProject的项目。然后,它添加了一个名为utils的子目录,然后在子目录中可能有另一个独立的CMakeLists.txt来构建utils子项目。接下来,它创建一个可执行文件MyApp,编译main.cpp和helper.cpp这两个源文件,并将库MyLibrary链接到它上面。最后,它将include目录添加到包含路径,并创建一个名为MyLibrary的库,编译utils.cpp和helper.cpp这两个源文件。
在CMake中,有两种类型的变量:全局变量和局部变量。全局变量可以在整个项目中使用,而局部变量仅在特定的范围内有效,例如在一个函数内部。
设置变量的值:
set
命令可以设置变量的值。语法为set(<variable> <value>)
。例如,set(SRC_FILES main.cpp helper.cpp)
将变量SRC_FILES
设置为main.cpp
和helper.cpp
。list
命令可以将多个值放入一个变量中。语法为list(APPEND <list_variable> <value1> <value2> ...)
。例如,list(APPEND SRC_FILES main.cpp helper.cpp)
将main.cpp
和helper.cpp
添加到SRC_FILES
变量中。访问变量的值:
${<variable>}
语法可以访问变量的值。例如,${SRC_FILES}
表示变量SRC_FILES
的值。set(<variable>)
命令可以通过将变量传递给set
命令来获取变量的值。例如,set(SRC_FILES)
中的SRC_FILES
将获得SRC_FILES
变量的值。函数的使用:
add_executable
和add_library
就是内置函数。function
命令创建自定义函数。函数可以接受参数并执行一些操作。语法为function(<function_name> [arg1 [arg2 ...]])
。例如,function(print_message MESSAGE)
定义了一个名为print_message
的函数,它接受一个参数MESSAGE
。${<variable>}
语法可以访问函数参数的值。以一个示例说明变量和函数的使用方法:
set(MY_NAME "Alice")
set(SRC_FILES main.cpp helper.cpp)
function(print_message MESSAGE)
message("Hello, ${MESSAGE}!")
endfunction()
message("My name is ${MY_NAME}")
message("Source files: ${SRC_FILES}")
print_message("Bob")
在这个例子中,首先设置了两个变量MY_NAME
和SRC_FILES
。然后定义了一个名为print_message
的函数,它接受一个参数MESSAGE
并打印出一个消息。最后,使用message
命令打印出变量MY_NAME
和SRC_FILES
的值,并调用print_message
函数打印出一个消息。
运行以上示例的CMakeLists.txt,将会输出以下内容:
My name is Alice
Source files: main.cpp;helper.cpp
Hello, Bob!
CMake的构建流程通常如下:
- 创建一个构建目录,该目录用于存储生成的构建系统文件和构建产物,与源代码目录分开。
- 在构建目录中运行
cmake
命令,指定源代码目录的路径。例如:cmake /path/to/source
。
- CMake会解析源代码目录中的CMakeLists.txt文件,并根据其中的指令和变量进行配置。
- CMake生成目标构建系统文件,如Makefiles、IDE项目文件等,根据所选的生成器确定。
- 使用生成的构建系统文件进行构建,如使用Make工具运行
make
命令或使用IDE进行构建。
选项 | 描述 |
---|---|
-H | 用于指定CMakeLists.txt文件所在的目录。 |
-D | 用于定义一个CMake变量。它允许您在命令行中传递变量值给CMake,而无需修改CMakeLists.txt文件。 |
-G | 用于指定生成器的名称。生成器用于生成适合特定平台或开发环境的构建系统文件(如Makefiles、IDE项目文件等)。例如,cmake -G "Ninja" .. 将使用Ninja生成器来生成构建系统文件。 |
-C | 用于指定一个预定义的CMake缓存文件。缓存文件中可以包含预先定义的变量和选项。例如,cmake -C mycache.cmake .. 将使用mycache.cmake 中定义的变量进行配置。 |
-B | 用于指定生成构建系统文件的目录。该目录通常是一个单独的构建目录,与源代码目录分开。例如,cmake -B build .. 将在build 目录中生成构建系统文件。 |
-S | 用于指定源代码目录。这是源代码存储的目录,其中包含CMakeLists.txt文件。例如,cmake -S myproject .. 将在myproject 目录中搜索CMakeLists.txt文件。 |
-D
是CMake的一个选项,用于定义一个CMake变量。它允许您在命令行中传递变量值给CMake,而无需修改CMakeLists.txt文件。
使用-D
选项的一般语法是:-D<variable>=<value>
。其中,<variable>
是你要定义的变量名,<value>
是你要为该变量设置的值。
例如,假设有一个名为DEBUG_MODE
的变量,您可以使用-D
选项来定义它的值,如下所示:
cmake -DDEBUG_MODE=ON ..
在CMakeLists.txt文件中,您可以使用${DEBUG_MODE}
来引用该变量。例如,您可以在CMakeLists.txt文件中使用if语句来根据DEBUG_MODE
变量的值执行不同的操作:
if(DEBUG_MODE)
message("Debug mode is enabled.")
else()
message("Debug mode is disabled.")
endif()
通过使用-D
选项,您可以通过命令行轻松地传递变量值给CMake,以便进行自定义配置,而无需手动编辑CMakeLists.txt文件。
使用CMake进行构建和安装项目的一般流程如下:
创建一个构建目录,用于存储生成的构建系统文件和构建产物。最好将构建目录与源代码目录分开。
进入构建目录,并运行cmake
命令来配置项目。可以使用以下命令:
cmake /path/to/source
或者,如果想指定生成器和其他选项:
cmake -G "GeneratorName" -DCMAKE_INSTALL_PREFIX=/path/to/install /path/to/source
上述命令中:
/path/to/source
是源代码目录的路径。-G "GeneratorName"
是指定生成器的选项,例如使用Make生成器可以使用-G "Unix Makefiles"
,使用Ninja生成器可以使用-G "Ninja"
。-DCMAKE_INSTALL_PREFIX=/path/to/install
是指定安装目录的选项,即项目构建完成后要安装到的目标路径。运行make
或cmake --build
命令来进行构建。例如:
make
如果使用的是Ninja生成器,可以运行:
cmake --build .
或者,如果希望使用多线程编译,可以使用-j
选项。例如:
make -j4
构建命令将根据生成的构建系统文件编译项目,并生成构建产物。
运行make install
命令来安装项目到指定的安装目录。例如:
make install
安装命令将把构建产物复制到指定的安装目录,以便在其他地方使用该项目。
完成上述步骤后,项目的构建和安装就完成了。可以根据具体的项目需要进行自定义配置和调整。
在CMake中指定源文件和头文件的位置有多种方法。以下是几种常用的方法:
add_executable
或add_library
命令:add_executable(my_executable main.cpp other.cpp)
这样做将会将main.cpp
和other.cpp
作为源文件添加到可执行文件my_executable
中。
file
命令:file(GLOB SOURCES src/*.cpp)
file(GLOB HEADERS include/*.h)
add_executable(my_executable ${SOURCES} ${HEADERS})
这样做将会使用GLOB
模式匹配指定文件夹下的所有.cpp
文件和.h
文件,并将它们添加到可执行文件my_executable
中。
target_sources
命令:target_sources(my_executable PRIVATE main.cpp other.cpp)
这样做将会将main.cpp
和other.cpp
作为源文件添加到可执行文件my_executable
中。
对于头文件的指定,通常会使用include_directories
命令或target_include_directories
命令,以指定头文件搜索路径。例如:
include_directories(include)
或者
target_include_directories(my_executable PRIVATE include)
在CMake中设置编译选项和链接库有多种方法。以下是几种常用的方法:
set
命令设置编译选项:set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
这样做将会将编译选项-Wall
和-Wextra
添加到默认的C++编译选项中。
target_compile_options
命令设置目标的编译选项:target_compile_options(my_executable PRIVATE -Wall -Wextra)
这样做将会将编译选项-Wall
和-Wextra
添加到目标my_executable
的编译选项中。
target_link_libraries
命令链接库:target_link_libraries(my_executable PRIVATE my_library)
这样做将会将库my_library
链接到目标my_executable
中。
对于编译选项和链接库的设置,可以在全局范围使用set
命令,也可以在目标级别使用target_compile_options
和target_link_libraries
命令。根据具体的项目需求,你可以选择适合的方法来设置编译选项和链接库。
CMAKE_CXX_FLAGS
是一个CMake变量,用于设置C++编译器的编译选项。
CMake生成的Makefile或其他构建系统文件会在使用C++编译器编译源代码时使用CMAKE_CXX_FLAGS
变量的值作为编译选项。
通过设置CMAKE_CXX_FLAGS
变量,可以向编译器传递一些参数和选项,例如优化级别、警告选项等。
以下是一些常用的C++编译选项示例:
-Wall
: 开启所有警告选项。-Werror
: 将所有警告视为错误。-O2
: 开启优化级别2。-std=c++11
: 指定使用C++11标准进行编译。你可以通过在CMakeLists.txt文件中设置CMAKE_CXX_FLAGS
变量的值来自定义编译选项,例如:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -O2")
这将会将-Wall -Werror -O2
这些选项添加到默认的C++编译选项中。
注意,CMAKE_CXX_FLAGS
是一个全局变量,会影响整个项目中使用C++编译器的地方。如果你只想为特定的目标设置编译选项,你可以使用target_compile_options
命令。
在CMake中,可以使用add_executable
、add_library
和add_subdirectory
来构建可执行文件、库文件和模块。
构建可执行文件:
使用add_executable
命令指定可执行文件的名称和源文件,如下所示:
add_executable(my_exe main.cpp)
这将生成名为my_exe
的可执行文件,该文件由main.cpp
源文件构建而成。
构建库文件:
使用add_library
命令指定库文件的名称、库类型和源文件,如下所示:
add_library(my_lib STATIC my_lib.cpp)
这将生成名为my_lib
的静态库文件,该文件由my_lib.cpp
源文件构建而成。你也可以使用SHARED
关键字来构建共享库。
构建模块:
使用add_subdirectory
命令将一个子目录添加到构建中,该子目录包含一个CMakeLists.txt文件。这个子目录可以是一个模块,可以包含库、可执行文件等构建规则。
add_subdirectory(my_module)
这将添加名为my_module
的子目录,并在该目录的CMakeLists.txt文件中执行构建规则。
在CMake中,可以使用install
命令将生成的文件安装到指定的目录。
安装可执行文件:
使用install
命令指定要安装的可执行文件,以及安装目标的路径,如下所示:
install(TARGETS my_exe DESTINATION bin)
这将把名为my_exe
的可执行文件安装到bin
目录下。
安装库文件:
使用install
命令指定要安装的库文件,以及安装目标的路径,如下所示:
install(TARGETS my_lib DESTINATION lib)
这将把名为my_lib
的库文件安装到lib
目录下。
安装头文件:
使用install
命令指定要安装的头文件,以及安装目标的路径,如下所示:
install(FILES my_header.h DESTINATION include)
这将把名为my_header.h
的头文件安装到include
目录下。
在CMake中,可以使用CTest来构建和运行测试。
创建测试:
使用add_test
命令添加测试。例如:
add_executable(my_test my_test.cpp)
add_test(NAME MyTest COMMAND my_test)
这将创建一个名为MyTest
的测试,并执行名为my_test
的可执行文件。
运行测试:
使用ctest
命令运行测试。例如:
ctest
这将运行所有配置好的测试。你也可以通过指定测试名称或者使用正则表达式来运行特定的测试。例如:
ctest -R MyTest
此外,你还可以使用add_test
命令的COMMAND
选项来指定测试运行的具体命令,而不是使用可执行文件。这样可以更灵活地设置测试环境和参数。
注意:
在使用CTest之前,确保在你的项目中包含了如下语句:
include(CTest)
这将使CTest相关的命令和变量可用。
有了这些设置,你就可以使用CMake和CTest方便地构建和运行测试了。
CMake提供了一些高级功能,帮助你更灵活地管理项目的条件编译和依赖管理。下面介绍一些常用的高级功能:
条件编译:
使用if
语句可以针对不同的条件选择编译选项。例如:
if(CONDITION)
# do something
else()
# do something else
endif()
可以使用DEFINED
、NOT
、AND
、OR
等条件运算符来组合条件。
依赖管理:
使用find_package
命令可以查找外部库的位置和版本,并将其添加到项目中。例如:
find_package(OpenCV REQUIRED)
target_link_libraries(my_project ${OpenCV_LIBS})
这将查找OpenCV库,并将其链接到my_project
可执行文件或库文件中。
外部项目管理:
使用ExternalProject_Add
命令可以在构建过程中下载和构建外部项目。例如:
ExternalProject_Add(MyLibrary
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/external/MyLibrary
GIT_REPOSITORY https://github.com/user/MyLibrary.git
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
)
这将下载并构建名为MyLibrary
的外部项目,并将其安装到指定的目录。
自定义编译选项:
可以使用option
命令创建自定义的编译选项。例如:
option(ENABLE_FEATURE "Enable feature" ON)
if(ENABLE_FEATURE)
add_definitions(-DENABLE_FEATURE)
endif()
这将创建一个名为ENABLE_FEATURE
的选项,用户可以通过设置-DENABLE_FEATURE=ON
或-DENABLE_FEATURE=OFF
来决定是否启用某个特性。
这些只是CMake的一些高级功能的简介,CMake还提供了许多其他功能,如自定义函数和宏、生成器表达式、包装器等,可以帮助你更好地管理和构建项目。可以通过查阅CMake官方文档或参考各种CMake的教程来深入了解这些功能的使用方法。
下面是一些示例和用法说明,演示了CMake的高级功能:
条件编译示例:
if(USE_CUDA)
add_definitions(-DUSE_CUDA)
find_package(CUDA REQUIRED)
include_directories(${CUDA_INCLUDE_DIRS})
target_link_libraries(my_project ${CUDA_LIBRARIES})
else()
find_package(OpenCV REQUIRED)
target_link_libraries(my_project ${OpenCV_LIBS})
endif()
这个示例根据USE_CUDA
变量的值选择编译选项。如果USE_CUDA
为真,将添加一个定义USE_CUDA
,然后查找并链接CUDA库。否则,查找并链接OpenCV库。
依赖管理示例:
find_package(Boost REQUIRED COMPONENTS filesystem)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(my_project ${Boost_LIBRARIES})
这个示例使用find_package
命令查找并链接Boost库的filesystem组件。Boost_INCLUDE_DIRS
变量保存了Boost库的头文件路径,Boost_LIBRARIES
变量保存了链接Boost库所需的库文件。
外部项目管理示例:
ExternalProject_Add(MyLibrary
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/external/MyLibrary
GIT_REPOSITORY https://github.com/user/MyLibrary.git
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
)
这个示例使用ExternalProject_Add
命令下载并构建名为MyLibrary
的外部项目。PREFIX
选项指定了项目的构建目录,GIT_REPOSITORY
选项指定了项目的Git仓库地址,CMAKE_ARGS
选项指定了传递给外部项目的CMake参数。
自定义编译选项示例:
option(ENABLE_FEATURE "Enable feature" ON)
if(ENABLE_FEATURE)
add_definitions(-DENABLE_FEATURE)
endif()
这个示例创建了一个名为ENABLE_FEATURE
的选项。如果用户在构建过程中设置了-DENABLE_FEATURE=ON
,则会添加一个定义ENABLE_FEATURE
。
引入外部依赖库有几种常见的方式。下面是两个示例:
使用find_package命令:
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(my_project ${OpenCV_LIBS})
这个示例使用find_package命令查找OpenCV库,并通过include_directories命令将OpenCV的头文件路径添加到项目中。然后使用target_link_libraries命令将OpenCV库链接到项目中。
使用add_subdirectory命令和add_library命令:
add_subdirectory(external/MyLibrary)
include_directories(external/MyLibrary/include)
target_link_libraries(my_project MyLibrary)
这个示例假设外部依赖库的源代码位于项目根目录下的external/MyLibrary
目录中。通过add_subdirectory命令将该目录中的源代码添加到项目中。然后使用include_directories命令将该库的头文件路径添加到项目中。最后使用target_link_libraries命令将该库链接到项目中。
在CMake中,可以使用option命令来定义自定义构建选项。下面是一个示例:
option(ENABLE_FEATURE_A "Enable Feature A" ON)
option(ENABLE_FEATURE_B "Enable Feature B" OFF)
这个示例定义了两个自定义构建选项:ENABLE_FEATURE_A
和ENABLE_FEATURE_B
。ON
和OFF
分别表示选项的默认值为打开和关闭。
然后,你可以在CMakeLists.txt文件中根据这些选项进行条件编译和添加功能。示例如下:
if(ENABLE_FEATURE_A)
message("Feature A is enabled")
add_definitions(-DENABLE_FEATURE_A)
endif()
if(ENABLE_FEATURE_B)
message("Feature B is enabled")
add_definitions(-DENABLE_FEATURE_B)
endif()
# 添加功能代码
在这个示例中,如果ENABLE_FEATURE_A
选项被打开,将会在编译过程中添加-DENABLE_FEATURE_A
宏定义,并打印一条相关信息。同样,如果ENABLE_FEATURE_B
选项被打开,将会添加-DENABLE_FEATURE_B
宏定义并打印一条相关信息。然后,你可以根据这些宏定义来添加相应的功能代码。
通过定义自定义构建选项,你可以根据需求来开启或关闭特定的功能,并且在构建过程中根据这些选项进行条件编译。如果你需要更多的自定义选项,可以继续使用option命令来定义。
CMake是一个跨平台的构建工具,可以方便地在不同的操作系统上进行开发。下面是使用CMake进行跨平台开发的一些常见方法和示例:
可以使用CMake的变量CMAKE_SYSTEM_NAME
来获取当前的操作系统名称。根据不同的操作系统类型,可以进行不同的设置和配置。例如,可以使用以下代码在Windows系统上设置特定的编译选项:
if(WIN32)
# Windows-specific settings
endif()
可以使用CMake的变量CMAKE_C_COMPILER
和CMAKE_CXX_COMPILER
来获取当前使用的C和C++编译器路径。根据不同的编译器类型,可以进行不同的设置和配置。
在不同的操作系统上,可能会有不同的标准库和头文件路径。通过使用CMake的变量CMAKE_INCLUDE_PATH
和CMAKE_LIBRARY_PATH
,可以设置特定的路径,以确保在不同的平台上能够正确地找到所需的库和头文件。
if(UNIX)
set(CMAKE_INCLUDE_PATH "/usr/local/include")
set(CMAKE_LIBRARY_PATH "/usr/local/lib")
elseif(WIN32)
set(CMAKE_INCLUDE_PATH "C:/Program Files/SomeLibrary/include")
set(CMAKE_LIBRARY_PATH "C:/Program Files/SomeLibrary/lib")
endif()
不同的操作系统和编译器可能需要不同的编译选项。可以使用CMake的变量CMAKE_C_FLAGS
和CMAKE_CXX_FLAGS
来设置特定的编译选项。例如,可以使用以下代码设置在Linux上使用特定的编译选项:
if(UNIX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
CMake提供了一系列的Find模块,用于自动查找各种常见的第三方库。你可以使用find_package
命令来调用相应的Find模块,并根据结果进行配置。例如,使用以下代码在项目中查找和使用OpenCV库:
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(MyApp ${OpenCV_LIBS})
通过以上方法,你可以使用CMake方便地进行跨平台开发,确保你的项目可以在不同的操作系统上正确编译和运行。
当在不同的操作系统和开发环境上使用CMake时,可以按照以下步骤进行:
安装CMake:
首先,在你的目标操作系统上安装CMake。你可以从CMake的官方网站(https://cmake.org)上下载适合你操作系统的安装程序,并按照提示进行安装。
创建CMakeLists.txt文件:
在你的项目根目录下创建一个名为CMakeLists.txt的文本文件。这个文件将包含构建你项目所需的CMake配置和构建指令。
写入CMake配置和构建指令:
编辑CMakeLists.txt文件,并根据你的项目需求编写CMake配置和构建指令。这些指令包括设置项目名称、指定源文件、定义目标(可执行文件、库等)、链接库和设置编译选项等。
在命令行中使用CMake:
打开终端或命令提示符,并导航到你的项目根目录。在命令行中输入以下命令来运行CMake配置和生成构建系统所需的Makefile或项目文件:
cmake .
注意,这里的"."表示当前目录,你也可以指定其他目录作为参数来进行配置和生成。
构建项目:
在运行CMake之后,根据你的操作系统和开发环境,使用相应的构建工具(如make、Ninja、Visual Studio等)来构建你的项目。使用以下命令进行构建:
make # 在Linux或macOS上使用make进行构建
ninja # 在任何平台上使用ninja进行构建
msbuild # 在Windows上使用Visual Studio进行构建
运行和调试:
构建成功后,你可以在生成的可执行文件或库的目录中找到生成的输出文件。你可以在适合你的操作系统和开发环境中运行、调试和测试你的项目。
当在不同的平台上使用CMake时,可能会面临一些特殊的注意事项和使用技巧。以下是针对不同平台的一些常见问题和解决方案:
Linux上的注意事项和技巧:
gdb <executable> # 用你的可执行文件的名称替换<executable>
Windows上的注意事项和技巧:
cmake -G "Visual Studio 16" . # 生成Visual Studio 2019的项目文件
macOS上的注意事项和技巧:
cmake -G "Xcode" . # 生成Xcode项目文件
在不同平台上安装CMake的具体方法如下:
Windows上安装CMake:
macOS上安装CMake:
通过Homebrew安装(推荐):
打开终端,并运行以下命令来安装Homebrew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
安装Homebrew后,运行以下命令来安装CMake:
brew install cmake
通过安装程序安装:
Linux上安装CMake:
通过包管理器安装(根据不同的Linux发行版可能会有所不同):
sudo apt-get install cmake
sudo dnf install cmake
sudo pacman -S cmake
通过源代码安装:
在CMake的官方网站(https://cmake.org/download/)上下载最新的源代码包。
解压下载的源代码包,并进入解压后的目录。
在终端中运行以下命令来编译和安装CMake:
./bootstrap
make
sudo make install
CMake提供了一些配置选项和环境变量,可以用来自定义构建过程和设置一些参数。下面是一些常用的配置选项和环境变量的设置方式:
配置选项:
cmake -DCMAKE_BUILD_TYPE=Release ..
环境变量:
这些配置选项和环境变量可以在CMakeLists.txt文件中使用,或者通过命令行传递给CMake。例如,在CMakeLists.txt中可以使用set()
命令来设置变量的默认值,或者使用if()
语句根据不同的变量值进行条件判断。在命令行中,可以使用cmake -D
选项来设置变量的值。
在使用CMake时,可能会遇到一些常见的问题。以下是几个常见问题及其解决方案:
CMake找不到依赖库:
find_library()
或find_package()
命令来告诉CMake查找依赖库的路径。CMAKE_PREFIX_PATH
环境变量来指定依赖库的安装路径。CMake生成的Makefile或项目文件无法编译通过:
add_executable()
或add_library()
命令时。无法生成所需的构建目标或输出文件:
CMake生成的项目文件不能正确地与IDE集成:
CMake缓存问题:
在使用CMake时,以下是一些常用的调试技巧和命令:
message()
命令输出调试信息:将需要跟踪的变量或信息输出到终端,以便调试和排除问题。message("Variable X has value: ${X}")
在CMakeLists.txt中启用调试模式:使用set(CMAKE_VERBOSE_MAKEFILE ON)
命令启用详细的Makefile输出,以查看生成的构建规则和命令。
使用--trace
选项运行CMake:在命令行中使用cmake --trace
命令,可以打印出CMake在解析和处理CMakeLists.txt文件时的详细信息,有助于排查问题。
使用--build
选项编译项目:在命令行中使用cmake --build <build_dir>
命令,可以手动触发构建过程,并查看编译器输出的详细信息和错误提示。
使用--debug-output
选项调试生成过程:在命令行中使用cmake --debug-output
命令,可以获得更详细的CMake生成过程信息,包括搜索和配置文件的路径。
使用ctest
命令运行测试:CMake集成了CTest框架,可以使用ctest
命令执行项目中的测试,并查看测试结果和详细输出。
使用外部工具调试:可以使用其他调试工具(如GDB或LLDB)来调试生成的可执行文件。在CMakeLists.txt中设置CMAKE_BUILD_TYPE
为Debug
,并使用适当的编译选项来生成可调试的二进制文件。
示例1:
hello.cpp
#include <iostream>
using namespace std;
int main()
{
cout << "hello world!" << endl;
return 0;
}
CMakeLists.txt
cmake_minimum_required (VERSION 3.10)
project (learn_cmake)
add_executable (App hello.cpp)
运行结果
示例2:
test1.h
#ifndef TEST1_H
#define TEST1_H
void test01(int num = 8);
#endif
test1.cpp
#include <iostream>
#include "test1.h"
using namespace std;
void test01(int num)
{
cout << num << endl;
}
main.cpp
#include <iostream>
#include "test1.h"
using namespace std;
int main()
{
cout << "hello world!" << endl;
test01();
test01(66);
return 0;
}
CMakeLists.txt
cmake_minimum_required (VERSION 3.10)
project (learn_cmake)
include_directories(./include )
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source SRC_DIR)
add_executable (App ${SRC_DIR})
运行结果
示例3:
在CMakeLists.txt定义代码中使用的宏
main.c
#include <stdio.h>
int main()
{
#if DEBUG
printf("调试信息\n");
#endif
return 0;
}
cmake_minimum_required(VERSION 3.10)
project(cmake03)
add_definitions(-DDEBUG)
aux_source_directory(./src SRC_LIST)
add_executable(main ${SRC_LIST})
运行结果
定义CMakeLists.txt文件中使用的宏,通过命令行传入
CMakeLists.txt
if(DEBUG_MODE)
message("ON------${DEBUG_MODE}")
else()
message("OFF------${DEBUG_MODE}")
endif()
运行结果:
示例4:
message(消息等级 "消息内容" ...)
string(ASCII 27 Esc)
set(R "${Esc}[32;1m")
set(E "${Esc}[0m")
message(FATAL_ERROR "${R}this is test${E}")
(无) :重要消息
message("${R}this is test${E}")
运行结果
STATUS :非重要消息
message(STATUS "${R}this is test${E}")
运行结果
WARNING:警告, 会继续执行
message(WARNING "${R}this is test${E}")
运行结果
AUTHOR_WARNING:警告 (dev), 会继续执行
message(AUTHOR_WARNING "${R}this is test${E}")
运行结果
SEND_ERROR:错误, 会继续执行,跳过生成的步骤
message(SEND_ERROR "${R}this is test${E}")
运行结果
FATAL_ERROR:错误, 终止
message(FATAL_ERROR "${R}this is test${E}")
运行结果
示例5:
运行结果
CMake是一种流行的构建系统工具,用于自动化软件的构建过程。它有以下几个优势:
跨平台: CMake可以在多个操作系统上运行,包括Windows、Linux和macOS。这使得开发人员可以在不同平台之间共享和重用构建配置。
简单易用: CMake使用简洁的语法,易于理解和学习。开发人员可以通过编写简单的CMakeLists.txt文件来描述项目的构建过程。
可扩展性: CMake提供了丰富的功能和模块,可以满足各种项目的需求。使用CMake,开发人员可以自定义构建过程,并集成其他工具和库。
并行构建: CMake支持并行构建,可以加快项目的构建速度。开发人员可以指定同时构建多个目标,提高构建效率。
支持多种构建系统: CMake可以生成多种构建系统的配置文件,包括Makefile、Ninja和Visual Studio等。这使得开发人员可以根据自己的需求选择合适的构建系统。
CMake的不足之处包括:
学习曲线较陡峭: 尽管CMake的语法相对简洁,但对于新手来说,学习和理解CMake的概念和工作原理可能需要一定的时间和经验。CMake使用一种基于脚本的语法,对于初学者来说,学习和理解CMake的语法可能需要一些时间。
缺乏良好的文档: 虽然CMake有一些文档和教程可供参考,但相对不够全面和详细。这可能导致开发人员在遇到问题时难以找到合适的解决方案。
速度较慢: CMake的速度相对较慢,特别是在处理大型项目时。这可能影响项目的构建效率。
缺乏直观性: CMake的语法相对复杂,有时候很难直观地理解脚本的含义。
生成的构建脚本可能不够优化: 由于CMake的生成脚本是自动生成的,有时候可能会存在一些不够优化的问题,需要手动对生成的脚本进行优化。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。