Browse Source

ExternalProject: Preserve colons and semicolons in the path

The _ep_set_directories() function needs to ensure paths passed in
are in the expected CMake path form. The special character that
file(TO_CMAKE_PATH) interprets as a path separator must be
masked to prevent it splitting paths that contain that character
(semicolons on Windows, colons on other platforms).

Fixes: #26490
Craig Scott 10 months ago
parent
commit
b1ff216656
1 changed files with 25 additions and 5 deletions
  1. 25 5
      Modules/ExternalProject.cmake

+ 25 - 5
Modules/ExternalProject.cmake

@@ -1310,6 +1310,26 @@ define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED)
 define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED)
 define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED)
 
+# file(TO_CMAKE_PATH) will interpret a platform-specific character as a path
+# separator, and if its input contains that character, it will treat the input
+# as a list. Sometimes we have a string that we know is always a single path,
+# but it may contain the separator character. To prevent it being treated as a
+# list of paths, this function masks the separator character while calling
+# file(TO_CMAKE_PATH).
+function(_ep_to_single_cmake_path out_var input)
+  if(WIN32)
+    set(unsafe_char ";")
+  else()
+    set(unsafe_char ":")
+  endif()
+
+  string(REPLACE "${unsafe_char}" "__EP_MARKER__" safe_input "${input}")
+  file(TO_CMAKE_PATH "${safe_input}" converted_input)
+  string(REPLACE "__EP_MARKER__" "${unsafe_char}" output "${converted_input}")
+
+  set(${out_var} "${output}" PARENT_SCOPE)
+endfunction()
+
 function(_ep_set_directories name)
   get_property(prefix TARGET ${name} PROPERTY _EP_PREFIX)
   if(NOT prefix)
@@ -1322,7 +1342,7 @@ function(_ep_set_directories name)
     endif()
   endif()
   if(prefix)
-    file(TO_CMAKE_PATH "${prefix}" prefix)
+    _ep_to_single_cmake_path(prefix "${prefix}")
     set(tmp_default "${prefix}/tmp")
     set(download_default "${prefix}/src")
     set(source_default "${prefix}/src/${name}")
@@ -1330,7 +1350,7 @@ function(_ep_set_directories name)
     set(stamp_default "${prefix}/src/${name}-stamp")
     set(install_default "${prefix}")
   else()
-    file(TO_CMAKE_PATH "${base}" base)
+    _ep_to_single_cmake_path(base "${base}")
     set(tmp_default "${base}/tmp/${name}")
     set(download_default "${base}/Download/${name}")
     set(source_default "${base}/Source/${name}")
@@ -1360,7 +1380,7 @@ function(_ep_set_directories name)
     if(NOT IS_ABSOLUTE "${${var}_dir}")
       get_filename_component(${var}_dir "${top}/${${var}_dir}" ABSOLUTE)
     endif()
-    file(TO_CMAKE_PATH "${${var}_dir}" ${var}_dir)
+    _ep_to_single_cmake_path(${var}_dir "${${var}_dir}")
     set_property(TARGET ${name} PROPERTY _EP_${VAR}_DIR "${${var}_dir}")
     set(_EP_${VAR}_DIR "${${var}_dir}" PARENT_SCOPE)
   endforeach()
@@ -1373,7 +1393,7 @@ function(_ep_set_directories name)
   if(NOT IS_ABSOLUTE "${log_dir}")
     get_filename_component(log_dir "${top}/${log_dir}" ABSOLUTE)
   endif()
-  file(TO_CMAKE_PATH "${log_dir}" log_dir)
+  _ep_to_single_cmake_path(log_dir "${log_dir}")
   set_property(TARGET ${name} PROPERTY _EP_LOG_DIR "${log_dir}")
   set(_EP_LOG_DIR "${log_dir}" PARENT_SCOPE)
 
@@ -1388,7 +1408,7 @@ function(_ep_set_directories name)
   else()
     # Prefix with a slash so that when appended to the source directory, it
     # behaves as expected.
-    file(TO_CMAKE_PATH "${source_subdir}" source_subdir)
+    _ep_to_single_cmake_path(source_subdir "${source_subdir}")
     set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "/${source_subdir}")
     set(_EP_SOURCE_SUBDIR "/${source_subdir}" PARENT_SCOPE)
   endif()