summaryrefslogtreecommitdiffstats
path: root/Source/cmMakefile.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmMakefile.cxx')
-rw-r--r--Source/cmMakefile.cxx247
1 files changed, 235 insertions, 12 deletions
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 53f4c3c..3fa846a 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -22,6 +22,7 @@
#include "cmFunctionBlocker.h"
#include "cmListFileCache.h"
#include "cmCommandArgumentParserHelper.h"
+#include "cmDocumentCompileDefinitions.h"
#include "cmTest.h"
#ifdef CMAKE_BUILD_WITH_CMAKE
# include "cmVariableWatch.h"
@@ -43,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 = "^.*$";
@@ -91,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;
@@ -128,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;
}
//----------------------------------------------------------------------------
@@ -570,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;
@@ -610,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
@@ -644,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
@@ -686,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;
}
@@ -758,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,
@@ -1626,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();
@@ -1690,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 )
@@ -1700,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 )
@@ -2054,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);
@@ -2081,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)
{
@@ -2615,6 +2744,27 @@ void cmMakefile::SetHomeOutputDirectory(const char* lib)
}
}
+void cmMakefile::SetScriptModeFile(const char* scriptfile)
+{
+ this->AddDefinition("CMAKE_SCRIPT_MODE_FILE", scriptfile);
+}
+
+void cmMakefile::SetArgcArgv(const std::vector<std::string>& args)
+{
+ cmOStringStream strStream;
+ strStream << args.size();
+ this->AddDefinition("CMAKE_ARGC", strStream.str().c_str());
+ //this->MarkVariableAsUsed("CMAKE_ARGC");
+
+ for (unsigned int t = 0; t < args.size(); ++t)
+ {
+ cmOStringStream tmpStream;
+ tmpStream << "CMAKE_ARGV" << t;
+ this->AddDefinition(tmpStream.str().c_str(), args[t].c_str());
+ //this->MarkVariableAsUsed(tmpStream.str().c_str());
+ }
+}
+
//----------------------------------------------------------------------------
cmSourceFile* cmMakefile::GetSource(const char* sourceName)
{
@@ -2717,14 +2867,51 @@ int cmMakefile::TryCompile(const char *srcdir, const char *bindir,
cm.SetStartOutputDirectory(bindir);
cm.SetCMakeCommand(cmakeCommand.c_str());
cm.LoadCache();
+ if(!gg->IsMultiConfig())
+ {
+ if(const char* config =
+ this->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"))
+ {
+ // Tell the single-configuration generator which one to use.
+ // Add this before the user-provided CMake arguments in case
+ // one of the arguments is -DCMAKE_BUILD_TYPE=...
+ cm.AddCacheEntry("CMAKE_BUILD_TYPE", config,
+ "Build configuration", cmCacheManager::STRING);
+ }
+ }
// 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
gg->EnableLanguagesFromGenerator
- (this->LocalGenerator->GetGlobalGenerator());
+ (this->LocalGenerator->GetGlobalGenerator(), this);
if(this->IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS"))
{
cm.AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS",
@@ -3403,12 +3590,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)
@@ -3433,7 +3656,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
{
@@ -3492,14 +3722,7 @@ void cmMakefile::DefineProperties(cmake *cm)
"are not supported by the native build tool. "
"The VS6 IDE does not support definition values with spaces "
"(but NMake does).\n"
- "Dislaimer: Most native build tools have poor support for escaping "
- "certain values. CMake has work-arounds for many cases but some "
- "values may just not be possible to pass correctly. If a value "
- "does not seem to be escaped correctly, do not attempt to "
- "work-around the problem by adding escape sequences to the value. "
- "Your work-around may break in a future version of CMake that "
- "has improved escape support. Instead consider defining the macro "
- "in a (configured) header file. Then report the limitation.");
+ CM_DOCUMENT_COMPILE_DEFINITIONS_DISCLAIMER);
cm->DefineProperty
("COMPILE_DEFINITIONS_<CONFIG>", cmProperty::DIRECTORY,