You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多子目录单可执行文件CMake配置:编译慢与宏定义问题排查

解决方案:优化编译速度与宏定义问题

一、编译速度慢的优化方案

1. 将共享库改为静态库或对象库

当前子模块均使用SHARED库,每个库单独编译、链接会带来额外开销。对于单可执行文件场景,改用**静态库(STATIC)或对象库(OBJECT)**可大幅减少编译链接成本:

  • 静态库会打包所有目标文件,最终直接链接到可执行文件;
  • 对象库直接生成目标文件,最后合并到可执行文件,省去库打包步骤。

修改子目录的add_library命令,例如src/control/CMakeLists.txt:

# 替换原SHARED为STATIC或OBJECT
add_library(control STATIC ${control_source_files})

2. 启用并行编译

通过CMake开启多线程并行编译,利用CPU多核缩短总编译时间:

  • 在顶层src/CMakeLists.txt开头添加:
# 根据CPU核心数自动设置并行编译线程数,也可手动指定数字(如8)
set(CMAKE_BUILD_PARALLEL_LEVEL ${CMAKE_PROCESSOR_COUNT})
  • 或构建时通过命令行指定:cmake --build . -j8

3. 修复依赖传递错误并优化头文件包含

现有配置存在target名称错误(如control模块中误用robot_control、first模块中误用motor作为target名),导致include目录无法正确传递,引发重复包含或头文件解析冗余。修正后通过PUBLIC/INTERFACE正确传递依赖,避免重复配置:

修正src/control/CMakeLists.txt的include目录:

# 将robot_control改为实际库名control
target_include_directories(control PUBLIC 
    ${CMAKE_CURRENT_SOURCE_DIR}/../second
    ${CMAKE_CURRENT_SOURCE_DIR}/../third
)

修正src/first/CMakeLists.txt的include目录:

# 将motor改为实际库名first
target_include_directories(first PUBLIC 
    ${CMAKE_CURRENT_SOURCE_DIR}/../second
    ${CMAKE_CURRENT_SOURCE_DIR}/../logger
    ${CMAKE_CURRENT_SOURCE_DIR}/../../3rd_party/date/include/
)

修正后,可执行文件链接库时会自动继承其include目录,无需重复设置。

4. 第三方库优化:禁用不必要特性

针对spdlog,禁用示例、测试等非必要功能以减少编译时间:

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../3rd_party/spdlog ${CMAKE_CURRENT_BINARY_DIR}/spdlog)
# 禁用spdlog冗余构建目标
set(SPDLOG_BUILD_EXAMPLES OFF CACHE BOOL "")
set(SPDLOG_BUILD_TESTS OFF CACHE BOOL "")
set(SPDLOG_BUILD_BENCH OFF CACHE BOOL "")

二、宏定义(DEBUG/RELEASE)不生效的解决方案

1. 基于构建类型自动设置宏

利用CMake的CMAKE_BUILD_TYPE配合生成器表达式,自动为不同构建类型定义宏,确保所有子模块都能获取到宏定义:

在顶层src/CMakeLists.txt添加:

# 根据构建类型自动定义对应宏
add_compile_definitions(
    $<$<CONFIG:Debug>:DEBUG>
    $<$<CONFIG:Release>:RELEASE>
    $<$<CONFIG:RelWithDebInfo>:RELEASE>
)

构建时指定类型即可自动生效,例如:

  • Debug模式:cmake -DCMAKE_BUILD_TYPE=Debug ..,自动定义DEBUG宏
  • Release模式:cmake -DCMAKE_BUILD_TYPE=Release ..,自动定义RELEASE宏

2. 确保宏定义传递到所有子库

若需手动定义宏,不要仅给可执行文件设置,需通过全局编译定义或子库传递:

  • 全局设置(所有目标继承):
add_compile_definitions(RELEASE)
  • 子库单独设置(以control模块为例):
target_compile_definitions(control PUBLIC RELEASE)

3. 修正原有宏定义的无效问题

原配置中仅给可执行文件设置target_compile_definitions(${PROJECT_NAME} PUBLIC RELEASE),但子库是单独编译的,无法继承该定义。通过上述两种方法,可确保宏定义在子库编译阶段生效。

三、优化后的顶层src/CMakeLists.txt示例

cmake_minimum_required(VERSION 3.18)

project(integration LANGUAGES CXX)

# 启用并行编译
set(CMAKE_BUILD_PARALLEL_LEVEL ${CMAKE_PROCESSOR_COUNT})

# 基于构建类型自动设置宏
add_compile_definitions(
    $<$<CONFIG:Debug>:DEBUG>
    $<$<CONFIG:Release>:RELEASE>
)

# 配置spdlog构建选项
set(SPDLOG_BUILD_EXAMPLES OFF CACHE BOOL "")
set(SPDLOG_BUILD_TESTS OFF CACHE BOOL "")
set(SPDLOG_BUILD_BENCH OFF CACHE BOOL "")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../3rd_party/spdlog ${CMAKE_CURRENT_BINARY_DIR}/spdlog)

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

# 添加子模块
add_subdirectory(first)
add_subdirectory(second)
add_subdirectory(third)
add_subdirectory(control)
add_subdirectory(logger) 

set(src_main main.cpp)
add_executable(${PROJECT_NAME} ${src_main})

target_link_libraries(${PROJECT_NAME} 
    PRIVATE 
        control 
        first 
        second 
        third 
        logger
        Threads::Threads
        spdlog::spdlog
)

四、关键注意事项

  • 所有子模块的target_include_directories必须与add_library定义的库名一致,否则依赖传递失效;
  • 优先使用PUBLIC/INTERFACE/PRIVATE控制依赖范围,避免重复配置;
  • 构建时明确指定CMAKE_BUILD_TYPE,确保宏定义与编译优化策略正确生效。

内容的提问来源于stack exchange,提问作者user2424097

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 13:50:19