Commit 7155e358 authored by Daan De Meyer's avatar Daan De Meyer
Browse files

ExternalProject: Add CONFIGURE_HANDLED_BY_BUILD option

Fixes #21592.
parent 0f61fac6
external-project-configure-handled-by-build
-------------------------------------------
* The :module:`ExternalProject` function ``ExternalProject_Add`` learned a new
``CONFIGURE_HANDLED_BY_BUILD`` option to have subsequent runs of the configure
step be triggered by the build step when an external project dependency
rebuilds instead of always rerunning the configure step when an external
project dependency rebuilds.
......@@ -543,6 +543,18 @@ External Project Definition
When ``BUILD_IN_SOURCE`` option is enabled, the ``BUILD_COMMAND``
is used to point to an alternative directory within the source tree.
``CONFIGURE_HANDLED_BY_BUILD <bool>``
.. versionadded:: 3.20
Enabling this option relaxes the dependencies of the configure step on
other external projects to order-only. This means the configure step will
be executed after its external project dependencies are built but it will
not be marked dirty when one of its external project dependencies is
rebuilt. This option can be enabled when the build step is smart enough
to figure out if the configure step needs to be rerun. CMake and Meson are
examples of build systems whose build step is smart enough to know if the
configure step needs to be rerun.
**Build Step Options:**
If the configure step assumed the external project uses CMake as its build
system, the build step will also. Otherwise, the build step will assume a
......@@ -3081,6 +3093,23 @@ function(_ep_add_patch_command name)
)
endfunction()
function(_ep_get_file_deps var name)
set(file_deps)
get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS)
foreach(dep IN LISTS deps)
get_property(dep_type TARGET ${dep} PROPERTY TYPE)
if(dep_type STREQUAL "UTILITY")
get_property(is_ep TARGET ${dep} PROPERTY _EP_IS_EXTERNAL_PROJECT)
if(is_ep)
_ep_get_step_stampfile(${dep} "done" done_stamp_file)
list(APPEND file_deps ${done_stamp_file})
endif()
endif()
endforeach()
set("${var}" "${file_deps}" PARENT_SCOPE)
endfunction()
function(_ep_extract_configure_command var name)
get_property(cmd_set TARGET ${name} PROPERTY _EP_CONFIGURE_COMMAND SET)
......@@ -3189,19 +3218,13 @@ endfunction()
function(_ep_add_configure_command name)
ExternalProject_Get_Property(${name} binary_dir tmp_dir)
# Depend on other external projects (file-level).
set(file_deps)
get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS)
foreach(dep IN LISTS deps)
get_property(dep_type TARGET ${dep} PROPERTY TYPE)
if(dep_type STREQUAL "UTILITY")
get_property(is_ep TARGET ${dep} PROPERTY _EP_IS_EXTERNAL_PROJECT)
if(is_ep)
_ep_get_step_stampfile(${dep} "done" done_stamp_file)
list(APPEND file_deps ${done_stamp_file})
endif()
endif()
endforeach()
get_property(configure_handled_by_build TARGET ${name}
PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD)
if(NOT configure_handled_by_build)
# Depend on other external projects (file-level)
_ep_get_file_deps(file_deps ${name})
endif()
_ep_extract_configure_command(cmd ${name})
......@@ -3252,6 +3275,14 @@ endfunction()
function(_ep_add_build_command name)
ExternalProject_Get_Property(${name} binary_dir)
set(file_deps)
get_property(configure_handled_by_build TARGET ${name}
PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD)
if(configure_handled_by_build)
# Depend on other external projects (file-level)
_ep_get_file_deps(file_deps ${name})
endif()
get_property(cmd_set TARGET ${name} PROPERTY _EP_BUILD_COMMAND SET)
if(cmd_set)
get_property(cmd TARGET ${name} PROPERTY _EP_BUILD_COMMAND)
......@@ -3294,6 +3325,7 @@ function(_ep_add_build_command name)
BYPRODUCTS \${build_byproducts}
WORKING_DIRECTORY \${binary_dir}
DEPENDEES configure
DEPENDS \${file_deps}
ALWAYS \${always}
${log}
${uses_terminal}
......
file(TIMESTAMP "${STAMP_DIR}/proj1-configure" PROJ1_CONFIGURE_TIMESTAMP_AFTER "%s")
# When BUILD_ALWAYS is set, the build stamp is never created.
file(TIMESTAMP "${STAMP_DIR}/proj2-configure" PROJ2_CONFIGURE_TIMESTAMP_AFTER "%s")
file(TIMESTAMP "${STAMP_DIR}/proj2-build" PROJ2_BUILD_TIMESTAMP_AFTER "%s")
if(NOT PROJ1_CONFIGURE_TIMESTAMP_BEFORE EQUAL PROJ1_CONFIGURE_TIMESTAMP_AFTER)
set(RunCMake_TEST_FAILED "Unexpected rebuild of proj1 configure step (${PROJ1_CONFIGURE_TIMESTAMP_BEFORE} != ${PROJ1_CONFIGURE_TIMESTAMP_AFTER})")
return()
endif()
if(NOT PROJ2_CONFIGURE_TIMESTAMP_BEFORE EQUAL PROJ2_CONFIGURE_TIMESTAMP_AFTER)
set(RunCMake_TEST_FAILED "Unexpected rebuild of proj2 configure step (${PROJ2_CONFIGURE_TIMESTAMP_BEFORE} != ${PROJ2_CONFIGURE_TIMESTAMP_AFTER})")
return()
endif()
if(PROJ2_BUILD_TIMESTAMP_BEFORE EQUAL PROJ2_BUILD_TIMESTAMP_AFTER)
set(RunCMake_TEST_FAILED "proj2 build step did not rebuild (${PROJ2_BUILD_TIMESTAMP_BEFORE} != ${PROJ2_BUILD_TIMESTAMP_AFTER})")
return()
endif()
include(ExternalProject)
# Given this setup, on the first build, both configure steps and both build
# steps will run. On a noop rebuild, only the build steps will run. Without
# CONFIGURE_HANDLED_BY_BUILD, the configure step of proj2 would also run on a
# noop rebuild.
ExternalProject_Add(proj1
DOWNLOAD_COMMAND ""
SOURCE_DIR ""
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Doing something"
# file(TIMESTAMP) gives back the timestamp in seconds so we sleep a second to
# make sure we get a different timestamp on the stamp file
BUILD_COMMAND ${CMAKE_COMMAND} -E sleep 1
INSTALL_COMMAND ""
BUILD_ALWAYS ON
STAMP_DIR "stamp"
)
ExternalProject_Add(proj2
DOWNLOAD_COMMAND ""
SOURCE_DIR ""
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Doing something"
BUILD_COMMAND ${CMAKE_COMMAND} -E sleep 1
INSTALL_COMMAND ""
CONFIGURE_HANDLED_BY_BUILD ON
DEPENDS proj1
STAMP_DIR "stamp"
)
......@@ -151,3 +151,33 @@ endif()
if(doSubstitutionTest)
__ep_test_with_build(Substitutions)
endif()
function(__ep_test_CONFIGURE_HANDLED_BY_BUILD)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CONFIGURE_HANDLED_BY_BUILD-build)
run_cmake(CONFIGURE_HANDLED_BY_BUILD)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(BUILD_CONFIG --config Debug)
set(STAMP_DIR "${RunCMake_TEST_BINARY_DIR}/stamp/Debug")
else()
set(BUILD_CONFIG "")
set(STAMP_DIR "${RunCMake_TEST_BINARY_DIR}/stamp")
endif()
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-build ${CMAKE_COMMAND} --build . ${BUILD_CONFIG})
# Calculate timestamps before rebuilding so we can compare before and after in
# CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake
file(TIMESTAMP "${STAMP_DIR}/proj1-configure" PROJ1_CONFIGURE_TIMESTAMP_BEFORE "%s")
# When BUILD_ALWAYS is set, the build stamp is never created.
file(TIMESTAMP "${STAMP_DIR}/proj2-configure" PROJ2_CONFIGURE_TIMESTAMP_BEFORE "%s")
file(TIMESTAMP "${STAMP_DIR}/proj2-build" PROJ2_BUILD_TIMESTAMP_BEFORE "%s")
run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-rebuild ${CMAKE_COMMAND} --build . ${BUILD_CONFIG})
endfunction()
if(NOT RunCMake_GENERATOR MATCHES "Visual Studio 9 ")
__ep_test_CONFIGURE_HANDLED_BY_BUILD()
endif()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment