'CMake - How to stop building every dependency every time I build my project

I have a cmake list file that is pulling in dependencies via ExternalProject_Add. On initial compilation of my project (which uses these dependencies) I would expect them to be built (or if the configuration changes say from Debug to Release). This occurs, however it occurs after any subsequent code change in the project source. So for example if I simply add a line to cout something to the terminal and build my project, visual studio goes through every project that is associated with my project and rebuilds it. Granted it skips actually re-compiling when not necessary, but going through the whole build step for each dependency is not ideal.

Therefore I'm wondering how I might structure this cmake project to avoid each dependency having to be built every time I make a change in my project's source?

Below is my CMakeLists.txt file. It's a work in progress. I'm simply adding pieces and testing as I go to insure I'm receiving the results I want (also some non cross platform things in here that are done simply for testing purposes at the moment):

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
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})
#message("LOOKHERE:${CMAKE_STATIC_LIBRARY_PREFIX}glfw3${CMAKE_STATIC_LIBRARY_SUFFIX}")


# 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
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})




# 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_dependencies(${PROJECT_NAME} ${libGLFW} ${libGLAD})


# 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)

And this is the output I'm receiving in visual studio after every build:

1>------ Build started: Project: ZERO_CHECK, Configuration: Release x64 ------
2>------ Build started: Project: glad, Configuration: Release x64 ------
2>Performing update step for 'glad'
2>HEAD is now at 71b2aa6... readme: updates conan link
2>No patch step for 'glad'
2>Performing configure step for 'glad'
2>-- Selecting Windows SDK version 10.0.16299.0 to target Windows 10.0.18362.
2>-- Configuring done
2>-- Generating done
2>-- Build files have been written to: C:/Users/myuserid/Desktop/test/build/dep/glad/src/glad-build
2>Performing build step for 'glad'
2>Microsoft (R) Build Engine version 15.5.180.51428 for .NET Framework
2>Copyright (C) Microsoft Corporation. All rights reserved.
2>
2>  glad.vcxproj -> C:\Users\myuserid\Desktop\test\build\dep\glad\src\glad-build\Release\glad.lib
2>Performing install step for 'glad'
2>Microsoft (R) Build Engine version 15.5.180.51428 for .NET Framework
2>Copyright (C) Microsoft Corporation. All rights reserved.
2>
2>  glad.vcxproj -> C:\Users\myuserid\Desktop\test\build\dep\glad\src\glad-build\Release\glad.lib
2>  -- Install configuration: "Release"
2>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glad/install/lib/glad.lib
2>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glad/install/include/glad/glad.h
2>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glad/install/include/KHR/khrplatform.h
2>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glad/install/lib/cmake/glad/gladConfig.cmake
2>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glad/install/lib/cmake/glad/gladConfigVersion.cmake
2>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glad/install/lib/cmake/glad/gladTargets.cmake
2>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glad/install/lib/cmake/glad/gladTargets-release.cmake
2>Completed 'glad'
3>------ Build started: Project: glfw, Configuration: Release x64 ------
3>Performing update step for 'glfw'
3>No patch step for 'glfw'
3>Performing configure step for 'glfw'
3>-- Selecting Windows SDK version 10.0.16299.0 to target Windows 10.0.18362.
3>-- Using Win32 for window creation
3>-- Configuring done
3>-- Generating done
3>-- Build files have been written to: C:/Users/myuserid/Desktop/test/build/dep/glfw/src/glfw-build
3>Performing build step for 'glfw'
3>Microsoft (R) Build Engine version 15.5.180.51428 for .NET Framework
3>Copyright (C) Microsoft Corporation. All rights reserved.
3>
3>  glfw.vcxproj -> C:\Users\myuserid\Desktop\test\build\dep\glfw\src\glfw-build\src\Release\glfw3.lib
3>Performing install step for 'glfw'
3>Microsoft (R) Build Engine version 15.5.180.51428 for .NET Framework
3>Copyright (C) Microsoft Corporation. All rights reserved.
3>
3>  glfw.vcxproj -> C:\Users\myuserid\Desktop\test\build\dep\glfw\src\glfw-build\src\Release\glfw3.lib
3>  -- Install configuration: "Release"
3>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glfw/install/include/GLFW
3>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glfw/install/include/GLFW/glfw3.h
3>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glfw/install/include/GLFW/glfw3native.h
3>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glfw/install/lib/cmake/glfw3/glfw3Config.cmake
3>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glfw/install/lib/cmake/glfw3/glfw3ConfigVersion.cmake
3>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glfw/install/lib/cmake/glfw3/glfw3Targets.cmake
3>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glfw/install/lib/cmake/glfw3/glfw3Targets-release.cmake
3>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glfw/install/lib/pkgconfig/glfw3.pc
3>  -- Up-to-date: C:/Users/myuserid/Desktop/test/build/dep/glfw/install/lib/glfw3.lib
3>Completed 'glfw'
4>------ Build started: Project: test, Configuration: Release x64 ------
4>Test.cpp
4>test.vcxproj -> C:\Users\myuserid\Desktop\test\build\Release\test.lib
========== Build: 4 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========



Solution 1:[1]

Adding UPDATE_COMMAND "" to ExternalProject_Add achieves the behavior I was after. This keeps the build system from walking through each dependency and checking it for changes every time something that depends on it is built. Switching configurations (ie Debug/Release) triggers the rebuild as expected. Then if you ever need to rebuild, you would just rebuild the specific dependency.

# 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
CMAKE_ARGS      -
# BEGIN ADD THIS
UPDATE_COMMAND  ""
# END ADD THIS
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
)

Solution 2:[2]

There's a bug in CMake that results in ExternalProject_Add with GIT_TAG always rebuilding, as it doesn't know if the git update has changed anything. See:

https://gitlab.kitware.com/cmake/cmake/-/issues/16419 https://gitlab.kitware.com/cmake/cmake/-/issues/19703

As in whitwhoa's answer, using UPDATE_COMMAND may workaround this.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 srce