
02-4Kconfig 如何控制 CMake 和源码编译日期2026-07-22所属章节第 2 章 Kconfig验证方式.config、compile_commands.json、object、ELF symbol、native_sim/native/64验证结论模拟确认1. 本节目标理解一条完整的功能裁剪链路Kconfig symbol - .config 最终值 - CMake 条件判断 - 是否把 .c 文件加入 target - 是否编译 object - 是否进入 ELF前几节主要看到的是CONFIG_* 改变 C 文件内部的代码分支本节进一步验证CONFIG_* 可以让整个 .c 文件完全不参与构建2. 实验 Kconfigconfig LEARN_COMPILED_FEATURE bool Compile the optional feature source file default n该 symbol 只表达软件功能是否需要编入镜像。3. Application CMake 条件源码实验CMakeLists.txttarget_sources(app PRIVATE src/main.c) target_sources_ifdef(CONFIG_LEARN_COMPILED_FEATURE app PRIVATE src/compiled_feature.c )含义main.c 始终加入 app target compiled_feature.c 只有 CONFIG_LEARN_COMPILED_FEATUREy 时加入 app targetZephyr 对target_sources_ifdef()的实现可以简化理解为if(CONFIG_LEARN_COMPILED_FEATURE) target_sources(app PRIVATE src/compiled_feature.c) endif()Kconfig 生成的最终 symbol 会成为 CMake 可读取的变量。本地 Zephyr 实现位置zephyr/cmake/modules/extensions.cmake:19994. 条件源码内容src/compiled_feature.cvoidlearn_compiled_feature_run(void){printf(cmake_featurecompiled and linked\n);}该文件提供一个容易在 object、map 和 ELF 中搜索的唯一 symbollearn_compiled_feature_run5. 调用方也必须处理功能关闭main.c#ifdefined(CONFIG_LEARN_COMPILED_FEATURE)voidlearn_compiled_feature_run(void);#endif/* ... */#ifdefined(CONFIG_LEARN_COMPILED_FEATURE)learn_compiled_feature_run();#elseprintf(cmake_featurenot compiled\n);#endif为什么调用也必须保护当功能关闭时compiled_feature.c 没有编译 - 没有 learn_compiled_feature_run() 实现 - 无条件调用会产生 undefined reference 链接错误常见解决方式调用处使用同一个#if头文件在关闭时提供static inline空实现核心 API 始终编译只有可选后端条件编译使用函数指针/API table仅注册已编译实现。6. 实验一功能开启compiled_on.confCONFIG_LEARN_GREETINGconditional source enabled CONFIG_LEARN_COMPILED_FEATUREy构建cd~/project/exportZEPHYR_SDK_INSTALL_DIR/home/yff/zephyr-sdk/zephyr-sdk-0.17.1sourcezephyr/zephyr-env.sh west build\-bnative_sim/native/64\/mnt/c/study/1-zephyr/labs/ch02_kconfig\--build-dir /mnt/c/study/1-zephyr/work/ch02_compiled_on\-palways\---DCONF_FILEcompiled_on.conf west build\--build-dir /mnt/c/study/1-zephyr/work/ch02_compiled_on\-trun实际输出节选*** Booting Zephyr OS build v4.1.0-rc1 *** cmake_featurecompiled and linked7. 实验二功能关闭compiled_off.confCONFIG_LEARN_GREETINGconditional source disabled CONFIG_LEARN_COMPILED_FEATUREn构建west build\-bnative_sim/native/64\/mnt/c/study/1-zephyr/labs/ch02_kconfig\--build-dir /mnt/c/study/1-zephyr/work/ch02_compiled_off\-palways\---DCONF_FILEcompiled_off.conf实际输出节选*** Booting Zephyr OS build v4.1.0-rc1 *** cmake_featurenot compiled8. 五层证据验证不要只根据程序输出判断。分别检查以下证据。8.1 最终 KconfigrgLEARN_COMPILED_FEATURE\/mnt/c/study/1-zephyr/work/ch02_compiled_on/zephyr/.config\/mnt/c/study/1-zephyr/work/ch02_compiled_off/zephyr/.config实际结果ch02_compiled_on/zephyr/.config: CONFIG_LEARN_COMPILED_FEATUREy ch02_compiled_off/zephyr/.config: # CONFIG_LEARN_COMPILED_FEATURE is not set结论两套 build directory 保存了各自独立的最终配置。编译确认8.2 编译数据库rgcompiled_feature\.c\/mnt/c/study/1-zephyr/work/ch02_compiled_on/compile_commands.json\/mnt/c/study/1-zephyr/work/ch02_compiled_off/compile_commands.json实际结果ch02_compiled_on/compile_commands.json 能找到 compiled_feature.c ch02_compiled_off/compile_commands.json 找不到 compiled_feature.ccompile_commands.json是理解“某文件使用什么编译器、宏和 include path”的重要生成文件。结论CMake 只为 on build 生成了该 translation unit 的编译规则。编译确认8.3 Object 文件find/mnt/c/study/1-zephyr/work/ch02_compiled_on/CMakeFiles/app.dir\-name*compiled_feature*find/mnt/c/study/1-zephyr/work/ch02_compiled_off/CMakeFiles/app.dir\-name*compiled_feature*实际只有 on build 存在ch02_compiled_on/CMakeFiles/app.dir/src/compiled_feature.c.objoff build 中没有同名 object。编译确认8.4 ELF symbolnm-n/mnt/c/study/1-zephyr/work/ch02_compiled_on/zephyr/zephyr.elf\|rglearn_compiled_feature_runnm-n/mnt/c/study/1-zephyr/work/ch02_compiled_off/zephyr/zephyr.elf\|rglearn_compiled_feature_run实际结果on ELF: 0000000000000000 T learn_compiled_feature_run off ELF: 无匹配 symbolT表示该 symbol 位于代码段地址显示为0与此处 native ELF 的链接形式有关本节只关注 symbol 是否存在。编译确认8.5 运行结果on - cmake_featurecompiled and linked off - cmake_featurenot compiled两套zephyr.exe均实际运行并得到上述输出。模拟确认五层证据构成完整链路.config - compile command - object - ELF symbol - runtime behavior本次实验五层证据相互吻合因此不是仅根据源码或预期得出的推断。9. 三种常用 Zephyr CMake 条件函数9.1target_sources_ifdeftarget_sources_ifdef(CONFIG_FOO app PRIVATE foo.c)适合 application target 或已知 CMake target 的条件源码。9.2zephyr_library_sources_ifdefzephyr_library_sources_ifdef(CONFIG_FOO foo.c)适合已经通过zephyr_library()创建的 Zephyr library。本地 Zephyr 实现位置zephyr/cmake/modules/extensions.cmake:20419.3add_subdirectory_ifdefadd_subdirectory_ifdef(CONFIG_FOO foo)适合控制整个驱动、子系统或功能目录是否进入 CMake 构建树。本地 Zephyr 实现位置zephyr/cmake/modules/extensions.cmake:1993选择层级控制整个功能目录 - add_subdirectory_ifdef 控制 library 内单个实现文件 - zephyr_library_sources_ifdef 控制 app target 内单个文件 - target_sources_ifdef10. CMake 条件与 C 预处理条件的区别CMake 条件target_sources_ifdef(CONFIG_FOO app PRIVATE foo.c)功能关闭时文件不产生编译命令不产生 object不检查该文件中的 include、语法和引用缩短构建时间特别适合完整后端和驱动实现。C 预处理条件#ifdefined(CONFIG_FOO)/* ... */#endif文件本身仍然参与编译只是部分 token 在预处理阶段被移除。适合小范围代码差异结构体成员不同 API 调用header 中的声明或 inline stub。原则整个实现文件可选时优先从 CMake 裁剪单个文件内部的小差异用#if。11. 常见排查方法怀疑“配置开了但源码没编进去”时按顺序检查.config中 symbol 最终是否为y父目录是否被add_subdirectory_ifdef()加入当前目录 CMake 是否通过*_sources_ifdef()加入文件compile_commands.json是否存在该文件对应.obj是否生成zephyr.map或nm zephyr.elf是否有目标 symbol是否被 linker garbage collection 移除。不要只检查prj.conf因为它不是最终配置。12. 本节结论Kconfig 决定功能最终是否启用 CMake 根据最终 CONFIG_* 决定目录和 translation unit C preprocessor 决定已选中 translation unit 内部保留哪些代码 Linker 合并 object并可能移除未引用 section完整裁剪链路Kconfig - CMake - Compiler - Linker - ELF14. 自测题CONFIG_FOOn时CMake 条件排除和#if排除有什么区别什么情况下使用add_subdirectory_ifdef()什么情况下使用zephyr_library_sources_ifdef()off build 中没有 object能说明哪一阶段已排除源码object 存在但 ELF 中没有 symbol可能是什么原因为什么调用可选实现时也需要条件保护或 stubUSS 主开关与USS_PROFILING分别在哪个粒度控制构建下一节02-5-常见警告、配置追踪与第二章总结。