diff options
author | Brad King <brad.king@kitware.com> | 2011-01-27 20:34:07 (GMT) |
---|---|---|
committer | CMake Topic Stage <kwrobot@kitware.com> | 2011-01-27 20:34:07 (GMT) |
commit | 182eb7c7eff70bc5af6ca77adf15b59e87400f2f (patch) | |
tree | d4a2bd71cb9993e6d354e8913444baf0d3a9255f /Source | |
parent | 451c5f61c26fc1db154605dc5e4b1c07d584b24b (diff) | |
parent | dd2f81491e6e17681dd18882a8ccfa01fa32a3f7 (diff) | |
download | CMake-182eb7c7eff70bc5af6ca77adf15b59e87400f2f.zip CMake-182eb7c7eff70bc5af6ca77adf15b59e87400f2f.tar.gz CMake-182eb7c7eff70bc5af6ca77adf15b59e87400f2f.tar.bz2 |
Merge topic 'dev/strict-mode'
dd2f814 Merge branch 'dev/add_test-working-directory' into dev/strict-mode
949d32c Unwatch manual variables upon removal in cmake-gui
3939032 Unwatch manual variables upon removal in ccmake
8354413 Add method to unwatch a manual variable
8ed3c85 Give a better message for unused variables
729db48 Fix ArgumentExpansion test expected results
89c2544 Checking for a definition is a usage
5625dee Don't output to stderr in the GUI
ad25a96 Merge branch 'ImprovedDotSupport2' into dev/strict-mode
c128abe Merge branch 'AddCMAKE_CURRENT_LIST_DIR' into dev/strict-mode
9bcaff0 Merge branch 'cmake-guiRememberAdvancedCheckbox' into dev/strict-mode
544d0c3 Fix expected output for WarnUninitialized test
4e3bea4 Update expected messages to new format
8e8c9e4 Don't check at destruction for usage
668e005 Use cmake::IssueMessage for warnings
88cd4c1 Use 'CMake Warning' versus 'warning' for CDash
3c3b98d Initialize the class before setting warn flags
cf8b15a Ignore files under the CMakeFiles directory
fd50f06 Don't check for unused vars at configure time
447a04c Don't warn during configure when doing everything
b97ee21 Check for unused variables at the end of generate
c18c977 When checking for variables, specify a reason
3f1121f Use a long int since Line is a long as well
2507f93 Change the failure case string to 'Unexpected'
fe390a2 Add 'ArgumentExpansion' test
8dbb209 Wrong boolean value for CLI warnings
d4ee998 Hard-code the --no-warn-unused-cli flag
a267b99 Fix line lengths
82ed104 Flag that the directories have been set
5aa535b Add argument to arg parsing to not set directories
367e5c3 Revert "Revert "When calling CMake, set the args and the cache""
ab5d4e4 Revert "When calling CMake, set the args and the cache"
9b90040 When calling CMake, set the args and the cache
fe56002 Fix long lines for KWStyle
5d30cfc Set a watch on variables added through the gui
33c63b1 Add a method to put a watch for variables
535253f Initialize the warning variables earlier
cbb286c Fix the path detection to work for top-level
62be1f7 Initialize the usage stack earlier
c6e7fab Factor out the checks for unused variables
5e41ba8 When using the API, check for Add vs. Remove
dee1976 Fix typo in VariableUnusedViaUnset test
f231ce5 Remove old false positive avoidance code
a117e02 Revert "Add test for unused warnings at the end of scope"
2c82f2b Exempt CMAKE(CURRENT|PARENT)_LIST_FILE from usage
6d7d449 Ignore CLI warnings for ABI determination
7740a73 Only return local keys that are defined
bef3aee Use the API so that warnings can be tracked
05cb0f4 Check for unused variables in the dtor
91c4c99 Add test for unused warnings at the end of scope
ca90f67 Fix detection of unused variables when setting
f7438ca Add test for unused detection via setting it
995cfb0 Don't warn if the variable wasn't defined
aefc91d Add test for usage checks via unset
a8e97f8 Remove VarRemoved code since it's been superceded
59463ef Rework CheckVariableForUnused usage
f117423 Fix line lengths to be no more than 78
e49a935 Improve unused warning logic
e01e40c Mark ARGC, ARGV*, and ARGN as used
a17aff7 Ignore CMAKE_MATCH_* variables for usage
02a114d Add method to allow variables to be marked as used
a0b0d23 CMAKE_DO_TRY_COMPILE is no longer used
ae3eff3 Fix the path used for ignoring system warnings
056b441 Fix missing case for usage of a variable
980e048 Factor out checks for unused variables
83acb0a Remove now unused variables
3801463 Use built-ins for readability and maintainability
8b52015 Push the initialize and unused states when copying
439877f Be consistent with single and double quotes
4cf1706 Add documentation for check-system-vars
b74777f Fix the spelling of the flag for warn-unused-vars
b948120 Change logic of flag to turn off cli unused checks
f047a17 Add test for uninitialized variables
75bda38 Add tests for unused command line variables
300fc15 Fix detection of system files
d784e6a Run the unused variables check on the final pass
9efc057 VariableWatch is not available when bootstrapping
2e78224 Add a missing comma to the warning message
7499700 Add a flag to warn about system files
fff9f6d Rename flags again and use variablewatch for cli
786e269 Add warn-unused to the Qt interface
636e6c4 Default to marking things as used
4ff0340 Rename find-unused to warn-unused
d7999e9 Rename strict-mode to warn-uninitialized
e141bc9 Detect unused variables
d3e8eb5 Add flags to detect unused variables
f332e14 Complete strict-mode checks for uninitialized vars
52f9637 Add method to get the local scope variables
f794d58 Make --strict-mode option, and integrate with cmake-gui
48b5b85 Add a warning when variables are used uninitialized.
cd626ea For macros make sure the FilePath points to a valid pointer in the args.
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CursesDialog/cmCursesMainForm.cxx | 1 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetupDialog.cxx | 16 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetupDialog.h | 2 | ||||
-rw-r--r-- | Source/QtDialog/QCMake.cxx | 18 | ||||
-rw-r--r-- | Source/QtDialog/QCMake.h | 7 | ||||
-rw-r--r-- | Source/cmCommandArgumentParserHelper.cxx | 28 | ||||
-rw-r--r-- | Source/cmCommandArgumentParserHelper.h | 2 | ||||
-rw-r--r-- | Source/cmCoreTryCompile.cxx | 4 | ||||
-rw-r--r-- | Source/cmDefinitions.cxx | 16 | ||||
-rw-r--r-- | Source/cmDefinitions.h | 3 | ||||
-rw-r--r-- | Source/cmFunctionCommand.cxx | 4 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.cxx | 2 | ||||
-rw-r--r-- | Source/cmMacroCommand.cxx | 12 | ||||
-rw-r--r-- | Source/cmMacroCommand.h | 1 | ||||
-rw-r--r-- | Source/cmMakefile.cxx | 202 | ||||
-rw-r--r-- | Source/cmMakefile.h | 17 | ||||
-rw-r--r-- | Source/cmStringCommand.cxx | 2 | ||||
-rw-r--r-- | Source/cmake.cxx | 89 | ||||
-rw-r--r-- | Source/cmake.h | 23 | ||||
-rw-r--r-- | Source/cmakemain.cxx | 11 |
20 files changed, 445 insertions, 15 deletions
diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx index c93f353..e1876b9 100644 --- a/Source/CursesDialog/cmCursesMainForm.cxx +++ b/Source/CursesDialog/cmCursesMainForm.cxx @@ -789,6 +789,7 @@ void cmCursesMainForm::RemoveEntry(const char* value) const char* val = (*it)->GetValue(); if ( val && !strcmp(value, val) ) { + this->CMakeInstance->UnwatchUnusedCli(value); this->Entries->erase(it); break; } diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index 408dbac..c8c4bfa 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -118,8 +118,15 @@ CMakeSetupDialog::CMakeSetupDialog() this, SLOT(doInstallForCommandLine())); #endif QMenu* OptionsMenu = this->menuBar()->addMenu(tr("&Options")); - this->SuppressDevWarningsAction = OptionsMenu->addAction(tr("&Suppress dev Warnings (-Wno-dev)")); + this->SuppressDevWarningsAction = + OptionsMenu->addAction(tr("&Suppress dev Warnings (-Wno-dev)")); this->SuppressDevWarningsAction->setCheckable(true); + this->WarnUninitializedAction = + OptionsMenu->addAction(tr("&Warn Uninitialized (--warn-uninitialized)")); + this->WarnUninitializedAction->setCheckable(true); + this->WarnUnusedAction = + OptionsMenu->addAction(tr("&Warn Unused (--warn-unused-vars)")); + this->WarnUnusedAction->setCheckable(true); QAction* debugAction = OptionsMenu->addAction(tr("&Debug Output")); debugAction->setCheckable(true); @@ -247,6 +254,13 @@ void CMakeSetupDialog::initialize() QObject::connect(this->SuppressDevWarningsAction, SIGNAL(triggered(bool)), this->CMakeThread->cmakeInstance(), SLOT(setSuppressDevWarnings(bool))); + QObject::connect(this->WarnUninitializedAction, SIGNAL(triggered(bool)), + this->CMakeThread->cmakeInstance(), + SLOT(setWarnUninitializedMode(bool))); + QObject::connect(this->WarnUnusedAction, SIGNAL(triggered(bool)), + this->CMakeThread->cmakeInstance(), + SLOT(setWarnUnusedMode(bool))); + if(!this->SourceDirectory->text().isEmpty() || !this->BinaryDirectory->lineEdit()->text().isEmpty()) { diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h index 1934795..5121759 100644 --- a/Source/QtDialog/CMakeSetupDialog.h +++ b/Source/QtDialog/CMakeSetupDialog.h @@ -97,6 +97,8 @@ protected: QAction* ConfigureAction; QAction* GenerateAction; QAction* SuppressDevWarningsAction; + QAction* WarnUninitializedAction; + QAction* WarnUnusedAction; QAction* InstallForCommandLineAction; State CurrentState; diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index dc31fad..a40a175 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -28,6 +28,8 @@ QCMake::QCMake(QObject* p) : QObject(p) { this->SuppressDevWarnings = false; + this->WarnUninitializedMode = false; + this->WarnUnusedMode = false; qRegisterMetaType<QCMakeProperty>(); qRegisterMetaType<QCMakePropertyList>(); @@ -164,6 +166,8 @@ void QCMake::configure() this->CMakeInstance->CreateGlobalGenerator(this->Generator.toAscii().data())); this->CMakeInstance->LoadCache(); this->CMakeInstance->SetSuppressDevWarnings(this->SuppressDevWarnings); + this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode); + this->CMakeInstance->SetWarnUnused(this->WarnUnusedMode); this->CMakeInstance->PreLoadCMakeFiles(); cmSystemTools::ResetErrorOccuredFlag(); @@ -238,12 +242,16 @@ void QCMake::setProperties(const QCMakePropertyList& newProps) // remove some properites foreach(QString s, toremove) { + this->CMakeInstance->UnwatchUnusedCli(s.toAscii().data()); + cachem->RemoveCacheEntry(s.toAscii().data()); } // add some new properites foreach(QCMakeProperty s, props) { + this->CMakeInstance->WatchUnusedCli(s.Key.toAscii().data()); + if(s.Type == QCMakeProperty::BOOL) { this->CMakeInstance->AddCacheEntry(s.Key.toAscii().data(), @@ -417,3 +425,13 @@ void QCMake::setSuppressDevWarnings(bool value) { this->SuppressDevWarnings = value; } + +void QCMake::setWarnUninitializedMode(bool value) +{ + this->WarnUninitializedMode = value; +} + +void QCMake::setWarnUnusedMode(bool value) +{ + this->WarnUnusedMode = value; +} diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index bbfb3d7..0d10823 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h @@ -88,6 +88,10 @@ public slots: void setDebugOutput(bool); /// set whether to do suppress dev warnings void setSuppressDevWarnings(bool value); + /// set whether to run cmake with warnings about uninitialized variables + void setWarnUninitializedMode(bool value); + /// set whether to run cmake with warnings about unused variables + void setWarnUnusedMode(bool value); public: /// get the list of cache properties @@ -133,6 +137,9 @@ protected: static void errorCallback(const char* msg, const char* title, bool&, void* cd); bool SuppressDevWarnings; + bool WarnUninitializedMode; + bool WarnUnusedMode; + bool WarnUnusedAllMode; QString SourceDirectory; QString BinaryDirectory; QString Generator; diff --git a/Source/cmCommandArgumentParserHelper.cxx b/Source/cmCommandArgumentParserHelper.cxx index 234c37e..a781767 100644 --- a/Source/cmCommandArgumentParserHelper.cxx +++ b/Source/cmCommandArgumentParserHelper.cxx @@ -20,6 +20,8 @@ int cmCommandArgument_yyparse( yyscan_t yyscanner ); // cmCommandArgumentParserHelper::cmCommandArgumentParserHelper() { + this->WarnUninitialized = false; + this->CheckSystemVars = false; this->FileLine = -1; this->FileName = 0; this->RemoveEmpty = true; @@ -119,10 +121,32 @@ char* cmCommandArgumentParserHelper::ExpandVariable(const char* var) cmOStringStream ostr; ostr << this->FileLine; return this->AddString(ostr.str().c_str()); - } + } const char* value = this->Makefile->GetDefinition(var); if(!value && !this->RemoveEmpty) { + // check to see if we need to print a warning + // if strict mode is on and the variable has + // not been "cleared"/initialized with a set(foo ) call + if(this->WarnUninitialized && !this->Makefile->VariableInitialized(var)) + { + if (this->CheckSystemVars || + cmSystemTools::IsSubDirectory(this->FileName, + this->Makefile->GetHomeDirectory()) || + cmSystemTools::IsSubDirectory(this->FileName, + this->Makefile->GetHomeOutputDirectory())) + { + cmOStringStream msg; + cmListFileBacktrace bt; + cmListFileContext lfc; + lfc.FilePath = this->FileName; + lfc.Line = this->FileLine; + bt.push_back(lfc); + msg << "uninitialized variable \'" << var << "\'"; + this->Makefile->GetCMakeInstance()->IssueMessage(cmake::AUTHOR_WARNING, + msg.str().c_str(), bt); + } + } return 0; } if (this->EscapeQuotes && value) @@ -319,6 +343,8 @@ void cmCommandArgumentParserHelper::Error(const char* str) void cmCommandArgumentParserHelper::SetMakefile(const cmMakefile* mf) { this->Makefile = mf; + this->WarnUninitialized = mf->GetCMakeInstance()->GetWarnUninitialized(); + this->CheckSystemVars = mf->GetCMakeInstance()->GetCheckSystemVars(); } void cmCommandArgumentParserHelper::SetResult(const char* value) diff --git a/Source/cmCommandArgumentParserHelper.h b/Source/cmCommandArgumentParserHelper.h index 62cbc80..a211e95 100644 --- a/Source/cmCommandArgumentParserHelper.h +++ b/Source/cmCommandArgumentParserHelper.h @@ -96,6 +96,8 @@ private: const cmMakefile* Makefile; std::string Result; const char* FileName; + bool WarnUninitialized; + bool CheckSystemVars; long FileLine; bool EscapeQuotes; std::string ErrorString; diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index e1cb0bb..96a214e 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -251,10 +251,8 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv) CMAKE_TRY_COMPILE_OSX_ARCHITECTURE first to i386 and then to ppc to have the tests run for each specific architecture. Since cmLocalGenerator doesn't allow building for "the other" - architecture only via CMAKE_OSX_ARCHITECTURES,use to CMAKE_DO_TRY_COMPILE - to enforce it for this case here. + architecture only via CMAKE_OSX_ARCHITECTURES. */ - cmakeFlags.push_back("-DCMAKE_DO_TRY_COMPILE=TRUE"); if(this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_OSX_ARCHITECTURES")!=0) { std::string flag="-DCMAKE_OSX_ARCHITECTURES="; diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx index 2d40718..9d28700 100644 --- a/Source/cmDefinitions.cxx +++ b/Source/cmDefinitions.cxx @@ -85,6 +85,22 @@ const char* cmDefinitions::Set(const char* key, const char* value) } //---------------------------------------------------------------------------- +std::set<cmStdString> cmDefinitions::LocalKeys() const +{ + std::set<cmStdString> keys; + // Consider local definitions. + for(MapType::const_iterator mi = this->Map.begin(); + mi != this->Map.end(); ++mi) + { + if (mi->second.Exists) + { + keys.insert(mi->first); + } + } + return keys; +} + +//---------------------------------------------------------------------------- cmDefinitions cmDefinitions::Closure() const { return cmDefinitions(ClosureTag(), this); diff --git a/Source/cmDefinitions.h b/Source/cmDefinitions.h index e385e88..4834d84 100644 --- a/Source/cmDefinitions.h +++ b/Source/cmDefinitions.h @@ -40,6 +40,9 @@ public: /** Set (or unset if null) a value associated with a key. */ const char* Set(const char* key, const char* value); + /** Get the set of all local keys. */ + std::set<cmStdString> LocalKeys() const; + /** Compute the closure of all defined keys with values. This flattens the scope. The result has no parent. */ cmDefinitions Closure() const; diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx index b05642e..ec4fd16 100644 --- a/Source/cmFunctionCommand.cxx +++ b/Source/cmFunctionCommand.cxx @@ -113,6 +113,7 @@ bool cmFunctionHelperCommand::InvokeInitialPass cmOStringStream strStream; strStream << expandedArgs.size(); this->Makefile->AddDefinition("ARGC",strStream.str().c_str()); + this->Makefile->MarkVariableAsUsed("ARGC"); // set the values for ARGV0 ARGV1 ... for (unsigned int t = 0; t < expandedArgs.size(); ++t) @@ -121,6 +122,7 @@ bool cmFunctionHelperCommand::InvokeInitialPass tmpStream << "ARGV" << t; this->Makefile->AddDefinition(tmpStream.str().c_str(), expandedArgs[t].c_str()); + this->Makefile->MarkVariableAsUsed(tmpStream.str().c_str()); } // define the formal arguments @@ -153,7 +155,9 @@ bool cmFunctionHelperCommand::InvokeInitialPass cnt ++; } this->Makefile->AddDefinition("ARGV", argvDef.c_str()); + this->Makefile->MarkVariableAsUsed("ARGV"); this->Makefile->AddDefinition("ARGN", argnDef.c_str()); + this->Makefile->MarkVariableAsUsed("ARGN"); // Invoke all the functions that were collected in the block. // for each function diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 6c8938e..d47fb6f 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -903,6 +903,8 @@ void cmGlobalGenerator::Generate() } this->CMakeInstance->UpdateProgress("Generating done", -1); + + this->CMakeInstance->RunCheckForUnusedVariables("generation"); } //---------------------------------------------------------------------------- diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index 497949a..774f32b 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -31,6 +31,7 @@ public: // we must copy when we clone newC->Args = this->Args; newC->Functions = this->Functions; + newC->FilePath = this->FilePath; newC->Policies = this->Policies; return newC; } @@ -78,6 +79,7 @@ public: std::vector<std::string> Args; std::vector<cmListFileFunction> Functions; cmPolicies::PolicyMap Policies; + std::string FilePath; }; @@ -121,7 +123,10 @@ bool cmMacroHelperCommand::InvokeInitialPass std::string argnDef; bool argnDefInitialized = false; bool argvDefInitialized = false; - + if( this->Functions.size()) + { + this->FilePath = this->Functions[0].FilePath; + } // Invoke all the functions that were collected in the block. cmListFileFunction newLFF; // for each function @@ -135,10 +140,13 @@ bool cmMacroHelperCommand::InvokeInitialPass newLFF.Line = this->Functions[c].Line; // for each argument of the current function - for (std::vector<cmListFileArgument>::const_iterator k = + for (std::vector<cmListFileArgument>::iterator k = this->Functions[c].Arguments.begin(); k != this->Functions[c].Arguments.end(); ++k) { + // Set the FilePath on the arguments to match the function since it is + // not stored and the original values may be freed + k->FilePath = this->FilePath.c_str(); tmps = k->Value; // replace formal arguments for (unsigned int j = 1; j < this->Args.size(); ++j) diff --git a/Source/cmMacroCommand.h b/Source/cmMacroCommand.h index 3457da2..93e10b2 100644 --- a/Source/cmMacroCommand.h +++ b/Source/cmMacroCommand.h @@ -112,7 +112,6 @@ public: "policies inside macros." ; } - cmTypeMacro(cmMacroCommand, cmCommand); }; diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index e443b86..e14e44d 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -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 { diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 8b8a3f8..837a352 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -61,6 +61,14 @@ public: unsigned int GetCacheMajorVersion(); unsigned int GetCacheMinorVersion(); + /* Check for unused variables in this scope */ + void CheckForUnusedVariables() const; + /* Mark a variable as used */ + void MarkVariableAsUsed(const char* var); + /* return true if a variable has been initialized */ + bool VariableInitialized(const char* ) const; + /* return true if a variable has been used */ + bool VariableUsed(const char* ) const; /** Return whether compatibility features needed for a version of the cache or lower should be enabled. */ bool NeedCacheCompatibility(int major, int minor); @@ -836,7 +844,10 @@ public: protected: // add link libraries and directories to the target void AddGlobalLinkInformation(const char* name, cmTarget& target); - + + // Check for a an unused variable + void CheckForUnused(const char* reason, const char* name) const; + std::string Prefix; std::vector<std::string> AuxSourceDirectories; // @@ -929,6 +940,10 @@ private: // should this makefile be processed before or after processing the parent bool PreOrder; + // Unused variable flags + bool WarnUnused; + bool CheckSystemVars; + // stack of list files being read std::deque<cmStdString> ListFileStack; diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index 2b4414d..949fcf5 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx @@ -482,6 +482,7 @@ void cmStringCommand::ClearMatches(cmMakefile* mf) char name[128]; sprintf(name, "CMAKE_MATCH_%d", i); mf->AddDefinition(name, ""); + mf->MarkVariableAsUsed(name); } } @@ -493,6 +494,7 @@ void cmStringCommand::StoreMatches(cmMakefile* mf,cmsys::RegularExpression& re) char name[128]; sprintf(name, "CMAKE_MATCH_%d", i); mf->AddDefinition(name, re.match(i).c_str()); + mf->MarkVariableAsUsed(name); } } diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 1b49837..bab0aaf 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -138,9 +138,20 @@ void cmNeedBackwardsCompatibility(const std::string& variable, #endif } +void cmWarnUnusedCliWarning(const std::string& variable, + int, void* ctx, const char*, const cmMakefile*) +{ + cmake* cm = reinterpret_cast<cmake*>(ctx); + cm->MarkCliAsUsed(variable); +} + cmake::cmake() { this->Trace = false; + this->WarnUninitialized = false; + this->WarnUnused = false; + this->WarnUnusedCli = true; + this->CheckSystemVars = false; this->SuppressDevWarnings = false; this->DoSuppressDevWarnings = false; this->DebugOutput = false; @@ -367,6 +378,10 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) { this->CacheManager->AddCacheEntry(var.c_str(), value.c_str(), "No help, variable specified on the command line.", type); + if(this->WarnUnusedCli) + { + this->WatchUnusedCli(var.c_str()); + } } else { @@ -509,9 +524,10 @@ void cmake::ReadListFile(const char *path) } // Parse the args -void cmake::SetArgs(const std::vector<std::string>& args) +void cmake::SetArgs(const std::vector<std::string>& args, + bool directoriesSetBefore) { - bool directoriesSet = false; + bool directoriesSet = directoriesSetBefore; for(unsigned int i=1; i < args.size(); ++i) { std::string arg = args[i]; @@ -613,6 +629,28 @@ void cmake::SetArgs(const std::vector<std::string>& args) std::cout << "Running with trace output on.\n"; this->SetTrace(true); } + else if(arg.find("--warn-uninitialized",0) == 0) + { + std::cout << "Warn about uninitialized values.\n"; + this->SetWarnUninitialized(true); + } + else if(arg.find("--warn-unused-vars",0) == 0) + { + std::cout << "Finding unused variables.\n"; + this->SetWarnUnused(true); + } + else if(arg.find("--no-warn-unused-cli",0) == 0) + { + std::cout << "Not searching for unused variables given on the " << + "command line.\n"; + this->SetWarnUnusedCli(false); + } + else if(arg.find("--check-system-vars",0) == 0) + { + std::cout << "Also check system files when warning about unused and " << + "uninitialized variables.\n"; + this->SetCheckSystemVars(true); + } else if(arg.find("-G",0) == 0) { std::string value = arg.substr(2); @@ -2272,6 +2310,11 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure) std::string oldstartoutputdir = this->GetStartOutputDirectory(); this->SetStartDirectory(this->GetHomeDirectory()); this->SetStartOutputDirectory(this->GetHomeOutputDirectory()); + const bool warncli = this->WarnUnusedCli; + if (!this->ScriptMode) + { + this->WarnUnusedCli = false; + } int ret = this->Configure(); if (ret || this->ScriptMode) { @@ -2293,6 +2336,7 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure) #endif return ret; } + this->WarnUnusedCli = warncli; ret = this->Generate(); std::string message = "Build files have been written to: "; message += this->GetHomeOutputDirectory(); @@ -2835,6 +2879,10 @@ const char* cmake::GetCPackCommand() return this->CPackCommand.c_str(); } +void cmake::MarkCliAsUsed(const std::string& variable) +{ + this->UsedCliVariables[variable] = true; +} void cmake::GenerateGraphViz(const char* fileName) const { @@ -4255,3 +4303,40 @@ int cmake::Build(const std::string& dir, config.c_str(), clean, false, 0, true, 0, nativeOptions); } + +void cmake::WatchUnusedCli(const char* var) +{ +#ifdef CMAKE_BUILD_WITH_CMAKE + this->VariableWatch->AddWatch(var, cmWarnUnusedCliWarning, this); + this->UsedCliVariables[var] = false; +#endif +} + +void cmake::UnwatchUnusedCli(const char* var) +{ +#ifdef CMAKE_BUILD_WITH_CMAKE + this->VariableWatch->RemoveWatch(var, cmWarnUnusedCliWarning); + this->UsedCliVariables[var] = true; +#endif +} + +void cmake::RunCheckForUnusedVariables(const std::string& reason) const +{ +#ifdef CMAKE_BUILD_WITH_CMAKE + if(this->WarnUnusedCli) + { + std::map<std::string, bool>::const_iterator it; + for(it = this->UsedCliVariables.begin(); + it != this->UsedCliVariables.end(); ++it) + { + if(!it->second) + { + std::string message = "CMake Warning: The variable, '" + it->first + + "', specified manually, was not used during the " + reason + + "."; + cmSystemTools::Message(message.c_str()); + } + } + } +#endif +} diff --git a/Source/cmake.h b/Source/cmake.h index 435d38b..1bb42d3 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -214,7 +214,8 @@ class cmake bool CommandExists(const char* name) const; ///! Parse command line arguments - void SetArgs(const std::vector<std::string>&); + void SetArgs(const std::vector<std::string>&, + bool directoriesSetBefore = false); ///! Is this cmake running as a result of a TRY_COMPILE command bool GetIsInTryCompile() { return this->InTryCompile; } @@ -308,6 +309,17 @@ class cmake // Do we want trace output during the cmake run. bool GetTrace() { return this->Trace;} void SetTrace(bool b) { this->Trace = b;} + bool GetWarnUninitialized() { return this->WarnUninitialized;} + void SetWarnUninitialized(bool b) { this->WarnUninitialized = b;} + bool GetWarnUnused() { return this->WarnUnused;} + void SetWarnUnused(bool b) { this->WarnUnused = b;} + bool GetWarnUnusedCli() { return this->WarnUnusedCli;} + void SetWarnUnusedCli(bool b) { this->WarnUnusedCli = b;} + bool GetCheckSystemVars() { return this->CheckSystemVars;} + void SetCheckSystemVars(bool b) { this->CheckSystemVars = b;} + + void MarkCliAsUsed(const std::string& variable); + // Define a property void DefineProperty(const char *name, cmProperty::ScopeType scope, const char *ShortDescription, @@ -353,6 +365,10 @@ class cmake const std::string& config, const std::vector<std::string>& nativeOptions, bool clean); + + void UnwatchUnusedCli(const char* var); + void WatchUnusedCli(const char* var); + void RunCheckForUnusedVariables(const std::string& reason) const; protected: void InitializeProperties(); int HandleDeleteCacheVariables(const char* var); @@ -445,6 +461,11 @@ private: bool ScriptMode; bool DebugOutput; bool Trace; + bool WarnUninitialized; + bool WarnUnused; + bool WarnUnusedCli; + bool CheckSystemVars; + std::map<std::string, bool> UsedCliVariables; std::string CMakeEditCommand; std::string CMakeCommand; std::string CXXEnvironment; diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index ddff71d..a51673c 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -120,6 +120,17 @@ static const char * cmDocumentationOptions[][3] = {"--trace", "Put cmake in trace mode.", "Print a trace of all calls made and from where with " "message(send_error ) calls."}, + {"--warn-uninitialized", "Warn about uninitialized values.", + "Print a warning when an uninitialized variable is used."}, + {"--warn-unused-vars", "Warn about unused variables.", + "Find variables that are declared or set, but not used."}, + {"--no-warn-unused-cli", "Don't warn about command line options.", + "Don't find variables that are declared on the command line, but not " + "used."}, + {"--check-system-vars", "Find problems with variable usage in system " + "files.", "Normally, unused and uninitialized variables are searched for " + "only in CMAKE_SOURCE_DIR and CMAKE_BINARY_DIR. This flag tells CMake to " + "warn about other files as well."}, {"--help-command cmd [file]", "Print help for a single command and exit.", "Full documentation specific to the given command is displayed. " "If a file is specified, the documentation is written into and the output " |