为什么不纯手写 Makefile
在面向裸机与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.cmakeset(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.txtadd_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 -mcpucortex-m3 -mthumb ${OPTIMIZATION} -ffunction-sections -fdata-sections -fno-builtin -fno-common ) target_link_options(board_bsp PUBLIC -mcpucortex-m3 -mthumb -Wl,--gc-sections -T${CMAKE_CURRENT_SOURCE_DIR}/stm32f103xb.ld )要点把“启动文件/链接脚本/编译器 CPU 选项/MCU 宏”封装在board_bsp上层只需链接该库即可继承属性。更换板卡切换BOARD子目录最小化对应用层的影响。驱动与中间件drivers/CMakeLists.txtadd_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_MUTEXES1) endif()构建原则INTERFACE只传播编译/链接属性不产出对象如编译选项集、公共宏、头文件路径集合。OBJECT产出.obj供多个最终目标重用适合 HAL/驱动模块。STATIC打包成libxxx.a提供给应用或中间件。应用层src/CMakeLists.txtadd_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} --formatberkeley $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