CMake优化:如何避免每次构建项目时重新构建所有依赖项
从你的描述和构建输出来看,问题核心在于ExternalProject的默认行为以及项目依赖关系的设置——每次构建主项目时,CMake都会强制触发ExternalProject的更新、配置等冗余步骤,哪怕这些依赖已经完全是最新状态。下面是几个针对性的优化方案,一步步帮你解决这个问题:
1. 给ExternalProject添加UPDATE_DISCONNECTED ON参数
这个参数会告诉CMake:除非你手动触发更新(比如删除依赖的构建目录),否则不会去检查Git仓库的更新,直接跳过耗时的update步骤。这能省去每次构建时拉取仓库、校验HEAD的时间。
修改你的GLFW和GLAD的ExternalProject_Add配置:
# SETUP GLFW set(libGLFW glfw) ExternalProject_Add(${libGLFW} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW} GIT_REPOSITORY https://github.com/glfw/glfw.git GIT_TAG 3.3.4 GIT_SHALLOW ON UPDATE_DISCONNECTED ON # 新增这一行 CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW}/install -DGLFW_BUILD_DOCS:BOOL=OFF -DGLFW_BUILD_EXAMPLES:BOOL=OFF -DGLFW_BUILD_TESTS:BOOL=OFF ) # SETUP GLAD set(libGLAD glad) ExternalProject_Add(${libGLAD} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD} GIT_REPOSITORY https://github.com/Dav1dde/glad.git GIT_TAG origin/master GIT_SHALLOW ON UPDATE_DISCONNECTED ON # 新增这一行 CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD}/install -DGLAD_INSTALL:BOOL=ON -DGLAD_PROFILE:STRING="core" -DGLAD_ALL_EXTENSIONS:BOOL=ON -DUSE_MSVC_RUNTIME_LIBRARY_DLL:BOOL=OFF )
2. 调整依赖关系:让导入库依赖ExternalProject,而非主项目直接依赖
你当前用add_dependencies(${PROJECT_NAME} ${libGLFW} ${libGLAD})让主项目直接绑定ExternalProject目标,这会导致每次构建主项目时都要先跑一遍ExternalProject的全流程。正确的做法是让导入的静态库目标(GLFW_LIBRARY、GLAD_LIBRARY)依赖ExternalProject目标,这样只有当导入库的文件不存在时,才会触发依赖的构建。
修改这部分代码:
# 删除原来的 add_dependencies(${PROJECT_NAME} ${libGLFW} ${libGLAD}) # 改为让导入库依赖ExternalProject目标 add_dependencies(GLFW_LIBRARY ${libGLFW}) add_dependencies(GLAD_LIBRARY ${libGLAD})
这样主项目只依赖GLFW_LIBRARY和GLAD_LIBRARY这两个导入库,而导入库只会在自身的导入文件缺失时,才会触发对应的ExternalProject构建,不会每次主项目构建都跑一遍依赖的冗余流程。
3. 优化ZERO_CHECK项目(可选,进一步提升体验)
Visual Studio中的ZERO_CHECK项目是CMake用来检查CMakeLists.txt是否有变化的,默认每次构建都会运行它。如果你不想每次构建都检查CMake配置,可以在生成VS项目时添加参数:
cmake .. -DCMAKE_SUPPRESS_REGENERATION=ON
这样只有当你手动修改了CMakeLists.txt后,才会触发重新生成配置,否则ZERO_CHECK会直接跳过。
修改后的完整CMakeLists.txt
把上面的修改整合后,你的CMakeLists.txt会变成这样:
cmake_minimum_required(VERSION 3.20.0) # Define our project name set(PROJECT_NAME test) project(${PROJECT_NAME}) if(MSVC) get_filename_component(_vs_bin_path "${CMAKE_LINKER}" DIRECTORY) set(libexe "${_vs_bin_path}/lib.exe") message("LOOKHERE3:${libexe}") endif() # Make sure binary directory is not the same as source directory if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.") endif() # This Project Depends on External Project(s) include(ExternalProject) # SETUP GLFW set(libGLFW glfw) ExternalProject_Add(${libGLFW} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW} GIT_REPOSITORY https://github.com/glfw/glfw.git GIT_TAG 3.3.4 GIT_SHALLOW ON UPDATE_DISCONNECTED ON CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW}/install -DGLFW_BUILD_DOCS:BOOL=OFF -DGLFW_BUILD_EXAMPLES:BOOL=OFF -DGLFW_BUILD_TESTS:BOOL=OFF ) set(GLFW_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW}/install) add_library(GLFW_LIBRARY STATIC IMPORTED) set_target_properties(GLFW_LIBRARY PROPERTIES IMPORTED_LOCATION ${GLFW_INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}glfw3${CMAKE_STATIC_LIBRARY_SUFFIX} ) add_dependencies(GLFW_LIBRARY ${libGLFW}) # 新增依赖 # SETUP GLAD set(libGLAD glad) ExternalProject_Add(${libGLAD} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD} GIT_REPOSITORY https://github.com/Dav1dde/glad.git GIT_TAG origin/master GIT_SHALLOW ON UPDATE_DISCONNECTED ON CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD}/install -DGLAD_INSTALL:BOOL=ON -DGLAD_PROFILE:STRING="core" -DGLAD_ALL_EXTENSIONS:BOOL=ON -DUSE_MSVC_RUNTIME_LIBRARY_DLL:BOOL=OFF ) set(GLAD_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD}/install) add_library(GLAD_LIBRARY STATIC IMPORTED) set_target_properties(GLAD_LIBRARY PROPERTIES IMPORTED_LOCATION ${GLAD_INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}glad${CMAKE_STATIC_LIBRARY_SUFFIX} ) add_dependencies(GLAD_LIBRARY ${libGLAD}) # 新增依赖 # CREATE OUR LIBRARY file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/inc/*.h") file(GLOB_RECURSE sources CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") add_library(${PROJECT_NAME} STATIC ${headers} ${sources}) # Add all include file paths target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc PUBLIC ${GLFW_INSTALL_DIR}/include PUBLIC ${GLAD_INSTALL_DIR}/include ) # JOIN ALL LIBS set(LIBNAME "${CMAKE_CURRENT_BINARY_DIR}/final/combinedLib.lib") add_custom_command( OUTPUT ${LIBNAME} COMMAND ${libexe} /OUT:${LIBNAME} $<TARGET_FILE:GLFW_LIBRARY> $<TARGET_FILE:GLAD_LIBRARY> $<TARGET_FILE:${PROJECT_NAME}> DEPENDS GLFW_LIBRARY GLAD_LIBRARY ${PROJECT_NAME} COMMENT "Combining libs..." ) add_custom_target(combinedLibGenerator DEPENDS ${LIBNAME} ) add_library(combinedLib STATIC IMPORTED) set_property(TARGET combinedLib PROPERTY IMPORTED_LOCATION ${LIBNAME}) add_dependencies(combinedLib combinedLibGenerator)
验证效果
修改后,当你再次修改主项目的源码(比如添加cout输出)并构建时,VS只会构建test项目和combinedLibGenerator,不会再跑glad和glfw的update、configure、build步骤——除非你删除了对应的依赖安装目录,或者手动触发了依赖的更新。
内容的提问来源于stack exchange,提问作者whitwhoa




