CMAKE_TOOLCHAIN_FILE: Avoid excess reconfigures from relative paths

Save original input value of CMAKE_TOOLCHAIN_FILE also before
normalization or relative searching to avoid reconfiguring because the
full path doesn't match the input. Tests correct behavior avoiding
reconfigures and ensuring reconfigure on toolchain change from preset.

Fixes: #24942
This commit is contained in:
Aiden Woodruff
2026-07-16 16:24:07 -04:00
parent b8f081ee87
commit 7bc37a0561
20 changed files with 139 additions and 4 deletions

View File

@@ -145,6 +145,8 @@ 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)
# Retain input value to detect changes requiring re-configuration.
set(_CMAKE_INPUT_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}")
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)
@@ -225,6 +227,7 @@ if(CMAKE_BINARY_DIR)
set(INCLUDE_CMAKE_TOOLCHAIN_FILE_IF_REQUIRED)
if(CMAKE_TOOLCHAIN_FILE)
string(CONCAT INCLUDE_CMAKE_TOOLCHAIN_FILE_IF_REQUIRED
"set(_CMAKE_INPUT_TOOLCHAIN_FILE \"${_CMAKE_INPUT_TOOLCHAIN_FILE}\")\n"
"set(_CMAKE_SYSTEM_TOOLCHAIN_FILE \"${CMAKE_TOOLCHAIN_FILE}\")\n"
"include(\"\${_CMAKE_SYSTEM_TOOLCHAIN_FILE}\")"
)

View File

@@ -701,13 +701,18 @@ void cmGlobalGenerator::EnableLanguage(
fpath += "/CMakeSystem.cmake";
if (cmSystemTools::FileExists(fpath)) {
mf->ReadListFile(fpath);
// Fail early if CMAKE_TOOLCHAIN_FILE is different than what is stored in
// CMakeSystem.cmake to erase the cache because introspection results may
// become invalid
// If the toolchain file changes, the introspection results may become
// invalid, and so the cache must be deleted. The CMAKE_TOOLCHAIN_FILE
// value could be input the same way but be different in the cache due to
// normalization and relative path searching, so the value is checked
// against the original input to decide if the file path has changed.
cmValue toolchainFile = mf->GetDefinition("CMAKE_TOOLCHAIN_FILE");
cmValue inputToolchainFile =
mf->GetDefinition("_CMAKE_INPUT_TOOLCHAIN_FILE");
cmValue storedToolchainFile =
mf->GetDefinition("_CMAKE_SYSTEM_TOOLCHAIN_FILE");
if (toolchainFile && toolchainFile != storedToolchainFile) {
if (toolchainFile && toolchainFile != inputToolchainFile &&
toolchainFile != storedToolchainFile) {
mf->GetState()->AddDeleteCacheChangeVar("CMAKE_TOOLCHAIN_FILE",
*toolchainFile);
for (std::string const& lang : cur_languages) {

View File

@@ -0,0 +1,11 @@
{
"version": 3,
"configurePresets": [
{
"name": "default",
"generator": "@RunCMake_GENERATOR@",
"binaryDir": "${sourceDir}-build",
"toolchainFile": "@CompilerChange_Toolchain@"
}
]
}

View File

@@ -0,0 +1,4 @@
file(COPY
"${RunCMake_BINARY_DIR}/foo.cmake"
DESTINATION "${RunCMake_TEST_BINARY_DIR}"
)

View File

@@ -0,0 +1,2 @@
message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Using toolchain: ${toolchain_var}")

View File

@@ -0,0 +1,2 @@
message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Using toolchain: ${toolchain_var}")

View File

@@ -0,0 +1,9 @@
file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}")
file(COPY
"${RunCMake_SOURCE_DIR}/CMakeLists.txt"
"${RunCMake_SOURCE_DIR}/RelativeToolchainSource-step1.cmake"
"${RunCMake_SOURCE_DIR}/RelativeToolchainSource-step2.cmake"
"${RunCMake_BINARY_DIR}/foo.cmake"
DESTINATION "${RunCMake_TEST_SOURCE_DIR}"
)

View File

@@ -0,0 +1,2 @@
message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Using toolchain: ${toolchain_var}")

View File

@@ -0,0 +1,2 @@
message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Using toolchain: ${toolchain_var}")

View File

@@ -75,3 +75,52 @@ block()
"-DCMAKE_TOOLCHAIN_FILE=${RunCMake_BINARY_DIR}/bar.cmake"
)
endblock()
# Running without toolchain argument does not trigger reconfigure
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ToolchainFromCmdlineOnce-build)
set(RunCMake-stdout-file
${RunCMake_SOURCE_DIR}/ToolchainFromCmdlineOnce-stdout.txt
)
run_cmake_with_options(ToolchainFromCmdlineOnce-step1
"-DCMAKE_TOOLCHAIN_FILE=${RunCMake_BINARY_DIR}/foo.cmake"
)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake(ToolchainFromCmdlineOnce-step2)
endblock()
# New toolchain path comes from a preset file
block()
set(RunCMake_TEST_SOURCE_DIR ${RunCMake_BINARY_DIR}/ToolchainFromPreset)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ToolchainFromPreset-build)
run_cmake_with_options(ToolchainFromPreset-step1 --preset default)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_with_options(ToolchainFromPreset-step2 --preset default)
endblock()
# Unchanged relative toolchain file in binary dir does not trigger reconfigure
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/RelativeToolchainBinary)
run_cmake_with_options(RelativeToolchainBinary-step1
"-DCMAKE_TOOLCHAIN_FILE=foo.cmake"
)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_with_options(RelativeToolchainBinary-step2
"-DCMAKE_TOOLCHAIN_FILE=foo.cmake"
)
endblock()
# Unchanged relative toolchain file in source dir does not trigger reconfigure
block()
set(RunCMake_TEST_SOURCE_DIR ${RunCMake_BINARY_DIR}/RelativeToolchainSource)
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/RelativeToolchainSource-build
)
run_cmake_with_options(RelativeToolchainSource-step1
"-DCMAKE_TOOLCHAIN_FILE=foo.cmake"
)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_with_options(RelativeToolchainSource-step2
"-DCMAKE_TOOLCHAIN_FILE=foo.cmake"
)
endblock()

