CMake命令--add_custom_command

一 概述

add_custom_command 是 CMake 中用于定义自定义构建规则的指令。这个命令允许你指定一个或多个依赖项,一个或多个输出文件,以及当依赖项改变时需要执行的命令。当依赖项比输出文件新,或者输出文件不存在时,指定的命令就会执行。

add_custom_command 的基本语法如下:

cmake复制代码

add_custom_command(OUTPUT output1 [output2 ...]
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCY depend]
[DEPENDS [depends...]]
[IMPLICIT_DEPENDS <lang> depend1 ...]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] </
<think>我们正在解决用户在执行 `cmake --build {output_path} -j12 --clean-first` 时遇到的并行运行失败问题。根据之前讨论,并行编译涉及两个层面:CMake构建系统并行(通过`-j12`控制)和编译器内部并行(如MSVC的`/MP`选项)。失败可能由多种原因导致,我们将逐步排查。 ### 解决步骤 #### 1. **验证CMake生成器支持并行** 检查CMake使用的底层构建工具是否支持多线程: ```bash cmake --build {output_path} --help ``` - **关键输出**: - 若使用`Ninja`,显示`--parallel [=N]`则支持 - 若使用`Visual Studio`生成器,显示`/maxcpucount[:n]`则支持 - 若使用`MinGW Makefiles`,需确保为`make`而非`nmake` > ⚠️ 注意:部分生成器(如`Visual Studio 10`)需显式启用并行: > `cmake --build -- /m:12`代替`-j12`[^1] #### 2. **检查资源冲突** 当同时启用构建系统并行和编译器内部并行时,可能因线程超额订阅导致崩溃: - **典型症状**:编译中途报错`fatal error C1060`(MSVC内存不足)或`g++: fatal error: Killed signal terminated program cc1plus` - **解决方案**: - **方案A**:降低总并行度 将`-j12`改为`-j$(nproc)`(Linux)或`-j$env:NUMBER_OF_PROCESSORS`(PowerShell),并关闭编译器内部并行: ```cmake # CMakeLists.txt 调整 if(MSVC) add_compile_options($<$<CONFIG:RELEASE>:/MP2>) # Release模式只用2线程 endif() ``` - **方案B**:手动平衡资源 总线程数公式:$$ \text{CMake并行数} \times \text{编译器线程数} \leq \text{CPU逻辑核心数} $$ 例如8核CPU:`cmake --build build -j4` + MSVC设置`/MP2` #### 3. **处理依赖问题** `--clean-first`可能在并行清理时引发竞态条件: - **现象**:清理未完成即开始编译,导致文件丢失 - **解决方案**: - 分步执行清理和构建: ```bash cmake --build {output_path} --target clean cmake --build {output_path} -j12 ``` - 或在CMake中显式声明依赖: ```cmake add_custom_target(clean_all COMMAND ${CMAKE_COMMAND} -E remove_directory ${PROJECT_BINARY_DIR}/*) add_dependencies(your_target clean_all) # 强制清理先于编译 ``` #### 4. **诊断编译器兼容性** 某些编译器在特定版本下存在并行编译缺陷: - **MSVC**:v16.11之前版本可能因`/MP`与预编译头冲突而崩溃 - **GCC**:`-flto`与`-pipe`组合时可能死锁 - **排查方法**: ```bash cmake --build {output_path} -j1 # 单线程测试是否基础编译通过 ``` 若单线程成功,则问题在并行逻辑。 #### 5. **捕获错误日志** 通过重定向输出定位具体失败点: ```bash cmake --build {output_path} -j12 --clean-first -- VERBOSE=1 2>&1 | tee build.log ``` - **关键分析点**: - 搜索`error:`或`fatal error` - 检查是否在链接阶段失败(可能因并行编译目标未完成) ### 配置调整示例 ```cmake # 禁用问题编译器的内部并行(以Clang为例) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -j1") # 强制单线程编译 endif() # 设置全局并行构建线程数(需CMake≥3.12) set(CMAKE_BUILD_PARALLEL_LEVEL 6) # 覆盖命令-j12 ``` ### 备选方案 如上述方法无效,可尝试切换构建系统: ```bash # 切换到Ninja(通常有更好的并行稳定性) cmake -G "Ninja" -S . -B {output_path} cmake --build {output_path} -j12 ``` §§相关问题§§ 1. 如何修复CMake并行编译中的`fatal error LNK1104: cannot open file 'xxx.lib'`错误? 2. 嵌入式系统中如何优化CMake编译内存消耗? 3. CMake如何为不同目标设置差异化的并行编译策略? 4. 如何利用CMake的`try_compile`测试编译器并行兼容性? </think>### 解决方法:`cmake --build {output_path} -j12 --clean-first` 并行失败问题 #### 1. **验证构建系统支持并行** [^1] - **问题**:部分构建工具(如旧版 NMake)不支持 `-j` 参数 - **诊断**:检查 CMake 生成的构建文件类型: ```bash cat {output_path}/CMakeCache.txt | grep "CMAKE_GENERATOR" ``` - 若输出 `CMAKE_GENERATOR:INTERNAL=NMake Makefiles` → 不支持并行 - **解决**:重新生成支持并行的构建系统: ```bash # 切换到 Ninja(推荐) cmake -G "Ninja" -S . -B {output_path} # 或使用 Makefiles cmake -G "Unix Makefiles" -S . -B {output_path} ``` #### 2. **解决依赖冲突** - **问题**:`--clean-first` 可能破坏并行依赖关系 - **现象**:清理未完成时编译已启动,导致文件缺失 - **解决**:分步执行清理和构建: ```bash cmake --build {output_path} --target clean # 先完成清理 cmake --build {output_path} -j12 # 再并行编译 ``` #### 3. **避免资源争用** - **问题**:并行任务过多导致内存/CPU 超载 - **诊断**:观察失败时的系统资源占用(任务管理器) - **优化**:动态计算线程数(Windows PowerShell): ```powershell $cores = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors cmake --build build -j ($cores * 0.8) # 使用 80% 核心数 ``` #### 4. **检查编译器并行兼容性** | 编译器 | 并行选项 | 常见问题 | |----------|---------------|-------------------------| | MSVC | `/MP` | 需禁用预编译头 (`/Y-`) | | GCC/Clang| `-flto=auto` | 需足够内存(每线程 2GB+)| | GHS | `-jobs=n` | 需许可证支持多线程 | **解决方案**: ```cmake # 在 CMakeLists.txt 中关闭问题选项 if(MSVC) add_compile_options(/MP0) # 禁用 MSVC 内部并行 endif() ``` #### 5. **诊断构建日志** - **捕获详细日志**: ```bash cmake --build {output_path} -j12 --clean-first -- VERBOSE=1 2>&1 | tee build.log ``` - **关键排查点**: - `fatal error LNK1104`: 文件被多个进程争用 - `cannot open output file ... in use`: 清理与编译同时访问文件 - `C1060: compiler is out of heap space`: 内存耗尽 #### 6. **强制单线程测试** ```bash cmake --build {output_path} -j1 --clean-first # 测试基础功能 ``` - 若成功 → 确认是并行问题 - 若失败 → 检查代码/环境配置问题 ### 推荐配置模板 ```cmake # CMakeLists.txt 最佳实践 cmake_minimum_required(VERSION 3.12) project(MyProject) # 设置全局并行级别 (CMake 3.12+) set(CMAKE_BUILD_PARALLEL_LEVEL $ENV{NUMBER_OF_PROCESSORS}) # 按需禁用编译器内部并行 if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") add_compile_options(/MP0) # 显式禁用 MSVC 并行 endif() ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小蘑菇二号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值