1. CMake工程
1.1 基本CMake工程
- CMake
# CMake最低版本
cmake_minimum_required(VERSION 3.21)
# 工程名、版本号、工程描述
project(FlyShearSim
VERSION 0.1
DESCRIPTION "Test")
# C/C++ 标准版本
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
# 指定在当前目录或以下目录下通过命令(如 add_executable() )链接稍后创建的任何目标时要使用的库或标志
LINK_LIBRARIES(m)
# 工程源文件
add_executable(${PROJECT_NAME}
./Src/main.c
./Src/FlyingShear.c)
# 工程包含目录
include_directories(./Inc)
- 工程目录结构
.
├── CMakeLists.txt
├── Inc
│ ├── FlyingShear.h
│ └── main.h
└── Src
├── FlyingShear.c
└── main.c
1.2. 多模块CMake工程
1.3. CMakePresets.json
# ===== 基本系统信息 =====
set(CMAKE_SYSTEM_NAME Generic) # 设置目标系统名称为 "Generic"(裸机或无操作系统)
set(CMAKE_SYSTEM_PROCESSOR arm) # 设置目标处理器架构为 ARM
# ===== 编译器标识(可省略,此处手动声明) =====
set(CMAKE_C_COMPILER_ID GNU) # 声明 C 编译器 ID 为 GNU(GCC)
set(CMAKE_CXX_COMPILER_ID GNU) # 声明 C++ 编译器 ID 为 GNU(G++)
# ===== 工具链前缀与可执行文件路径 =====
# 注意:arm-none-eabi- 的 bin 目录必须在 PATH 环境变量中,或使用绝对路径
set(TOOLCHAIN_PREFIX E:/sofeWare/GCC/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-) # 定义工具链的可执行文件前缀(含完整路径)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc.exe) # 指定 C 编译器
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) # 汇编编译器与 C 编译器相同(GCC 支持汇编)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++.exe) # 指定 C++ 编译器
set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}g++.exe) # 链接器使用 g++(可以链接 C++ 标准库,也可用 ld)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy.exe) # 指定 objcopy 工具(用于格式转换,如生成 hex/bin)
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size.exe) # 指定 size 工具(查看段大小)
set(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump.exe) # 指定 objdump 工具(反汇编等)
set(CMAKE_NM ${TOOLCHAIN_PREFIX}nm.exe) # 指定 nm 工具(列出符号)
# ===== 可执行文件后缀 =====
set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf") # 汇编源文件构建的可执行文件后缀设为 .elf
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") # C 源文件构建的可执行文件后缀设为 .elf
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf") # C++ 源文件构建的可执行文件后缀设为 .elf
# ===== 交叉编译检测设置 =====
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # 告诉 CMake 在探测编译器能力时,构建静态库而非可执行文件(因为裸机无法直接运行)
# ===== MCU 特定编译标志 =====
set(TARGET_FLAGS "-mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard ") # 定义目标 MCU 的 CPU、FPU 和浮点 ABI 选项
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_FLAGS}") # 将 MCU 标志追加到 C 编译标志
set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -MMD -MP") # 汇编标志:继承 C 标志,并添加汇编预处理器支持和自动生成依赖文件
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fdata-sections -ffunction-sections") # 添加警告和分节编译选项(便于链接时 --gc-sections 优化)
# ===== Debug 与 Release 模式标志 =====
set(CMAKE_C_FLAGS_DEBUG "-O0 -g3") # Debug 模式下:无优化,最高调试信息级别
set(CMAKE_C_FLAGS_RELEASE "-Os -g0") # Release 模式下:体积优化,无调试信息
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") # C++ Debug 标志
set(CMAKE_CXX_FLAGS_RELEASE "-Os -g0") # C++ Release 标志
# ===== C++ 专用编译标志 =====
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics") # C++ 标志:继承 C 标志,并禁用 RTTI、异常和线程安全的静态变量
# ===== 链接标志 =====
set(CMAKE_EXE_LINKER_FLAGS "${TARGET_FLAGS}") # 设置链接器基础标志(与编译标志相同的 MCU 选项)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T \"${CMAKE_SOURCE_DIR}/gd32h7xx_flash.ld\"") # 指定链接脚本路径(注意:此处需要根据实际芯片更改链接脚本)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --specs=nano.specs") # 使用 newlib-nano 库规格,减少库体积
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections") # 生成 map 文件,并启用未用段的垃圾回收
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--print-memory-usage") # 链接时打印内存使用信息
# ===== 链接库 =====
set(TOOLCHAIN_LINK_LIBRARIES "m") # 指定要链接的库(这里为数学库 libm)
{ // 根对象开始:CMakePresets 的顶层结构
"version": 3, // 指定预设文件的架构版本(必须为 3)。版本 3 支持 buildPresets 和 testPresets
"configurePresets": [ // 定义配置预设数组,每个对象描述一套配置选项
{
"name": "default", // 该预设的名称(必须唯一)。这里命名为 "default"
"hidden": true, // 标记此预设为隐藏,不会直接显示给用户(通常作为基础预设被其他预设继承)
"generator": "Ninja", // 指定构建系统生成器(Ninja、Unix Makefiles 等)
"binaryDir": "${sourceDir}/build/${presetName}", // 设置构建目录,宏变量支持:${sourceDir} 源码根目录,${presetName} 预设名称,最终为 "源码目录/build/default"
"toolchainFile": "${sourceDir}/Configs/gcc-arm-none-eabi.cmake", // 指定工具链文件路径,用于交叉编译(这里是 ARM GCC 工具链)
"cacheVariables": { // 定义 CMake 缓存变量(此处为空对象,未设置额外变量)
}
},
{
"name": "Debug", // 配置预设 "Debug"
"inherits": "default", // 继承 "default" 预设的所有属性,可在此基础上覆盖或添加
"cacheVariables": { // 该预设的缓存变量
"CMAKE_BUILD_TYPE": "Debug" // 设置构建类型为 Debug(包含调试符号,未优化)
}
},
{
"name": "Release", // 配置预设 "Release"
"inherits": "default", // 同样继承 "default"
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release" // 设置构建类型为 Release(优化,无调试符号)
}
}
],
"buildPresets": [ // 定义构建预设数组,与 configurePresets 关联
{
"name": "Debug", // 构建预设 "Debug"
"configurePreset": "Debug" // 关联到同名的配置预设 "Debug"
},
{
"name": "Release", // 构建预设 "Release"
"configurePreset": "Release" // 关联到同名的配置预设 "Release"
}
]
} // 根对象结束
2. CMake其他功能
2.1. CMake自定义指令
add_custom_command 是 CMake 中用来向构建系统添加自定义构建规则的命令。它主要有两种用法:生成文件(OUTPUT) 和 目标触发(TARGET)。
- 生成文件模式
用于描述如何生成一个或多个输出文件。它与其他构建规则一样,只有当输出文件被某个 target 依赖时才会执行。
add_custom_command( # 开始定义一条自定义命令(生成文件模式)
OUTPUT output1 [output2 ...] # 【必需】指定命令要生成的一个或多个输出文件。构建系统以此文件名判断命令是否需要执行。
COMMAND command1 [ARGS] [args1...] # 【必需】定义要执行的第一条命令(可带参数)。方括号内 ARGS 是旧式占位符,直接写实际参数即可。
[COMMAND command2 [ARGS] [args2...] ...] # 【可选】定义后续命令,按书写顺序依次执行。支持多条 COMMAND。
[MAIN_DEPENDENCY depend] # 【可选】为 IDE(如 Visual Studio)指定一个“主要依赖”文件,用于在工程树上定位源文件。
[DEPENDS [depends...]] # 【可选】声明该命令的依赖文件或 CMake 目标。当任何依赖发生变化时,命令会重新执行。
[BYPRODUCTS [files...]] # 【可选】声明命令产生的“副产品”文件。它们不是输出,但构建工具(Ninja)需要知道,以正确推断依赖。
[IMPLICIT_DEPENDS <lang1> depend1 ...] # 【可选】要求扫描指定语言文件的隐式依赖(如 C 文件的 #include),自动添加为依赖。
[WORKING_DIRECTORY dir] # 【可选】设置命令执行时的工作目录。
[COMMENT "comment"] # 【可选】构建期间显示的说明文字,会在执行命令前打印。
[DEPFILE depfile] # 【可选】指定一个 .d 格式的依赖文件(通常由命令自身生成),用于动态发现依赖关系。
[JOB_POOL pool] # 【可选】将该命令放入指定的作业池(与 Ninja 的 job pool 配合),控制并发执行数量。
[VERBATIM] [APPEND] [USES_TERMINAL] # 【可选标志】VERBATIM:对命令参数进行正确的跨平台转义(强烈推荐);APPEND:向已存在的 OUTPUT 自定义命令追加 COMMAND;USES_TERMINAL:命令需要连接到终端。
[COMMAND_EXPAND_LISTS] # 【可选】若命令参数是列表变量,则将其展开为多个独立的参数,而非整体作为一个参数。
) # 结束自定义命令的定义
- 目标触发模式
附加到一个现有 target(如可执行文件或库),在 target 构建的某个阶段执行额外命令。不产生输出文件,仅用于副作用。
add_custom_command( # 开始定义一条自定义命令(目标触发模式)
TARGET <target> # 【必需】指定要挂载自定义命令的 CMake 目标名(如可执行文件、库)。
PRE_BUILD | PRE_LINK | POST_BUILD # 【必需】选择命令的触发阶段:PRE_BUILD(构建前)、PRE_LINK(编译后链接前)、POST_BUILD(链接后)。
COMMAND command1 [ARGS] [args1...] # 【必需】定义要执行的第一条命令。ARGS 是旧式占位符,实际使用时直接写命令和参数即可。
[COMMAND command2 [ARGS] [args2...] ...] # 【可选】定义后续命令,按书写顺序依次执行。支持多条。
[BYPRODUCTS [files...]] # 【可选】声明命令产生的“副产品”文件,让构建工具(如 Ninja)正确处理依赖。
[WORKING_DIRECTORY dir] # 【可选】设置命令执行时的工作目录。
[COMMENT "comment"] # 【可选】构建时打印的说明信息。
[VERBATIM] [USES_TERMINAL] # 【可选标志】VERBATIM:跨平台正确转义参数(强烈推荐);USES_TERMINAL:命令需要与终端交互。
[COMMAND_EXPAND_LISTS] # 【可选】若命令参数是列表变量,则将其展开为多个独立参数,而不是当做一个整体。
) # 结束定义
2.1.1. 反汇编
# 构建后反汇编
add_custom_command(
TARGET ${CMAKE_PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_OBJDUMP} -d -S $<TARGET_FILE:${CMAKE_PROJECT_NAME}> > ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.asm
COMMENT "反汇编中..."
VERBATIM
)
2.1.2. elf转bin/Hex
# 构建后输出Hex和bin
add_custom_command(
TARGET ${CMAKE_PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.hex
COMMENT "输出Hex文件..."
VERBATIM
)
add_custom_command(
TARGET ${CMAKE_PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin
COMMENT "输出Bin文件..."
VERBATIM
)
2.1.3. 构建后导出符号表
# 构建后导出符号表
add_custom_command(
TARGET ${CMAKE_PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_NM} -n $<TARGET_FILE:${CMAKE_PROJECT_NAME}> > ${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}_symbol_table.txt
COMMENT "导出符号表中..."
VERBATIM
)
2.1.4. 编译着色器
cmake_minimum_required(VERSION 3.10)
project(Shader)
# 查找 glslc 编译器
set(GLSLC ${CMAKE_SOURCE_DIR}/glslc/bin/glslc.exe)
# 定义着色器文件列表
set(SHADER_FILES
shader.vert
shader.frag
)
if(WIN32)
# Windows平台特定的设置
set(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/BIN/$<CONFIGURATION>/WIN32")
elseif(APPLE)
# Apple平台特定的设置
set(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/BIN/$<CONFIGURATION>/APPLE")
elseif(UNIX) # 注意:Linux和macOS都满足UNIX为TRUE,所以通常先判断APPLE
# Linux或其他类Unix系统的设置
set(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/BIN/$<CONFIGURATION>/UNIX")
endif()
# 遍历所有着色器,编译为 .spv 文件
foreach(SHADER ${SHADER_FILES})
# 输出文件路径(放在 build/shaders 目录)
set(SPIRV "${CMAKE_BINARY_DIR}/shaders/${SHADER}.spv")
# 创建输出目录
add_custom_command(
OUTPUT ${SPIRV}
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/shaders/"
# 调用 glslc 编译
COMMAND ${GLSLC} ${CMAKE_CURRENT_SOURCE_DIR}/${SHADER} -o ${SPIRV}
DEPENDS ${SHADER} # 依赖源文件,源文件修改后自动重新编译
COMMENT "Compiling ${SHADER} to SPIR-V"
)
# 添加到构建目标
list(APPEND SPIRV_FILES ${SPIRV})
endforeach()
# 创建一个目标,确保着色器在项目构建时被编译
add_custom_target(Shaders ALL DEPENDS ${SPIRV_FILES})