在面向裸机与RTOS项目中,CMake应用可以更好的让项目保持构建清晰、可维护、可扩展。
为什么不纯手写 Makefile
- 跨平台与可移植:同一套
CMakeLists.txt可生成 Ninja/Makefile/VS/Xcode 等工程。 - 模块化管理:子目录+目标(Targets)驱动,天然支持分层(驱动/中间件/应用/板级)与多变体。
- 交叉编译简化:工具链通过
toolchain.cmake一次定义,多平台(ARM/RISC‑V)统一入口。 - 依赖治理:
find_package/add_subdirectory/FetchContent三路管理第三方与内部组件。 - 可观测与可维护:目标属性、生成产物、后处理(objcopy/objdump/签名)声明式配置。
工程目录参考
my_project/ ├─ CMakeLists.txt # 根工程:全局选项、变体聚合、安装与产物 ├─ CMakePresets.json # 预设:开发/CI/不同芯片与工具链一键生成 ├─ cmake/ │ ├─ toolchain-arm-none-eabi.cmake │ ├─ toolchain-riscv-none-elf.cmake │ ├─ FindCMSIS.cmake # 可选:自定义 find 模块示例 │ └─ LinkerFlags.cmake # 可选:链接与后处理的公共封装 ├─ boards/ # 板级支持包(BSP)与链接脚本 │ ├─ nucleo-f103rb/ │ │ ├─ CMakeLists.txt │ │ ├─ startup_stm32f103xb.s │ │ └─ stm32f103xb.ld │ └─ esp32c3-devkit/ # 示例:RISC-V 变体 │ ├─ CMakeLists.txt │ └─ esp32c3.ld ├─ drivers/ # 芯片厂商 HAL/LL、板载驱动、外设抽象 │ ├─ stm32/ │ └─ common/ ├─ middleware/ # 中间件与协议栈:FreeRTOS、LWIP、FatFS... │ └─ freertos/ ├─ include/ # 公共头文件(对外接口) │ └─ my_project/ │ └─ app_api.h ├─ src/ # 业务/应用层源码 │ ├─ main.c │ └─ app/ ├─ tests/ # 主机侧或仿真侧单元测试(可选) └─ build/ # 构建输出(建议 gitignore)要点:
- 将“芯片/板级差异”隔离在
boards/;将“供应商驱动/外设”放在在drivers/;将“可复用中间件”统一放在middleware/。 - 应用仅通过接口(
include/)与抽象层交互,避免在高层感知具体芯片差异。
工具链文件
以 ARM GCC 为例:cmake/toolchain-arm-none-eabi.cmake
set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) set(TOOLCHAIN_PREFIX arm-none-eabi) find_program(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) find_program(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}-gcc) find_program(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}-objcopy) find_program(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}-objdump) find_program(CMAKE_SIZE ${TOOLCHAIN_PREFIX}-size) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)板级目录
boards/nucleo-f103rb/CMakeLists.txt
add_library(board_bsp STATIC startup_stm32f103xb.s ) target_include_directories(board_bsp PUBLIC ${CMAKE_SOURCE_DIR}/include) target_compile_definitions(board_bsp PUBLIC STM32F103xB USE_HAL_DRIVER ) target_compile_options(board_bsp PUBLIC -mcpu=cortex-m3 -mthumb ${OPTIMIZATION} -ffunction-sections -fdata-sections -fno-builtin -fno-common ) target_link_options(board_bsp PUBLIC -mcpu=cortex-m3 -mthumb -Wl,--gc-sections -T${CMAKE_CURRENT_SOURCE_DIR}/stm32f103xb.ld )要点:
- 把“启动文件/链接脚本/编译器 CPU 选项/MCU 宏”封装在
board_bsp,上层只需链接该库即可继承属性。 - 更换板卡=切换
BOARD子目录,最小化对应用层的影响。
驱动与中间件
drivers/CMakeLists.txt
add_library(hal_stm32f1 OBJECT stm32/hal_gpio.c stm32/hal_uart.c # ... ) target_include_directories(hal_stm32f1 PUBLIC stm32/include) target_compile_definitions(hal_stm32f1 PUBLIC STM32F103xB)middleware/CMakeLists.txt(以 FreeRTOS 为例)
option(ENABLE_FREERTOS "Enable FreeRTOS" ON) if(ENABLE_FREERTOS) add_library(freertos STATIC freertos/portable/MemMang/heap4.c freertos/portable/GCC/ARM_CM3/port.c freertos/Source/list.c freertos/Source/queue.c freertos/Source/tasks.c # ... ) target_include_directories(freertos PUBLIC freertos/include) target_compile_definitions(freertos PUBLIC configUSE_MUTEXES=1) endif()构建原则:
- INTERFACE:只传播编译/链接属性,不产出对象(如编译选项集、公共宏、头文件路径集合)。
- OBJECT:产出
.obj供多个最终目标重用(适合 HAL/驱动模块)。 - STATIC:打包成
libxxx.a提供给应用或中间件。
应用层
src/CMakeLists.txt
add_executable(app.elf main.c app/app_task.c ) target_link_libraries(app.elf PRIVATE board_bsp hal_stm32f1 ) if(ENABLE_FREERTOS) target_link_libraries(app.elf PRIVATE freertos) endif() # 生成 hex/bin/map、符号与尺寸统计 add_custom_command(TARGET app.elf POST_BUILD COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:app.elf> ${CMAKE_BINARY_DIR}/app.hex COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:app.elf> ${CMAKE_BINARY_DIR}/app.bin COMMAND ${CMAKE_OBJDUMP} -h -S $<TARGET_FILE:app.elf> > ${CMAKE_BINARY_DIR}/app.list COMMAND ${CMAKE_SIZE} --format=berkeley $<TARGET_FILE:app.elf> > ${CMAKE_BINARY_DIR}/size.txt BYPRODUCTS ${CMAKE_BINARY_DIR}/app.hex ${CMAKE_BINARY_DIR}/app.bin ${CMAKE_BINARY_DIR}/app.list ${CMAKE_BINARY_DIR}/size.txt ) add_custom_target(app.hex DEPENDS app.elf) add_custom_target(app.bin DEPENDS app.elf) add_custom_target(mapfile COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:app.elf> ${CMAKE_BINARY_DIR}/app.map DEPENDS app.elf) add_custom_target(sizeinfo DEPENDS app.elf)要点:
- 所有后处理显式声明在目标上,CI/CD 中可直接依赖
app.hex/app.bin。 - 可按需追加签名/校验(如 Bootloader 校验)步骤。
依赖管理法
- find_package:优先用于系统安装或外部预装组件(CMSIS、系统工具)。
find_package(CMSIS REQUIRED) target_include_directories(hal_stm32f1 PUBLIC ${CMSIS_INCLUDE_DIRS})- add_subdirectory:用于本仓库内的第三方子模块或子仓(git submodule)。
add_subdirectory(external/lwip) target_link_libraries(app.elf PRIVATE lwip)选择建议:本地强依赖→add_subdirectory;系统依赖→find_package;