<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://kumo-pub.github.io/goose/zh-cn/blog</id>
    <title>GOOSE Blog</title>
    <updated>2025-08-01T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://kumo-pub.github.io/goose/zh-cn/blog"/>
    <subtitle>GOOSE Blog</subtitle>
    <icon>https://kumo-pub.github.io/goose/zh-cn/img/favicon.svg</icon>
    <entry>
        <title type="html"><![CDATA[为什么用cmake]]></title>
        <id>https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog</id>
        <link href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog"/>
        <updated>2025-08-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[如果你是C++开发者，大概率埋没过“构建地狱”的深渊：对着Makefile的嵌套语法抓耳挠腮，改一行编译选项要翻遍百行脚本；切换到CMake后，又被targetlinklibraries的PUBLIC/PRIVATE逻辑绕晕，写个简单库的配置文件比业务代码还长；偶尔想试试Bazel、Blade、Xmake，却发现每个工具都有专属“黑话体系”，光学习成本就够喝一壶——更别提生产环境里的终极折磨：为了集成一个第三方库，反复安装依赖、调试编译参数，甚至折腾半个月才勉强跑通，最后还得面对“换台机器就崩”的噩梦。难道构建C++项目，非要先渡劫成“构建工具专家”才能开工？]]></summary>
        <content type="html"><![CDATA[<p>如果你是C++开发者，大概率埋没过“构建地狱”的深渊：对着Makefile的嵌套语法抓耳挠腮，改一行编译选项要翻遍百行脚本；切换到CMake后，又被<code>target_link_libraries</code>的PUBLIC/PRIVATE逻辑绕晕，写个简单库的配置文件比业务代码还长；偶尔想试试Bazel、Blade、Xmake，却发现每个工具都有专属“黑话体系”，光学习成本就够喝一壶——更别提生产环境里的终极折磨：为了集成一个第三方库，反复安装依赖、调试编译参数，甚至折腾半个月才勉强跑通，最后还得面对“换台机器就崩”的噩梦。难道构建C++项目，非要先渡劫成“构建工具专家”才能开工？</p>
<p>直到我用第一性原理重新审视这个问题，才发现我们都被工具绑架了：我们要的从来不是“精通CMake”，而是“快速搭建可运行、可集成、可部署的项目”，是“不用为了一个库浪费半个月”的效率。而要实现这个目标，光喊口号没用——必须先把CMake的构建流程拆透，知道每个阶段能做什么、该怎么做，才能用第一性原理重构出真正高效的框架。于是有了kmcmake——一个基于CMake底层流程设计，把复杂逻辑踩在脚下，让你5分钟上手、零侵入集成的CMake框架，它的核心使命就是：终结“为编译一个库折腾半月”的荒诞历史，同时让你明白“背后的逻辑”，而非盲目使用。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="先搞懂cmake构建的底层流程是什么第一性原理的基础">先搞懂：CMake构建的“底层流程”是什么？（第一性原理的基础）<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#%E5%85%88%E6%90%9E%E6%87%82cmake%E6%9E%84%E5%BB%BA%E7%9A%84%E5%BA%95%E5%B1%82%E6%B5%81%E7%A8%8B%E6%98%AF%E4%BB%80%E4%B9%88%E7%AC%AC%E4%B8%80%E6%80%A7%E5%8E%9F%E7%90%86%E7%9A%84%E5%9F%BA%E7%A1%80" class="hash-link" aria-label="先搞懂：CMake构建的“底层流程”是什么？（第一性原理的基础）的直接链接" title="先搞懂：CMake构建的“底层流程”是什么？（第一性原理的基础）的直接链接" translate="no">​</a></h2>
<p>第一性原理的核心是“回归本质”，而CMake的本质是“跨平台构建流程的组织者”——它不直接编译代码，而是按固定阶段生成对应平台的构建文件（Makefile、Visual Studio解决方案等）。要让构建变简单，必须先吃透这<strong>三个核心阶段</strong>，知道每个阶段的职责边界和操作逻辑：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="阶段1配置阶段configure-phase找东西定规则">阶段1：配置阶段（Configure Phase）——“找东西+定规则”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#%E9%98%B6%E6%AE%B51%E9%85%8D%E7%BD%AE%E9%98%B6%E6%AE%B5configure-phase%E6%89%BE%E4%B8%9C%E8%A5%BF%E5%AE%9A%E8%A7%84%E5%88%99" class="hash-link" aria-label="阶段1：配置阶段（Configure Phase）——“找东西+定规则”的直接链接" title="阶段1：配置阶段（Configure Phase）——“找东西+定规则”的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>核心任务</strong>：解析CMakeLists.txt，查找依赖库、设置编译选项、定义目标（库/可执行文件）、配置安装规则。</li>
<li class=""><strong>关键操作</strong>：<!-- -->
<ol>
<li class="">依赖查找：通过<code>find_package</code>、<code>find_library</code>、<code>find_path</code>查找第三方库（如spdlog、Protobuf），确定库文件和头文件路径；</li>
<li class="">目标定义：用<code>add_library</code>/<code>add_executable</code>创建目标，用<code>target_include_directories</code>设置包含路径，用<code>target_link_libraries</code>链接依赖；</li>
<li class="">变量配置：设置<code>CMAKE_BUILD_TYPE</code>（Debug/Release）、<code>CMAKE_INSTALL_PREFIX</code>（安装根目录）、<code>CMAKE_CXX_STANDARD</code>（C++标准）等核心变量；</li>
<li class="">条件判断：通过<code>if-else</code>适配不同平台（Linux/macOS/Windows）、不同编译器（GCC/Clang/MSVC）。</li>
</ol>
</li>
<li class=""><strong>生产环境的坑</strong>：曾经为了查找一个自定义安装的Protobuf库，手动设置<code>CMAKE_PREFIX_PATH</code>、<code>PROTOBUF_INCLUDE_DIR</code>、<code>PROTOBUF_LIBRARY</code>三个变量，反复调试了2天；还有一次因为<code>find_package</code>的<code>CONFIG</code>模式和<code>MODULE</code>模式搞混，导致CI环境找不到库，折腾了一周才定位问题。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="阶段2生成阶段generate-phase写文件">阶段2：生成阶段（Generate Phase）——“写文件”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#%E9%98%B6%E6%AE%B52%E7%94%9F%E6%88%90%E9%98%B6%E6%AE%B5generate-phase%E5%86%99%E6%96%87%E4%BB%B6" class="hash-link" aria-label="阶段2：生成阶段（Generate Phase）——“写文件”的直接链接" title="阶段2：生成阶段（Generate Phase）——“写文件”的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>核心任务</strong>：根据配置阶段的规则，生成对应平台的构建文件（如Linux的Makefile、macOS的Xcode项目、Windows的VS解决方案）。</li>
<li class=""><strong>关键操作</strong>：<!-- -->
<ol>
<li class="">自动生成编译命令：将<code>target_compile_options</code>设置的 flags 转化为Makefile中的<code>CXXFLAGS</code>；</li>
<li class="">生成依赖关系：将<code>target_link_libraries</code>的依赖转化为Makefile中的链接命令，处理静态库/共享库的依赖传递；</li>
<li class="">生成安装规则：将<code>install</code>指令转化为Makefile中的<code>install</code>目标，定义头文件、库文件、可执行文件的安装路径。</li>
</ol>
</li>
<li class=""><strong>生产环境的坑</strong>：曾经用原生CMake写<code>install</code>规则时，没注意<code>CMAKE_INSTALL_LIBDIR</code>（库安装目录）和<code>CMAKE_INSTALL_INCLUDEDIR</code>（头文件安装目录）的平台差异，导致在Linux上安装正常，在macOS上库文件和头文件路径错位，第三方项目引用时找不到头文件。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="阶段3构建安装阶段buildinstall-phase执行命令">阶段3：构建/安装阶段（Build/Install Phase）——“执行命令”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#%E9%98%B6%E6%AE%B53%E6%9E%84%E5%BB%BA%E5%AE%89%E8%A3%85%E9%98%B6%E6%AE%B5buildinstall-phase%E6%89%A7%E8%A1%8C%E5%91%BD%E4%BB%A4" class="hash-link" aria-label="阶段3：构建/安装阶段（Build/Install Phase）——“执行命令”的直接链接" title="阶段3：构建/安装阶段（Build/Install Phase）——“执行命令”的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>核心任务</strong>：调用编译器（GCC/Clang/MSVC）编译代码、链接库文件，最终生成目标文件（库/可执行文件）；执行<code>make install</code>时，按生成阶段的规则复制文件到指定目录。</li>
<li class=""><strong>关键操作</strong>：<!-- -->
<ol>
<li class="">编译：按生成的构建文件执行编译命令，将源码文件（.cc/.cpp）编译为目标文件（.o/.obj）；</li>
<li class="">链接：将目标文件和依赖库链接为最终的静态库（.a/.lib）、共享库（.so/.dll）或可执行文件；</li>
<li class="">安装：复制库文件、头文件、可执行文件到<code>CMAKE_INSTALL_PREFIX</code>指定的目录（如<code>/usr/local/lib</code>、<code>/usr/local/include</code>）。</li>
</ol>
</li>
<li class=""><strong>生产环境的坑</strong>：曾经因为<code>target_link_libraries</code>的<code>PUBLIC</code>/<code>PRIVATE</code>设置错误，导致共享库的依赖传递失败——自己的库编译正常，但第三方项目链接时提示“找不到依赖的spdlog库”，最后发现是把<code>PUBLIC</code>写成了<code>PRIVATE</code>，导致spdlog的链接信息没有传递给第三方项目。</li>
</ul>
<p>这三个阶段环环相扣，任何一个环节的配置失误都会导致构建失败。而第一性原理告诉我们：<strong>构建工具的价值，是把每个阶段的复杂操作“模块化、自动化”，让开发者不用关心“阶段细节”，只需要告诉工具“最终目标”</strong>。kmcmake的所有设计，都严格遵循CMake的底层流程，在每个阶段做“精准封装”，既不破坏CMake的原生逻辑，又能让复杂操作变简单——让CMake从“必须学的技能”变成“不用管的实现细节”，让“集成一个库”从“半个月工程”变成“5分钟操作”。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7个核心目标kmcmake如何基于cmake流程实现第一性原理的落地">7个核心目标，kmcmake如何基于CMake流程实现？（第一性原理的落地）<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#7%E4%B8%AA%E6%A0%B8%E5%BF%83%E7%9B%AE%E6%A0%87kmcmake%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8Ecmake%E6%B5%81%E7%A8%8B%E5%AE%9E%E7%8E%B0%E7%AC%AC%E4%B8%80%E6%80%A7%E5%8E%9F%E7%90%86%E7%9A%84%E8%90%BD%E5%9C%B0" class="hash-link" aria-label="7个核心目标，kmcmake如何基于CMake流程实现？（第一性原理的落地）的直接链接" title="7个核心目标，kmcmake如何基于CMake流程实现？（第一性原理的落地）的直接链接" translate="no">​</a></h2>
<p>我的核心诉求很明确——终结折腾、提升效率，而kmcmake就是这些目标在CMake三个阶段的“精准映射”，每个功能都能对应到CMake的具体操作，没有任何含糊其辞：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-5分钟搭建项目配置阶段的模块化封装">1. 5分钟搭建项目：配置阶段的“模块化封装”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#1-5%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E9%98%B6%E6%AE%B5%E7%9A%84%E6%A8%A1%E5%9D%97%E5%8C%96%E5%B0%81%E8%A3%85" class="hash-link" aria-label="1. 5分钟搭建项目：配置阶段的“模块化封装”的直接链接" title="1. 5分钟搭建项目：配置阶段的“模块化封装”的直接链接" translate="no">​</a></h3>
<p>原生CMake在配置阶段，要手动写<code>add_library</code>、<code>target_include_directories</code>、<code>target_link_libraries</code>、<code>install</code>等一系列指令，还要考虑目标别名、静态/共享库切换、依赖传递等细节。比如要创建一个支持静态/共享库的项目，原生CMake需要写：</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 原生CMake：创建静态库+共享库+安装规则+别名（50多行代码）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">option(BUILD_SHARED_LIBS "Build shared library" ON)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">add_library(my_lib src/my_lib.cc)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">add_library(my_lib::my_lib ALIAS my_lib)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">target_include_directories(my_lib</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  PUBLIC</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    $&lt;BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    $&lt;INSTALL_INTERFACE:include&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">target_link_libraries(my_lib PRIVATE spdlog::spdlog)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">target_compile_features(my_lib PUBLIC cxx_std_17)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">install(TARGETS my_lib</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  EXPORT my_lib_targets</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">install(EXPORT my_lib_targets</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  FILE my_libTargets.cmake</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  NAMESPACE my_lib::</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/my_lib</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span></code></pre></div></div>
<p>这还只是基础配置，要是加上编译选项、条件判断，代码量会翻倍。而kmcmake在配置阶段做了“模块化封装”，把上述所有操作浓缩成一个宏，你只需要告诉它“目标、源码、包含路径、依赖”，剩下的配置阶段操作kmcmake自动完成：</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># kmcmake：一行宏搞定配置阶段所有操作（5分钟上手）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kmcmake_cc_library(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    NAME my_lib</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    SOURCES src/my_lib.cc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    INCLUDES include</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    LINKS spdlog::spdlog</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    CXXOPTS -std=c++17</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span></code></pre></div></div>
<ul>
<li class="">底层逻辑：kmcmake在配置阶段自动执行以下操作：<!-- -->
<ol>
<li class="">调用<code>add_library</code>创建目标，支持<code>BUILD_SHARED_LIBS</code>开关切换静态/共享库；</li>
<li class="">自动设置<code>target_include_directories</code>的<code>BUILD_INTERFACE</code>和<code>INSTALL_INTERFACE</code>，不用手动写生成器表达式；</li>
<li class="">自动创建<code>{NAME}::{NAME}</code>别名，统一目标引用方式；</li>
<li class="">自动添加<code>install</code>规则，遵循<code>GNUInstallDirs</code>标准，不用关心<code>CMAKE_INSTALL_LIBDIR</code>等变量；</li>
<li class="">自动传递依赖关系，<code>LINKS</code>参数默认按<code>PUBLIC</code>/<code>PRIVATE</code>智能区分（库依赖用<code>PRIVATE</code>，工具链依赖用<code>PUBLIC</code>）。</li>
</ol>
</li>
</ul>
<p>曾经为了配置一个带静态/共享库的项目，我在原生CMake里写了50多行代码，反复调试了3小时；用kmcmake后，5分钟就搞定，还不用担心生成器表达式写错、
安装路径错位等问题。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-无缝集成ci配置阶段的跨平台自动适配">2. 无缝集成CI：配置阶段的“跨平台自动适配”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#2-%E6%97%A0%E7%BC%9D%E9%9B%86%E6%88%90ci%E9%85%8D%E7%BD%AE%E9%98%B6%E6%AE%B5%E7%9A%84%E8%B7%A8%E5%B9%B3%E5%8F%B0%E8%87%AA%E5%8A%A8%E9%80%82%E9%85%8D" class="hash-link" aria-label="2. 无缝集成CI：配置阶段的“跨平台自动适配”的直接链接" title="2. 无缝集成CI：配置阶段的“跨平台自动适配”的直接链接" translate="no">​</a></h3>
<p>CI/CD的核心痛点是“跨平台配置适配”——不同类Unix系统（Linux/macOS）的依赖路径、编译选项差异，需要在配置阶段写大量<code>if-else</code>判断。比如原生CMake要适配GCC和Clang的编译选项，需要：</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 原生CMake：跨平台编译选项适配（冗余且易出错）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  target_compile_options(my_lib PRIVATE -Wall -Wextra -Werror)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  target_compile_options(my_lib PRIVATE -Wall -Wextra -Werror -Wno-gnu-zero-variadic-macro-arguments)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">endif()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 依赖查找适配</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if(UNIX AND NOT APPLE)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  set(PROTOBUF_LIBRARY_PATH /usr/lib/x86_64-linux-gnu)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">elseif(APPLE)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  set(PROTOBUF_LIBRARY_PATH /usr/local/lib)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">endif()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">find_library(PROTOBUF_LIBRARY protobuf PATHS ${PROTOBUF_LIBRARY_PATH})</span><br></span></code></pre></div></div>
<p>而kmcmake在配置阶段做了“跨平台自动适配”，底层逻辑直接对接CMake的平台判断接口：</p>
<ul>
<li class="">自动识别<code>CMAKE_CXX_COMPILER_ID</code>，内置GCC/Clang的通用编译选项，不用手动写<code>if-else</code>；</li>
<li class="">自动遵循<code>GNUInstallDirs</code>标准，依赖查找优先使用<code>find_package</code>的<code>CONFIG</code>模式，找不到再用<code>MODULE</code>模式，跨平台路径差异由CMake原生处理；</li>
<li class="">内置测试宏<code>kmcmake_cc_test</code>，在配置阶段自动注册测试目标，CI中只需加<code>-DKMCMAKE_BUILD_TEST=ON</code>，不用手动写<code>add_test</code>指令。</li>
</ul>
<p>曾经为了让CI能编译一个带Protobuf依赖的项目，光是适配<code>protoc</code>路径和编译选项就折腾了一周；现在用kmcmake，CI脚本简化到：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 配置+编译+测试+安装，全程无额外操作</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cmake --preset default -DKMCMAKE_BUILD_TEST=ON</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cmake --build build</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ctest --test-dir build</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cmake --install build</span><br></span></code></pre></div></div>
<p>不用关心跨平台差异，配置阶段的适配逻辑kmcmake已经全部封装好。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-轻松部署生成阶段安装阶段的规则自动化">3. 轻松部署：生成阶段+安装阶段的“规则自动化”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#3-%E8%BD%BB%E6%9D%BE%E9%83%A8%E7%BD%B2%E7%94%9F%E6%88%90%E9%98%B6%E6%AE%B5%E5%AE%89%E8%A3%85%E9%98%B6%E6%AE%B5%E7%9A%84%E8%A7%84%E5%88%99%E8%87%AA%E5%8A%A8%E5%8C%96" class="hash-link" aria-label="3. 轻松部署：生成阶段+安装阶段的“规则自动化”的直接链接" title="3. 轻松部署：生成阶段+安装阶段的“规则自动化”的直接链接" translate="no">​</a></h3>
<p>部署的核心是“让第三方项目能找到你的库”，这需要在生成阶段生成<code>Config.cmake</code>文件，在安装阶段正确复制头文件、库文件和配置文件。原生CMake要实现这一点，需要手动写：</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 原生CMake：生成Config.cmake文件（冗余且易出错）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">include(CMakePackageConfigHelpers)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">write_basic_package_version_file(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  my_libConfigVersion.cmake</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  VERSION 1.0.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  COMPATIBILITY SameMajorVersion</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">configure_package_config_file(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ${CMAKE_CURRENT_SOURCE_DIR}/my_libConfig.cmake.in</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ${CMAKE_CURRENT_BINARY_DIR}/my_libConfig.cmake</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/my_lib</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">install(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  FILES</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ${CMAKE_CURRENT_BINARY_DIR}/my_libConfig.cmake</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ${CMAKE_CURRENT_BINARY_DIR}/my_libConfigVersion.cmake</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/my_lib</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span></code></pre></div></div>
<p>还要手动创建<code>my_libConfig.cmake.in</code>模板文件，稍不注意就会导致第三方项目<code>find_package</code>失败。而kmcmake的解决方案是：</p>
<ul>
<li class="">生成阶段：自动调用<code>write_basic_package_version_file</code>和<code>configure_package_config_file</code>，生成<code>Config.cmake</code>和
<code>ConfigVersion.cmake</code>文件，不用手动写模板；</li>
<li class="">安装阶段：自动安装配置文件到<code>${CMAKE_INSTALL_LIBDIR}/cmake/{NAME}</code>目录，确保第三方项目能通过<code>find_package</code>找到；</li>
<li class="">底层逻辑：kmcmake在生成阶段自动提取目标的包含路径、依赖关系、编译选项，写入配置文件，第三方项目调用<code>find_package</code>时，CMake会自动
加载这些信息，实现“一键链接”。</li>
</ul>
<p>曾经我写的一个库，因为没处理好<code>Config.cmake</code>文件，用户反馈“找了三天都不知道怎么链接”，最后我远程协助调试了半天；用kmcmake后，部署时只
需要<code>make install</code>，第三方项目直接用<code>find_package(my_lib REQUIRED)</code>就能链接，再也没有“找不到库”的问题。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-无隐藏条件配置阶段的明牌逻辑">4. 无隐藏条件：配置阶段的“明牌逻辑”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#4-%E6%97%A0%E9%9A%90%E8%97%8F%E6%9D%A1%E4%BB%B6%E9%85%8D%E7%BD%AE%E9%98%B6%E6%AE%B5%E7%9A%84%E6%98%8E%E7%89%8C%E9%80%BB%E8%BE%91" class="hash-link" aria-label="4. 无隐藏条件：配置阶段的“明牌逻辑”的直接链接" title="4. 无隐藏条件：配置阶段的“明牌逻辑”的直接链接" translate="no">​</a></h3>
<p>很多CMake新手踩坑，是因为不了解“配置阶段的执行顺序”和“变量作用域”——比如<code>find_package</code>要放在<code>add_library</code>之前，
<code>target_include_directories</code>的<code>PUBLIC</code>/<code>PRIVATE</code>要区分清楚，这些“隐藏规则”让新手望而却步。kmcmake的原则是“明牌逻辑”，所有操作
都遵循CMake的原生执行顺序，没有任何隐藏条件：</p>
<ul>
<li class="">参数含义明确：<code>INCLUDES</code>对应<code>target_include_directories(PUBLIC)</code>，<code>PINCLUDES</code>对应<code>target_include_directories(PRIVATE)</code>，<code>LINKS</code>对应<code>target_link_libraries(PUBLIC)</code>，<code>PLINKS</code>对应<code>target_link_libraries(PRIVATE)</code>，没有歧义；</li>
<li class="">执行顺序固定：kmcmake宏内部先执行<code>find_package</code>（如果有依赖），再执行<code>add_library</code>，最后执行<code>target_*</code>系列指令，完全符合CMake的原生
执行逻辑；</li>
<li class="">报错信息明确：如果依赖找不到，kmcmake会直接打印“找不到XXX库，请安装或设置XXX路径”，不会默默忽略，方便定位问题。</li>
</ul>
<p>曾经为了让一个库能被<code>find_package</code>找到，我踩了无数坑，最后发现是<code>CMAKE_INSTALL_PREFIX</code>设置错了——这种隐藏条件，新手根本猜不到；而kmcmake把所有规则“明牌化”，不用记CMake的执行顺序，按直觉配置就能成功。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-兼容类unix系统配置阶段生成阶段的标准适配">5. 兼容类Unix系统：配置阶段+生成阶段的“标准适配”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#5-%E5%85%BC%E5%AE%B9%E7%B1%BBunix%E7%B3%BB%E7%BB%9F%E9%85%8D%E7%BD%AE%E9%98%B6%E6%AE%B5%E7%94%9F%E6%88%90%E9%98%B6%E6%AE%B5%E7%9A%84%E6%A0%87%E5%87%86%E9%80%82%E9%85%8D" class="hash-link" aria-label="5. 兼容类Unix系统：配置阶段+生成阶段的“标准适配”的直接链接" title="5. 兼容类Unix系统：配置阶段+生成阶段的“标准适配”的直接链接" translate="no">​</a></h3>
<p>类Unix系统的兼容问题，本质是“配置阶段的依赖查找”和“生成阶段的路径规则”差异。kmcmake的解决方案是：</p>
<ul>
<li class="">配置阶段：自动调用<code>include(GNUInstallDirs)</code>，使用<code>CMAKE_INSTALL_LIBDIR</code>、<code>CMAKE_INSTALL_INCLUDEDIR</code>等标准变量，不用手动设置路径；</li>
<li class="">生成阶段：生成的Makefile遵循类Unix系统的编译规范，库文件安装到<code>/usr/local/lib</code>，头文件安装到<code>/usr/local/include</code>，符合第三方库的默认查找路径；</li>
<li class="">依赖查找：优先使用<code>find_package</code>的标准模式，支持<code>CMAKE_PREFIX_PATH</code>环境变量，类Unix系统下不用手动设置依赖路径。</li>
</ul>
<p>曾经在Linux和macOS上编译同一个项目，用原生CMake要写两套<code>if-else</code>判断依赖路径，现在用kmcmake，一套配置搞定，不用关心系统差异。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-零侵入配置阶段的原生兼容">6. 零侵入：配置阶段的“原生兼容”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#6-%E9%9B%B6%E4%BE%B5%E5%85%A5%E9%85%8D%E7%BD%AE%E9%98%B6%E6%AE%B5%E7%9A%84%E5%8E%9F%E7%94%9F%E5%85%BC%E5%AE%B9" class="hash-link" aria-label="6. 零侵入：配置阶段的“原生兼容”的直接链接" title="6. 零侵入：配置阶段的“原生兼容”的直接链接" translate="no">​</a></h3>
<p>kmcmake的核心是“封装”而非“替代”，完全兼容CMake的原生指令——在kmcmake宏之后，你可以继续用原生CMake指令扩展配置，不会破坏原有逻辑：</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># kmcmake宏 + 原生CMake指令，零侵入扩展</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kmcmake_cc_library(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    NAME my_lib</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    SOURCES src/my_lib.cc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    INCLUDES include</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 原生CMake扩展：添加自定义编译选项（配置阶段执行）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">target_compile_options(my_lib PRIVATE -Wno-unused-parameter)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 原生CMake扩展：添加条件判断（配置阶段执行）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if(BUILD_TESTING)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  add_subdirectory(tests)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">endif()</span><br></span></code></pre></div></div>
<p>底层逻辑：kmcmake宏执行后，会创建标准的CMake目标（如<code>my_lib</code>），后续的原生<code>target_*</code>指令可以直接操作该目标，完全符合CMake的目标管理逻辑。</p>
<p>曾经我用某个框架，因为它强制要求源码放在<code>src</code>目录下，我不得不重构整个项目的目录结构，花了一周时间；而kmcmake不强制任何目录结构，不绑
架你的项目，想加就加，想改就改。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-不造新工具基于cmake原生流程的封装层">7. 不造新工具：基于CMake原生流程的“封装层”<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#7-%E4%B8%8D%E9%80%A0%E6%96%B0%E5%B7%A5%E5%85%B7%E5%9F%BA%E4%BA%8Ecmake%E5%8E%9F%E7%94%9F%E6%B5%81%E7%A8%8B%E7%9A%84%E5%B0%81%E8%A3%85%E5%B1%82" class="hash-link" aria-label="7. 不造新工具：基于CMake原生流程的“封装层”的直接链接" title="7. 不造新工具：基于CMake原生流程的“封装层”的直接链接" translate="no">​</a></h3>
<p>第一性原理强调“避免冗余”：CMake已经是行业标准，生态完善，支持所有类Unix系统，没必要再造一个新的构建工具（既费时又费力，还没人用）。
kmcmake的定位是“CMake的封装层”，所有功能都基于CMake的三个核心阶段实现：</p>
<ul>
<li class="">不修改CMake的原生流程，只是在配置阶段自动执行重复的<code>target_*</code>、<code>install</code>等指令；</li>
<li class="">不依赖额外工具，只需要CMake 3.21+，不用安装其他二进制文件；</li>
<li class="">不破坏CMake的生态，支持所有<code>find_package</code>能找到的库，支持所有CMake的原生变量和指令。</li>
</ul>
<p>就像你用手机不用懂芯片原理，用kmcmake也不用懂CMake的底层流程——但如果你想懂，每个功能都能对应到CMake的具体操作，完全具备信服力。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="总结第一性原理cmake流程让构建变简单">总结：第一性原理+CMake流程，让构建变简单<a href="https://kumo-pub.github.io/goose/zh-cn/blog/mdx-blog#%E6%80%BB%E7%BB%93%E7%AC%AC%E4%B8%80%E6%80%A7%E5%8E%9F%E7%90%86cmake%E6%B5%81%E7%A8%8B%E8%AE%A9%E6%9E%84%E5%BB%BA%E5%8F%98%E7%AE%80%E5%8D%95" class="hash-link" aria-label="总结：第一性原理+CMake流程，让构建变简单的直接链接" title="总结：第一性原理+CMake流程，让构建变简单的直接链接" translate="no">​</a></h2>
<p>我们之所以觉得构建C++项目麻烦，是因为把“工具语法”和“核心需求”搞反了——我们要的不是“精通CMake的三个阶段和所有指令”，而是“不用为了
一个库折腾半个月”；不是“掌握生成器表达式、依赖传递等复杂概念”，而是“5分钟搭建项目，无缝集成CI，轻松部署”。</p>
<p>而第一性原理的价值，就是帮我们剥离噪音，回归CMake的底层流程本质：把每个阶段的复杂操作“模块化、自动化”，让开发者不用关心“阶段细节”，只需要告诉
工具“最终目标”。kmcmake没有创造新的构建逻辑，只是用第一性原理把CMake的三个核心阶段“翻译”成了开发者能直接用的简单接口——每个宏都对应着CMake配
置、生成、安装阶段的一系列标准操作，既精准又透明，没有任何含糊。</p>
<p>现在，你不用再花几周时间学CMake，不用再为Makefile的语法头疼，不用再担心切换工具链的成本，更不用为了集成一个库折腾半个月——这些都交给kmcmake，你
只需要专注于写好业务代码。</p>
<p>毕竟，我们是C++开发者，不是“构建工具工程师”。让工具回归工具的本质（按底层流程自动完成重复工作），让我们回归开发的本质（专注业务逻辑），这才是第一性
原理的真正力量。</p>
<p>如果你也受够了构建工具的内卷，受够了为编译一个库折腾半月的痛苦，不妨试试kmcmake——5分钟上手，零学习成本，让构建C++项目像呼吸一样简单，同时让你明白“背后的逻辑”，而非盲目使用。</p>]]></content>
        <author>
            <name>Bohu li</name>
            <uri>https://pub.kumo.cc</uri>
        </author>
        <category label="CMake" term="CMake"/>
    </entry>
</feed>