View File

@@ -0,0 +1,3 @@
-- Toolchain file: [^
]*/Tests/RunCMake/CompilerChange/foo\.cmake
-- Using toolchain: foo

View File

@@ -0,0 +1,2 @@
message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Using toolchain: ${toolchain_var}")

View File

@@ -0,0 +1,2 @@
message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Using toolchain: ${toolchain_var}")

View File

@@ -0,0 +1,13 @@
file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}")
file(COPY
"${RunCMake_SOURCE_DIR}/CMakeLists.txt"
"${RunCMake_SOURCE_DIR}/ToolchainFromPreset-step1.cmake"
DESTINATION "${RunCMake_TEST_SOURCE_DIR}"
)
set(CompilerChange_Toolchain "${RunCMake_BINARY_DIR}/foo.cmake")
configure_file(
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
"${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json"
@ONLY
)

View File

@@ -0,0 +1,3 @@
-- Toolchain file: [^
]*/Tests/RunCMake/CompilerChange/foo\.cmake
-- Using toolchain: foo

View File

@@ -0,0 +1,2 @@
message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Using toolchain: ${toolchain_var}")

View File

@@ -0,0 +1,11 @@
file(COPY
"${RunCMake_SOURCE_DIR}/CMakeLists.txt"
"${RunCMake_SOURCE_DIR}/ToolchainFromPreset-step2.cmake"
DESTINATION "${RunCMake_TEST_SOURCE_DIR}"
)
set(CompilerChange_Toolchain "${RunCMake_BINARY_DIR}/bar.cmake")
configure_file(
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
"${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json"
@ONLY
)

View File

@@ -0,0 +1,5 @@
You have changed variables that require your cache to be deleted.
Configure will be re-run and you may have to reset some variables.
The following variables have changed:
CMAKE_TOOLCHAIN_FILE= [^
]*/Tests/RunCMake/CompilerChange/bar\.cmake

View File

@@ -0,0 +1,3 @@
-- Toolchain file: [^
]*/Tests/RunCMake/CompilerChange/bar\.cmake
-- Using toolchain: bar

View File

@@ -0,0 +1,2 @@
message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Using toolchain: ${toolchain_var}")