From 9d2e85373a61b856ff7c6f3cd6a17ff1a4b9f615 Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Wed, 20 May 2026 19:47:51 -0400 Subject: [PATCH] macro: Add policy to preserve backslashes in arguments Preserve backslashes in arguments passed to macros by re-escaping them before substituting in the macro body, where they will be parsed again. Fixes: #19281, #22157, #23641, #25803, #27645, #20891 --- Help/manual/cmake-policies.7.rst | 1 + Help/policy/CMP0219.rst | 103 ++++++++++++++++++ Help/release/dev/macro-argument-escape.rst | 7 ++ Source/cmMacroCommand.cxx | 22 +++- Source/cmMakefile.cxx | 77 +++++++++++++ Source/cmMakefile.h | 15 +++ Source/cmPolicies.h | 5 +- Source/cmVariableWatchCommand.cxx | 12 ++ Tests/RunCMake/CMP0219/AllNEW-Forward.cmake | 17 +++ Tests/RunCMake/CMP0219/Basic-NEW.cmake | 18 +++ Tests/RunCMake/CMP0219/Basic-OLD-result.txt | 1 + Tests/RunCMake/CMP0219/Basic-OLD-stderr.txt | 1 + Tests/RunCMake/CMP0219/Basic-OLD.cmake | 8 ++ Tests/RunCMake/CMP0219/CMP0219-helpers.cmake | 20 ++++ Tests/RunCMake/CMP0219/CMakeLists.txt | 3 + .../CMP0219/CheckCSourceCompiles-NEW.cmake | 8 ++ .../CheckCSourceCompiles-OLD-result.txt | 1 + .../CheckCSourceCompiles-OLD-stderr.txt | 1 + .../CMP0219/CheckCSourceCompiles-OLD.cmake | 5 + .../CMP0219/Defer-EndPolicy-NEW.cmake | 30 +++++ .../CMP0219/Defer-EndPolicy-OLD-result.txt | 1 + .../CMP0219/Defer-EndPolicy-OLD-stderr.txt | 1 + .../CMP0219/Defer-EndPolicy-OLD.cmake | 15 +++ .../CMP0219/DependencyProviderMacro-NEW.cmake | 7 ++ .../DependencyProviderMacro-OLD-result.txt | 1 + .../DependencyProviderMacro-OLD-stderr.txt | 1 + .../CMP0219/DependencyProviderMacro-OLD.cmake | 3 + .../DependencyProviderMacro-TopInclude.cmake | 11 ++ .../CMP0219/DynamicDispatch-NEW.cmake | 9 ++ Tests/RunCMake/CMP0219/Escape2-NEW.cmake | 8 ++ .../CMP0219/MixedChain-OLD-NEWMiddle.cmake | 48 ++++++++ Tests/RunCMake/CMP0219/NEW-calls-OLD.cmake | 12 ++ .../RunCMake/CMP0219/OLD-calls-NEW-result.txt | 1 + .../RunCMake/CMP0219/OLD-calls-NEW-stderr.txt | 1 + Tests/RunCMake/CMP0219/OLD-calls-NEW.cmake | 11 ++ .../CMP0219/ParseArguments-Semicolon.cmake | 46 ++++++++ .../CMP0219/ReturnAndPolicyPropagation.cmake | 32 ++++++ Tests/RunCMake/CMP0219/RunCMakeTest.cmake | 30 +++++ .../SemicolonEscape-QuotedUnquoted.cmake | 62 +++++++++++ .../RunCMake/CMP0219/VariableWatch-NEW.cmake | 11 ++ .../CMP0219/VariableWatch-OLD-result.txt | 1 + .../CMP0219/VariableWatch-OLD-stderr.txt | 1 + .../RunCMake/CMP0219/VariableWatch-OLD.cmake | 9 ++ .../CMP0219/Warn-Unset-Macro-Multi-stderr.txt | 22 ++++ .../CMP0219/Warn-Unset-Macro-Multi.cmake | 9 ++ .../CMP0219/Warn-Unset-Macro-stderr.txt | 20 ++++ Tests/RunCMake/CMP0219/Warn-Unset-Macro.cmake | 8 ++ .../Warn-Unset-VariableWatch-stderr.txt | 20 ++++ .../CMP0219/Warn-Unset-VariableWatch.cmake | 6 + Tests/RunCMake/CMakeLists.txt | 1 + .../CheckSourceCompiles/CMakeLists.txt | 4 + Tests/RunCMake/Syntax/Escape2-stderr.txt | 6 +- Tests/RunCMake/Syntax/Escape2.cmake | 4 + .../cmake_parse_arguments/CornerCases.cmake | 1 + 54 files changed, 771 insertions(+), 7 deletions(-) create mode 100644 Help/policy/CMP0219.rst create mode 100644 Help/release/dev/macro-argument-escape.rst create mode 100644 Tests/RunCMake/CMP0219/AllNEW-Forward.cmake create mode 100644 Tests/RunCMake/CMP0219/Basic-NEW.cmake create mode 100644 Tests/RunCMake/CMP0219/Basic-OLD-result.txt create mode 100644 Tests/RunCMake/CMP0219/Basic-OLD-stderr.txt create mode 100644 Tests/RunCMake/CMP0219/Basic-OLD.cmake create mode 100644 Tests/RunCMake/CMP0219/CMP0219-helpers.cmake create mode 100644 Tests/RunCMake/CMP0219/CMakeLists.txt create mode 100644 Tests/RunCMake/CMP0219/CheckCSourceCompiles-NEW.cmake create mode 100644 Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-result.txt create mode 100644 Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-stderr.txt create mode 100644 Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD.cmake create mode 100644 Tests/RunCMake/CMP0219/Defer-EndPolicy-NEW.cmake create mode 100644 Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-result.txt create mode 100644 Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-stderr.txt create mode 100644 Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD.cmake create mode 100644 Tests/RunCMake/CMP0219/DependencyProviderMacro-NEW.cmake create mode 100644 Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-result.txt create mode 100644 Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-stderr.txt create mode 100644 Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD.cmake create mode 100644 Tests/RunCMake/CMP0219/DependencyProviderMacro-TopInclude.cmake create mode 100644 Tests/RunCMake/CMP0219/DynamicDispatch-NEW.cmake create mode 100644 Tests/RunCMake/CMP0219/Escape2-NEW.cmake create mode 100644 Tests/RunCMake/CMP0219/MixedChain-OLD-NEWMiddle.cmake create mode 100644 Tests/RunCMake/CMP0219/NEW-calls-OLD.cmake create mode 100644 Tests/RunCMake/CMP0219/OLD-calls-NEW-result.txt create mode 100644 Tests/RunCMake/CMP0219/OLD-calls-NEW-stderr.txt create mode 100644 Tests/RunCMake/CMP0219/OLD-calls-NEW.cmake create mode 100644 Tests/RunCMake/CMP0219/ParseArguments-Semicolon.cmake create mode 100644 Tests/RunCMake/CMP0219/ReturnAndPolicyPropagation.cmake create mode 100644 Tests/RunCMake/CMP0219/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/CMP0219/SemicolonEscape-QuotedUnquoted.cmake create mode 100644 Tests/RunCMake/CMP0219/VariableWatch-NEW.cmake create mode 100644 Tests/RunCMake/CMP0219/VariableWatch-OLD-result.txt create mode 100644 Tests/RunCMake/CMP0219/VariableWatch-OLD-stderr.txt create mode 100644 Tests/RunCMake/CMP0219/VariableWatch-OLD.cmake create mode 100644 Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi-stderr.txt create mode 100644 Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi.cmake create mode 100644 Tests/RunCMake/CMP0219/Warn-Unset-Macro-stderr.txt create mode 100644 Tests/RunCMake/CMP0219/Warn-Unset-Macro.cmake create mode 100644 Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch-stderr.txt create mode 100644 Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch.cmake diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 6728fee232..994061436a 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -100,6 +100,7 @@ Policies Introduced by CMake 4.4 .. toctree:: :maxdepth: 1 + CMP0219: Macro invocations preserve backslashes in arguments. CMP0218: The CMAKE_WARN_DEPRECATED and CMAKE_ERROR_DEPRECATED variables are ignored. CMP0217: The MACROS directory property does not exist anymore. CMP0216: Swift targets have a default project name. diff --git a/Help/policy/CMP0219.rst b/Help/policy/CMP0219.rst new file mode 100644 index 0000000000..2ae3c249f8 --- /dev/null +++ b/Help/policy/CMP0219.rst @@ -0,0 +1,103 @@ +CMP0219 +------- + +.. versionadded:: 4.4 + +:command:`macro` invocations preserve backslashes in arguments. + +In CMake 4.3 and below, macro argument references (``${ARGN}``, ``${ARGV}``, +``${ARGV}``, and named macro arguments) are substituted textually and then +evaluated by the called command. Backslashes in substituted values are +re-evaluated as escape prefixes, and invalid escape sequences (such as +``\b`` in Windows paths) can cause errors. The same re-evaluation can also +occur for arguments passed to a :command:`variable_watch` callback command. + +CMake 4.4 and above prefer to preserve literal backslashes in those macro +argument references. + +The ``OLD`` behavior for this policy is to interpret escape sequences in macro +argument references and in arguments passed to ``variable_watch()`` callback +commands. The ``NEW`` behavior is to preserve backslashes in those values +before command invocation. + +This policy applies to macro argument references and to arguments passed to +``variable_watch()`` callback commands. Other variable references keep their +existing behavior. + +Mixed Policy Chains +^^^^^^^^^^^^^^^^^^^ + +Whether pre-escaping occurs is determined by the policy setting at each +call site. When a project and its dependencies use different ``CMP0219`` +settings, each macro call in the chain follows the policy where that call +appears. + +Consider a project (``CMP0219`` is ``OLD``) that calls a macro from +dependency A (upgraded to +:command:`cmake_minimum_required(VERSION 4.4) `, +so ``CMP0219`` is ``NEW``), which in turn forwards arguments to a macro +from dependency B (not yet upgraded, so ``CMP0219`` is ``OLD``): + +.. code-block:: cmake + + # Dependency B: cmake_minimum_required(VERSION 3.24) + # CMP0219 is OLD inside dependency B. + macro(depB_store var_name) + set(${var_name} "${ARGN}") + endmacro() + + # Dependency A: cmake_minimum_required(VERSION 4.4) + # CMP0219 is NEW inside dependency A. + macro(depA_forward var_name) + depB_store(${var_name} ${ARGN}) # call site is NEW -> pre-escapes + endmacro() + + # Project: cmake_minimum_required(VERSION 3.24) + # CMP0219 is OLD in the project. + macro(my_wrapper var_name) + depA_forward(${var_name} ${ARGN}) # call site is OLD -> no pre-escaping + endmacro() + +In a fully ``OLD`` chain, each macro boundary re-evaluates backslash +escapes, so callers must add extra escaping layers to compensate. +To pass the Windows path ``C:\build\new\temp`` through three ``OLD`` +boundaries, the caller needs eight backslashes per separator: + +.. code-block:: cmake + + my_wrapper(result "C:\\\\\\\\build\\\\\\\\new\\\\\\\\temp") + message("${result}") # -> C:\build\new\temp + +When dependency A upgrades and the middle boundary becomes ``NEW``, +that boundary preserves backslashes instead of consuming a level. +The same input now retains an extra layer: + +.. code-block:: cmake + + # Same 8x-escaped input through OLD -> NEW -> OLD: + my_wrapper(result "C:\\\\\\\\build\\\\\\\\new\\\\\\\\temp") + message("${result}") # -> C:\\build\\new\\temp (one extra layer) + +The caller must then use half the escaping to reach the native path: + +.. code-block:: cmake + + # Only 4x-escaped input needed for OLD -> NEW -> OLD: + my_wrapper(result "C:\\\\build\\\\new\\\\temp") + message("${result}") # -> C:\build\new\temp + +When a dependency updates its :command:`cmake_minimum_required` version +to 4.4 or above, call sites inside that dependency begin pre-escaping. +Callers that previously added extra backslash layers to compensate for +multiple levels of ``OLD`` re-evaluation may then need fewer escape +layers. + +Where possible, converting paths to forward slashes with +:command:`cmake_path(CONVERT ... TO_CMAKE_PATH_LIST)` avoids the issue entirely, because +forward slashes require no escaping regardless of the policy setting. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns when backslashes are present +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst diff --git a/Help/release/dev/macro-argument-escape.rst b/Help/release/dev/macro-argument-escape.rst new file mode 100644 index 0000000000..95ef776afe --- /dev/null +++ b/Help/release/dev/macro-argument-escape.rst @@ -0,0 +1,7 @@ +macro-argument-escape +--------------------- + +* :command:`macro` invocations now preserve backslashes in arguments. + See policy :policy:`CMP0219`. +* :command:`variable_watch` callback command arguments preserve backslashes. + See policy :policy:`CMP0219`. diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index ee50fb6093..9025c59eeb 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -63,9 +63,6 @@ bool cmMacroHelperCommand::operator()( return false; } - cmMakefile::MacroPushPop macroScope(&makefile, this->FilePath, - this->Policies, this->Diagnostics); - // set the value of argc std::string argcDef = std::to_string(expandedArgs.size()); @@ -73,6 +70,21 @@ bool cmMacroHelperCommand::operator()( std::string expandedArgn = cmList::to_string(cmMakeRange(expIt, expandedArgs.end())); std::string expandedArgv = cmList::to_string(expandedArgs); + { + cmPolicies::PolicyStatus cmp0219 = + makefile.CheckCMP0219(this->Args[0], expandedArgs); + if (cmp0219 == cmPolicies::NEW) { + // Escape macro argument backslashes so that callees receive the same + // values the caller had. + for (std::string& expandedArg : expandedArgs) { + cmSystemTools::ReplaceString(expandedArg, "\\", "\\\\"); + } + cmSystemTools::ReplaceString(expandedArgn, "\\", "\\\\"); + cmSystemTools::ReplaceString(expandedArgv, "\\", "\\\\"); + } else if (cmp0219 == cmPolicies::WARN) { + makefile.IssueCMP0219Warning(this->Args[0], expandedArgs); + } + } std::vector variables; variables.reserve(this->Args.size() - 1); for (unsigned int j = 1; j < this->Args.size(); ++j) { @@ -83,6 +95,10 @@ bool cmMacroHelperCommand::operator()( for (unsigned int j = 0; j < expandedArgs.size(); ++j) { argVs.emplace_back(cmStrCat("${ARGV", j, '}')); } + + cmMakefile::MacroPushPop macroScope(&makefile, this->FilePath, + this->Policies, this->Diagnostics); + // Invoke all the functions that were collected in the block. // for each function for (cmListFileFunction const& func : this->Functions) { diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 9b9a3f86d0..68ba773dcd 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -2625,6 +2625,83 @@ cm::optional cmMakefile::DeferGetCall(std::string const& id) const return call; } +namespace { +cmPolicies::PolicyStatus CheckCMP0219Impl(cmPolicies::PolicyStatus status, + std::string const& calleeName, + bool hasBackslashes, + std::set& warned) +{ + if (status == cmPolicies::WARN && hasBackslashes && + warned.insert(calleeName).second) { + return cmPolicies::WARN; + } + // Suppress WARN when there are no backslashes or already warned. + return status == cmPolicies::NEW ? cmPolicies::NEW : cmPolicies::OLD; +} +} + +cmPolicies::PolicyStatus cmMakefile::CheckCMP0219( + std::string const& calleeName, std::vector const& args) +{ + bool const hasBackslashes = + std::any_of(args.begin(), args.end(), [](std::string const& s) { + return s.find('\\') != std::string::npos; + }); + return CheckCMP0219Impl(GetPolicyStatus(cmPolicies::CMP0219), calleeName, + hasBackslashes, WarnedCMP0219); +} + +cmPolicies::PolicyStatus cmMakefile::CheckCMP0219( + std::string const& calleeName, std::vector const& args) +{ + bool const hasBackslashes = + std::any_of(args.begin(), args.end(), [](cmListFileArgument const& s) { + return s.Value.find('\\') != std::string::npos; + }); + return CheckCMP0219Impl(GetPolicyStatus(cmPolicies::CMP0219), calleeName, + hasBackslashes, WarnedCMP0219); +} + +void cmMakefile::IssueCMP0219Warning( + std::string const& calleeName, std::vector const& args) const +{ + std::string oldArgs; + for (std::string const& arg : args) { + if (arg.find('\\') == std::string::npos) { + continue; + } + if (!oldArgs.empty()) { + oldArgs += '\n'; + } + oldArgs += cmStrCat(" \"", arg, '"'); + } + + std::string newArgs = oldArgs; + cmSystemTools::ReplaceString(newArgs, "\\", "\\\\"); + + this->IssueDiagnostic( + cmDiagnostics::CMD_POLICY, + cmStrCat( + cmPolicies::GetPolicyWarning(cmPolicies::CMP0219), '\n', "Command \"", + calleeName, "\" called with arguments containing backslashes.\n", + "Since the policy is not set, backslashes in the arguments:\n", oldArgs, + "\n", "will be interpreted as escape sequences for compatibility.\n", + "Set the policy to NEW to instead pass\n", newArgs, "\n", + "so that argument parsing will preserve the original values.")); +} + +void cmMakefile::IssueCMP0219Warning( + std::string const& calleeName, + std::vector const& args) const +{ + std::vector stringArgs; + stringArgs.reserve(args.size()); + for (cmListFileArgument const& arg : args) { + stringArgs.push_back(arg.Value); + } + this->IssueCMP0219Warning(calleeName, stringArgs); +} + MessageType cmMakefile::ExpandVariablesInStringImpl( std::string& errorstr, std::string& source, bool escapeQuotes, bool noEscapes, bool atOnly, char const* filename, long line, diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 955aa428b2..437a13a9ec 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -1186,6 +1186,20 @@ public: cm::optional DeferGetCallIds() const; cm::optional DeferGetCall(std::string const& id) const; + //! Check CMP0219 policy status for the given callee and arguments. + //! Returns the effective policy status: OLD, NEW, or WARN (only on + //! first occurrence of calleeName with backslashes present). The + //! caller is responsible for issuing any warning when WARN is returned. + cmPolicies::PolicyStatus CheckCMP0219(std::string const& calleeName, + std::vector const& args); + cmPolicies::PolicyStatus CheckCMP0219( + std::string const& calleeName, + std::vector const& args); + void IssueCMP0219Warning(std::string const& calleeName, + std::vector const& args) const; + void IssueCMP0219Warning(std::string const& calleeName, + std::vector const& args) const; + protected: // add link libraries and directories to the target void AddGlobalLinkInformation(cmTarget& target); @@ -1350,6 +1364,7 @@ private: bool CheckCMP0000; std::set WarnedCMP0074; std::set WarnedCMP0144; + std::set WarnedCMP0219; bool IsSourceFileTryCompile; ImportedTargetScope CurrentImportedTargetScope = ImportedTargetScope::Local; }; diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index a7aab42e29..1142b0ed9f 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -654,7 +654,10 @@ class cmMakefile; SELECT(POLICY, CMP0218, \ "The CMAKE_WARN_DEPRECATED and CMAKE_ERROR_DEPRECATED variables " \ "are ignored.", \ - 4, 4, 0, WARN) + 4, 4, 0, WARN) \ + SELECT(POLICY, CMP0219, \ + "Macro invocations preserve backslashes in arguments.", 4, 4, 0, \ + WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ diff --git a/Source/cmVariableWatchCommand.cxx b/Source/cmVariableWatchCommand.cxx index 930d5d6000..7607bd31bc 100644 --- a/Source/cmVariableWatchCommand.cxx +++ b/Source/cmVariableWatchCommand.cxx @@ -10,6 +10,7 @@ #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmValue.h" @@ -56,6 +57,17 @@ void cmVariableWatchCommandVariableAccessed( { *currentListFile, cmListFileArgument::Quoted, fakeLineNo }, { stack, cmListFileArgument::Quoted, fakeLineNo } }; + { + cmPolicies::PolicyStatus cmp0219 = + makefile->CheckCMP0219(data->Command, newLFFArgs); + if (cmp0219 == cmPolicies::NEW) { + for (cmListFileArgument& arg : newLFFArgs) { + cmSystemTools::ReplaceString(arg.Value, "\\", "\\\\"); + } + } else if (cmp0219 == cmPolicies::WARN) { + makefile->IssueCMP0219Warning(data->Command, newLFFArgs); + } + } cmListFileFunction newLFF{ data->Command, fakeLineNo, fakeLineNo, std::move(newLFFArgs) }; diff --git a/Tests/RunCMake/CMP0219/AllNEW-Forward.cmake b/Tests/RunCMake/CMP0219/AllNEW-Forward.cmake new file mode 100644 index 0000000000..4a2f5ef601 --- /dev/null +++ b/Tests/RunCMake/CMP0219/AllNEW-Forward.cmake @@ -0,0 +1,17 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_inner) + set(cmp0219_inner_argn "${ARGN}") +endmacro() + +macro(cmp0219_middle) + cmp0219_inner(${ARGN}) +endmacro() + +macro(cmp0219_outer) + cmp0219_middle(${ARGN}) +endmacro() + +cmp0219_outer(HINTS "${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_inner_argn}" "HINTS;${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/Basic-NEW.cmake b/Tests/RunCMake/CMP0219/Basic-NEW.cmake new file mode 100644 index 0000000000..bf17b540ad --- /dev/null +++ b/Tests/RunCMake/CMP0219/Basic-NEW.cmake @@ -0,0 +1,18 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_capture package_name first_named) + set(cmp0219_named "${first_named}") + set(cmp0219_argn "${ARGN}") + set(cmp0219_argv "${ARGV}") + set(cmp0219_argv0 "${ARGV0}") + set(cmp0219_argv1 "${ARGV1}") +endmacro() + +cmp0219_capture(pybind11 HINTS "${cmp0219_path_native}") + +cmp0219_assert_equal("${cmp0219_named}" "HINTS") +cmp0219_assert_equal("${cmp0219_argn}" "${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_argv}" "pybind11;HINTS;${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_argv0}" "pybind11") +cmp0219_assert_equal("${cmp0219_argv1}" "HINTS") diff --git a/Tests/RunCMake/CMP0219/Basic-OLD-result.txt b/Tests/RunCMake/CMP0219/Basic-OLD-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CMP0219/Basic-OLD-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0219/Basic-OLD-stderr.txt b/Tests/RunCMake/CMP0219/Basic-OLD-stderr.txt new file mode 100644 index 0000000000..4116c67ae6 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Basic-OLD-stderr.txt @@ -0,0 +1 @@ +Invalid character escape '\\b'\. diff --git a/Tests/RunCMake/CMP0219/Basic-OLD.cmake b/Tests/RunCMake/CMP0219/Basic-OLD.cmake new file mode 100644 index 0000000000..6ea405b2e7 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Basic-OLD.cmake @@ -0,0 +1,8 @@ +cmake_policy(SET CMP0219 OLD) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_capture_old) + set(cmp0219_old_argn "${ARGN}") +endmacro() + +cmp0219_capture_old(HINTS "${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/CMP0219-helpers.cmake b/Tests/RunCMake/CMP0219/CMP0219-helpers.cmake new file mode 100644 index 0000000000..eb88100a0d --- /dev/null +++ b/Tests/RunCMake/CMP0219/CMP0219-helpers.cmake @@ -0,0 +1,20 @@ +function(cmp0219_assert_equal actual expected) + if(NOT "${actual}" STREQUAL "${expected}") + message(FATAL_ERROR + "Assertion failed:\n" + " expected=[${expected}]\n" + " actual=[${actual}]") + endif() +endfunction() + +function(cmp0219_assert_undefined variable_name) + if(DEFINED ${variable_name}) + message(FATAL_ERROR + "Assertion failed:\n" + " ${variable_name} should be undefined\n" + " actual=[${${variable_name}}]") + endif() +endfunction() + +set(cmp0219_path_fwd "C:/build_bot/new/temp/vendor") +string(REPLACE "/" "\\" cmp0219_path_native "${cmp0219_path_fwd}") diff --git a/Tests/RunCMake/CMP0219/CMakeLists.txt b/Tests/RunCMake/CMP0219/CMakeLists.txt new file mode 100644 index 0000000000..30c0273a61 --- /dev/null +++ b/Tests/RunCMake/CMP0219/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.10) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/CMP0219/CheckCSourceCompiles-NEW.cmake b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-NEW.cmake new file mode 100644 index 0000000000..53f1944ee3 --- /dev/null +++ b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-NEW.cmake @@ -0,0 +1,8 @@ +cmake_policy(SET CMP0219 NEW) + +enable_language(C) +include(CheckCSourceCompiles) +check_c_source_compiles("int main() { return '\\0'; }" RESULT) +if(NOT RESULT) + message(FATAL_ERROR "check_c_source_compiles failed for escaped NUL character source") +endif() diff --git a/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-result.txt b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-stderr.txt b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-stderr.txt new file mode 100644 index 0000000000..ea542472f7 --- /dev/null +++ b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-stderr.txt @@ -0,0 +1 @@ +Invalid character escape '\\0'\. diff --git a/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD.cmake b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD.cmake new file mode 100644 index 0000000000..27715d1913 --- /dev/null +++ b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD.cmake @@ -0,0 +1,5 @@ +cmake_policy(SET CMP0219 OLD) + +enable_language(C) +include(CheckCSourceCompiles) +check_c_source_compiles("int main() { return '\\0'; }" RESULT) diff --git a/Tests/RunCMake/CMP0219/Defer-EndPolicy-NEW.cmake b/Tests/RunCMake/CMP0219/Defer-EndPolicy-NEW.cmake new file mode 100644 index 0000000000..420f2f06c2 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Defer-EndPolicy-NEW.cmake @@ -0,0 +1,30 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_defer_capture var_name) + set(${var_name} "${ARGN}") +endmacro() + +# Deferred call is scheduled with OLD at the call site, +# but executed with NEW at end-of-file. +cmake_policy(SET CMP0219 OLD) +cmake_language( + DEFER CALL + cmp0219_defer_capture cmp0219_defer_old_callsite + HINTS "${cmp0219_path_native}") + +# Deferred call is scheduled with NEW at the call site, +# and executed with NEW at end-of-file. +cmake_policy(SET CMP0219 NEW) +cmake_language( + DEFER CALL + cmp0219_defer_capture cmp0219_defer_new_callsite + HINTS "${cmp0219_path_native}") + +cmake_language( + DEFER CALL + cmp0219_assert_equal "${cmp0219_defer_old_callsite}" + "HINTS;${cmp0219_path_native}") +cmake_language( + DEFER CALL + cmp0219_assert_equal "${cmp0219_defer_new_callsite}" + "HINTS;${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-result.txt b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-stderr.txt b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-stderr.txt new file mode 100644 index 0000000000..4116c67ae6 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-stderr.txt @@ -0,0 +1 @@ +Invalid character escape '\\b'\. diff --git a/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD.cmake b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD.cmake new file mode 100644 index 0000000000..5901ca1170 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD.cmake @@ -0,0 +1,15 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_defer_capture var_name) + set(${var_name} "${ARGN}") +endmacro() + +# Deferred call is scheduled with NEW at the call site, +# but executed with OLD at end-of-file. +cmake_policy(SET CMP0219 NEW) +cmake_language( + DEFER CALL + cmp0219_defer_capture cmp0219_defer_new_callsite + HINTS "${cmp0219_path_native}") + +cmake_policy(SET CMP0219 OLD) diff --git a/Tests/RunCMake/CMP0219/DependencyProviderMacro-NEW.cmake b/Tests/RunCMake/CMP0219/DependencyProviderMacro-NEW.cmake new file mode 100644 index 0000000000..c0e410fce4 --- /dev/null +++ b/Tests/RunCMake/CMP0219/DependencyProviderMacro-NEW.cmake @@ -0,0 +1,7 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +find_package(pybind11 HINTS "${cmp0219_path_native}") + +cmp0219_assert_equal("${cmp0219_provider_method}" "FIND_PACKAGE") +cmp0219_assert_equal("${cmp0219_provider_package}" "pybind11") +cmp0219_assert_equal("${cmp0219_provider_argn}" "HINTS;${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-result.txt b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-stderr.txt b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-stderr.txt new file mode 100644 index 0000000000..4116c67ae6 --- /dev/null +++ b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-stderr.txt @@ -0,0 +1 @@ +Invalid character escape '\\b'\. diff --git a/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD.cmake b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD.cmake new file mode 100644 index 0000000000..1baa8c3b94 --- /dev/null +++ b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD.cmake @@ -0,0 +1,3 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +find_package(pybind11 HINTS "${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/DependencyProviderMacro-TopInclude.cmake b/Tests/RunCMake/CMP0219/DependencyProviderMacro-TopInclude.cmake new file mode 100644 index 0000000000..6ba459f41d --- /dev/null +++ b/Tests/RunCMake/CMP0219/DependencyProviderMacro-TopInclude.cmake @@ -0,0 +1,11 @@ +macro(cmp0219_provider method package_name) + set(cmp0219_provider_method "${method}") + set(cmp0219_provider_package "${package_name}") + set(cmp0219_provider_argn "${ARGN}") + set(${package_name}_FOUND TRUE) +endmacro() + +cmake_language( + SET_DEPENDENCY_PROVIDER cmp0219_provider + SUPPORTED_METHODS FIND_PACKAGE +) diff --git a/Tests/RunCMake/CMP0219/DynamicDispatch-NEW.cmake b/Tests/RunCMake/CMP0219/DynamicDispatch-NEW.cmake new file mode 100644 index 0000000000..0cbd9696ee --- /dev/null +++ b/Tests/RunCMake/CMP0219/DynamicDispatch-NEW.cmake @@ -0,0 +1,9 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_dispatch) + set(cmp0219_dispatch_argn "${ARGN}") +endmacro() + +cmake_language(CALL cmp0219_dispatch HINTS "${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_dispatch_argn}" "HINTS;${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/Escape2-NEW.cmake b/Tests/RunCMake/CMP0219/Escape2-NEW.cmake new file mode 100644 index 0000000000..a8ed1bf562 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Escape2-NEW.cmake @@ -0,0 +1,8 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_escape str) + cmp0219_assert_equal("${str}" "\\") +endmacro() + +cmp0219_escape("\\") diff --git a/Tests/RunCMake/CMP0219/MixedChain-OLD-NEWMiddle.cmake b/Tests/RunCMake/CMP0219/MixedChain-OLD-NEWMiddle.cmake new file mode 100644 index 0000000000..1a9cef1b9a --- /dev/null +++ b/Tests/RunCMake/CMP0219/MixedChain-OLD-NEWMiddle.cmake @@ -0,0 +1,48 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +# Build progressively escaped variants to model callers that added extra +# backslashes to make an OLD-only macro chain work. +string(REPLACE "\\" "\\\\" cmp0219_path_2 "${cmp0219_path_native}") +string(REPLACE "\\" "\\\\" cmp0219_path_4 "${cmp0219_path_2}") +string(REPLACE "\\" "\\\\" cmp0219_path_8 "${cmp0219_path_4}") + +cmake_policy(SET CMP0219 OLD) + +macro(cmp0219_leaf var_name) + set(${var_name} "${ARGN}") +endmacro() + +macro(cmp0219_middle_old) + cmp0219_leaf(cmp0219_old_chain_capture ${ARGN}) +endmacro() + +macro(cmp0219_middle_new) + # Simulate a dependency update opting this middle forwarding layer into NEW. + cmake_policy(PUSH) + cmake_policy(SET CMP0219 NEW) + cmp0219_leaf(cmp0219_mixed_chain_capture ${ARGN}) + cmake_policy(POP) +endmacro() + +macro(cmp0219_outer_to_old) + cmp0219_middle_old(${ARGN}) +endmacro() + +macro(cmp0219_outer_to_new_middle) + cmp0219_middle_new(${ARGN}) +endmacro() + +# OLD->OLD->OLD requires heavily escaped input and produces a native path. +cmp0219_outer_to_old(HINTS "${cmp0219_path_8}") +cmp0219_assert_equal( + "${cmp0219_old_chain_capture}" "HINTS;${cmp0219_path_native}") + +# OLD->NEW->OLD with the same input preserves one extra layer. +cmp0219_outer_to_new_middle(HINTS "${cmp0219_path_8}") +cmp0219_assert_equal( + "${cmp0219_mixed_chain_capture}" "HINTS;${cmp0219_path_2}") + +# OLD->NEW->OLD needs fewer escape layers to reach a native path. +cmp0219_outer_to_new_middle(HINTS "${cmp0219_path_4}") +cmp0219_assert_equal( + "${cmp0219_mixed_chain_capture}" "HINTS;${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/NEW-calls-OLD.cmake b/Tests/RunCMake/CMP0219/NEW-calls-OLD.cmake new file mode 100644 index 0000000000..ee5457ac92 --- /dev/null +++ b/Tests/RunCMake/CMP0219/NEW-calls-OLD.cmake @@ -0,0 +1,12 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0219 OLD) +macro(cmp0219_old_capture) + set(cmp0219_old_argn "${ARGN}") +endmacro() +cmake_policy(POP) + +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +cmp0219_old_capture(HINTS "${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_old_argn}" "HINTS;${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/OLD-calls-NEW-result.txt b/Tests/RunCMake/CMP0219/OLD-calls-NEW-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CMP0219/OLD-calls-NEW-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0219/OLD-calls-NEW-stderr.txt b/Tests/RunCMake/CMP0219/OLD-calls-NEW-stderr.txt new file mode 100644 index 0000000000..4116c67ae6 --- /dev/null +++ b/Tests/RunCMake/CMP0219/OLD-calls-NEW-stderr.txt @@ -0,0 +1 @@ +Invalid character escape '\\b'\. diff --git a/Tests/RunCMake/CMP0219/OLD-calls-NEW.cmake b/Tests/RunCMake/CMP0219/OLD-calls-NEW.cmake new file mode 100644 index 0000000000..b3fe04e230 --- /dev/null +++ b/Tests/RunCMake/CMP0219/OLD-calls-NEW.cmake @@ -0,0 +1,11 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0219 NEW) +macro(cmp0219_new_capture) + set(cmp0219_new_argn "${ARGN}") +endmacro() +cmake_policy(POP) + +cmake_policy(SET CMP0219 OLD) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +cmp0219_new_capture(HINTS "${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/ParseArguments-Semicolon.cmake b/Tests/RunCMake/CMP0219/ParseArguments-Semicolon.cmake new file mode 100644 index 0000000000..cd0fb6fcf8 --- /dev/null +++ b/Tests/RunCMake/CMP0219/ParseArguments-Semicolon.cmake @@ -0,0 +1,46 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") +cmake_policy(SET CMP0219 NEW) + +macro(cmp0219_parse_corner_case) + set(_options) + set(_one_value_args FOO) + set(_multi_value_args) + cmake_parse_arguments(cmp0219_q + "${_options}" + "${_one_value_args}" + "${_multi_value_args}" + "${ARGN}") + cmake_parse_arguments(cmp0219_u + "${_options}" + "${_one_value_args}" + "${_multi_value_args}" + ${ARGN}) +endmacro() + +cmp0219_parse_corner_case(FOO "foo\\;bar") + +cmp0219_assert_equal("${cmp0219_q_FOO}" "foo;bar") +cmp0219_assert_undefined(cmp0219_q_UNPARSED_ARGUMENTS) +cmp0219_assert_equal("${cmp0219_u_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") + +cmp0219_parse_corner_case(FOO "foo\;bar") + +cmp0219_assert_equal("${cmp0219_q_FOO}" "foo;bar") +cmp0219_assert_undefined(cmp0219_q_UNPARSED_ARGUMENTS) +cmp0219_assert_equal("${cmp0219_u_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") + +cmp0219_parse_corner_case(FOO "foo;bar") + +cmp0219_assert_equal("${cmp0219_q_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") +cmp0219_assert_equal("${cmp0219_u_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") + +cmp0219_parse_corner_case(FOO foo;bar) + +cmp0219_assert_equal("${cmp0219_q_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") +cmp0219_assert_equal("${cmp0219_u_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") diff --git a/Tests/RunCMake/CMP0219/ReturnAndPolicyPropagation.cmake b/Tests/RunCMake/CMP0219/ReturnAndPolicyPropagation.cmake new file mode 100644 index 0000000000..a95a477339 --- /dev/null +++ b/Tests/RunCMake/CMP0219/ReturnAndPolicyPropagation.cmake @@ -0,0 +1,32 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +cmake_policy(PUSH) +cmake_policy(SET CMP0219 OLD) +macro(cmp0219_old_return) + set(cmp0219_old_return_argn "${ARGN}" PARENT_SCOPE) + return() +endmacro() + +macro(cmp0219_old_set_policy) + set(cmp0219_old_set_policy_argn "${ARGN}") + cmake_policy(SET CMP0210 OLD) +endmacro() +cmake_policy(POP) + +function(cmp0219_test_return) + set(cmp0219_return_state "before" PARENT_SCOPE) + cmp0219_old_return(HINTS "${cmp0219_path_native}") + set(cmp0219_return_state "after" PARENT_SCOPE) +endfunction() + +cmp0219_test_return() +cmp0219_assert_equal("${cmp0219_return_state}" "before") +cmp0219_assert_equal( + "${cmp0219_old_return_argn}" "HINTS;${cmp0219_path_native}") + +cmp0219_old_set_policy(HINTS "${cmp0219_path_native}") +cmake_policy(GET CMP0210 cmp0219_cmp0210_status) +cmp0219_assert_equal("${cmp0219_cmp0210_status}" "OLD") +cmp0219_assert_equal( + "${cmp0219_old_set_policy_argn}" "HINTS;${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/RunCMakeTest.cmake b/Tests/RunCMake/CMP0219/RunCMakeTest.cmake new file mode 100644 index 0000000000..07d63caa2c --- /dev/null +++ b/Tests/RunCMake/CMP0219/RunCMakeTest.cmake @@ -0,0 +1,30 @@ +include(RunCMake) + +run_cmake_script(Basic-NEW) +run_cmake_script(Basic-OLD) +run_cmake_script(Escape2-NEW) +run_cmake_script(AllNEW-Forward) +run_cmake_script(NEW-calls-OLD) +run_cmake_script(OLD-calls-NEW) +run_cmake_script(MixedChain-OLD-NEWMiddle) +run_cmake_script(DynamicDispatch-NEW) +run_cmake_script(ParseArguments-Semicolon) +run_cmake_script(SemicolonEscape-QuotedUnquoted) +run_cmake_script(ReturnAndPolicyPropagation) +run_cmake_script(Warn-Unset-Macro) +run_cmake_script(Warn-Unset-Macro-Multi) +run_cmake_script(Warn-Unset-VariableWatch) +run_cmake_script(VariableWatch-OLD) +run_cmake_script(VariableWatch-NEW) +run_cmake(CheckCSourceCompiles-OLD) +run_cmake(CheckCSourceCompiles-NEW) +run_cmake_with_options(DependencyProviderMacro-OLD + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=${RunCMake_SOURCE_DIR}/DependencyProviderMacro-TopInclude.cmake" + -D "CMAKE_POLICY_DEFAULT_CMP0219=OLD" +) +run_cmake_with_options(DependencyProviderMacro-NEW + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=${RunCMake_SOURCE_DIR}/DependencyProviderMacro-TopInclude.cmake" + -D "CMAKE_POLICY_DEFAULT_CMP0219=NEW" +) +run_cmake(Defer-EndPolicy-NEW) +run_cmake(Defer-EndPolicy-OLD) diff --git a/Tests/RunCMake/CMP0219/SemicolonEscape-QuotedUnquoted.cmake b/Tests/RunCMake/CMP0219/SemicolonEscape-QuotedUnquoted.cmake new file mode 100644 index 0000000000..da46a71e72 --- /dev/null +++ b/Tests/RunCMake/CMP0219/SemicolonEscape-QuotedUnquoted.cmake @@ -0,0 +1,62 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +function(cmp0219_assert_encoded actual expected) + string(REPLACE "\\" "" cmp0219_encoded "${actual}") + string(REPLACE ";" "" cmp0219_encoded "${cmp0219_encoded}") + cmp0219_assert_equal("${cmp0219_encoded}" "${expected}") +endfunction() + +function(cmp0219_sink out_prefix) + math(EXPR cmp0219_sink_argc "${ARGC} - 1") + set(${out_prefix}_argc "${cmp0219_sink_argc}" PARENT_SCOPE) + if(cmp0219_sink_argc GREATER 0) + set(${out_prefix}_arg0 "${ARGV1}" PARENT_SCOPE) + endif() + if(cmp0219_sink_argc GREATER 1) + set(${out_prefix}_arg1 "${ARGV2}" PARENT_SCOPE) + endif() +endfunction() + +macro(cmp0219_semicolon_probe prefix value) + set(${prefix}_named_q "${value}") + set(${prefix}_named_u ${value}) + set(${prefix}_argv1_q "${ARGV1}") + set(${prefix}_argv1_u ${ARGV1}) + set(${prefix}_argn_q "${ARGN}") + set(${prefix}_argn_u ${ARGN}) + + cmp0219_sink("${prefix}_sink_named_q" "${value}") + cmp0219_sink("${prefix}_sink_named_u" ${value}) + cmp0219_sink("${prefix}_sink_argn_q" "${ARGN}") + cmp0219_sink("${prefix}_sink_argn_u" ${ARGN}) +endmacro() + +function(cmp0219_run_semicolon_escape_mode mode) + cmake_policy(SET CMP0219 "${mode}") + + # Value passed with a quoted argument keeps '\;' in textual substitutions + # until an unquoted use decodes it. + cmp0219_semicolon_probe("${mode}_quoted" "foo\\;bar" "foo\\;bar") + cmp0219_assert_encoded("${${mode}_quoted_named_q}" "foobar") + cmp0219_assert_encoded("${${mode}_quoted_named_u}" "foobar") + cmp0219_assert_equal("${${mode}_quoted_sink_named_q_argc}" "1") + cmp0219_assert_equal("${${mode}_quoted_sink_named_u_argc}" "1") + cmp0219_assert_equal("${${mode}_quoted_sink_argn_q_argc}" "1") + cmp0219_assert_equal("${${mode}_quoted_sink_argn_u_argc}" "1") + + # Value passed with an unquoted argument uses '\;' to keep one argument at + # the call site, but unquoted forwarding splits at ';' in the macro body. + cmp0219_semicolon_probe("${mode}_unquoted" foo\;bar foo\;bar) + cmp0219_assert_encoded("${${mode}_unquoted_named_q}" "foobar") + cmp0219_assert_equal("${${mode}_unquoted_sink_named_q_argc}" "1") + cmp0219_assert_equal("${${mode}_unquoted_sink_named_u_argc}" "2") + cmp0219_assert_encoded("${${mode}_unquoted_sink_named_u_arg0}" "foo") + cmp0219_assert_encoded("${${mode}_unquoted_sink_named_u_arg1}" "bar") + cmp0219_assert_equal("${${mode}_unquoted_sink_argn_q_argc}" "1") + cmp0219_assert_equal("${${mode}_unquoted_sink_argn_u_argc}" "2") + cmp0219_assert_encoded("${${mode}_unquoted_sink_argn_u_arg0}" "foo") + cmp0219_assert_encoded("${${mode}_unquoted_sink_argn_u_arg1}" "bar") +endfunction() + +cmp0219_run_semicolon_escape_mode(OLD) +cmp0219_run_semicolon_escape_mode(NEW) diff --git a/Tests/RunCMake/CMP0219/VariableWatch-NEW.cmake b/Tests/RunCMake/CMP0219/VariableWatch-NEW.cmake new file mode 100644 index 0000000000..2874ab755e --- /dev/null +++ b/Tests/RunCMake/CMP0219/VariableWatch-NEW.cmake @@ -0,0 +1,11 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +function(cmp0219_watch_callback variable access value current_list_file stack) + set(cmp0219_watch_value "${value}" PARENT_SCOPE) +endfunction() + +variable_watch(cmp0219_watched cmp0219_watch_callback) +set(cmp0219_watched "${cmp0219_path_native}") + +cmp0219_assert_equal("${cmp0219_watch_value}" "${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/VariableWatch-OLD-result.txt b/Tests/RunCMake/CMP0219/VariableWatch-OLD-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CMP0219/VariableWatch-OLD-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0219/VariableWatch-OLD-stderr.txt b/Tests/RunCMake/CMP0219/VariableWatch-OLD-stderr.txt new file mode 100644 index 0000000000..4116c67ae6 --- /dev/null +++ b/Tests/RunCMake/CMP0219/VariableWatch-OLD-stderr.txt @@ -0,0 +1 @@ +Invalid character escape '\\b'\. diff --git a/Tests/RunCMake/CMP0219/VariableWatch-OLD.cmake b/Tests/RunCMake/CMP0219/VariableWatch-OLD.cmake new file mode 100644 index 0000000000..794c0ddbd5 --- /dev/null +++ b/Tests/RunCMake/CMP0219/VariableWatch-OLD.cmake @@ -0,0 +1,9 @@ +cmake_policy(SET CMP0219 OLD) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +function(cmp0219_watch_callback variable access value current_list_file stack) + set(cmp0219_watch_value "${value}" PARENT_SCOPE) +endfunction() + +variable_watch(cmp0219_watched cmp0219_watch_callback) +set(cmp0219_watched "${cmp0219_path_native}") diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi-stderr.txt b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi-stderr.txt new file mode 100644 index 0000000000..f4735666e1 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi-stderr.txt @@ -0,0 +1,22 @@ +CMake Warning \(policy\) at .*/Warn-Unset-Macro-Multi\.cmake:[0-9]+ \(cmp0219_warn_macro\): + Policy CMP0219 is not set: Macro invocations preserve backslashes in + arguments\. Run "cmake --help-policy CMP0219" for policy details\. Use the + cmake_policy command to set the policy and suppress this warning\. + + Command "cmp0219_warn_macro" called with arguments containing backslashes\. + + Since the policy is not set, backslashes in the arguments: + + "prefix\\tsuffix" + "left\\;right" + + will be interpreted as escape sequences for compatibility\. + + Set the policy to NEW to instead pass + + "prefix\\\\tsuffix" + "left\\\\;right" + + so that argument parsing will preserve the original values\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi.cmake b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi.cmake new file mode 100644 index 0000000000..1fae871005 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi.cmake @@ -0,0 +1,9 @@ +set(cmp0219_warn_value1 "prefix\\tsuffix") +set(cmp0219_warn_value2 "left\\;right") + +macro(cmp0219_warn_macro arg1 arg2) + set(cmp0219_warn_seen1 "${arg1}") + set(cmp0219_warn_seen2 "${arg2}") +endmacro() + +cmp0219_warn_macro("${cmp0219_warn_value1}" "${cmp0219_warn_value2}") diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-Macro-stderr.txt b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-stderr.txt new file mode 100644 index 0000000000..ac462cf87f --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-stderr.txt @@ -0,0 +1,20 @@ +CMake Warning \(policy\) at .*/Warn-Unset-Macro\.cmake:[0-9]+ \(cmp0219_warn_macro\): + Policy CMP0219 is not set: Macro invocations preserve backslashes in + arguments\. Run "cmake --help-policy CMP0219" for policy details\. Use the + cmake_policy command to set the policy and suppress this warning\. + + Command "cmp0219_warn_macro" called with arguments containing backslashes\. + + Since the policy is not set, backslashes in the arguments: + + "prefix\\tsuffix" + + will be interpreted as escape sequences for compatibility\. + + Set the policy to NEW to instead pass + + "prefix\\\\tsuffix" + + so that argument parsing will preserve the original values\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-Macro.cmake b/Tests/RunCMake/CMP0219/Warn-Unset-Macro.cmake new file mode 100644 index 0000000000..ec7d7a12d4 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-Macro.cmake @@ -0,0 +1,8 @@ +set(cmp0219_warn_value "prefix\\tsuffix") + +macro(cmp0219_warn_macro value) + set(cmp0219_warn_seen "${value}") +endmacro() + +cmp0219_warn_macro("${cmp0219_warn_value}") +cmp0219_warn_macro("${cmp0219_warn_value}") diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch-stderr.txt b/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch-stderr.txt new file mode 100644 index 0000000000..1c1c39676b --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch-stderr.txt @@ -0,0 +1,20 @@ +CMake Warning \(policy\) at .*/Warn-Unset-VariableWatch\.cmake:[0-9]+ \(set\): + Policy CMP0219 is not set: Macro invocations preserve backslashes in + arguments\. Run "cmake --help-policy CMP0219" for policy details\. Use the + cmake_policy command to set the policy and suppress this warning\. + + Command "cmp0219_warn_watch" called with arguments containing backslashes\. + + Since the policy is not set, backslashes in the arguments: + + "prefix\\tsuffix" + + will be interpreted as escape sequences for compatibility\. + + Set the policy to NEW to instead pass + + "prefix\\\\tsuffix" + + so that argument parsing will preserve the original values\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch.cmake b/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch.cmake new file mode 100644 index 0000000000..f5898e8347 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch.cmake @@ -0,0 +1,6 @@ +function(cmp0219_warn_watch variable access value current_list_file stack) +endfunction() + +variable_watch(cmp0219_warned cmp0219_warn_watch) +set(cmp0219_warned "prefix\\tsuffix") +set(cmp0219_warned "prefix\\tsuffix") diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 48a7bfe1c8..c81dac7586 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -185,6 +185,7 @@ if(WIN32) add_RunCMake_test(CMP0212) endif() add_RunCMake_test(CMP0217) +add_RunCMake_test(CMP0219) if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") add_RunCMake_test(CMP0194 -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) diff --git a/Tests/RunCMake/CheckSourceCompiles/CMakeLists.txt b/Tests/RunCMake/CheckSourceCompiles/CMakeLists.txt index 0421e28036..87023e0200 100644 --- a/Tests/RunCMake/CheckSourceCompiles/CMakeLists.txt +++ b/Tests/RunCMake/CheckSourceCompiles/CMakeLists.txt @@ -1,5 +1,9 @@ cmake_minimum_required(VERSION 3.13) +if(POLICY CMP0219) + cmake_policy(SET CMP0219 OLD) +endif() + project(${RunCMake_TEST} LANGUAGES NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Syntax/Escape2-stderr.txt b/Tests/RunCMake/Syntax/Escape2-stderr.txt index a94469490e..9cc9c3632b 100644 --- a/Tests/RunCMake/Syntax/Escape2-stderr.txt +++ b/Tests/RunCMake/Syntax/Escape2-stderr.txt @@ -1,7 +1,7 @@ -CMake Error at Escape2\.cmake:2 \(message\): +CMake Error at Escape2\.cmake:[0-9]+ \(message\): Syntax error in cmake code at - .*/Tests/RunCMake/Syntax/Escape2\.cmake:2 + .*/Tests/RunCMake/Syntax/Escape2\.cmake:[0-9]+ when parsing string @@ -9,5 +9,5 @@ CMake Error at Escape2\.cmake:2 \(message\): Invalid character escape '\\' \(at end of input\)\. Call Stack \(most recent call first\): - Escape2\.cmake:5 \(escape\) + Escape2\.cmake:[0-9]+ \(escape\) CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/Syntax/Escape2.cmake b/Tests/RunCMake/Syntax/Escape2.cmake index 6e8b07bbee..2a40c6d32b 100644 --- a/Tests/RunCMake/Syntax/Escape2.cmake +++ b/Tests/RunCMake/Syntax/Escape2.cmake @@ -2,4 +2,8 @@ macro (escape str) message("${str}") endmacro () +if(POLICY CMP0219) + cmake_policy(SET CMP0219 OLD) +endif() + escape("\\") diff --git a/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake b/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake index 028bfaf3ec..2b374eef29 100644 --- a/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake +++ b/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake @@ -1,4 +1,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/test_utils.cmake) +cmake_policy(SET CMP0219 OLD) # example from the documentation # OPTIONAL is a keyword and therefore terminates the definition of