summaryrefslogtreecommitdiffstats
path: root/Source/cmMakefile.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmMakefile.cxx')
-rw-r--r--Source/cmMakefile.cxx202
1 files changed, 199 insertions, 3 deletions
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 1463680..ef81529 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -43,12 +43,22 @@ class cmMakefile::Internals
{
public:
std::stack<cmDefinitions, std::list<cmDefinitions> > VarStack;
+ std::stack<std::set<cmStdString> > VarInitStack;
+ std::stack<std::set<cmStdString> > VarUsageStack;
};
// default is not to be building executables
cmMakefile::cmMakefile(): Internal(new Internals)
{
- this->Internal->VarStack.push(cmDefinitions());
+ const cmDefinitions& defs = cmDefinitions();
+ const std::set<cmStdString> globalKeys = defs.LocalKeys();
+ this->Internal->VarStack.push(defs);
+ this->Internal->VarInitStack.push(globalKeys);
+ this->Internal->VarUsageStack.push(globalKeys);
+
+ // Initialize these first since AddDefaultDefinitions calls AddDefinition
+ this->WarnUnused = false;
+ this->CheckSystemVars = false;
// Setup the default include file regular expression (match everything).
this->IncludeFileRegularExpression = "^.*$";
@@ -91,6 +101,8 @@ cmMakefile::cmMakefile(): Internal(new Internals)
cmMakefile::cmMakefile(const cmMakefile& mf): Internal(new Internals)
{
this->Internal->VarStack.push(mf.Internal->VarStack.top().Closure());
+ this->Internal->VarInitStack.push(mf.Internal->VarInitStack.top());
+ this->Internal->VarUsageStack.push(mf.Internal->VarUsageStack.top());
this->Prefix = mf.Prefix;
this->AuxSourceDirectories = mf.AuxSourceDirectories;
@@ -128,8 +140,10 @@ cmMakefile::cmMakefile(const cmMakefile& mf): Internal(new Internals)
this->SubDirectoryOrder = mf.SubDirectoryOrder;
this->Properties = mf.Properties;
this->PreOrder = mf.PreOrder;
- this->ListFileStack = mf.ListFileStack;
+ this->WarnUnused = mf.WarnUnused;
this->Initialize();
+ this->CheckSystemVars = mf.CheckSystemVars;
+ this->ListFileStack = mf.ListFileStack;
}
//----------------------------------------------------------------------------
@@ -570,6 +584,7 @@ bool cmMakefile::ReadListFile(const char* filename_in,
std::string currentFile
= this->GetSafeDefinition("CMAKE_CURRENT_LIST_FILE");
this->AddDefinition("CMAKE_PARENT_LIST_FILE", filename_in);
+ this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE");
const char* external = 0;
std::string external_abs;
@@ -610,8 +625,10 @@ bool cmMakefile::ReadListFile(const char* filename_in,
}
this->AddDefinition("CMAKE_CURRENT_LIST_FILE", filenametoread);
+ this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE");
this->AddDefinition("CMAKE_CURRENT_LIST_DIR",
cmSystemTools::GetFilenamePath(filenametoread).c_str());
+ this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_DIR");
// try to see if the list file is the top most
// list file for a project, and if it is, then it
@@ -644,9 +661,12 @@ bool cmMakefile::ReadListFile(const char* filename_in,
*fullPath = "";
}
this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentParentFile.c_str());
+ this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE");
this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile.c_str());
+ this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE");
this->AddDefinition("CMAKE_CURRENT_LIST_DIR",
cmSystemTools::GetFilenamePath(currentFile).c_str());
+ this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_DIR");
return false;
}
// add this list file to the list of dependencies
@@ -686,13 +706,19 @@ bool cmMakefile::ReadListFile(const char* filename_in,
}
this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentParentFile.c_str());
+ this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE");
this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile.c_str());
+ this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE");
this->AddDefinition("CMAKE_CURRENT_LIST_DIR",
cmSystemTools::GetFilenamePath(currentFile).c_str());
+ this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_DIR");
// pop the listfile off the stack
this->ListFileStack.pop_back();
+ // Check for unused variables
+ this->CheckForUnusedVariables();
+
return true;
}
@@ -758,6 +784,8 @@ void cmMakefile::SetLocalGenerator(cmLocalGenerator* lg)
this->AddSourceGroup("Resources", "\\.plist$");
#endif
+ this->WarnUnused = this->GetCMakeInstance()->GetWarnUnused();
+ this->CheckSystemVars = this->GetCMakeInstance()->GetCheckSystemVars();
}
bool cmMakefile::NeedBackwardsCompatibility(unsigned int major,
@@ -1625,6 +1653,13 @@ void cmMakefile::AddDefinition(const char* name, const char* value)
#endif
this->Internal->VarStack.top().Set(name, value);
+ if (this->Internal->VarUsageStack.size() &&
+ this->VariableInitialized(name))
+ {
+ this->CheckForUnused("changing definition", name);
+ this->Internal->VarUsageStack.top().erase(name);
+ }
+ this->Internal->VarInitStack.top().insert(name);
#ifdef CMAKE_BUILD_WITH_CMAKE
cmVariableWatch* vv = this->GetVariableWatch();
@@ -1689,6 +1724,13 @@ void cmMakefile::AddCacheDefinition(const char* name, const char* value,
void cmMakefile::AddDefinition(const char* name, bool value)
{
this->Internal->VarStack.top().Set(name, value? "ON" : "OFF");
+ if (this->Internal->VarUsageStack.size() &&
+ this->VariableInitialized(name))
+ {
+ this->CheckForUnused("changing definition", name);
+ this->Internal->VarUsageStack.top().erase(name);
+ }
+ this->Internal->VarInitStack.top().insert(name);
#ifdef CMAKE_BUILD_WITH_CMAKE
cmVariableWatch* vv = this->GetVariableWatch();
if ( vv )
@@ -1699,9 +1741,90 @@ void cmMakefile::AddDefinition(const char* name, bool value)
#endif
}
+void cmMakefile::CheckForUnusedVariables() const
+{
+ const cmDefinitions& defs = this->Internal->VarStack.top();
+ const std::set<cmStdString>& locals = defs.LocalKeys();
+ std::set<cmStdString>::const_iterator it = locals.begin();
+ for (; it != locals.end(); ++it)
+ {
+ this->CheckForUnused("out of scope", it->c_str());
+ }
+}
+
+void cmMakefile::MarkVariableAsUsed(const char* var)
+{
+ this->Internal->VarUsageStack.top().insert(var);
+}
+
+bool cmMakefile::VariableInitialized(const char* var) const
+{
+ if(this->Internal->VarInitStack.top().find(var) !=
+ this->Internal->VarInitStack.top().end())
+ {
+ return true;
+ }
+ return false;
+}
+
+bool cmMakefile::VariableUsed(const char* var) const
+{
+ if(this->Internal->VarUsageStack.top().find(var) !=
+ this->Internal->VarUsageStack.top().end())
+ {
+ return true;
+ }
+ return false;
+}
+
+void cmMakefile::CheckForUnused(const char* reason, const char* name) const
+{
+ if (this->WarnUnused && !this->VariableUsed(name))
+ {
+ cmStdString path;
+ cmListFileBacktrace bt;
+ if (this->CallStack.size())
+ {
+ const cmListFileContext* file = this->CallStack.back().Context;
+ bt.push_back(*file);
+ path = file->FilePath.c_str();
+ }
+ else
+ {
+ path = this->GetStartDirectory();
+ path += "/CMakeLists.txt";
+ cmListFileContext lfc;
+ lfc.FilePath = path;
+ lfc.Line = 0;
+ bt.push_back(lfc);
+ }
+ if (this->CheckSystemVars ||
+ cmSystemTools::IsSubDirectory(path.c_str(),
+ this->GetHomeDirectory()) ||
+ (cmSystemTools::IsSubDirectory(path.c_str(),
+ this->GetHomeOutputDirectory()) &&
+ !cmSystemTools::IsSubDirectory(path.c_str(),
+ cmake::GetCMakeFilesDirectory())))
+ {
+ cmOStringStream msg;
+ msg << "unused variable (" << reason << ") \'" << name << "\'";
+ this->GetCMakeInstance()->IssueMessage(cmake::AUTHOR_WARNING,
+ msg.str().c_str(),
+ bt);
+ }
+ }
+}
+
void cmMakefile::RemoveDefinition(const char* name)
{
this->Internal->VarStack.top().Set(name, 0);
+ if (this->Internal->VarUsageStack.size() &&
+ this->VariableInitialized(name))
+ {
+ this->CheckForUnused("unsetting", name);
+ this->Internal->VarUsageStack.top().erase(name);
+ }
+ this->Internal->VarInitStack.top().insert(name);
#ifdef CMAKE_BUILD_WITH_CMAKE
cmVariableWatch* vv = this->GetVariableWatch();
if ( vv )
@@ -2053,6 +2176,7 @@ const char* cmMakefile::GetRequiredDefinition(const char* name) const
bool cmMakefile::IsDefinitionSet(const char* name) const
{
const char* def = this->Internal->VarStack.top().Get(name);
+ this->Internal->VarUsageStack.top().insert(name);
if(!def)
{
def = this->GetCacheManager()->GetCacheValue(name);
@@ -2080,6 +2204,10 @@ const char* cmMakefile::GetDefinition(const char* name) const
RecordPropertyAccess(name,cmProperty::VARIABLE);
}
#endif
+ if (this->WarnUnused)
+ {
+ this->Internal->VarUsageStack.top().insert(name);
+ }
const char* def = this->Internal->VarStack.top().Get(name);
if(!def)
{
@@ -2717,6 +2845,31 @@ int cmMakefile::TryCompile(const char *srcdir, const char *bindir,
// if cmake args were provided then pass them in
if (cmakeArgs)
{
+ // FIXME: Workaround to ignore unused CLI variables in try-compile.
+ //
+ // Ideally we should use SetArgs to honor options like --warn-unused-vars.
+ // However, there is a subtle problem when certain arguments are passed to
+ // a macro wrapping around try_compile or try_run that does not escape
+ // semicolons in its parameters but just passes ${ARGV} or ${ARGN}. In
+ // this case a list argument like "-DVAR=a;b" gets split into multiple
+ // cmake arguments "-DVAR=a" and "b". Currently SetCacheArgs ignores
+ // argument "b" and uses just "-DVAR=a", leading to a subtle bug in that
+ // the try_compile or try_run does not get the proper value of VAR. If we
+ // call SetArgs here then it would treat "b" as the source directory and
+ // cause an error such as "The source directory .../CMakeFiles/CMakeTmp/b
+ // does not exist", thus breaking the try_compile or try_run completely.
+ //
+ // Strictly speaking the bug is in the wrapper macro because the CMake
+ // language has always flattened nested lists and the macro should escape
+ // the semicolons in its arguments before forwarding them. However, this
+ // bug is so subtle that projects typically work anyway, usually because
+ // the value VAR=a is sufficient for the try_compile or try_run to get the
+ // correct result. Calling SetArgs here would break such projects that
+ // previously built. Instead we work around the issue by never reporting
+ // unused arguments and ignoring options such as --warn-unused-vars.
+ cm.SetWarnUnusedCli(false);
+ //cm.SetArgs(*cmakeArgs, true);
+
cm.SetCacheArgs(*cmakeArgs);
}
// to save time we pass the EnableLanguage info directly
@@ -3335,12 +3488,48 @@ std::string cmMakefile::GetListFileStack()
void cmMakefile::PushScope()
{
cmDefinitions* parent = &this->Internal->VarStack.top();
+ const std::set<cmStdString>& init = this->Internal->VarInitStack.top();
+ const std::set<cmStdString>& usage = this->Internal->VarUsageStack.top();
this->Internal->VarStack.push(cmDefinitions(parent));
+ this->Internal->VarInitStack.push(init);
+ this->Internal->VarUsageStack.push(usage);
}
void cmMakefile::PopScope()
{
+ cmDefinitions* current = &this->Internal->VarStack.top();
+ std::set<cmStdString> init = this->Internal->VarInitStack.top();
+ std::set<cmStdString> usage = this->Internal->VarUsageStack.top();
+ const std::set<cmStdString>& locals = current->LocalKeys();
+ // Remove initialization and usage information for variables in the local
+ // scope.
+ std::set<cmStdString>::const_iterator it = locals.begin();
+ for (; it != locals.end(); ++it)
+ {
+ init.erase(*it);
+ if (!this->VariableUsed(it->c_str()))
+ {
+ this->CheckForUnused("out of scope", it->c_str());
+ }
+ else
+ {
+ usage.erase(*it);
+ }
+ }
this->Internal->VarStack.pop();
+ this->Internal->VarInitStack.pop();
+ this->Internal->VarUsageStack.pop();
+ // Push initialization and usage up to the parent scope.
+ it = init.begin();
+ for (; it != init.end(); ++it)
+ {
+ this->Internal->VarInitStack.top().insert(*it);
+ }
+ it = usage.begin();
+ for (; it != usage.end(); ++it)
+ {
+ this->Internal->VarUsageStack.top().insert(*it);
+ }
}
void cmMakefile::RaiseScope(const char *var, const char *varDef)
@@ -3365,7 +3554,14 @@ void cmMakefile::RaiseScope(const char *var, const char *varDef)
// directory's scope was initialized by the closure of the parent
// scope, so we do not need to localize the definition first.
cmMakefile* parent = plg->GetMakefile();
- parent->Internal->VarStack.top().Set(var, varDef);
+ if (varDef)
+ {
+ parent->AddDefinition(var, varDef);
+ }
+ else
+ {
+ parent->RemoveDefinition(var);
+ }
}
else
{