在我的Autotools 介绍中,我演示了如何使用 GNU Autotools 管理代码的构建和打包。它是一个强大且通用的平台,可以轻松集成到许多打包系统中,包括 RPM、APT、pkgsrc 等。它的语法和结构可能令人困惑,但幸运的是,还有其他替代方案,包括开源的 CMake 框架。
CMake 是一个跨平台套件,用于构建、测试和打包软件。它使用简单且文档清晰的语法,因此即使您以前从未使用过构建系统,也很容易开始使用。
安装 CMake
您的 Linux 系统可能已经安装了 CMake。如果没有,您可以使用发行版的软件包管理器安装它
$ sudo dnf install cmake
在 Debian 或类似系统上
$ sudo apt install cmake
对于 Mac,您可以使用 MacPorts 或 Homebrew
$ sudo port install cmake
在 Windows 上,您可以使用 Chocolatey 或直接从 CMake 网站下载二进制文件。
CMake 的工作原理
对于想要从源代码构建软件的开发人员或用户来说,CMake 是一种快速简便的编译和安装方法。CMake 分阶段工作
- 首先,在
cmake
步骤中,CMake 扫描主机系统(正在运行它的计算机)以发现默认设置。默认设置包括支持库的位置以及新软件在系统中的放置位置。 - 接下来,您可以使用系统的
make
命令(通常是 Linux 上的 GNU Make,NetBSD 上的 NetBSD Make 等)来构建应用程序,通常是将人类可读的源代码转换为机器语言。 - 最后,在
make install
步骤中,构建的文件被复制到计算机上相应的 locations(在cmake
阶段检测到)位置。
这看起来很简单,而当您使用 CMake 时,它确实如此。
CMake 的可移植性
CMake 在设计时考虑了可移植性。虽然它不能使您的项目在所有 POSIX 平台上都能工作(这取决于您,作为程序员),但它可以确保您标记为安装的文件安装到已知平台上最合理的位置。并且由于像 CMake 这样的工具,高级用户可以轻松地自定义和覆盖任何非最佳值,以满足其系统的需求。
使用 CMake,您只需知道哪些文件需要安装到哪个大致位置即可。它会处理其他一切。不再有在任何未经测试的操作系统上都会崩溃的自定义安装脚本。
打包
与 Autotools 一样,CMake 也得到了很好的支持。将带有 CMake 的项目交给发行版打包者,无论他们是打包 RPM、DEB 还是 TGZ(或其他任何格式),他们的工作都简单而直接。打包工具了解 CMake,因此很可能不需要任何修补、黑客或调整。在许多情况下,将 CMake 项目合并到管道中可以实现自动化。
如何使用 CMake
要开始将 CMake 用于您的项目,您只需在项目目录中创建一个 CMakeLists.txt
文件。首先,声明 CMake 的最低必需版本以及项目标题和版本。CMake 努力尽可能长时间地保持兼容性,但是您使用它并关注其开发的时间越长,您就越了解您依赖的功能。
cmake_minimum_required(VERSION 3.10)
project(Hello VERSION 1.0)
您可能已经注意到,CMake 的语法是一个命令,后跟括号中的参数。大写的 VERSION
字符串并非随意或仅为样式;它们是 project
命令的有效参数。
在继续之前,生成一个 C 或 C++ 的示例 hello world
应用程序。为了简单起见,我编写了六行 C 代码并将其保存为 hello.c
(以匹配我在 CMakeLists.txt
中列出的可执行文件)
#include <stdio.h>
int main() {
printf("Hello open source\n");
return 0;
}
但请不要误会,CMake 的用途不仅限于 C 和 C++。它可以处理任意文件,并具有许多可用的命令,因此它可以帮助您维护各种形式的项目。
CMake 网站记录了所有有效的内置命令及其可用参数,因此无论您想做什么,都很容易找到您需要的功能。这是一个简单的示例,但是您需要的下一个命令是必不可少的 - 您必须为 CMake 定义您正在构建的代码
add_executable(Hello hello.c)
这会将编译后的二进制文件的名称设置为 Hello
,因此在功能上,它与在终端中使用 -o Hello
运行 gcc
相同。
在一个复杂的项目中,您可能既有库也有可执行文件。您可以使用 add_library
命令添加库。
在设置了要构建和标记为安装的文件之后,您必须告诉 CMake 用户安装您的应用程序后,完成的产品应最终放置在何处。
在这个简单的示例中,只有一个东西被标记为安装,因此您只需向 CMakeLists
添加一个 install
行。install
命令接受一些参数,但在这种情况下,所有必需的只是 TARGETS
参数,后跟要安装的文件的名称
install(TARGETS Hello)
向 CMake 项目添加文件
一个软件项目很少只向其用户交付代码。通常还有一些附加数据,例如手册页或信息页、示例项目或配置文件。您可以使用与包含编译文件类似的工作流程,在 CMake 项目中包含任意数据:首先,将文件添加到 CMakeLists.txt
,然后描述如何安装它。
例如,要将名为 assets
的目录包含在您的示例应用程序中,您可以使用 file
命令,后跟 COPY
和 DESTINATION
参数,告诉 CMake 将您的附加文件复制到您的可分发包中
file(COPY assets DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
${CMAKE_CURRENT_BINARY_DIR}
是一个特殊的内置 CMake 变量,表示 CMake 当前正在处理的目录的路径。换句话说,您的任意数据将被复制到构建目录(在您运行 cmake
之后,这一点会更加清楚,所以请注意稍后再次出现这种情况)。
由于数据目录往往是拥挤的地方(如果您不相信我,请查看 /usr/share
),因此为了每个人的利益,您应该为自己的项目创建一个子目录,最好带有版本控制。您可以通过在 CMAKE_CURRENT_BINARY_DIR
中指定一个新目录来执行此操作,该目录使用您选择的项目名称,后跟一个名为您的项目和您在项目声明中设置的 VERSION
的特殊变量
file(COPY assets DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/Hello-${Hello_VERSION}")
定义安装位置
您已经定义了构建过程的文件,因此现在您必须告诉 CMake 在安装过程中将其放置在何处。与您的主可执行文件一样,这使用 install
命令
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Hello-${Hello_VERSION}" TYPE DATA)
这里有一些新的参数。DIRECTORY
参数将数据源标识为目录(而不是 FILE
或 SCRIPT
等)。您正在使用与将数据文件复制到构建位置时相同的变量。此外,必须为 install
提供 TYPE
或 DESTINATION
(而不是两者)。TYPE
参数指定通用文件类型,该类型放置在适合目标系统的位置。在 Linux 上,TYPE DATA
目录通常放置在 /usr/local/share
或 /usr/share
中,除非用户或打包者定义了不同的数据位置。
这是像 CMake 这样的优秀构建系统强大的功能之一。您不必担心文件最终会放在哪里,因为您知道用户可以提醒 CMake 他们的首选默认值,并且 CMake 将构建代码以使其工作。
运行 CMake
CMake 有多个界面。您可以从终端将其用作命令或交互式应用程序,也可以使用其图形用户界面 (GUI) 前端。我倾向于使用终端命令,但我同样喜欢其他用户体验(它们绝对比在 Makefiles 中搜索晦涩变量以重新定义要好)。
对于任何构建过相当多的开源 C++ 项目的人来说,第一步是创建一个 build
目录,切换到该目录,然后运行 cmake ..
命令。我是一个懒惰的打字员,所以我将我的构建目录命名为 b
,但您可以使用对您最有意义的任何名称
$ mkdir b
$ cd b
$ cmake ..
-- The C compiler identification is GNU 11.1.1
-- The CXX compiler identification is GNU 11.1.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /var/home/seth/demo-hello/b
$
这或多或少相当于经典 ./configure; make; make install
咒语中的 ./configure
。查看您的构建目录会发现 CMake 生成了几个新文件来帮助您的项目组合在一起。有一些 CMake 数据、一个常规的 Makefile(对于免费的 247 行代码来说,这已经很多了,但对于复杂的项目来说,还要多得多)以及 Hello-1.0
数据目录,其中包含与此示例应用程序一起分发的任意非编译数据
$ ls
CMakeCache.txt
CMakeFiles
Makefile
Hello-1.0
cmake_install.cmake
接下来,您可以构建。您可以使用 CMake 使用 --build
选项来执行此操作,将当前构建目录用作源目录
$ cmake --build .
Scanning dependencies of target Hello
[ 50%] Building C object CMakeFiles/Hello.dir/hello.c.o
[100%] Linking C executable Hello
[100%] Built target Hello
或者您可以运行 make
命令。这将读取 CMake 生成的 Makefile
。在此示例中,Make 的默认操作是编译其目标 hello.c
$ make
Scanning dependencies of target Hello
[ 50%] Building C object CMakeFiles/Hello.dir/hello.c.o
[100%] Linking C executable Hello
[100%] Built target Hello
$
正如您可能期望的那样,Hello
二进制可执行文件现在存在于您的当前构建目录中。因为它是一个简单的独立应用程序,所以您可以运行它进行测试
$ ./Hello
Hello open source
$
最后,您可以使用 --install
选项进行安装。因为我不希望我的简单“hello world”应用程序实际安装在我的系统上,所以我将 --prefix
选项设置为将 CMake 的目标从根目录 (/
) 重定向到 /tmp
中的子目录
$ cmake --install . --prefix /tmp/hello/
-- Install configuration: ""
-- Installing: /tmp/dist-hello/usr/local/bin/Hello
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0/assets/file0
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0/assets/file1
或者,您可以运行 make install
来调用 Makefile 的安装操作。同样,为了避免将演示应用程序安装到我的系统上,我在此示例中设置了 DESTDIR
变量,以将安装目标重定向到 /tmp
中的子目录
$ mkdir /tmp/dist-hello
$ make install DESTDIR=/tmp/dist-hello
[100%] Built target Hello
Install the project...
-- Install configuration: ""
-- Installing: /tmp/dist-hello/usr/local/bin/Hello
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0/assets/file0
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0/assets/file1
输出确认了其操作,并且应用程序已安装。
快速自定义
CMake 的安装前缀(CMAKE_INSTALL_PREFIX
变量)默认为 /usr/local
,但是当您使用 -D
选项运行 cmake
时,可以自定义任何 CMake 变量
$ cmake -DCMAKE_INSTALL_PREFIX=/usr ..
$ make install DESTDIR=/tmp/dist-hello
$ make install DESTDIR=/tmp/dist-hello
[100%] Built target Hello
Install the project...
-- Install configuration: ""
-- Installing: /tmp/dist-hello/usr/bin/Hello
-- Installing: /tmp/dist-hello/usr/share/Hello-1.0
-- Installing: /tmp/dist-hello/usr/share/Hello-1.0/assets/file0
-- Installing: /tmp/dist-hello/usr/share/Hello-1.0/assets/file1
CMake 使用的任何变量都可以通过这种方式自定义。
交互式 CMake
CMake 的交互模式是一种友好且有用的方法来配置安装环境。要求您的用户了解您的项目使用的所有可能的 CMake 变量有点过分,因此 CMake 交互式界面是他们发现自定义选项而无需查看 Makefiles 和 CMakeLists 的简便方法。
要调用交互式 CMake 会话,请使用 ccmake
命令。对于这个简单的示例项目,没有什么可看的,但是像数字音频工作站 Rosegarden 这样的大型项目使用户界面变得非常宝贵。

(Seth Kenlon, CC BY-SA 4.0)
更多 CMake
CMake 还有更多内容。作为一名开发人员,我喜欢 CMake 的简单语法和广泛的 文档、可扩展性和便捷性。作为一名用户,我欣赏 CMake 的友好且有用的错误消息和用户界面。如果您没有为您的项目使用构建系统,请了解一下 CMake。您以及任何稍后尝试打包您的应用程序的人都不会后悔。
3 条评论