使用CMake ExternalProject构建项目时链接耗时过长问题排查
问题描述
我正尝试基于CMake构建一个简单的C++项目,该项目依赖Spdlog库。需求是将依赖库本地存放在源码旁,每次构建应用时可同时编译依赖库,既能随时升级库版本,也能在无网络时不依赖远程仓库,且仅在删除build目录时才重新编译依赖库,无需每次清理项目后都重新编译。
项目结构
├── CMakeLists.txt ├── inc/ │ ├── main.h ├── lib/ │ ├── spdlog/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── spdlog/ │ │ │ ├── spdlog.h │ │ │ ├── ... │ │ ├── src/ │ │ │ ├── ... │ │ ├── tests/ │ │ │ ├── ... │ │ ├── CMakeLists.txt │ │ ├── ... ├── src/ │ ├── main.cpp └── build/
main.cpp代码
#include <iostream> #include <spdlog/spdlog.h> int main() { spdlog::info("Hello , World!"); return 0; }
现有CMake脚本
cmake_minimum_required(VERSION 3.10) project(MyProjec) set(CMAKE_VERBOSE_MAKEFILE ON) # Add ExternalProject_Add for spdlog include(ExternalProject) ExternalProject_Add(spdlog_external PREFIX ${CMAKE_BINARY_DIR}/spdlog SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/spdlog BINARY_DIR ${CMAKE_BINARY_DIR}/spdlog_build INSTALL_COMMAND "" CMAKE_ARGS -DSPDLOG_BUILD_SHARED=ON # Adjust options as needed -DSPDLOG_BUILD_STATIC=OFF -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/spdlog_install BUILD_COMMAND ${CMAKE_COMMAND} --build . --target install && ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/lib/spdlog/include ${CMAKE_BINARY_DIR}/spdlog_build/include ) # Set the include directory for the main project include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc) include_directories(${CMAKE_BINARY_DIR}/spdlog_build/include) # Set the source files for the main project set(SOURCES src/main.cpp ) # Set the header files for the main project set(HEADERS inc/main.h ) # Create the executable for the main project add_executable(MyProjec ${SOURCES} ${HEADERS}) # Specify the include directories for the executable target_include_directories(MyProjec PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc ${CMAKE_BINARY_DIR}/spdlog_build/include) # Ensure spdlog is built before building the main project add_dependencies(MyProjec spdlog_external) # Get the spdlog library path ExternalProject_Get_Property(spdlog_external BINARY_DIR) set(SPDLOG_LIB_DIR ${BINARY_DIR}) set(SPDLOG_INCLUDE_DIR ${BINARY_DIR}/include) # Link the spdlog library to the main project if (UNIX) target_link_libraries(MyProjec PRIVATE ${SPDLOG_LIB_DIR}/libspdlog.so) else() target_link_libraries(MyProjec PRIVATE ${SPDLOG_LIB_DIR}/spdlog.dll) endif()
当前问题:Spdlog可正常编译,CMake也能将头文件复制到对应目录,但每次构建应用(哪怕仅对main.cpp做简单修改)时,链接步骤耗时过长(普通笔记本约60秒),询问CMake脚本是否存在问题。
问题分析与修复方案
核心问题
你的CMake脚本存在两个关键问题导致链接耗时过长:
- 手动复制头文件触发不必要的依赖:
BUILD_COMMAND里的copy_directory操作会在每次构建时执行,导致spdlog_external目标被重新触发,间接让链接步骤重复执行不必要的操作。 - 直接链接.so/.dll文件而非CMake目标:手动指定库文件路径没有利用CMake的依赖追踪机制,无法正确识别库是否已更新,导致每次都重新链接。
修复后的CMake脚本
cmake_minimum_required(VERSION 3.10) project(MyProjec) set(CMAKE_VERBOSE_MAKEFILE ON) # 控制spdlog的编译选项(需在add_subdirectory前设置) set(SPDLOG_BUILD_SHARED ON CACHE BOOL "Build shared library" FORCE) set(SPDLOG_BUILD_STATIC OFF CACHE BOOL "Build static library" FORCE) # 直接引入本地spdlog库,替代ExternalProject add_subdirectory(lib/spdlog) # 设置主项目源文件 set(SOURCES src/main.cpp ) set(HEADERS inc/main.h ) add_executable(MyProjec ${SOURCES} ${HEADERS}) # 指定主项目的包含目录 target_include_directories(MyProjec PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc ) # 直接链接spdlog的CMake目标,自动处理依赖和路径 target_link_libraries(MyProjec PRIVATE spdlog::spdlog)
修复说明
用
add_subdirectory替代ExternalProject_Add:- 本地依赖库直接通过
add_subdirectory引入,CMake会自动处理spdlog的构建流程,仅在spdlog源码或配置变更时才重新编译,完全符合你“仅删除build目录才重新编译依赖”的需求。 - 无需手动复制头文件,spdlog的CMake配置已经自动导出了包含目录,通过链接目标时会自动传递包含路径。
- 本地依赖库直接通过
链接CMake目标而非直接库文件:
spdlog::spdlog是spdlog提供的官方CMake目标,链接它会自动处理库路径、包含目录、编译选项等,CMake能精准追踪库的更新状态,只有当库本身变化时才会重新链接,避免不必要的耗时操作。
简化编译选项设置:
- 在
add_subdirectory前设置spdlog的缓存变量,直接控制其编译模式,无需在ExternalProject里传递复杂参数。
- 在
额外优化建议
如果需要隔离依赖的构建目录(比如不想让spdlog的build文件混在主项目build里),可以添加以下配置:
# 自定义spdlog的构建目录 set(SPDLOG_BUILD_DIR ${CMAKE_BINARY_DIR}/spdlog_build) add_subdirectory(lib/spdlog ${SPDLOG_BUILD_DIR})
内容的提问来源于stack exchange,提问作者Mahmoud Hosseinipour
相关产品推荐
相关产品推荐

