|  | /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | 
|  | file LICENSE.rst or https://cmake.org/licensing for details.  */ | 
|  | #include "cmGlobVerificationManager.h" | 
|  |  | 
|  | #include <sstream> | 
|  |  | 
|  | #include "cmsys/FStream.hxx" | 
|  |  | 
|  | #include "cmGeneratedFileStream.h" | 
|  | #include "cmGlobCacheEntry.h" | 
|  | #include "cmListFileCache.h" | 
|  | #include "cmMessageType.h" | 
|  | #include "cmMessenger.h" | 
|  | #include "cmStringAlgorithms.h" | 
|  | #include "cmSystemTools.h" | 
|  | #include "cmVersion.h" | 
|  |  | 
|  | bool cmGlobVerificationManager::SaveVerificationScript(std::string const& path, | 
|  | cmMessenger* messenger) | 
|  | { | 
|  | if (this->Cache.empty()) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::string scriptFile = cmStrCat(path, "/CMakeFiles"); | 
|  | std::string stampFile = scriptFile; | 
|  | cmSystemTools::MakeDirectory(scriptFile); | 
|  | scriptFile += "/VerifyGlobs.cmake"; | 
|  | stampFile += "/cmake.verify_globs"; | 
|  | cmGeneratedFileStream verifyScriptFile(scriptFile); | 
|  | verifyScriptFile.SetCopyIfDifferent(true); | 
|  | if (!verifyScriptFile) { | 
|  | cmSystemTools::Error("Unable to open verification script file for save. " + | 
|  | scriptFile); | 
|  | 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); | 
|  | messenger->PrintBacktraceTitle(verifyScriptFile, std::get<1>(bt)); | 
|  | verifyScriptFile << "\n"; | 
|  | } | 
|  |  | 
|  | k.PrintGlobCommand(verifyScriptFile, "NEW_GLOB"); | 
|  | verifyScriptFile << "\n"; | 
|  |  | 
|  | verifyScriptFile << "set(OLD_GLOB\n"; | 
|  | for (std::string const& file : v.Files) { | 
|  | verifyScriptFile << "  \"" << file << "\"\n"; | 
|  | } | 
|  | verifyScriptFile << "  )\n"; | 
|  |  | 
|  | verifyScriptFile << "if(NOT \"${NEW_GLOB}\" STREQUAL \"${OLD_GLOB}\")\n" | 
|  | << "  message(\"-- GLOB mismatch!\")\n" | 
|  | << "  set(NEW_ONLY ${NEW_GLOB})\n" | 
|  | << "  set(OLD_ONLY ${OLD_GLOB})\n" | 
|  | << "  list(REMOVE_ITEM NEW_ONLY ${OLD_GLOB})\n" | 
|  | << "  list(REMOVE_ITEM OLD_ONLY ${NEW_GLOB})\n" | 
|  | << "  if(NEW_ONLY)\n" | 
|  | << "    message(\"The following files were added:\")\n" | 
|  | << "    foreach(VAR_FILE IN LISTS NEW_ONLY)\n" | 
|  | << "      message(\"  +${VAR_FILE}\")\n" | 
|  | << "    endforeach()\n" | 
|  | << "  endif()\n" | 
|  | << "  if(OLD_ONLY)\n" | 
|  | << "    message(\"The following files were removed:\")\n" | 
|  | << "    foreach(VAR_FILE IN LISTS OLD_ONLY)\n" | 
|  | << "      message(\"  -${VAR_FILE}\")\n" | 
|  | << "    endforeach()\n" | 
|  | << "  endif()\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); | 
|  | 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<( | 
|  | CacheEntryKey const& 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, std::string const& 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( | 
|  | cmGlobCacheEntry const& entry, std::string const& variable, | 
|  | cmListFileBacktrace const& backtrace, cmMessenger* messenger) | 
|  | { | 
|  | CacheEntryKey key = | 
|  | CacheEntryKey(entry.Recurse, entry.ListDirectories, entry.FollowSymlinks, | 
|  | entry.Relative, entry.Expression); | 
|  | CacheEntryValue& value = this->Cache[key]; | 
|  | if (!value.Initialized) { | 
|  | value.Files = entry.Files; | 
|  | value.Initialized = true; | 
|  | value.Backtraces.emplace_back(variable, backtrace); | 
|  | } else if (value.Initialized && value.Files != entry.Files) { | 
|  | std::ostringstream message; | 
|  | message << std::boolalpha; | 
|  | message << "The glob expression\n "; | 
|  | key.PrintGlobCommand(message, variable); | 
|  | message << "\nwas already present in the glob cache but the directory " | 
|  | "contents have changed during the configuration run.\n"; | 
|  | message << "Matching glob expressions:"; | 
|  | for (auto const& bt : value.Backtraces) { | 
|  | message << "\n  " << std::get<0>(bt); | 
|  | messenger->PrintBacktraceTitle(message, std::get<1>(bt)); | 
|  | } | 
|  | messenger->IssueMessage(MessageType::FATAL_ERROR, message.str(), | 
|  | backtrace); | 
|  | } else { | 
|  | value.Backtraces.emplace_back(variable, backtrace); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<cmGlobCacheEntry> cmGlobVerificationManager::GetCacheEntries() | 
|  | const | 
|  | { | 
|  | std::vector<cmGlobCacheEntry> entries; | 
|  | for (auto const& i : this->Cache) { | 
|  | CacheEntryKey k = std::get<0>(i); | 
|  | CacheEntryValue v = std::get<1>(i); | 
|  | if (v.Initialized) { | 
|  | entries.emplace_back(k.Recurse, k.ListDirectories, k.FollowSymlinks, | 
|  | k.Relative, k.Expression, v.Files); | 
|  | } | 
|  | } | 
|  | return entries; | 
|  | } | 
|  |  | 
|  | void cmGlobVerificationManager::Reset() | 
|  | { | 
|  | this->Cache.clear(); | 
|  | this->VerifyScript.clear(); | 
|  | this->VerifyStamp.clear(); | 
|  | } |