Browse Source

cmake: Restore acceptance of -DCMAKE_TOOLCHAIN_FILE=//... on non-Windows

POSIX specifies that two leading slashes have implementation-defined
interpretation, so CMake 3.31 and below did not normalize away leading
double slashes.  However, most implementations simply treat a leading
`//` as just `/`, so CMake 4.0 now normalizes them away when they do not
correspond to a network path on Windows.

This change exposed that we were not normalizing `CMAKE_TOOLCHAIN_FILE`
before passing its value to `include()` the first time if it was not
passed with the `FILEPATH` or `PATH` cache entry type.  Fix that.

Fixes: #27010
Brad King 5 months ago
parent
commit
c393300e2b

+ 17 - 10
Modules/CMakeDetermineSystem.cmake

@@ -139,17 +139,24 @@ endif()
 # variables around so they can be used in CMakeLists.txt.
 # In all other cases, the host and target platform are the same.
 if(CMAKE_TOOLCHAIN_FILE)
-  # at first try to load it as path relative to the directory from which cmake has been run
-  include("${CMAKE_BINARY_DIR}/${CMAKE_TOOLCHAIN_FILE}" OPTIONAL RESULT_VARIABLE _INCLUDED_TOOLCHAIN_FILE)
-  if(NOT _INCLUDED_TOOLCHAIN_FILE)
-     # if the file isn't found there, check the default locations
-     include("${CMAKE_TOOLCHAIN_FILE}" OPTIONAL RESULT_VARIABLE _INCLUDED_TOOLCHAIN_FILE)
-  endif()
-
-  if(_INCLUDED_TOOLCHAIN_FILE)
-    set(CMAKE_TOOLCHAIN_FILE "${_INCLUDED_TOOLCHAIN_FILE}" CACHE FILEPATH "The CMake toolchain file" FORCE)
+  if(IS_ABSOLUTE "${CMAKE_TOOLCHAIN_FILE}" AND EXISTS "${CMAKE_TOOLCHAIN_FILE}")
+    # Normalize the absolute path.
+    set(CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" CACHE FILEPATH "The CMake toolchain file" FORCE)
+    set(CMAKE_TOOLCHAIN_FILE "$CACHE{CMAKE_TOOLCHAIN_FILE}")
+    include("${CMAKE_TOOLCHAIN_FILE}" OPTIONAL RESULT_VARIABLE _INCLUDED_TOOLCHAIN_FILE)
   else()
-    message(FATAL_ERROR "Could not find toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
+    # at first try to load it as path relative to the directory from which cmake has been run
+    include("${CMAKE_BINARY_DIR}/${CMAKE_TOOLCHAIN_FILE}" OPTIONAL RESULT_VARIABLE _INCLUDED_TOOLCHAIN_FILE)
+    if(NOT _INCLUDED_TOOLCHAIN_FILE)
+      # if the file isn't found there, check the default locations
+      include("${CMAKE_TOOLCHAIN_FILE}" OPTIONAL RESULT_VARIABLE _INCLUDED_TOOLCHAIN_FILE)
+    endif()
+    if(_INCLUDED_TOOLCHAIN_FILE)
+      set(CMAKE_TOOLCHAIN_FILE "${_INCLUDED_TOOLCHAIN_FILE}" CACHE FILEPATH "The CMake toolchain file" FORCE)
+    endif()
+  endif()
+  if(NOT _INCLUDED_TOOLCHAIN_FILE)
+    message(FATAL_ERROR "Could not find toolchain file:\n \"${CMAKE_TOOLCHAIN_FILE}\"")
   endif()
 endif()
 

+ 4 - 0
Tests/RunCMake/CommandLine/RunCMakeTest.cmake

@@ -264,6 +264,10 @@ function(run_Toolchain)
   run_cmake_with_options(toolchain-no-arg -S ${source_dir} --toolchain=)
   run_cmake_with_options(toolchain-valid-abs-path -S ${source_dir} --toolchain "${source_dir}/toolchain.cmake")
   run_cmake_with_options(toolchain-valid-rel-src-path -S ${source_dir} --toolchain=toolchain.cmake)
+  run_cmake_with_options(toolchain-D-abs-path -S ${source_dir} -DCMAKE_TOOLCHAIN_FILE=${source_dir}/toolchain.cmake)
+  if(CMAKE_HOST_UNIX AND NOT CMAKE_SYSTEM_NAME STREQUAL "CYGWIN" AND NOT CMAKE_SYSTEM_NAME STREQUAL "MSYS")
+    run_cmake_with_options(toolchain-D-slash-abs-path -S ${source_dir} -DCMAKE_TOOLCHAIN_FILE=/${source_dir}/toolchain.cmake)
+  endif()
 
   set(RunCMake_TEST_NO_CLEAN 1)
   set(binary_dir ${RunCMake_BINARY_DIR}/Toolchain-build)

+ 1 - 0
Tests/RunCMake/CommandLine/toolchain-D-abs-path-stdout.txt

@@ -0,0 +1 @@
+CMAKE_TOOLCHAIN_FILE='[^']*/Tests/RunCMake/CommandLine/Toolchain/toolchain\.cmake'

+ 1 - 0
Tests/RunCMake/CommandLine/toolchain-D-slash-abs-path-stdout.txt

@@ -0,0 +1 @@
+CMAKE_TOOLCHAIN_FILE='/[^/][^']*/Tests/RunCMake/CommandLine/Toolchain/toolchain\.cmake'