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.
This commit is contained in:
Brad King 2009-01-22 13:16:47 -05:00
parent 18eadebc4c
commit 3028ca756c
10 changed files with 105 additions and 2 deletions

View File

@ -123,6 +123,12 @@ public:
" cmake_policy(POP)\n" " cmake_policy(POP)\n"
"Each PUSH must have a matching POP to erase any changes. " "Each PUSH must have a matching POP to erase any changes. "
"This is useful to make temporary changes to policy settings." "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."
; ;
} }

View File

@ -36,6 +36,7 @@ public:
// we must copy when we clone // we must copy when we clone
newC->Args = this->Args; newC->Args = this->Args;
newC->Functions = this->Functions; newC->Functions = this->Functions;
newC->Policies = this->Policies;
return newC; return newC;
} }
@ -81,6 +82,7 @@ public:
std::vector<std::string> Args; std::vector<std::string> Args;
std::vector<cmListFileFunction> Functions; std::vector<cmListFileFunction> Functions;
cmPolicies::PolicyMap Policies;
}; };
@ -108,6 +110,10 @@ bool cmFunctionHelperCommand::InvokeInitialPass
cmMakefile::ScopePushPop varScope(this->Makefile); cmMakefile::ScopePushPop varScope(this->Makefile);
static_cast<void>(varScope); 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 // set the value of argc
cmOStringStream strStream; cmOStringStream strStream;
strStream << expandedArgs.size(); strStream << expandedArgs.size();
@ -165,6 +171,7 @@ bool cmFunctionHelperCommand::InvokeInitialPass
// The error message should have already included the call stack // The error message should have already included the call stack
// so we do not need to report an error here. // so we do not need to report an error here.
lexScope.Quiet(); lexScope.Quiet();
polScope.Quiet();
inStatus.SetNestedError(true); inStatus.SetNestedError(true);
return false; return false;
} }
@ -206,6 +213,7 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf,
cmFunctionHelperCommand *f = new cmFunctionHelperCommand(); cmFunctionHelperCommand *f = new cmFunctionHelperCommand();
f->Args = this->Args; f->Args = this->Args;
f->Functions = this->Functions; f->Functions = this->Functions;
mf.RecordPolicies(f->Policies);
// Set the FilePath on the arguments to match the function since it is // Set the FilePath on the arguments to match the function since it is
// not stored and the original values may be freed // not stored and the original values may be freed

View File

@ -104,7 +104,11 @@ public:
"will have the actual values of the arguments passed in. This " "will have the actual values of the arguments passed in. This "
"facilitates creating functions with optional arguments. Additionally " "facilitates creating functions with optional arguments. Additionally "
"ARGV holds the list of all arguments given to the function and ARGN " "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); cmTypeMacro(cmFunctionCommand, cmCommand);

View File

@ -36,6 +36,7 @@ public:
// we must copy when we clone // we must copy when we clone
newC->Args = this->Args; newC->Args = this->Args;
newC->Functions = this->Functions; newC->Functions = this->Functions;
newC->Policies = this->Policies;
return newC; return newC;
} }
@ -81,6 +82,7 @@ public:
std::vector<std::string> Args; std::vector<std::string> Args;
std::vector<cmListFileFunction> Functions; std::vector<cmListFileFunction> Functions;
cmPolicies::PolicyMap Policies;
}; };
@ -110,6 +112,10 @@ bool cmMacroHelperCommand::InvokeInitialPass
// Enforce matching logical blocks inside the macro. // Enforce matching logical blocks inside the macro.
cmMakefile::LexicalPushPop lexScope(this->Makefile); 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 // set the value of argc
cmOStringStream argcDefStream; cmOStringStream argcDefStream;
argcDefStream << expandedArgs.size(); argcDefStream << expandedArgs.size();
@ -219,6 +225,7 @@ bool cmMacroHelperCommand::InvokeInitialPass
// The error message should have already included the call stack // The error message should have already included the call stack
// so we do not need to report an error here. // so we do not need to report an error here.
lexScope.Quiet(); lexScope.Quiet();
polScope.Quiet();
inStatus.SetNestedError(true); inStatus.SetNestedError(true);
return false; return false;
} }
@ -264,6 +271,7 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf,
cmMacroHelperCommand *f = new cmMacroHelperCommand(); cmMacroHelperCommand *f = new cmMacroHelperCommand();
f->Args = this->Args; f->Args = this->Args;
f->Functions = this->Functions; f->Functions = this->Functions;
mf.RecordPolicies(f->Policies);
std::string newName = "_" + this->Args[0]; std::string newName = "_" + this->Args[0];
mf.GetCMakeInstance()->RenameCommand(this->Args[0].c_str(), mf.GetCMakeInstance()->RenameCommand(this->Args[0].c_str(),
newName.c_str()); newName.c_str());

View File

@ -111,7 +111,11 @@ public:
"are not variables in the usual CMake sense. They are string " "are not variables in the usual CMake sense. They are string "
"replacements much like the c preprocessor would do with a " "replacements much like the c preprocessor would do with a "
"macro. If you want true CMake variables you should look at " "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); cmTypeMacro(cmMacroCommand, cmCommand);

View File

@ -3774,3 +3774,15 @@ cmPolicies *cmMakefile::GetPolicies()
} }
return this->GetCMakeInstance()->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);
}
}

View File

@ -343,6 +343,7 @@ public:
bool SetPolicy(const char *id, cmPolicies::PolicyStatus status); bool SetPolicy(const char *id, cmPolicies::PolicyStatus status);
cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id); cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id);
bool SetPolicyVersion(const char *version); bool SetPolicyVersion(const char *version);
void RecordPolicies(cmPolicies::PolicyMap& pm);
//@} //@}
/** Helper class to push and pop policies automatically. */ /** Helper class to push and pop policies automatically. */

View File

@ -102,6 +102,7 @@ IF(BUILD_TESTING)
ADD_TEST_MACRO(Preprocess Preprocess) ADD_TEST_MACRO(Preprocess Preprocess)
ADD_TEST_MACRO(ExportImport ExportImport) ADD_TEST_MACRO(ExportImport ExportImport)
ADD_TEST_MACRO(Unset Unset) 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_SOURCE_DIR "${CMake_SOURCE_DIR}/Tests/COnly")
SET(CMAKE_BUILD_TEST_BINARY_DIR "${CMake_BINARY_DIR}/Tests/CMakeBuildCOnly") SET(CMAKE_BUILD_TEST_BINARY_DIR "${CMake_BINARY_DIR}/Tests/CMakeBuildCOnly")

View File

@ -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
Tests/PolicyScope/main.c Normal file
View File

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