diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Source/cmGlobVerificationManager.cxx | 172 | ||||
-rw-r--r-- | Source/cmGlobVerificationManager.h | 89 | ||||
-rw-r--r-- | Source/cmState.cxx | 36 | ||||
-rw-r--r-- | Source/cmState.h | 14 | ||||
-rw-r--r-- | Source/cmake.cxx | 28 | ||||
-rw-r--r-- | Source/cmake.h | 10 |
7 files changed, 351 insertions, 0 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index e547356..e23b070 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -246,6 +246,8 @@ set(SRCS cmGlobalGeneratorFactory.h cmGlobalUnixMakefileGenerator3.cxx cmGlobalUnixMakefileGenerator3.h + cmGlobVerificationManager.cxx + cmGlobVerificationManager.h cmGraphAdjacencyList.h cmGraphVizWriter.cxx cmGraphVizWriter.h diff --git a/Source/cmGlobVerificationManager.cxx b/Source/cmGlobVerificationManager.cxx new file mode 100644 index 0000000..e23b6ea --- /dev/null +++ b/Source/cmGlobVerificationManager.cxx @@ -0,0 +1,172 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmGlobVerificationManager.h" + +#include "cmsys/FStream.hxx" +#include <sstream> + +#include "cmGeneratedFileStream.h" +#include "cmListFileCache.h" +#include "cmSystemTools.h" +#include "cmVersion.h" +#include "cmake.h" + +bool cmGlobVerificationManager::SaveVerificationScript(const std::string& path) +{ + if (this->Cache.empty()) { + return true; + } + + std::string scriptFile = path; + scriptFile += cmake::GetCMakeFilesDirectory(); + std::string stampFile = scriptFile; + cmSystemTools::MakeDirectory(scriptFile); + scriptFile += "/VerifyGlobs.cmake"; + stampFile += "/cmake.verify_globs"; + cmGeneratedFileStream verifyScriptFile(scriptFile.c_str()); + verifyScriptFile.SetCopyIfDifferent(true); + if (!verifyScriptFile) { + cmSystemTools::Error("Unable to open verification script file for save. ", + scriptFile.c_str()); + cmSystemTools::ReportLastSystemError(""); + return false; + } + + verifyScriptFile << std::boolalpha; + verifyScriptFile << "# CMAKE generated file: DO NOT EDIT!\n" + << "# Generated by CMake Version " + << cmVersion::GetMajorVersion() << "." + << cmVersion::GetMinorVersion() << "\n"; + + for (auto const& i : this->Cache) { + CacheEntryKey k = std::get<0>(i); + CacheEntryValue v = std::get<1>(i); + + if (!v.Initialized) { + continue; + } + + verifyScriptFile << "\n"; + + for (auto const& bt : v.Backtraces) { + verifyScriptFile << "# " << std::get<0>(bt); + std::get<1>(bt).PrintTitle(verifyScriptFile); + verifyScriptFile << "\n"; + } + + k.PrintGlobCommand(verifyScriptFile, "NEW_GLOB"); + verifyScriptFile << "\n"; + + verifyScriptFile << "set(OLD_GLOB\n"; + for (const std::string& file : v.Files) { + verifyScriptFile << " \"" << file << "\"\n"; + } + verifyScriptFile << " )\n"; + + verifyScriptFile << "if(NOT \"${NEW_GLOB}\" STREQUAL \"${OLD_GLOB}\")\n" + << " message(\"-- GLOB mismatch!\")\n" + << " file(TOUCH_NOCREATE \"" << stampFile << "\")\n" + << "endif()\n"; + } + verifyScriptFile.Close(); + + cmsys::ofstream verifyStampFile(stampFile.c_str()); + if (!verifyStampFile) { + cmSystemTools::Error("Unable to open verification stamp file for write. ", + stampFile.c_str()); + return false; + } + verifyStampFile << "# This file is generated by CMake for checking of the " + "VerifyGlobs.cmake file\n"; + this->VerifyScript = scriptFile; + this->VerifyStamp = stampFile; + return true; +} + +bool cmGlobVerificationManager::DoWriteVerifyTarget() const +{ + return !this->VerifyScript.empty() && !this->VerifyStamp.empty(); +} + +bool cmGlobVerificationManager::CacheEntryKey::operator<( + const CacheEntryKey& r) const +{ + if (this->Recurse < r.Recurse) { + return true; + } + if (this->Recurse > r.Recurse) { + return false; + } + if (this->ListDirectories < r.ListDirectories) { + return true; + } + if (this->ListDirectories > r.ListDirectories) { + return false; + } + if (this->FollowSymlinks < r.FollowSymlinks) { + return true; + } + if (this->FollowSymlinks > r.FollowSymlinks) { + return false; + } + if (this->Relative < r.Relative) { + return true; + } + if (this->Relative > r.Relative) { + return false; + } + if (this->Expression < r.Expression) { + return true; + } + if (this->Expression > r.Expression) { + return false; + } + return false; +} + +void cmGlobVerificationManager::CacheEntryKey::PrintGlobCommand( + std::ostream& out, const std::string& cmdVar) +{ + out << "file(GLOB" << (this->Recurse ? "_RECURSE " : " "); + out << cmdVar << " "; + if (this->Recurse && this->FollowSymlinks) { + out << "FOLLOW_SYMLINKS "; + } + out << "LIST_DIRECTORIES " << this->ListDirectories << " "; + if (!this->Relative.empty()) { + out << "RELATIVE \"" << this->Relative << "\" "; + } + out << "\"" << this->Expression << "\")"; +} + +void cmGlobVerificationManager::AddCacheEntry( + const bool recurse, const bool listDirectories, const bool followSymlinks, + const std::string& relative, const std::string& expression, + const std::vector<std::string>& files, const std::string& variable, + const cmListFileBacktrace& backtrace) +{ + CacheEntryKey key = CacheEntryKey(recurse, listDirectories, followSymlinks, + relative, expression); + CacheEntryValue& value = this->Cache[key]; + if (!value.Initialized) { + value.Files = files; + value.Initialized = true; + value.Backtraces.emplace_back(variable, backtrace); + } else if (value.Initialized && value.Files != files) { + std::ostringstream message; + message << std::boolalpha; + message << "The glob expression\n"; + key.PrintGlobCommand(message, variable); + backtrace.PrintTitle(message); + message << "\nwas already present in the glob cache but the directory\n" + "contents have changed during the configuration run.\n"; + message << "Matching glob expressions:"; + for (auto const& bt : value.Backtraces) { + message << "\n " << std::get<0>(bt); + std::get<1>(bt).PrintTitle(message); + } + cmSystemTools::Error(message.str().c_str()); + } else { + value.Backtraces.emplace_back(variable, backtrace); + } +} diff --git a/Source/cmGlobVerificationManager.h b/Source/cmGlobVerificationManager.h new file mode 100644 index 0000000..4508602 --- /dev/null +++ b/Source/cmGlobVerificationManager.h @@ -0,0 +1,89 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmGlobVerificationManager_h +#define cmGlobVerificationManager_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmListFileCache.h" + +#include <iosfwd> +#include <map> +#include <string> +#include <utility> +#include <vector> + +/** \class cmGlobVerificationManager + * \brief Class for expressing build-time dependencies on glob expressions. + * + * Generates a CMake script which verifies glob outputs during prebuild. + * + */ +class cmGlobVerificationManager +{ +public: + cmGlobVerificationManager() {} + +protected: + ///! Save verification script for given makefile. + ///! Saves to output <path>/<CMakeFilesDirectory>/VerifyGlobs.cmake + bool SaveVerificationScript(const std::string& path); + + ///! Add an entry into the glob cache + void AddCacheEntry(bool recurse, bool listDirectories, bool followSymlinks, + const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + const cmListFileBacktrace& bt); + + ///! Check targets should be written in generated build system. + bool DoWriteVerifyTarget() const; + + ///! Get the paths to the generated script and stamp files + std::string const& GetVerifyScript() const { return this->VerifyScript; } + std::string const& GetVerifyStamp() const { return this->VerifyStamp; } + +private: + struct CacheEntryKey + { + const bool Recurse; + const bool ListDirectories; + const bool FollowSymlinks; + const std::string Relative; + const std::string Expression; + CacheEntryKey(const bool rec, const bool l, const bool s, + const std::string& rel, const std::string& e) + : Recurse(rec) + , ListDirectories(l) + , FollowSymlinks(s) + , Relative(rel) + , Expression(e) + { + } + bool operator<(const CacheEntryKey& r) const; + void PrintGlobCommand(std::ostream& out, const std::string& cmdVar); + }; + + struct CacheEntryValue + { + bool Initialized; + std::vector<std::string> Files; + std::vector<std::pair<std::string, cmListFileBacktrace>> Backtraces; + CacheEntryValue() + : Initialized(false) + { + } + }; + + typedef std::map<CacheEntryKey, CacheEntryValue> CacheEntryMap; + CacheEntryMap Cache; + std::string VerifyScript; + std::string VerifyStamp; + + // Only cmState should be able to add cache values. + // cmGlobVerificationManager should never be used directly. + friend class cmState; // allow access to add cache values +}; + +#endif diff --git a/Source/cmState.cxx b/Source/cmState.cxx index bb891b5..a93fb11 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -13,6 +13,7 @@ #include "cmCommand.h" #include "cmDefinitions.h" #include "cmDisallowedCommand.h" +#include "cmGlobVerificationManager.h" #include "cmListFileCache.h" #include "cmStatePrivate.h" #include "cmStateSnapshot.h" @@ -31,11 +32,13 @@ cmState::cmState() , MSYSShell(false) { this->CacheManager = new cmCacheManager; + this->GlobVerificationManager = new cmGlobVerificationManager; } cmState::~cmState() { delete this->CacheManager; + delete this->GlobVerificationManager; cmDeleteAll(this->BuiltinCommands); cmDeleteAll(this->ScriptedCommands); } @@ -207,6 +210,39 @@ void cmState::AddCacheEntry(const std::string& key, const char* value, this->CacheManager->AddCacheEntry(key, value, helpString, type); } +bool cmState::DoWriteGlobVerifyTarget() const +{ + return this->GlobVerificationManager->DoWriteVerifyTarget(); +} + +std::string const& cmState::GetGlobVerifyScript() const +{ + return this->GlobVerificationManager->GetVerifyScript(); +} + +std::string const& cmState::GetGlobVerifyStamp() const +{ + return this->GlobVerificationManager->GetVerifyStamp(); +} + +bool cmState::SaveVerificationScript(const std::string& path) +{ + return this->GlobVerificationManager->SaveVerificationScript(path); +} + +void cmState::AddGlobCacheEntry(bool recurse, bool listDirectories, + bool followSymlinks, + const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + cmListFileBacktrace const& backtrace) +{ + this->GlobVerificationManager->AddCacheEntry( + recurse, listDirectories, followSymlinks, relative, expression, files, + variable, backtrace); +} + void cmState::RemoveCacheEntry(std::string const& key) { this->CacheManager->RemoveCacheEntry(key); diff --git a/Source/cmState.h b/Source/cmState.h index 6cbf82d..4c6fc69 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -12,6 +12,7 @@ #include "cmDefinitions.h" #include "cmLinkedTree.h" +#include "cmListFileCache.h" #include "cmPolicies.h" #include "cmProperty.h" #include "cmPropertyDefinitionMap.h" @@ -21,6 +22,7 @@ class cmCacheManager; class cmCommand; +class cmGlobVerificationManager; class cmPropertyDefinition; class cmStateSnapshot; class cmMessenger; @@ -165,12 +167,24 @@ private: const char* helpString, cmStateEnums::CacheEntryType type); + bool DoWriteGlobVerifyTarget() const; + std::string const& GetGlobVerifyScript() const; + std::string const& GetGlobVerifyStamp() const; + bool SaveVerificationScript(const std::string& path); + void AddGlobCacheEntry(bool recurse, bool listDirectories, + bool followSymlinks, const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + cmListFileBacktrace const& bt); + std::map<cmProperty::ScopeType, cmPropertyDefinitionMap> PropertyDefinitions; std::vector<std::string> EnabledLanguages; std::map<std::string, cmCommand*> BuiltinCommands; std::map<std::string, cmCommand*> ScriptedCommands; cmPropertyMap GlobalProperties; cmCacheManager* CacheManager; + cmGlobVerificationManager* GlobVerificationManager; cmLinkedTree<cmStateDetail::BuildsystemDirectoryStateType> BuildsystemDirectory; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 323bcf6..41a3a22 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -1434,6 +1434,7 @@ int cmake::ActualConfigure() // only save the cache if there were no fatal errors if (this->GetWorkingMode() == NORMAL_MODE) { + this->State->SaveVerificationScript(this->GetHomeOutputDirectory()); this->SaveCache(this->GetHomeOutputDirectory()); } if (cmSystemTools::GetErrorOccuredFlag()) { @@ -1649,6 +1650,33 @@ void cmake::AddCacheEntry(const std::string& key, const char* value, this->UnwatchUnusedCli(key); } +bool cmake::DoWriteGlobVerifyTarget() const +{ + return this->State->DoWriteGlobVerifyTarget(); +} + +std::string const& cmake::GetGlobVerifyScript() const +{ + return this->State->GetGlobVerifyScript(); +} + +std::string const& cmake::GetGlobVerifyStamp() const +{ + return this->State->GetGlobVerifyStamp(); +} + +void cmake::AddGlobCacheEntry(bool recurse, bool listDirectories, + bool followSymlinks, const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + cmListFileBacktrace const& backtrace) +{ + this->State->AddGlobCacheEntry(recurse, listDirectories, followSymlinks, + relative, expression, files, variable, + backtrace); +} + std::string cmake::StripExtension(const std::string& file) const { auto dotpos = file.rfind('.'); diff --git a/Source/cmake.h b/Source/cmake.h index 1ac549b..5e4930d 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -255,6 +255,16 @@ public: void AddCacheEntry(const std::string& key, const char* value, const char* helpString, int type); + bool DoWriteGlobVerifyTarget() const; + std::string const& GetGlobVerifyScript() const; + std::string const& GetGlobVerifyStamp() const; + void AddGlobCacheEntry(bool recurse, bool listDirectories, + bool followSymlinks, const std::string& relative, + const std::string& expression, + const std::vector<std::string>& files, + const std::string& variable, + cmListFileBacktrace const& bt); + /** * Get the system information and write it to the file specified */ |