1
0
Эх сурвалжийг харах

ExternalProject,FetchContent: Avoid CMAKE_DISABLE_SOURCE_CHANGES error

The file(MAKE_DIRECTORY) implementation checks whether a path is
allowed to be written to before it checks if it already exists. For the
scenario where a SOURCE_DIR is an existing directory within the main
project's source directory, this triggers a fatal error if
CMAKE_DISABLE_SOURCE_CHANGES is set to true for ExternalProject,
and some FetchContent scenarios. Therefore, add an explicit check for
existence first to avoid making such error-triggering calls.

Fixes: #21872
Craig Scott 1 жил өмнө
parent
commit
0cc1b550dd

+ 6 - 1
Modules/ExternalProject/mkdirs.cmake.in

@@ -3,8 +3,13 @@
 
 cmake_minimum_required(VERSION 3.5)
 
+# If CMAKE_DISABLE_SOURCE_CHANGES is set to true and the source directory is an
+# existing directory in our source tree, calling file(MAKE_DIRECTORY) on it
+# would cause a fatal error, even though it would be a no-op.
+if(NOT EXISTS "@source_dir@")
+  file(MAKE_DIRECTORY "@source_dir@")
+endif()
 file(MAKE_DIRECTORY
-  "@source_dir@"
   "@binary_dir@"
   "@install_dir@"
   "@tmp_dir@"

+ 6 - 1
Modules/FetchContent.cmake

@@ -1657,8 +1657,13 @@ function(__FetchContent_populateDirect)
   set(_EP_TMP_DIR   "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-tmp")
   set(_EP_DOWNLOAD_DIR "${_EP_TMP_DIR}")
 
+  # If CMAKE_DISABLE_SOURCE_CHANGES is set to true and _EP_SOURCE_DIR is an
+  # existing directory in our source tree, calling file(MAKE_DIRECTORY) on it
+  # would cause a fatal error, even though it would be a no-op.
+  if(NOT EXISTS "${_EP_SOURCE_DIR}")
+    file(MAKE_DIRECTORY "${_EP_SOURCE_DIR}")
+  endif()
   file(MAKE_DIRECTORY
-    "${_EP_SOURCE_DIR}"
     "${_EP_BINARY_DIR}"
     "${_EP_STAMP_DIR}"
     "${_EP_TMP_DIR}"

+ 1 - 0
Tests/RunCMake/ExternalProject/RunCMakeTest.cmake

@@ -21,6 +21,7 @@ run_cmake(TLSVersionBadEnv)
 run_cmake(NoOptions)
 run_cmake(SourceEmpty)
 run_cmake(SourceMissing)
+run_cmake(SourceDirExisting)
 run_cmake(CMAKE_CACHE_ARGS)
 run_cmake(CMAKE_CACHE_DEFAULT_ARGS)
 run_cmake(CMAKE_CACHE_mix)

+ 16 - 0
Tests/RunCMake/ExternalProject/SourceDirExisting.cmake

@@ -0,0 +1,16 @@
+# We're providing a pre-existing source directory. Make sure we don't trigger
+# an error if the undocumented but used-in-the-wild CMAKE_DISABLE_SOURCE_CHANGES
+# variable is set.
+set(CMAKE_DISABLE_SOURCE_CHANGES TRUE)
+
+include(ExternalProject)
+
+ExternalProject_Add(source_dir_existing
+  SOURCE_DIR        "${CMAKE_CURRENT_LIST_DIR}/Foo"
+  DOWNLOAD_COMMAND  "${CMAKE_COMMAND}" -E echo "Download command executed"
+  UPDATE_COMMAND    ""
+  CONFIGURE_COMMAND ""
+  BUILD_COMMAND     ""
+  TEST_COMMAND      ""
+  INSTALL_COMMAND   ""
+)

+ 18 - 0
Tests/RunCMake/FetchContent/DisableSourceChanges.cmake

@@ -0,0 +1,18 @@
+cmake_policy(SET CMP0168 NEW)
+
+# Undocumented variable used to catch attempts to write to anywhere under the
+# source directory that isn't under the build directory. In order for this
+# code path to be checked for direct population mode, we need a non-empty
+# download, update, or patch command so that the population code path is used.
+# Custom commands might not write to the source directory and instead just
+# print messages or other non-modifying tasks, like is done here.
+set(CMAKE_DISABLE_SOURCE_CHANGES TRUE)
+
+include(FetchContent)
+
+FetchContent_Declare(
+  WithProject
+  SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/WithProject   # This exists
+  DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command executed"
+)
+FetchContent_MakeAvailable(WithProject)

+ 2 - 0
Tests/RunCMake/FetchContent/RunCMakeTest.cmake

@@ -106,3 +106,5 @@ run_cmake_command(ScriptMode-direct
   -DCMP0168=NEW
   -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake
 )
+
+run_cmake(DisableSourceChanges)