From 7bc37a0561d8e016a3b8c95fa2eded0d47df7865 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 16 Jul 2026 16:24:07 -0400 Subject: [PATCH] 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 --- Modules/CMakeDetermineSystem.cmake | 3 ++ Source/cmGlobalGenerator.cxx | 13 +++-- .../CompilerChange/CMakePresets.json.in | 11 +++++ .../RelativeToolchainBinary-step1-prep.cmake | 4 ++ .../RelativeToolchainBinary-step1.cmake | 2 + .../RelativeToolchainBinary-step2.cmake | 2 + .../RelativeToolchainSource-step1-prep.cmake | 9 ++++ .../RelativeToolchainSource-step1.cmake | 2 + .../RelativeToolchainSource-step2.cmake | 2 + .../CompilerChange/RunCMakeTest.cmake | 49 +++++++++++++++++++ .../ToolchainFromCmdlineOnce-stdout.txt | 3 ++ .../ToolchainFromCmdlineOnce-step1.cmake | 2 + .../ToolchainFromCmdlineOnce-step2.cmake | 2 + .../ToolchainFromPreset-step1-prep.cmake | 13 +++++ .../ToolchainFromPreset-step1-stdout.txt | 3 ++ .../ToolchainFromPreset-step1.cmake | 2 + .../ToolchainFromPreset-step2-prep.cmake | 11 +++++ .../ToolchainFromPreset-step2-stderr.txt | 5 ++ .../ToolchainFromPreset-step2-stdout.txt | 3 ++ .../ToolchainFromPreset-step2.cmake | 2 + 20 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 Tests/RunCMake/CompilerChange/CMakePresets.json.in create mode 100644 Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step1-prep.cmake create mode 100644 Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step1.cmake create mode 100644 Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step2.cmake create mode 100644 Tests/RunCMake/CompilerChange/RelativeToolchainSource-step1-prep.cmake create mode 100644 Tests/RunCMake/CompilerChange/RelativeToolchainSource-step1.cmake create mode 100644 Tests/RunCMake/CompilerChange/RelativeToolchainSource-step2.cmake create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-stdout.txt create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-step1.cmake create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-step2.cmake create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1-prep.cmake create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1-stdout.txt create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1.cmake create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-prep.cmake create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-stderr.txt create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-stdout.txt create mode 100644 Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2.cmake diff --git a/Modules/CMakeDetermineSystem.cmake b/Modules/CMakeDetermineSystem.cmake index e58758ba32..3720283672 100644 --- a/Modules/CMakeDetermineSystem.cmake +++ b/Modules/CMakeDetermineSystem.cmake @@ -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}\")" ) diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index ce8fdebc4b..880fd3270a 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -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) { diff --git a/Tests/RunCMake/CompilerChange/CMakePresets.json.in b/Tests/RunCMake/CompilerChange/CMakePresets.json.in new file mode 100644 index 0000000000..50649b3e7c --- /dev/null +++ b/Tests/RunCMake/CompilerChange/CMakePresets.json.in @@ -0,0 +1,11 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}-build", + "toolchainFile": "@CompilerChange_Toolchain@" + } + ] +} diff --git a/Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step1-prep.cmake b/Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step1-prep.cmake new file mode 100644 index 0000000000..6c7254b009 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step1-prep.cmake @@ -0,0 +1,4 @@ +file(COPY + "${RunCMake_BINARY_DIR}/foo.cmake" + DESTINATION "${RunCMake_TEST_BINARY_DIR}" +) diff --git a/Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step1.cmake b/Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step1.cmake new file mode 100644 index 0000000000..68a8f6a7d7 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step1.cmake @@ -0,0 +1,2 @@ +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "Using toolchain: ${toolchain_var}") diff --git a/Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step2.cmake b/Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step2.cmake new file mode 100644 index 0000000000..68a8f6a7d7 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/RelativeToolchainBinary-step2.cmake @@ -0,0 +1,2 @@ +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "Using toolchain: ${toolchain_var}") diff --git a/Tests/RunCMake/CompilerChange/RelativeToolchainSource-step1-prep.cmake b/Tests/RunCMake/CompilerChange/RelativeToolchainSource-step1-prep.cmake new file mode 100644 index 0000000000..61e4187b23 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/RelativeToolchainSource-step1-prep.cmake @@ -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}" +) diff --git a/Tests/RunCMake/CompilerChange/RelativeToolchainSource-step1.cmake b/Tests/RunCMake/CompilerChange/RelativeToolchainSource-step1.cmake new file mode 100644 index 0000000000..68a8f6a7d7 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/RelativeToolchainSource-step1.cmake @@ -0,0 +1,2 @@ +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "Using toolchain: ${toolchain_var}") diff --git a/Tests/RunCMake/CompilerChange/RelativeToolchainSource-step2.cmake b/Tests/RunCMake/CompilerChange/RelativeToolchainSource-step2.cmake new file mode 100644 index 0000000000..68a8f6a7d7 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/RelativeToolchainSource-step2.cmake @@ -0,0 +1,2 @@ +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "Using toolchain: ${toolchain_var}") diff --git a/Tests/RunCMake/CompilerChange/RunCMakeTest.cmake b/Tests/RunCMake/CompilerChange/RunCMakeTest.cmake index 4c50b468ee..c6b3767995 100644 --- a/Tests/RunCMake/CompilerChange/RunCMakeTest.cmake +++ b/Tests/RunCMake/CompilerChange/RunCMakeTest.cmake @@ -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() diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-stdout.txt b/Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-stdout.txt new file mode 100644 index 0000000000..2b4f91bfcc --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-stdout.txt @@ -0,0 +1,3 @@ +-- Toolchain file: [^ +]*/Tests/RunCMake/CompilerChange/foo\.cmake +-- Using toolchain: foo diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-step1.cmake b/Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-step1.cmake new file mode 100644 index 0000000000..68a8f6a7d7 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-step1.cmake @@ -0,0 +1,2 @@ +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "Using toolchain: ${toolchain_var}") diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-step2.cmake b/Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-step2.cmake new file mode 100644 index 0000000000..68a8f6a7d7 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromCmdlineOnce-step2.cmake @@ -0,0 +1,2 @@ +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "Using toolchain: ${toolchain_var}") diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1-prep.cmake b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1-prep.cmake new file mode 100644 index 0000000000..89083ba9f6 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1-prep.cmake @@ -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 +) diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1-stdout.txt b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1-stdout.txt new file mode 100644 index 0000000000..2b4f91bfcc --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1-stdout.txt @@ -0,0 +1,3 @@ +-- Toolchain file: [^ +]*/Tests/RunCMake/CompilerChange/foo\.cmake +-- Using toolchain: foo diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1.cmake b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1.cmake new file mode 100644 index 0000000000..68a8f6a7d7 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step1.cmake @@ -0,0 +1,2 @@ +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "Using toolchain: ${toolchain_var}") diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-prep.cmake b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-prep.cmake new file mode 100644 index 0000000000..d026c239d5 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-prep.cmake @@ -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 +) diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-stderr.txt b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-stderr.txt new file mode 100644 index 0000000000..e65f0d973a --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-stderr.txt @@ -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 diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-stdout.txt b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-stdout.txt new file mode 100644 index 0000000000..1b041ef37b --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2-stdout.txt @@ -0,0 +1,3 @@ +-- Toolchain file: [^ +]*/Tests/RunCMake/CompilerChange/bar\.cmake +-- Using toolchain: bar diff --git a/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2.cmake b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2.cmake new file mode 100644 index 0000000000..68a8f6a7d7 --- /dev/null +++ b/Tests/RunCMake/CompilerChange/ToolchainFromPreset-step2.cmake @@ -0,0 +1,2 @@ +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "Using toolchain: ${toolchain_var}")