From 3028ca756c8621b3cc37032987eb01fbe61da248 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 22 Jan 2009 13:16:47 -0500 Subject: [PATCH] ENH: Better policies for functions and macros This teaches functions and macros to use policies recorded at creation time when they are invoked. It restores the policies as a weak policy stack entry so that any policies set by a function escape to its caller as before. --- Source/cmCMakePolicyCommand.h | 6 ++++ Source/cmFunctionCommand.cxx | 8 +++++ Source/cmFunctionCommand.h | 6 +++- Source/cmMacroCommand.cxx | 8 +++++ Source/cmMacroCommand.h | 6 +++- Source/cmMakefile.cxx | 12 +++++++ Source/cmMakefile.h | 1 + Tests/CMakeLists.txt | 1 + Tests/PolicyScope/CMakeLists.txt | 55 ++++++++++++++++++++++++++++++++ Tests/PolicyScope/main.c | 4 +++ 10 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 Tests/PolicyScope/CMakeLists.txt create mode 100644 Tests/PolicyScope/main.c diff --git a/Source/cmCMakePolicyCommand.h b/Source/cmCMakePolicyCommand.h index d2315ea06..e74a6cf4a 100644 --- a/Source/cmCMakePolicyCommand.h +++ b/Source/cmCMakePolicyCommand.h @@ -123,6 +123,12 @@ public: " cmake_policy(POP)\n" "Each PUSH must have a matching POP to erase any changes. " "This is useful to make temporary changes to policy settings." + "\n" + "Functions and macros record policy settings when they are created " + "and use the pre-record policies when they are invoked. " + "If the function or macro implementation sets policies, the changes " + "automatically propagate up through callers until they reach the " + "closest nested policy stack entry." ; } diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx index 2b5223b3a..9095961cf 100644 --- a/Source/cmFunctionCommand.cxx +++ b/Source/cmFunctionCommand.cxx @@ -36,6 +36,7 @@ public: // we must copy when we clone newC->Args = this->Args; newC->Functions = this->Functions; + newC->Policies = this->Policies; return newC; } @@ -81,6 +82,7 @@ public: std::vector Args; std::vector Functions; + cmPolicies::PolicyMap Policies; }; @@ -108,6 +110,10 @@ bool cmFunctionHelperCommand::InvokeInitialPass cmMakefile::ScopePushPop varScope(this->Makefile); static_cast(varScope); + // Push a weak policy scope which restores the policies recorded at + // function creation. + cmMakefile::PolicyPushPop polScope(this->Makefile, true, this->Policies); + // set the value of argc cmOStringStream strStream; strStream << expandedArgs.size(); @@ -165,6 +171,7 @@ bool cmFunctionHelperCommand::InvokeInitialPass // The error message should have already included the call stack // so we do not need to report an error here. lexScope.Quiet(); + polScope.Quiet(); inStatus.SetNestedError(true); return false; } @@ -206,6 +213,7 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, cmFunctionHelperCommand *f = new cmFunctionHelperCommand(); f->Args = this->Args; f->Functions = this->Functions; + mf.RecordPolicies(f->Policies); // Set the FilePath on the arguments to match the function since it is // not stored and the original values may be freed diff --git a/Source/cmFunctionCommand.h b/Source/cmFunctionCommand.h index b9f9010e5..ced821899 100644 --- a/Source/cmFunctionCommand.h +++ b/Source/cmFunctionCommand.h @@ -104,7 +104,11 @@ public: "will have the actual values of the arguments passed in. This " "facilitates creating functions with optional arguments. Additionally " "ARGV holds the list of all arguments given to the function and ARGN " - "holds the list of argument pass the last expected argument."; + "holds the list of argument pass the last expected argument." + "\n" + "See the cmake_policy() command documentation for the behavior of " + "policies inside functions." + ; } cmTypeMacro(cmFunctionCommand, cmCommand); diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index 9cc2653ba..c736e0996 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -36,6 +36,7 @@ public: // we must copy when we clone newC->Args = this->Args; newC->Functions = this->Functions; + newC->Policies = this->Policies; return newC; } @@ -81,6 +82,7 @@ public: std::vector Args; std::vector Functions; + cmPolicies::PolicyMap Policies; }; @@ -110,6 +112,10 @@ bool cmMacroHelperCommand::InvokeInitialPass // Enforce matching logical blocks inside the macro. cmMakefile::LexicalPushPop lexScope(this->Makefile); + // Push a weak policy scope which restores the policies recorded at + // macro creation. + cmMakefile::PolicyPushPop polScope(this->Makefile, true, this->Policies); + // set the value of argc cmOStringStream argcDefStream; argcDefStream << expandedArgs.size(); @@ -219,6 +225,7 @@ bool cmMacroHelperCommand::InvokeInitialPass // The error message should have already included the call stack // so we do not need to report an error here. lexScope.Quiet(); + polScope.Quiet(); inStatus.SetNestedError(true); return false; } @@ -264,6 +271,7 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, cmMacroHelperCommand *f = new cmMacroHelperCommand(); f->Args = this->Args; f->Functions = this->Functions; + mf.RecordPolicies(f->Policies); std::string newName = "_" + this->Args[0]; mf.GetCMakeInstance()->RenameCommand(this->Args[0].c_str(), newName.c_str()); diff --git a/Source/cmMacroCommand.h b/Source/cmMacroCommand.h index 3231d7135..55da26ed8 100644 --- a/Source/cmMacroCommand.h +++ b/Source/cmMacroCommand.h @@ -111,7 +111,11 @@ public: "are not variables in the usual CMake sense. They are string " "replacements much like the c preprocessor would do with a " "macro. If you want true CMake variables you should look at " - "the function command."; + "the function command." + "\n" + "See the cmake_policy() command documentation for the behavior of " + "policies inside macros." + ; } cmTypeMacro(cmMacroCommand, cmCommand); diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 25d97fa92..4442bd304 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -3774,3 +3774,15 @@ cmPolicies *cmMakefile::GetPolicies() } return this->GetCMakeInstance()->GetPolicies(); } + +//---------------------------------------------------------------------------- +void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm) +{ + /* Record the setting of every policy. */ + typedef cmPolicies::PolicyID PolicyID; + for(PolicyID pid = cmPolicies::CMP0000; + pid != cmPolicies::CMPCOUNT; pid = PolicyID(pid+1)) + { + pm[pid] = this->GetPolicyStatus(pid); + } +} diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index a5fcec15e..1c59313c0 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -343,6 +343,7 @@ public: bool SetPolicy(const char *id, cmPolicies::PolicyStatus status); cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id); bool SetPolicyVersion(const char *version); + void RecordPolicies(cmPolicies::PolicyMap& pm); //@} /** Helper class to push and pop policies automatically. */ diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 7efb15046..70918446b 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -102,6 +102,7 @@ IF(BUILD_TESTING) ADD_TEST_MACRO(Preprocess Preprocess) ADD_TEST_MACRO(ExportImport ExportImport) ADD_TEST_MACRO(Unset Unset) + ADD_TEST_MACRO(PolicyScope PolicyScope) SET(CMAKE_BUILD_TEST_SOURCE_DIR "${CMake_SOURCE_DIR}/Tests/COnly") SET(CMAKE_BUILD_TEST_BINARY_DIR "${CMake_BINARY_DIR}/Tests/CMakeBuildCOnly") diff --git a/Tests/PolicyScope/CMakeLists.txt b/Tests/PolicyScope/CMakeLists.txt new file mode 100644 index 000000000..ccb64de34 --- /dev/null +++ b/Tests/PolicyScope/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 2.6.3) +project(PolicyScope C) + +#----------------------------------------------------------------------------- +# Helper function to report results. +function(check msg lhs rhs) + if(NOT "${lhs}" STREQUAL "${rhs}") + message(FATAL_ERROR "${msg}: expected [${lhs}], got [${rhs}]") + endif() +endfunction(check) + +#----------------------------------------------------------------------------- +# Test function and macro policy recording. + +# Create the functions in an isolated scope in which we change policies. +cmake_policy(PUSH) +if(1) + # Change CMP0002 + cmake_policy(SET CMP0002 OLD) + function(func1) + # CMP0002 should be changed when this function is invoked + cmake_policy(GET CMP0002 cmp) + check(CMP0002 "OLD" "${cmp}") + endfunction(func1) + + # Unset CMP0002 + cmake_policy(VERSION 2.4) + macro(macro1) + # CMP0002 should be unset when this macro is invoked + cmake_policy(GET CMP0002 cmp) + check(CMP0002 "" "${cmp}") + + # Setting the policy should work here and also in the caller. + cmake_policy(SET CMP0002 OLD) + cmake_policy(GET CMP0002 cmp) + check(CMP0002 "OLD" "${cmp}") + endmacro(macro1) +endif(1) +cmake_policy(POP) + +# CMP0002 should still be NEW in this context. +cmake_policy(GET CMP0002 cmp) +check(CMP0002 "NEW" "${cmp}") + +# Check the recorded policies +func1() +macro1() + +# The macro should have changed CMP0002. +cmake_policy(GET CMP0002 cmp) +check(CMP0002 "OLD" "${cmp}") + +#----------------------------------------------------------------------------- +# Dummy executable so the project can build and run. +add_executable(PolicyScope main.c) diff --git a/Tests/PolicyScope/main.c b/Tests/PolicyScope/main.c new file mode 100644 index 000000000..f8b643afb --- /dev/null +++ b/Tests/PolicyScope/main.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +}