<?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/ru/categories/tools/</link><description>Recent content in Tools on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/categories/tools/index.xml" rel="self" type="application/rss+xml"/><item><title>Руководство по настройке кроссплатформенной среды сборки C++ с использованием CMake</title><link>http://kenji.blog/ru/p/cmake-cross-platform-build-environment-guide/</link><pubDate>Sat, 12 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/ru/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 Руководство по настройке кроссплатформенной среды сборки C++ с использованием CMake" />&lt;p>При разработке программного обеспечения на C++ выбор и настройка «системы сборки» долгие годы были проблемой для многих разработчиков. Поскольку в C++ нет официального стандартного менеджера пакетов или системы сборки, приходилось использовать различные компиляторы и инструменты сборки (MSVC, GCC, Clang, Make, Ninja и т. д.) в зависимости от платформы (Windows, Linux, macOS).&lt;/p>
&lt;p>Однако сегодня &lt;strong>CMake&lt;/strong> стал де-факто отраслевым стандартом, и при правильном его использовании можно элегантно настроить кроссплатформенную среду сборки из единого &lt;code>CMakeLists.txt&lt;/code>.&lt;/p>
&lt;p>В этой статье подробно и основательно, от базовых до продвинутых техник, рассматривается процесс настройки кроссплатформенной среды сборки C++ с использованием современного CMake (Modern CMake).&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>), независимого от платформы и компилятора, и автоматической генерации нативных скриптов сборки, оптимальных для каждой среды (например, &lt;code>Makefile&lt;/code> для Linux, файлы проектов &lt;code>.sln&lt;/code> Visual Studio для Windows или быстрые &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 Build Files"]
B --> E["Visual Studio Solutions"]
B --> F["Xcode Projects"]
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>Цели (Targets)&lt;/strong> и &lt;strong>Свойства (Properties)&lt;/strong>. Это похоже на связь между классами и переменными-членами в объектно-ориентированном программировании.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Цель (Target)&lt;/strong>: Исполняемый файл (Executable) или библиотека (Library).&lt;/li>
&lt;li>&lt;strong>Свойство (Property)&lt;/strong>: Исходные файлы, каталоги для включения (include directories), параметры компиляции, другие библиотеки для компоновки и прочее, необходимое для сборки этой цели.&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;strong>&lt;code>PUBLIC&lt;/code>, &lt;code>PRIVATE&lt;/code>, &lt;code>INTERFACE&lt;/code>&lt;/strong>, которые используются в таких командах, как &lt;code>target_include_directories&lt;/code> и &lt;code>target_link_libraries&lt;/code>.&lt;/p>
&lt;p>Они предназначены для управления тем, нужны ли свойства цели (пути включения или зависимые библиотеки) &amp;ldquo;только для сборки самой цели?&amp;rdquo; или &amp;ldquo;они также должны распространяться на другие цели, зависящие от этой?&amp;rdquo;.&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> на зависимые цели (используется для библиотек, состоящих только из заголовочных файлов, и т. д.).&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 "Библиотеки"
MathLib["MathLib (Статическая библиотека)"]
NetworkLib["NetworkLib (Динамическая библиотека)"]
HeaderLib["HeaderLib (Библиотека только из заголовков)"]
end
subgraph "Приложение"
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>nlohmann/json&lt;/code> в свой публичный заголовочный файл &lt;code>MyLib.hpp&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>#include &amp;lt;nlohmann/json.hpp&amp;gt;&lt;/code> внутри &lt;code>MyLib.hpp&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>build/&lt;/code> в &lt;code>.gitignore&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>Ниже приведен пример внедрения GoogleTest с помощью FetchContent.&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>Используя &lt;strong>vcpkg&lt;/strong>, менеджер пакетов для C++, возглавляемый Microsoft, вы можете легко интегрировать тысячи библиотек. vcpkg разработан для бесшовной интеграции с CMake.&lt;/p>
&lt;p>Просто указав файл тулчейна (toolchain file) vcpkg при запуске CMake, команда &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> (режим манифеста) в корне проекта, вы можете полностью автоматизировать управление версиями необходимых библиотек.&lt;/p>
&lt;h2 id="6-флаги-компилятора-для-кроссплатформенности">6. Флаги компилятора для кроссплатформенности
&lt;/h2>&lt;p>Чтобы сборка успешно проходила в любой среде — Windows (MSVC), Linux (GCC/Clang) и macOS (Apple Clang), необходимо правильно настроить специфичные для компилятора флаги.&lt;/p>
&lt;p>Используя &lt;strong>Генераторные выражения (Generator Expressions)&lt;/strong> CMake, вы можете декларативно описывать условные ветвления в стиле: «если компилятор 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>CMakeLists.txt&lt;/code> из-за обилия условных ветвлений типа &lt;code>if(MSVC)&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>Процедура интеграции GoogleTest, внедренного ранее с помощью &lt;code>FetchContent&lt;/code>, с 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$), в то время как «окончательный процесс связывания (компоновки) линкером» по существу выполняется последовательно (доля $1-P$).&lt;/p>
&lt;p>Таким образом, независимо от того, сколько ядер процессора у вас есть ($N \to \infty$), пока существует узкое место в виде времени компоновки, максимальный коэффициент ускорения асимптотически приближается к следующей формуле:&lt;/p>
$$ \lim_{N \to \infty} S(N) = \frac{1}{1 - P} $$
&lt;p>Эта формула показывает, что «простое увеличение количества ядер ЦП имеет свои ограничения в сокращении времени сборки». Наиболее эффективной стратегией ускорения сборки на практике является максимизация доли $P$ и уменьшение объектов перекомпиляции во время инкрементальной сборки путем правильного использования &lt;code>PRIVATE&lt;/code> и &lt;code>INTERFACE&lt;/code> в современном CMake и минимизации зависимостей заголовочных файлов (например, с использованием опережающих объявлений).&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"># Настройка использования линкера lld в среде Clang/GCC
&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>Мы рассмотрели процедуру настройки кроссплатформенной среды сборки C++ с использованием CMake.
Давайте вспомним основные моменты:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Понимание мета-систем сборки&lt;/strong>: CMake — это инструмент, который генерирует скрипты сборки.&lt;/li>
&lt;li>&lt;strong>Строгое следование современному CMake&lt;/strong>: Не используйте переменные, инкапсулируйте настройки с помощью &lt;strong>целеориентированного (target-oriented)&lt;/strong> подхода, используя &lt;code>add_executable&lt;/code>, &lt;code>target_link_libraries&lt;/code>, &lt;code>target_include_directories&lt;/code> и т. д.&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 может показаться сложным, но как только вы поймете концепцию целей и свойств, вы сможете поддерживать упорядоченную среду сборки, независимо от того, насколько сложным и огромным является проект на C++. Обязательно используйте эту статью в качестве справочника и попробуйте настроить среду разработки на C++, используя синтаксис современного CMake.&lt;/p></description></item></channel></rss>