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

View File

@ -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

View File

@ -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);

View File

@ -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());

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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. */

View File

@ -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")

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;
}