diff options
Diffstat (limited to 'Source/cmMakefile.cxx')
-rw-r--r-- | Source/cmMakefile.cxx | 247 |
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, |