<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tools on kenji.blog</title><link>http://kenji.blog/zh-cn/categories/tools/</link><description>Recent content in Tools on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/zh-cn/categories/tools/index.xml" rel="self" type="application/rss+xml"/><item><title>使用 CMake 构建跨平台 C++ 编译环境的步骤</title><link>http://kenji.blog/zh-cn/p/cmake-cross-platform-build-environment-guide/</link><pubDate>Sat, 12 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/cmake-cross-platform-build-environment-guide/</guid><description>&lt;img src="http://kenji.blog/p/cmake-cross-platform-build-environment-guide/img/eyecatch.jpg" alt="Featured image of post 使用 CMake 构建跨平台 C++ 编译环境的步骤" />&lt;p>在 C++ 软件开发中，长久以来困扰许多开发者的一个问题就是“构建系统”的选择与搭建。由于 C++ 没有官方的标准包管理器和构建系统，因此需要根据不同平台（Windows、Linux、macOS）分别使用不同的编译器和构建工具（如 MSVC、GCC、Clang、Make、Ninja 等）。&lt;/p>
&lt;p>然而，目前 &lt;strong>CMake&lt;/strong> 已经成为事实上的行业标准。通过正确使用 CMake，我们可以仅凭单一的 &lt;code>CMakeLists.txt&lt;/code> 优雅地构建出跨平台的编译环境。&lt;/p>
&lt;p>本文将针对使用 CMake 搭建最新（现代 CMake）跨平台 C++ 编译环境的步骤，从基础到高级技巧，进行全面且详细的讲解。&lt;/p>
&lt;h2 id="1-cmake-是什么元构建系统的概念">1. CMake 是什么？（元构建系统的概念）
&lt;/h2>&lt;p>CMake 本身并不是直接编译源代码的工具。CMake 是一个“生成构建系统的系统”，即 &lt;strong>元构建系统 (Meta-Build System)&lt;/strong>。&lt;/p>
&lt;p>CMake 的主要作用是读取与平台和编译器无关的抽象配置文件（&lt;code>CMakeLists.txt&lt;/code>），并自动生成最适合各自环境的原生构建脚本（例如：Linux 下的 &lt;code>Makefile&lt;/code>、Windows 下的 Visual Studio &lt;code>.sln&lt;/code> 项目文件，或者是高速的 &lt;code>build.ninja&lt;/code>）。&lt;/p>
&lt;p>下图展示了 CMake 的生成过程。&lt;/p>
&lt;div class="mermaid">graph TD
A["CMakeLists.txt (抽象构建定义)"] --> B["CMake (配置与生成)"]
B --> C["Unix Makefiles"]
B --> D["Ninja 构建文件"]
B --> E["Visual Studio 解决方案"]
B --> F["Xcode 项目"]
C --> G["原生构建工具 (make, ninja, MSBuild, xcodebuild)"]
D --> G
E --> G
F --> G
G --> H["可执行文件 / 共享库 / 静态库"]&lt;/div>
&lt;p>像这样，通过在中间引入 CMake，开发者就可以在无需关注各个操作系统之间细微命令差异的情况下，对 C++ 项目进行管理。&lt;/p>
&lt;h2 id="2-现代-cmake-的基础从变量到目标">2. 现代 CMake 的基础：从变量到目标
&lt;/h2>&lt;p>CMake 3.0 之后的写法被称为“现代 CMake（Modern CMake）”，其设计理念与之前（传统 CMake）有着根本的不同。在传统的 CMake 中，主流做法是逐个目录地修改全局变量（例如使用 &lt;code>include_directories()&lt;/code> 或 &lt;code>link_libraries()&lt;/code>），但这往往会导致设置在无意中波及到其他模块，从而引发严重的副作用。&lt;/p>
&lt;p>在现代 CMake 中，一切都作为 &lt;strong>目标 (Target)&lt;/strong> 和 &lt;strong>属性 (Property)&lt;/strong> 来处理。这类似于面向对象编程中类与成员变量的关系。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>目标&lt;/strong>: 可执行文件（Executable）或库（Library）。&lt;/li>
&lt;li>&lt;strong>属性&lt;/strong>: 编译该目标所需的源文件、包含目录、编译选项、要链接的其他库等。&lt;/li>
&lt;/ul>
&lt;p>通过将设置仅封装（隔离）在特定的目标中，即使是大型项目也能实现安全且不会崩溃的构建定义。&lt;/p>
&lt;h3 id="极简的-cmakeliststxt">极简的 &lt;code>CMakeLists.txt&lt;/code>
&lt;/h3>&lt;p>首先，让我们来看一个最基础的 &lt;code>CMakeLists.txt&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 指定 CMake 的最低要求版本
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.20&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 指定项目名称和使用的语言
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyAwesomeApp&lt;/span> &lt;span class="s">VERSION&lt;/span> &lt;span class="s">1.0.0&lt;/span> &lt;span class="s">LANGUAGES&lt;/span> &lt;span class="s">CXX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 要求 C++ 标准（C++20）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">20&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD_REQUIRED&lt;/span> &lt;span class="s">ON&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_EXTENSIONS&lt;/span> &lt;span class="s">OFF&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c"># 禁用编译器特有的扩展功能
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 定义可执行目标
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyAwesomeApp&lt;/span> &lt;span class="s">main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>仅仅这几行代码，就完成了要求 C++20 且禁用编译器扩展的可移植执行文件的构建设置。&lt;/p>
&lt;h2 id="3-依赖关系与作用域-public--private--interface">3. 依赖关系与作用域: PUBLIC / PRIVATE / INTERFACE
&lt;/h2>&lt;p>要掌握现代 CMake，最重要也最难理解的就是 &lt;code>target_include_directories&lt;/code> 和 &lt;code>target_link_libraries&lt;/code> 等命令中使用的 &lt;strong>&lt;code>PUBLIC&lt;/code>, &lt;code>PRIVATE&lt;/code>, &lt;code>INTERFACE&lt;/code>&lt;/strong> 这三个访问修饰符（作用域）的概念。&lt;/p>
&lt;p>它们用于控制目标的属性（包含路径或依赖库）：“是否是自己构建所必需的？”以及“是否需要传递给依赖自己的其他目标？”。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>&lt;code>PRIVATE&lt;/code>&lt;/strong>: 仅在构建该目标自身时需要。&lt;strong>不会&lt;/strong>传递给依赖它的目标。&lt;/li>
&lt;li>&lt;strong>&lt;code>INTERFACE&lt;/code>&lt;/strong>: 构建该目标自身时不需要，但&lt;strong>会&lt;/strong>传递给依赖它的目标的构建过程中（常用于 Header-only 库等）。&lt;/li>
&lt;li>&lt;strong>&lt;code>PUBLIC&lt;/code>&lt;/strong>: 构建该目标自身时需要，并且&lt;strong>会&lt;/strong>传递给依赖它的目标（&lt;code>PRIVATE&lt;/code> + &lt;code>INTERFACE&lt;/code>）。&lt;/li>
&lt;/ol>
&lt;p>通过下图，我们可以将依赖关系的传递（Usage Requirements 的传递）可视化。&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Libraries"
MathLib["MathLib (静态库)"]
NetworkLib["NetworkLib (共享库)"]
HeaderLib["HeaderLib (Header Only)"]
end
subgraph "Application"
App["主应用程序"]
end
App -- "target_link_libraries(App PRIVATE MathLib)" --> MathLib
App -- "target_link_libraries(App PUBLIC NetworkLib)" --> NetworkLib
NetworkLib -- "target_link_libraries(NetworkLib INTERFACE HeaderLib)" --> HeaderLib
note1["App 依赖于 MathLib 的实现，但不向外暴露"]
note2["NetworkLib 公开 HeaderLib 的接口"]&lt;/div>
&lt;h3 id="作用域的具体使用示例">作用域的具体使用示例
&lt;/h3>&lt;p>假设某库 &lt;code>MyLib&lt;/code> 在内部实现中使用了 &lt;code>nlohmann/json&lt;/code>，且在其公开的头文件 &lt;code>MyLib.hpp&lt;/code> 中并没有包含 &lt;code>nlohmann/json&lt;/code>。在这种情况下，使用 &lt;code>MyLib&lt;/code> 的一方（应用程序）无需知道 JSON 库的存在。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 库的定义
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyLib&lt;/span> &lt;span class="s">src/MyLib.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 指定本项目的包含目录
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># include 目录对于使用 MyLib 的人来说也是必需的，因此设为 PUBLIC
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># src 目录仅在 MyLib 的实现中使用，因此设为 PRIVATE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">target_include_directories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyLib&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">PUBLIC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">$&amp;lt;&lt;/span>&lt;span class="nv">BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">$&amp;lt;&lt;/span>&lt;span class="nv">INSTALL_INTERFACE:include&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">PRIVATE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_CURRENT_SOURCE_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/src&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># json 库仅用于内部实现，因此以 PRIVATE 进行链接
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyLib&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">nlohmann_json::nlohmann_json&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>反之，如果在 &lt;code>MyLib.hpp&lt;/code> 中写了 &lt;code>#include &amp;lt;nlohmann/json.hpp&amp;gt;&lt;/code>，那么使用 &lt;code>MyLib&lt;/code> 的一方如果不知道 JSON 的头文件路径就会发生编译错误，因此必须以 &lt;code>PUBLIC&lt;/code> 进行链接。通过恰当地设置这种作用域，可以缩短编译时间并防止不必要的依赖泄漏。&lt;/p>
&lt;h2 id="4-源码外构建-out-of-source-build">4. 源码外构建 (Out-of-source Build)
&lt;/h2>&lt;p>在使用 CMake 时，务必遵守的最佳实践就是 &lt;strong>源码外构建 (Out-of-source Build)&lt;/strong>。
这种方法是指完全不在存放源代码的目录（源码树）中输出任何构建产物（目标文件或可执行文件），而是将其分离到另一个专用目录（通常是 &lt;code>build/&lt;/code>）中进行构建。&lt;/p>
&lt;div class="mermaid">graph TD
Root["项目根目录 (Git 仓库)"]
Root --> Src["src/"]
Root --> Inc["include/"]
Root --> CMake["CMakeLists.txt"]
Root -. "创建构建目录" .-> Build["build/ (源码外)"]
Build --> Obj["CMakeFiles/ (目标文件，缓存)"]
Build --> Bin["二进制文件 (MyApp.exe)"]
Build --> Gen["生成的 Makefile / build.ninja"]&lt;/div>
&lt;p>采用这种结构，当你想重置构建环境时，只需直接删除整个 &lt;code>build&lt;/code> 目录即可；由于不会污染源码树，Git 管理也变得轻松（只需在 &lt;code>.gitignore&lt;/code> 中添加 &lt;code>build/&lt;/code> 即可）。&lt;/p>
&lt;h3 id="执行构建的步骤">执行构建的步骤
&lt;/h3>&lt;p>在现代 CMake 中，可以使用不依赖于操作系统或构建工具的通用命令来执行构建。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 配置与生成（创建构建目录并进行配置）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake -S . -B build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 实际的构建（编译与链接）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build build --config Release
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># (可选) 若要进行多线程构建，请使用 -j 选项&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build build --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里的 &lt;code>cmake -S . -B build&lt;/code> 的意思是“将当前目录（&lt;code>.&lt;/code>）作为源码目录，并将 &lt;code>build&lt;/code> 设为构建目录”。&lt;/p>
&lt;h2 id="5-引入第三方库的方法">5. 引入第三方库的方法
&lt;/h2>&lt;p>在 C++ 开发中，引入外部库（第三方库）一直以来门槛都很高。不过现在，以下三种方法已成为标准做法。&lt;/p>
&lt;h3 id="51-find_package-查找系统已安装的库">5.1. find_package (查找系统已安装的库)
&lt;/h3>&lt;p>这是最传统的方法，用于查找并链接系统中已经安装好的库（如 OpenSSL 或 Zlib 等）。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">find_package&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ZLIB&lt;/span> &lt;span class="s">REQUIRED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ZLIB_FOUND&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyAwesomeApp&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">ZLIB::ZLIB&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-fetchcontent-从源码下载并集成">5.2. FetchContent (从源码下载并集成)
&lt;/h3>&lt;p>这是 CMake 3.11 中引入，并在 3.14 之后变得非常强大的模块。在构建时，它会直接从外部的 Git 仓库或 URL 下载源码，并作为项目的一部分一起进行编译。由于能集中管理依赖关系，因此在跨平台上的可重复性极高。&lt;/p>
&lt;p>以下是使用 FetchContent 引入 GoogleTest 的示例。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">FetchContent&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">FetchContent_Declare&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">googletest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">GIT_REPOSITORY&lt;/span> &lt;span class="s">https://github.com/google/googletest.git&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">GIT_TAG&lt;/span> &lt;span class="s">v1.14.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 将库引入项目中
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">FetchContent_MakeAvailable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">googletest&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 创建并链接测试用执行文件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyTests&lt;/span> &lt;span class="s">test/main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyTests&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">gtest_main&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="53-结合-vcpkg-使用">5.3. 结合 vcpkg 使用
&lt;/h3>&lt;p>使用由微软主导的 C++ 包管理器 &lt;strong>vcpkg&lt;/strong>，可以轻松引入成千上万的库。vcpkg 的设计初衷就是为了与 CMake 实现无缝集成。&lt;/p>
&lt;p>在运行 CMake 时，只需指定 vcpkg 的工具链文件，&lt;code>find_package&lt;/code> 就会自动在 vcpkg 内搜索库。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE&lt;span class="o">=&lt;/span>/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>此外，通过在项目根目录放置 &lt;code>vcpkg.json&lt;/code>（清单模式，Manifest mode），可以完全自动化所需库的版本管理。&lt;/p>
&lt;h2 id="6-跨平台的编译器标志">6. 跨平台的编译器标志
&lt;/h2>&lt;p>为了在 Windows (MSVC)、Linux (GCC/Clang) 或是 macOS (Apple Clang) 任何环境中都能顺利编译，必须恰当地设置编译器特定的标志。&lt;/p>
&lt;p>通过使用 CMake 的 &lt;strong>生成器表达式 (Generator Expressions)&lt;/strong>，可以声明式地编写条件分支，例如“如果编译器是 MSVC 则用这个标志，否则用那个标志”。生成器表达式使用 &lt;code>$&amp;lt;...&amp;gt;&lt;/code> 语法，在构建系统的生成阶段（Generate 阶段）进行计算评估。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 在所有平台上开启最高级别警告的示例
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">target_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyAwesomeApp&lt;/span> &lt;span class="s">PRIVATE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c"># 对于 MSVC
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="o">$&amp;lt;&lt;/span>&lt;span class="nv">$&amp;lt;CXX_COMPILER_ID:MSVC&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="s">:/W4&lt;/span> &lt;span class="s">/WX&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c"># 对于 GCC 或 Clang
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="o">$&amp;lt;&lt;/span>&lt;span class="nv">$&amp;lt;OR:$&amp;lt;CXX_COMPILER_ID:GNU&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="s">,&lt;/span>&lt;span class="o">$&amp;lt;&lt;/span>&lt;span class="nv">CXX_COMPILER_ID:Clang&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="s">,&lt;/span>&lt;span class="o">$&amp;lt;&lt;/span>&lt;span class="nv">CXX_COMPILER_ID:AppleClang&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="s">&amp;gt;:-Wall&lt;/span> &lt;span class="s">-Wextra&lt;/span> &lt;span class="s">-Wpedantic&lt;/span> &lt;span class="s">-Werror&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>使用这种方法，可以避免因大量使用 &lt;code>if(MSVC)&lt;/code> 类的条件分支而导致 &lt;code>CMakeLists.txt&lt;/code> 难以阅读的问题，并能为每个目标提供灵活的设置。&lt;/p>
&lt;h2 id="7-搭建测试环境-ctest">7. 搭建测试环境 (CTest)
&lt;/h2>&lt;p>在跨平台环境中的质量保证方面，引入自动化测试是必不可少的。CMake 标准自带了一个名为 &lt;strong>CTest&lt;/strong> 的测试运行器。&lt;/p>
&lt;p>将前文提到的通过 &lt;code>FetchContent&lt;/code> 引入的 GoogleTest 与 CTest 集成的步骤如下。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 启用测试功能（只需在根 CMakeLists.txt 中编写一次）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">enable_testing&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyMathTests&lt;/span> &lt;span class="s">test/math_test.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyMathTests&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">gtest_main&lt;/span> &lt;span class="s">MyLib&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 注册为 CTest 测试
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">GoogleTest&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">gtest_discover_tests&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyMathTests&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>构建完成后，只需在构建目录内执行 &lt;code>ctest&lt;/code> 命令，即可运行所有测试并报告结果。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ctest --output-on-failure -C Release
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="8-构建系统的理论与数学模型">8. 构建系统的理论与数学模型
&lt;/h2>&lt;p>在此我们稍微转换一下视角，使用数学模型来探讨大型项目中的构建系统与并行编译的效率问题。&lt;/p>
&lt;p>缩短构建（编译）时间是 C++ 开发中永恒的课题。通过对源码进行拆分和并行编译，可以有效缩短构建时间。这种因并行化带来的速度提升（Speedup），可以通过 &lt;strong>阿姆达尔定律 (Amdahl&amp;rsquo;s Law)&lt;/strong> 来进行建模。&lt;/p>
&lt;p>假设在程序中，可并行化的部分占比为 $P$，必须串行执行（不可并行化）的部分占比为 $1-P$，使用的处理器数量为 $N$，那么整体理论上的最大速度提升率 $S(N)$ 可由以下公式表示：&lt;/p>
$$ S(N) = \frac{1}{(1 - P) + \frac{P}{N}} $$
&lt;p>在 C++ 的构建过程中，“从各个 &lt;code>.cpp&lt;/code> 文件编译成 &lt;code>.o&lt;/code> 或 &lt;code>.obj&lt;/code>”的过程是独立且可并行的（即 $P$ 的部分），而“最终的链接器 (Linker) 合并处理”则基本上是串行执行的（即 $1-P$ 的部分）。&lt;/p>
&lt;p>因此，无论准备了多少核心的 CPU（$N \to \infty$），只要链接时间这个瓶颈依然存在，最大速度提升率最终都会渐近于以下公式：&lt;/p>
$$ \lim_{N \to \infty} S(N) = \frac{1}{1 - P} $$
&lt;p>这个公式启示我们：“单纯地增加 CPU 核心数对于缩短构建时间是有限度的”。在现代 CMake 中，恰当地区分使用 &lt;code>PRIVATE&lt;/code> 和 &lt;code>INTERFACE&lt;/code>，并尽量减少头文件的依赖关系（如善用前置声明等），从而增大 $P$ 的比例，减少增量构建时需要重新编译的对象，才是实际上最有效的构建加速策略。&lt;/p>
&lt;p>另外，在缩短链接时间方面，从静态库（Static Library）切换为共享库 / DLL（Shared Library），或者采用 LLD / Mold 等高速链接器，也是非常重要的。&lt;/p>
&lt;p>在 CMake 中，可以非常简单地指定链接器：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 在 Clang/GCC 环境下设置使用 lld 链接器
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">UNIX&lt;/span> &lt;span class="s">AND&lt;/span> &lt;span class="s">NOT&lt;/span> &lt;span class="s">APPLE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyAwesomeApp&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s2">&amp;#34;-fuse-ld=lld&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="9-复杂目录结构的实践案例">9. 复杂目录结构的实践案例
&lt;/h2>&lt;p>在实际的应用程序开发中，往往是由许多模块组合而成的目录结构。最后，我们将展示一个理想的中型项目的目录结构，以及父子 &lt;code>CMakeLists.txt&lt;/code> 之间的关系。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">ProjectRoot/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── CMakeLists.txt (根目录: 整个项目的定义)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── vcpkg.json (依赖库的定义)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── external/ (外部模块)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── include/ (公开头文件)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── myapp/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── src/ (源代码与内部构建定义)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── CMakeLists.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── main.cpp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── math/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ ├── CMakeLists.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ ├── Vector3.hpp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ └── Vector3.cpp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── network/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── CMakeLists.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── NetworkManager.cpp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── tests/ (测试代码)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── CMakeLists.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── math_test.cpp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>根目录下的 &lt;code>CMakeLists.txt&lt;/code> 仅进行环境设置和全局选项定义，随后通过 &lt;code>add_subdirectory()&lt;/code> 添加子目录。&lt;/p>
&lt;p>&lt;strong>根目录 &lt;code>CMakeLists.txt&lt;/code>&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.20&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ComplexApp&lt;/span> &lt;span class="s">LANGUAGES&lt;/span> &lt;span class="s">CXX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 全局设置
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">20&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD_REQUIRED&lt;/span> &lt;span class="s">ON&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 启用测试
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">enable_testing&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 添加子目录
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_subdirectory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">src&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_subdirectory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">tests&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>&lt;code>src/CMakeLists.txt&lt;/code>&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 添加各个模块
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_subdirectory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">math&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_subdirectory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">network&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 最终的可执行文件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ComplexApp&lt;/span> &lt;span class="s">main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 模块链接
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ComplexApp&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">PRIVATE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">MathLib&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">NetworkLib&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>像这样按目录拆分 &lt;code>CMakeLists.txt&lt;/code>，并以目标间依赖关系的形式进行定义，可以大幅提高模块的复用性，并提升构建的并行度。这正是现代 CMake 所倡导的“模块化构建环境”的精髓所在。&lt;/p>
&lt;h2 id="10-总结">10. 总结
&lt;/h2>&lt;p>本文讲解了使用 CMake 搭建跨平台 C++ 编译环境的具体步骤。
最后回顾一下重点：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>理解元构建系统&lt;/strong>: CMake 是一个生成构建脚本的工具。&lt;/li>
&lt;li>&lt;strong>彻底贯彻现代 CMake&lt;/strong>: 不使用全局变量，而是以 &lt;code>add_executable&lt;/code>、&lt;code>target_link_libraries&lt;/code>、&lt;code>target_include_directories&lt;/code> 等&lt;strong>面向目标 (Target-oriented)&lt;/strong> 的方式封装设置。&lt;/li>
&lt;li>&lt;strong>恰当设置作用域&lt;/strong>: 正确区分使用 &lt;code>PUBLIC&lt;/code>、&lt;code>PRIVATE&lt;/code> 和 &lt;code>INTERFACE&lt;/code>，从而控制依赖关系的影响范围。&lt;/li>
&lt;li>&lt;strong>坚决执行源码外构建&lt;/strong>: 在 &lt;code>build/&lt;/code> 目录内进行构建，保持源码树的整洁。&lt;/li>
&lt;li>&lt;strong>第三方库整合&lt;/strong>: 熟练使用 &lt;code>FetchContent&lt;/code> 或 &lt;code>vcpkg&lt;/code>，实现依赖库的自动解析。&lt;/li>
&lt;li>&lt;strong>活用生成器表达式&lt;/strong>: 巧妙地处理不同编译器的标志差异。&lt;/li>
&lt;li>&lt;strong>数学理论支撑&lt;/strong>: 意识到阿姆达尔定律的存在，通过减少依赖关系来提高并行编译的效率。&lt;/li>
&lt;/ol>
&lt;p>尽管 CMake 一开始可能会让人觉得晦涩难懂，但只要掌握了目标（Target）和属性（Property）的概念，无论多么复杂庞大的 C++ 项目，都能维持一个井然有序的构建环境。希望您能以本文为参考，使用最新现代 CMake 的写法，尝试搭建属于您的 C++ 开发环境。&lt;/p></description></item></channel></rss>