|
|
@@ -44,12 +44,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 = "^.*$";
|
|
|
@@ -92,6 +102,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;
|
|
|
@@ -129,8 +141,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;
|
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
@@ -571,6 +585,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;
|
|
|
@@ -611,8 +626,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
|
|
|
@@ -645,9 +662,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
|
|
|
@@ -687,13 +707,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;
|
|
|
}
|
|
|
|
|
|
@@ -759,6 +785,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,
|
|
|
@@ -1627,6 +1655,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();
|
|
|
@@ -1691,6 +1726,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 )
|
|
|
@@ -1701,9 +1743,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 )
|
|
|
@@ -2055,6 +2178,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);
|
|
|
@@ -2082,6 +2206,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)
|
|
|
{
|
|
|
@@ -2733,6 +2861,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
|
|
|
@@ -3416,12 +3569,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)
|
|
|
@@ -3446,7 +3635,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
|
|
|
{
|