如果您曾经工作或编写过 C 或 C++ 项目,那么您可能听说过称为 CMake 的构建系统。对于刚接触 C 或 C++ 的程序员来说,单独的构建系统使用单独的语言的概念可能看起来很奇怪。但是,如何正确编译、构建和打包项目的知识对于 C++ 程序员来说绝对是必不可少的知识。在本指南中,将解释 CMake 作为构建系统所扮演的角色,并讨论 CMake 最重要的方面和功能。您将在 20 分钟内学习如何使用 CMake 构建库和可执行文件。
一、什么是CMake ?
二、为什么要使用CMake ?
与许多其他编程语言不同,c++编译器没有一个集中分布的、单一的。c++语言是按照ISO标准规范建模的,像Microsoft或Clang这样的编译器供应商随后实现了ISO标准规范。每一个编译器都有不同的设置和选项,并且运行在不同的操作系统上。大多数c++程序都是为了跨平台和在任何现代编译器上运行而编写的,用定制的构建脚本来实现这一点是很困难的。这需要每个编译器和操作系统的详细知识,以及大量的知识。
CMake从一个用CMake语言编写的抽象脚本生成c++项目的构建系统。CMake提供了一个接口来指定构建选项和过程,而不涉及编译器或操作系统的特定细节。请注意,它只涵盖了用于构建项目的跨平台功能,您仍然需要负责在不同平台上运行的实际源代码。
三、项目结构和CMakeLists.txt文件
有多种方式来布局一个c++项目。有些项目将头文件和源文件分开,有一个include
目录和一个src
目录,而有些项目将头文件和源文件放在同一个目录中。为此目的,我们将使用头文件目录和源文件目录的组合,因此项目结构稍微简单一些。
在每个项目的开始必须是一个CMakeLists.txt文件。这是CMake识别为构建脚本文件的文件名。一个项目可以有多个CMakeLists.txt,但每个目录中只能有一个。对于项目顶层目录中的文件,必须在文件的顶部显示以下内容:
cmake_minimum_required(VERSION 3.6)
project(foo)
这两行代码表示了构建项目所需的cmake的最小版本,以及项目的名称。在这种情况下,最小版本并不重要,只要它至少是3.0。让我们再看看下面的示例项目布局:
- CMakeLists.txt
- some.h
- some.cpp
- main.cpp
这里有一个CMake文件CMakeLists.txt,一个头文件some.h和一个源文件some.cpp。还有另一个源文件main.cpp。在大多数c++项目中,有一个包含项目代码的库供其他项目和程序使用,还有一个可执行文件,允许项目中的一些代码作为CLI
程序运行。对于本例,some.cpp将表示核心库代码,而main.cpp将表示同样构建的可执行文件。
四、构建库
为了让CMake构建库,组成库的源文件需要在cmakelist .txt文件中指定。为此,我们必须构造一个文件列表并调用add_library
函数。
在CMake中,重要的是要理解变量和数据类型的工作方式可能与其他任何语言都不同。CMake中的所有变量都是字符串。CMake中的列表是包含分隔符的字符串; 。 使用set()
和unset()
函数创建和销毁变量。下面是一个创建列表并打印它的示例。
set(some_var "a" "b" "c")
message("${some_var}")
这将打印:
a;b;c
上面是CMake的另一个重要特性,变量访问。通过set()
函数创建变量后,它的值只能通过语法${var}
访问。这是因为在CMake中,所有内容都是字符串。即使没有带引号的值也是字符串。语句message(hello)
将hello
视为一个5个字符的字符串。
现在,为了构建库,我们必须将以下内容添加到构建文件CMakeLists.txt中:
set(FOO_SOURCES some.cpp)
add_library(foos STATIC "${FOO_SOURCES}")
在对add_library()
的调用中,通过${}
语法访问源变量。然而,我们也传递另一个参数SHARED
。这告诉CMake我们是想要静态库还是共享库。
五、添加可执行
现在我们已经向构建文件添加了一个库,我们可以扩展该文件以包含构建可执行文件并将库链接到该可执行文件的说明。让我们创建一个简单的c++文件main.cpp,它调用库中的一个函数:
#include "some.h"
int main(int argc, char const* argv[]) {
someFunc(); //定义在some.cpp
return 0;
}
然后,我们可以在构建文件中添加以下内容:
add_executable(foot main.cpp)
target_link_libraries(foot foos)
add_executable
函数的工作原理与add_library
非常相似,它接受一个name变量和一个表示源文件的列表。在这种情况下,因为我们知道我们的可执行文件只有一个额外的源文件,所以我们不需要一个单独的变量来将源文件表示为一个列表,我们可以直接传递它。target_link_libraries
函数的第一个参数是一个目标,然后是必须是库的其他目标的列表,就像第一个参数一样。
在CMake中,“目标(target)”是一个由CMake生成的生成系统构建的对象。在本例中,库和可执行文件都是构建目标。在更复杂的项目中,定制目标有时用于表示所构建项目所需的文件的创建。但是现在,让我们只关注基本的构建目标。
六、综合在一起
现在已经讨论了编写CMakeLists.txt文件的过程,我们可以开始运行CMake,然后使用CMake生成的构建系统来编译我们的代码。如果你的系统上还没有安装CMake,通常你可以通过运行brew install CMake
来安装它。否则,请尝试***此链接***。
CMake通常在一个空目录中运行,该目录引用CMakeLists.txt根文件所在的路径。例如,如果当前在项目的目录中,通常会使用以下命令:
mkdir build
cd build
cmake ..
这将运行CMake,并让它在当前目录中生成构建文件。它将打印出大量与它检测到并用于生成这些文件的编译器相关的信息。
接下来,您将实际运行CMake生成的文件并编译项目。如果您使用的是MacOS系统或Linux系统,您将运行make
。如果你是在windows系统上,你通常会运行 msbuild ALL_BUILD.vcxproj
。这将导致编译器运行并尝试编译您的代码。
注意:
如果你的代码有语法错误或写得不正确,这可能会失败。CMake不检测或检查语言语法,它只关心构建过程*。
就这样! 希望到现在为止,您对 CMake 感到很自在,可以编写自己的构建文件,并使用它在 C++ 中创建简单的项目。 总的来说,CMake 是一个庞大的框架,包含许多专门的组件,可以帮助构建几乎所有 C++ 项目。 随着您的进步和对它的熟悉,您可能需要更高级和复杂的 CMake,例如拥有多个子目录,或添加 3rd 方库作为项目的依赖项。
如有侵权,请联系删除。