CMake 第一课:什么是 CMake,为什么你需要它?
一、前言
软件开发的世界里,代码只是第一步。如何将这些代码转换成可执行的程序,就需要构建工具的帮助。今天,我们要一起认识一个非常重要的工具 —— CMake。
前面的一篇【不知道怎么学 CMake ?这份精炼的学习路线拯救你!】文章详细介绍了CMake的学习路线图。说明了如何从基础概念入手,逐步掌握高级技巧,并结合实践不断探索。你可能会疑惑,什么是 CMake?为什么我们要学习它? 简单来说,CMake 就像是一位优秀的“项目经理”,它负责管理你的项目构建过程,让你的代码能够顺利地在各种不同的环境中编译和运行。
这篇文章开始 CMake 的第一课:什么是 CMake?为什么需要 CMake?以及如何下载安装 CMake?
CMake 已经成为现代软件开发中不可或缺的一部分,特别是在 C++、C 等需要编译型语言的开发中,掌握 CMake 可以说是你进阶的必经之路。如果你正在学习或者使用 C++ 开发,那么本系列教程更是为你量身定做的,因为现代 C++ 项目几乎都离不开 CMake。
二、什么是 CMake?
CMake 的定义:CMake(Cross-platform Make)是一个开源的、跨平台的构建系统生成工具。可以理解为一位建筑师要建造一栋房子,不会直接拿起砖头开始堆砌,而是会先画出设计图纸,明确房子的结构和使用的材料;CMake 就类似于这个设计图纸,只不过它是为软件项目服务的。
CMake 不是一个编译器,也不是一个直接执行构建的工具。 你可以把它看作是一个“构建系统生成器”。即 CMake 的主要任务是读取项目描述(一个名为 CMakeLists.txt
的文件),然后根据描述生成特定平台所需要的构建系统文件。比如在 Linux/macOS 上的 Makefile
文件。
所以,CMake 本身不负责编译你的代码。 它就像一个“翻译器”,将项目描述翻译成不同平台能够理解的“语言”,然后让这些平台上的构建工具(比如 make
、Visual Studio 等)真正执行编译、链接等操作。
更重要的是,CMake 是跨平台的! 所以,无论是在 Windows、macOS、Linux 还是其他平台上开发,只需要编写一份 CMakeLists.txt
文件,CMake 就可以生成适用于各种平台的构建系统,从而避免了为每个平台编写不同构建脚本的麻烦。
已经了解了 CMake 是什么,接下来看看它是如何工作的。
CMake 的工作流程可以简化为以下几个步骤:
-
读取
CMakeLists.txt
文件。 -
根据
CMakeLists.txt
文件中的描述,分析项目结构和依赖关系。在这个阶段,CMake 会检测系统环境、编译器信息以及其他必要的配置。会生成一个内部的“项目模型”,这个模型包含了项目的所有必要信息。 -
CMake 根据之前生成的“项目模型”,选择合适的构建系统生成器(比如
Makefile
生成器,或者 Visual Studio 项目文件生成器),将项目模型转换为特定平台的构建系统文件。 -
使用对应的构建工具(例如
make
、Visual Studio)来实际编译代码。构建工具会读取 CMake 生成的文件,执行编译、链接等操作,最终生成可执行程序或库。
除了 CMake, 还存在着许多不同的构建工具,都致力于解决同一个问题:如何将源代码转化为可执行程序或库。比如 make
、Automake
和 SCons
等。
-
make
: 一个非常基础的构建工具,它读取Makefile
文件来执行构建操作。虽然make
很强大,但编写Makefile
文件需要一定的经验和技巧,而且不同的平台要不同的Makefile
文件。make
的缺点在于可移植性较差,对于大型项目,维护Makefile
文件会变得非常复杂。 -
Automake
: 旨在简化Makefile
的编写,但它需要与Autoconf
配合使用。Autoconf
会检测系统环境,并生成适合当前平台的Makefile
文件。Automake
和Autoconf
比较复杂,学习曲线较陡峭,而且生成的文件往往也比较庞大。 -
SCons
: 一个使用 Python 编写的构建工具,它可以自动检测依赖关系,并且相对容易配置。问题在于它需要安装 Python 环境,对于一些简单的项目来说,显得有些重型。
CMake 的优势:
-
CMake 的重点是“生成构建系统”而不是执行构建。它读取
CMakeLists.txt
文件,然后生成适用于不同平台的构建系统文件(例如Makefile
、Visual Studio 项目文件等)。因此,只需维护一个CMakeLists.txt
文件,而无需关心不同平台的构建细节。这与make
和Automake
的手动编写构建文件的方式有着根本的区别。 -
CMake 的跨平台能力非常强大。它可以在各种不同的操作系统(如 Linux、macOS、Windows)和编译器之间工作,只需一份
CMakeLists.txt
文件,就可以生成适用于不同平台的构建系统,而不需要修改构建脚本。这使得代码的移植和部署变得更加简单和高效。 -
相比
Automake
复杂的配置和make
的手动编写,CMake 的CMakeLists.txt
文件更加简洁和易于理解,学习曲线相对平缓,更容易上手。 -
CMake 提供了丰富的模块和功能,可以方便地管理依赖关系、测试框架、代码生成等任务。
许多初学者在接触 CMake 时,可能会有一些常见的误解。这里再次强调一下(虽然啰嗦了很多遍,但还是要再澄清一下):
-
CMake 不是编译器: CMake 本身不负责编译你的源代码。它只是一个构建系统生成器,它的作用是生成构建文件(如
Makefile
),然后由真正的编译器(如gcc
、clang
、Visual Studio 编译器等)来执行编译操作。 -
CMake 不会直接编译代码: 如上所述,CMake 只负责生成构建系统,实际的编译工作是由构建工具(如
make
、Visual Studio)和编译器完成的。 -
CMake 不是一个一体化的构建工具: CMake 的目标不是替代构建工具,而是更好地利用它们。它通过生成不同平台的构建文件,使得开发者可以在统一的环境下使用不同的构建工具。
-
CMakeLists.txt
文件不是脚本:CMakeLists.txt
文件描述的是项目的构建信息,它使用自己的语法,不是 shell 脚本或者其他编程语言的脚本。
三、为什么需要 CMake?
在软件开发过程中,经常会遇到一个棘手的问题:如何保证我们的代码可以在不同的操作系统、不同的编译器下都能正常构建? 这正是 CMake 发挥作用的地方。
一个跨平台的项目,需要在 Linux、macOS 和 Windows 等多个操作系统上运行。如果使用传统的构建方式,要为每个平台编写不同的构建脚本:
- Linux: 要编写
Makefile
文件,使用gcc
或clang
编译器。 - macOS: 要一个
Makefile
文件,使用clang
编译器。 - Windows: 要创建 Visual Studio 项目文件,使用 Visual Studio 编译器。
这是非常繁琐且容易出错的。不仅需要花费大量时间编写和维护这些构建脚本,还需要熟悉不同平台的构建方式和工具链。而且,即使编写了这些脚本,它们仍然可能存在一些细微的差异,导致在某些平台上构建失败。
更糟糕的是,如果项目依赖于第三方库,还要考虑如何在不同平台上正确地链接这些库。这更加复杂化了。
而 CMake 恰好是为了解决这些问题而生的:
-
只需要编写一个
CMakeLists.txt
文件,其中描述了项目的结构、依赖关系以及编译选项。这个文件是平台无关的,可以在所有支持 CMake 的平台上使用。 -
CMake 会读取
CMakeLists.txt
文件,然后根据当前所在的操作系统和编译器,自动生成相应的构建系统文件,如Makefile
(Linux/macOS) 或 Visual Studio 项目文件 (Windows)。 -
CMake 确保在不同平台上生成的构建文件具有一致的结构和行为,从而大大减少了构建过程中的错误。
-
CMake 提供了强大的依赖管理功能,可以轻松地找到并链接第三方库,无论它们在哪个平台上。在大型项目中,降低了管理依赖库的复杂性。
使用 CMake 后,不再需要手动编写和维护不同平台的构建脚本。可以专注于编写代码,而不用过多地操心构建细节。CMake 会自动处理这些繁琐的任务。
CMake 不仅解决跨平台构建的难题,还是一个强大的项目管理工具。 大型软件项目通常会依赖于许多第三方库、模块或子项目。CMake 通过一系列强大的功能来简化依赖管理:
-
find_package
命令:find_package
命令是 CMake 中最核心的依赖管理工具。它可以自动查找第三方库,并设置相应的编译和链接选项。 -
模块化管理: CMake 可以轻松地管理复杂的项目结构,包括子模块和子项目。
add_subdirectory
命令将子项目包含到主项目中,并使用target_link_libraries
命令将不同模块链接在一起。 -
CMake 提供了许多预定义的模块,用于查找和集成常见的第三方库,如
OpenSSL
、ZLIB
、Qt
等。 -
CMake 还提供
FetchContent
模块,用于直接从 URL 下载并构建依赖,简化了第三方库的集成流程。
一个工具的强大程度不仅取决于其功能,还取决于其背后的社区支持和应用范围。CMake 在这两方面都表现出色:
- CMake 拥有一个庞大而活跃的社区,提供丰富的官方文档、教程、示例代码。
- CMake 被广泛应用于各种类型的项目中,许多著名的开源项目都采用 CMake 作为其构建系统,如 LLVM、Qt、OpenCV、Boost 等。
四、下载安装 CMake
4.1、Linux 安装 CMake
大多数 Linux 发行版都提供了 CMake 的包管理器。可以使用以下命令安装 CMake:
# Debian/Ubuntu:
sudo apt update && sudo apt install cmake
# 或者 升级 CMake
sudo apt upgrade cmake
# Fedora:
sudo dnf install cmake
# 或者 升级 CMake
sudo dnf update cmake
# CentOS/RHEL:
sudo yum install cmake
# 或者 升级 CMake
sudo yum update cmake
# Arch Linux:
sudo pacman -S cmake
# 或者 升级 CMake
sudo pacman -Syu cmake
但是,在一些发行版中,CMake 版本可能比较旧,因此下载由Kitware提供的二进制文件就变成了首选,CMake 官网下载页面 https://cmake.org/download/。下面提供一个X86 64 bit系统自动下载对应CMake版本的shell脚本:
cmake_version="3.22.1"
target_path=$HOME/workspace/cmake/${cmake_version}
cmake_url="https://cmake.org/files/v${cmake_version%.*}/cmake-${cmake_version}-Linux-x86_64.tar.gz"
mkdir -p "${target_path}"
curl -Ls "${cmake_url}" | tar -xz -C "${target_path}" --strip-components=1
export PATH=$HOME/workspace/cmake/${cmake_version}/bin${PATH:+:$PATH}
cmake --version
如果你的 Linux 发行版也支持通过 snap
或 Flatpak
安装 CMake。可以使用 sudo snap install cmake --classic
命令安装CMake。
4.2、Windows 安装 CMake
访问 CMake 官网下载页面,在下载页面,会看到不同平台的安装包,下载 Windows Installer
版本(一个 .msi
文件)。选择与Windows 系统架构 (32 位 或 64 位) 相匹配的版本。推荐下载最新版本的 CMake,以确保拥有最新的特性和修复的 bug。
下载完成后,双击 .msi
文件运行安装程序。安装过程比较简单,按照安装程序的提示一直进行“next”操作即可。在安装过程中,安装程序询问是否将 CMake 添加到系统环境变量中。虽然是可选项,但是强烈建议勾选此选项。 如果没有勾选此选项,后面就要手动将 CMake 的安装路径添加到系统的 Path
环境变量中。添加系统环境变量后,就可以在命令行 (CMD 或 PowerShell) 中直接使用 cmake
命令。
在命令提示符 (CMD) 或 PowerShell 输入 cmake --version
并按下回车验证安装是否成功, 如果 CMake 安装成功,会看到 CMake 的版本信息。
除了前面说的直接下载安装包,Windows 上安装 CMake 还有一些更“高级”的玩法。
Visual Studio “自带”的 CMake: 如果用的是 Visual Studio 2017 或者更新的版本,那就方便了!Visual Studio 已经内置了对 CMake 的支持,都不需要单独安装 CMake 了,在安装Visual Studio 2017时 组件中勾选"用于Windows的c++ CMake工具"。
MSYS2,一个“伪 Linux” 的开发环境: MSYS2 就是一个在 Windows 上模拟 Linux 环境的工具,有类似 Linux 终端的命令行界面,还有强大的包管理器 pacman
。可以去 https://www.msys2.org 下载 MSYS2 的安装程序,然后按照官网的说明进行安装。然后使用包管理器pacman
安装 CMake。
安装 64位版本:
$ pacman -S mingw64/mingw-w64-x86_64-cmake
安装 32位版本:
$ pacman -S mingw64/mingw-w64-i686-cmake
4.3、macOS 安装 CMake
同样的,先访问 CMake 官网下载页面。在下载页面,选择 macOS
对应的 .dmg
文件。下载完成后,双击 .dmg
文件,然后将 CMake.app
拖放到 Applications
文件夹中。这就完成了 CMake 的安装。
或者直接获取最新的CMake版本:
$ brew upgrade cmake
为了能够在终端中使用 cmake
命令,需要创建一个软链接。
sudo "/Applications/CMake.app/Contents/bin/cmake" --install-prefix=/usr/local
或者 创建一个软链接到 /usr/local/bin:
sudo ln -s /Applications/CMake.app/Contents/bin/cmake /usr/local/bin/cmake
sudo ln -s /Applications/CMake.app/Contents/bin/cpack /usr/local/bin/cpack
sudo ln -s /Applications/CMake.app/Contents/bin/ctest /usr/local/bin/ctest
五、编译器
在 GNU/Linux 环境下,GNU 编译器集合 (GCC) 无疑是最为普遍和直接的选择。它不仅是自由开源软件,而且被几乎所有的 Linux 发行版所原生支持。GCC 包含了 C、C++、Fortran 等多种编程语言的编译器,可以满足绝大多数开发需求。
安装 GCC (以 Ubuntu 为例):
sudo apt-get update # 更新软件包列表 (强烈推荐)
sudo apt-get install g++ gcc
除了 GCC 之外,LLVM 项目下的 Clang 也是一个非常优秀的 C/C++ 编译器。 Clang 旨在提供更高的编译速度和更好的错误诊断信息。
sudo apt-get install clang clang++
在 macOS 上,苹果公司提供的 Xcode 开发工具包中已经包含了 LLVM 编译器套件。这个套件包含了 C 和 C++ 编译器(Clang,它与 Linux 系统上的 Clang 相同),可以直接用于 C 和 C++ 项目的编译。无需单独安装 LLVM 编译器。
在 Windows 上,通常有两种主要的编译器选择:
-
Visual Studio: 微软的 Visual Studio 提供了完善的开发环境,包括 C 和 C++ 编译器。Visual Studio 是商业软件,但社区版是免费的。
-
MSYS2/MinGW-w64: MSYS2 是一个为 Windows 提供类 Unix 环境的软件发行版。它包含了一个软件包管理器
pacman
,可以用于安装各种工具链,包括 GCC 和 Clang。MinGW-w64 是 MSYS2 提供的一个 GCC 编译套件,它为 Windows 提供了原生编译环境。
“交叉编译” 是一种重要的技术。它指的是在一个平台上(例如你的开发机)编译代码,然后在另一个不同的平台上(例如嵌入式设备、其他操作系统)运行。 为了实现这一点,需要使用专门的“交叉编译器”,而非普通的编译器。
对于类 Debian/Ubuntu 系统,使用 apt
包管理器来安装交叉编译器。
sudo apt-get update # 强烈建议先更新软件包列表
sudo apt-get install gcc-mingw-w64 g++-mingw-w64
安装了交叉编译器,就可以在 Linux 系统上编译出可在 Windows 上运行的可执行文件(例如 .exe
文件)。
在 macOS 上,使用 Homebrew 包管理器来安装针对 Windows 平台的交叉编译工具链:
brew install mingw-w64
除了使用包管理器直接安装交叉编译器外,另一种方法是使用 MXE (M Cross Environment),MXE 的官方网站 (https://mxe.cc) 。
六、总结
CMake 作为一款强大的构建工具,在 C/C++ 开发中扮演着重要的角色。它解决了跨平台构建的难题,提供了强大的功能和灵活性,但也存在一定的学习曲线和配置复杂性。如果你正在进行 C/C++ 项目的开发,尤其是跨平台项目,那么掌握 CMake 绝对是一项值得投资的技能。
在下一篇文章中,我们将开始学习如何编写第一个 CMakeLists.txt
文件,并使用 CMake 来构建项目。