Browse Source

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.
Brad King 17 years ago
parent
commit
3028ca756c

+ 6 - 0
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."
       ;
     }
   

+ 8 - 0
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<std::string> Args;
   std::vector<cmListFileFunction> Functions;
+  cmPolicies::PolicyMap Policies;
 };
 
 
@@ -108,6 +110,10 @@ bool cmFunctionHelperCommand::InvokeInitialPass
   cmMakefile::ScopePushPop varScope(this->Makefile);
   static_cast<void>(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

+ 5 - 1
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);

+ 8 - 0
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<std::string> Args;
   std::vector<cmListFileFunction> 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());

+ 5 - 1
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);

+ 12 - 0
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);
+    }
+}

+ 1 - 0
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.  */

+ 1 - 0
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")

+ 55 - 0
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)

+ 4 - 0
Tests/PolicyScope/main.c

@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}