CMake 介绍
CMake 简介
CMake 是一个跨平台的构建工具,用于管理和构建 C++ 项目。它的设计目标是提供一种简化的构建过程,使开发人员能够在不同的操作系统和编译器上轻松地生成可执行文件、库和其他构建目标。
CMake
的主要思想是通过描述项目的构建过程来生成构建系统所需的构建脚本。它使用一种称为
CMakeLists.txt
的文本文件来定义项目的目录结构、源文件、编译选项、依赖项和构建规则。CMake
具有很好的跨平台性,可以在多种操作系统上使用,包括 Windows、Linux、macOS
等。它可以生成不同的构建系统文件,如 Makefile、Ninja、Visual Studio
解决方案等,从而使开发人员能够在不同的开发环境中使用适合的构建系统。
总而言之,CMake 简化了跨平台 C++ 项目的构建过程,使开发人员能够更轻松地管理项目的编译和构建,同时提供了灵活性和可扩展性来处理复杂的项目结构和依赖关系。
学习 CMake 的目的,是为将来处理大型的 C / C++ / Java 项目做准备。
CMake 安装
绝大多数 Linux 系统已经安装了 CMake,没有安装的话可以去 CMake 官网 进行安装。
MacOS 也可以用 Homebrew 进行快速安装:
1 |
|
构建 CMake 项目
构建 CMake 项目,大致分为 3 个步骤:
- 创建
CMakeLists.txt
文件 - 使用
CMake
指令生成Makefile
文件 - 使用
make
指令进行编译
一个简单示例
创建一个
demo.cpp
:1
2
3
4
5
6
7
8#include <iostream>
int main() {
std::cout << "Hello world!\n";
return 0;
}同级目录下创建一个
CMakeLists.txt
文件,输入以下文本:1
2
3
4
5
6
7PROJECT(HELLO)
SET(SRC_LIST demo.cpp)
MESSAGE(STUTAS "Configuring project...")
ADD_EXECUTABLE(Demo ${SRC_LIST})终端下使用
cmake
指令生成Makefile
文件:1
cmake .
若运行成功,则目录下会生成
Makefile
文件。使用
make
指令进行编译:1
make
编译成功,则会生成可执行文件
Demo
。运行
Demo
即可:1
./Demo
CMakeLists.txt
基本语法介绍
PROJECT
关键字
PROJECT
关键字用于定义和配置项目。它用于指定项目的名称、版本号和语言。。
PROJECT关键字的基本语法如下:
1 |
|
其中,<project_name>
是要定义的项目名称。<version>
是可选的,用于指定项目的版本号。 <languages>
也是可选的,用于指定项目所使用的编程语言。
以下是一些 PROJECT
关键字的示例用法:
定义一个简单的项目:
1
PROJECT(MyProject)
定义一个带有版本号的项目:
1
PROJECT(MyProject VERSION 1.0)
定义一个使用多种编程语言的项目:
1
PROJECT(MyProject LANGUAGES CXX C)
SET
关键字
SET
关键字用于设置变量的值。语法如下:
1 |
|
其中,<variable>
是要设置的变量名,<value>
是要为变量设置的值。<value>
可以是一个字符串、一个列表、一个布尔值或一个数值。SET关键字还支持一些其他选项:
CACHE
:指定变量为缓存变量,即用户可以通过CMake的缓存机制进行设置或修改该变量的值。缓存变量的值可以在CMake运行期间持久保存,并在下一次运行时保持不变。<type>
:指定缓存变量的类型。可以是BOOL、STRING、PATH等类型。<docstring>
:可选项,用于提供变量的描述文本。FORCE
:可选项,强制设置变量的值,即使它已经被缓存。
以下是一些 SET
关键字的示例用法:
设置一个字符串变量:
1
SET(my_string "Hello, World!")
设置一个列表变量:
1
SET(my_list 1 2 3 4)
设置一个缓存变量:
1
SET(my_variable "default value" CACHE STRING "Description of my variable")
强制设置一个缓存变量的值:
1
SET(my_variable "new value" CACHE STRING "Description of my variable" FORCE)
通过 SET
关键字,你可以在 CMake
脚本中设置变量的值,并根据需要使用它们来控制构建过程、传递参数或处理其他逻辑。
变量取值:使用 ${}
,但是在 IF
控制语句中直接使用变量名。
MESSAGE
关键字
在 CMake 中,MESSAGE
是一个用于输出消息的关键字。它允许你在 CMake
脚本中打印信息,以便在构建过程中向用户提供有用的反馈或调试信息。
MESSAGE关键字的语法如下:
1 |
|
其中,<mode>
是可选的,用于指定消息的模式。常用的消息模式有以下几种:
STATUS
:以普通状态的形式输出消息。WARNING
:以警告的形式输出消息。AUTHOR_WARNING
:以作者警告的形式输出消息。SEND_ERROR
:以错误的形式输出消息,并终止 CMake 的配置过程。FATAL_ERROR
:以致命错误的形式输出消息,并终止 CMake 的配置过程。
<message>
是要输出的消息内容,可以是一个字符串或一个变量。
以下是一些MESSAGE关键字的示例用法:
输出普通状态消息:
1
MESSAGE(STATUS "Configuring project...")
输出警告消息:
1
MESSAGE(WARNING "Warning: Invalid configuration detected.")
输出错误消息并终止配置过程:
1
MESSAGE(SEND_ERROR "Error: Required library not found.")
ADD_EXECUTABLE
关键字
在 CMake 中,ADD_EXECUTABLE
是一个用于添加可执行文件的关键字。它用于指定要构建的可执行文件的名称和源代码文件。
ADD_EXECUTABLE
关键字的语法如下:
1 |
|
其中,<executable_name>
是要生成的可执行文件的名称。[WIN32]
和
[MACOSX_BUNDLE]
是可选的标志,用于指定在 Windows 上构建一个
GUI 应用程序或在 macOS
上构建一个应用程序捆绑包。[EXCLUDE_FROM_ALL]
也是可选的标志,表示该目标不会被默认构建,除非明确要求。
source1 [source2 ...]
是要包含在可执行文件中的源代码文件的列表。可以通过相对或绝对路径指定这些源文件。
以下是 ADD_EXECUTABLE
关键字的示例用法:
1 |
|
上述示例将创建一个名为 MyApp
的可执行文件,并将
main.cpp
和 utils.cpp
作为源代码文件包含在可执行文件中。
使用 ADD_EXECUTABLE
关键字时,CMake
会自动检测源代码文件的编程语言,并相应地设置编译器和编译选项。
ADD_EXECUTABLE
关键字允许你定义项目中的可执行文件,并指定与之相关的源代码文件。通过
ADD_EXECUTABLE
,你可以将源代码文件与特定的可执行文件关联起来,以便在构建过程中生成所需的可执行文件。
ADD_SUBDIRECTORY
关键字
ADD_SUBDIRECTORY
是用于向 CMake
构建系统添加子目录的关键字。它的作用是告诉 CMake
在当前项目中包含另一个目录,并在该子目录中查找并处理另一个
CMakeLists.txt
文件。
ADD_SUBDIRECTORY
的语法如下:
1 |
|
directory
:要添加的子目录的路径。这个路径可以是相对于当前 CMakeLists.txt 文件的相对路径,也可以是绝对路径。binary_dir
(可选):指定生成的二进制文件的输出目录。如果不提供,则使用默认值(通常是当前构建目录)。EXCLUDE_FROM_ALL
(可选):如果指定了这个选项,将会在构建目标时排除这个子目录。这对于包含一些可选的或不常用的子项目很有用。
使用 ADD_SUBDIRECTORY
时,CMake 会进入子目录并处理对应的
CMakeLists.txt
文件。这意味着子目录中可以有自己的构建规则、目标等。通过使用
ADD_SUBDIRECTORY
,可以将复杂的项目划分为多个子项目,并使用各自的
CMakeLists.txt 文件进行管理,从而提高项目的组织性和可维护性。
请注意,在使用 ADD_SUBDIRECTORY
之前,通常需要在子目录中准备一个有效的 CMakeLists.txt
文件,以定义子项目的构建规则、目标等。
以下是一个示例,展示如何使用 ADD_SUBDIRECTORY
:
1 |
|
1 |
|
上述示例中,主项目的 CMakeLists.txt
文件使用
ADD_SUBDIRECTORY
添加了一个名为 "subdir"
的子目录。然后,CMake 进入子目录并处理对应的 CMakeLists.txt
文件,执行子目录的构建规则和目标的设置。
通过使用 ADD_SUBDIRECTORY
,你可以将项目组织成多个子目录,每个子目录都可以有自己的
CMakeLists.txt
文件,方便地管理和构建大型项目。
INSTALL
关键字
INSTALL
用于定义要安装的文件、目录和相关设置。它用于在构建过程中指定将生成的文件复制到特定位置的规则。
INSTALL
关键字的基本语法如下:
1 |
|
下面是 INSTALL 关键字的一些常用选项:
CODE <code>
:指定自定义安装逻辑的 CMake 代码。SCRIPT <file>
:指定一个脚本文件,该脚本文件包含安装逻辑。SCRIPTS <files>...
:指定多个脚本文件,这些脚本文件包含安装逻辑。FILES <files>...
:指定要安装的普通文件。PROGRAMS <files>...
:指定要安装的可执行文件。DIRECTORY <dir>...
:指定要安装的目录。TARGETS <targets>...
:指定要安装的构建目标(可执行文件、静态库、共享库等)。EXPORT <export-name>
:指定要安装的导出目标。ALIAS <target>
:指定要安装的构建目标的别名。FILES_MATCHING
:指定在安装文件时进行匹配的模式。PATTERN <pattern> | REGEX <regex>
:指定文件匹配的模式或正则表达式。EXCLUDE
:排除匹配的文件。PERMISSIONS permissions...
:指定安装文件的权限。CONFIGURATIONS [Debug|Release|...]
:指定仅在特定构建配置下安装文件。
INSTALL 关键字允许您以灵活的方式定义文件和目录的安装规则。可以使用多个 INSTALL 关键字来安装多个文件和目录。例如:
1 |
|
上述示例演示了如何使用 INSTALL
关键字来指定不同类型的文件和目录的安装位置。
cmake
指令
cmake
基本语法:1
cmake [options] <path-to-source>
options
:CMake的选项,用于配置生成过程的行为和参数。<path-to-source>
:CMakeLists.txt
所在的源代码目录路径。
常用选项:
-G <generator>
:指定生成器,用于生成特定构建系统的文件(如 Makefile 或 Visual Studio 解决方案)。例如,-G "Unix Makefiles"
表示生成 Unix 系统下的 Makefile 文件。-D <var>=<value>
:设置 CMake 变量的值。例如,-D CMAKE_BUILD_TYPE=Release
设置 CMake 变量CMAKE_BUILD_TYPE
的值为Release
。-B <path-to-build>
:指定构建目录的路径。生成的构建系统文件将存储在该目录中。-S <path-to-source>
:指定源代码目录的路径。
示例用法:
- 在源代码目录中直接运行CMake(in-source build):
1
cd /path/to/source && cmake .
- 在指定的构建目录中运行CMake(out-of-source build):
1
cd /path/to/build && cmake /path/to/source
- 指定生成器和构建类型:
1
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release /path/to/source
- 在源代码目录中直接运行CMake(in-source build):
make
指令
Make
是一个常用的构建工具,用于自动化软件项目的编译和构建过程。它使用一个名为Makefile
的文件来定义构建规则和依赖关系,以确定如何生成目标文件(例如可执行文件、库文件等)。
以下是 make
指令的基本语法:
1 |
|
其中,options
是可选的命令行选项,用于控制
make
的行为。target
是要构建的目标,通常是在
Makefile
中定义的一个规则。
常用的 make
选项包括:
-f <filename>
:指定要使用的Makefile
文件的名称。如果未指定,则默认使用当前目录下的Makefile
文件。-C <dir>
:在指定的目录下执行make
命令。这可以用于指定Makefile
文件所在的目录。-j <num>
:指定并行构建的作业数,加快构建速度。
在 Makefile
文件中,你可以定义多个目标和规则,每个规则描述了如何根据依赖关系生成目标文件。一个基本的规则结构如下:
1 |
|
其中,target
是目标文件的名称,dependencies
是生成目标文件所依赖的文件或目标,commandX
是用于生成目标文件的命令。
以下是一个简单的示例 Makefile
文件的内容:
1 |
|
上述示例中,我们定义了一个目标 hello
,它依赖于
main.c
和 hello.c
这两个源代码文件。make
指令会检查依赖关系,并在需要时执行相应的命令来生成目标文件。
要使用 make
指令进行构建,只需在终端中进入包含
Makefile
文件的目录,并运行 make
命令。如果没有指定目标,默认会构建第一个目标。
Make 工具基于目标文件和规则之间的依赖关系,通过增量构建的方式进行构建,只重新构建已修改的文件和受其影响的文件,从而提高构建效率。Make 是一个强大而灵活的构建工具,它在软件开发中广泛应用。通过定义适当的规则和依赖关系,可以自动化构建过程,简化项目的管理和部署。
CMake 与 Make 之间的关系可以简述如下:
- CMake 负责生成适合不同平台和编译器的构建系统文件(如Makefile)。
- Make 负责根据 Makefile 中的规则和依赖关系来执行实际的编译和构建操作。
make install
指令
make install
用于将构建生成的文件复制到指定位置,完成软件的安装过程。这个指令通常与
Makefile
文件一起使用,用于定义安装规则。
在 Makefile
中,可以定义一个名为 install
的目标规则,该规则包含安装过程的指令。安装指令通常使用系统的
cp
命令或类似的工具来复制文件或目录到目标位置。
以下是一个示例 Makefile
文件中的 install
规则:
1 |
|
在上述示例中,install
规则中定义了两个命令。第一个命令使用 cp
命令将
myapp
可执行文件复制到 /usr/local/bin
目录下,第二个命令将 resources
目录递归复制到
/usr/local/share/myapp
目录下。
要执行安装过程,可以在命令行中运行 make install
命令。Make 将读取 Makefile 文件,并根据 install
目标规则中定义的指令来执行安装操作。
需要注意的是,make install
命令通常需要使用超级用户权限(sudo)来执行,因为将文件复制到系统目录通常需要管理员权限。如果没有权限,可以尝试将文件复制到用户的个人目录或其他有权限的目录。
使用 make install
指令可以方便地自动化软件的安装过程,将生成的文件复制到指定位置,使用户可以直接使用已安装的软件。
工程化目录结构
基本目录结构:
1 |
|
每个目录下都有一个 CMakeLists.txt
,用于实现 CMake
嵌套。
外层
CMakeLists.txt
:1
2
3PROJECT(HELLO) # 指定项目名称
ADD_SUBDIRECTORY(src bin) # 将 src 子目录加入工程并指定编译输出路径为 bin 目录内层
CMakeLists.txt
:1
2
3
4
5SET(SRC_LIST demo.cpp)
MESSAGE(STUTAS "Configuring project...")
ADD_EXECUTABLE(Demo ${SRC_LIST}) # 添加可执行项目bulid
文件夹:用于存储构建过程中生成的中间文件和最终的构建结果。src
文件夹:用于存放源代码doc
文件夹:用于存放文档COPYRIGHT
:版权runhello.sh
:安装脚本
构建库
ADD_LIBRARY
关键字
ADD_LIBRARY
是一个用于定义和构建库(Library)目标的关键字。它用于创建静态库(Static
Library)、共享库(Shared Library)或模块库(Module
Library)的规则,并将源代码文件编译成库文件。
ADD_LIBRARY 关键字的基本语法如下:
1 |
|
其中,<name>
指定要创建的库的名称。可以使用自定义名称或者 CMake
的变量作为库的名称。
选项 [STATIC | SHARED | MODULE]
指定库的类型。可以选择其中一种类型: -
STATIC
:创建静态库。静态库在链接时被静态地链接到可执行文件中。
-
SHARED
:创建共享库(也称为动态库)。共享库在运行时被动态地加载。
- MODULE
:创建模块库(适用于像 Apple 的 macOS
平台)。模块库是一种特殊的共享库类型。
可选项 [EXCLUDE_FROM_ALL]
表示将该库目标从生成的默认目标中排除。
后续的参数 source1
、source2
等指定库的源代码文件,可以是单个源文件,也可以是一个文件列表。CMake
将编译这些源代码文件,并生成库文件。
以下是一个示例:
1 |
|
INCLUDE_DIRECTORIES
关键字
INCLUDE_DIRECTORIES
是一个用于指定包含目录的关键字。它用于将一个目录添加到编译器的包含路径中,以使编译器能够找到所需的头文件。
INCLUDE_DIRECTORIES
关键字的基本语法如下:
1 |
|
其中,directory1
、directory2
等参数指定要添加到包含路径中的目录。可以指定一个或多个目录。
选项 [AFTER|BEFORE]
控制新目录的添加顺序。默认情况下,新目录会添加到已有包含目录列表的末尾,即在已有包含目录之后。如果指定了
AFTER
选项,新目录将添加到列表的末尾。如果指定了
BEFORE
选项,新目录将添加到列表的开头。
以下是一个示例:
1 |
|