diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Source/CMakeVersion.cmake | 2 | ||||
-rw-r--r-- | Source/cmFileCommand.cxx | 38 | ||||
-rw-r--r-- | Source/cmGlobVerificationManager.cxx | 172 | ||||
-rw-r--r-- | Source/cmGlobVerificationManager.h | 89 | ||||
-rw-r--r-- | Source/cmGlobalNinjaGenerator.cxx | 77 | ||||
-rw-r--r-- | Source/cmGlobalNinjaGenerator.h | 3 | ||||
-rw-r--r-- | Source/cmGlobalUnixMakefileGenerator3.cxx | 7 | ||||
-rw-r--r-- | Source/cmGlobalVisualStudio8Generator.cxx | 38 | ||||
-rw-r--r-- | Source/cmGlobalXCodeGenerator.cxx | 24 | ||||
-rw-r--r-- | Source/cmLocalNinjaGenerator.cxx | 10 | ||||
-rw-r--r-- | Source/cmLocalUnixMakefileGenerator3.cxx | 16 | ||||
-rw-r--r-- | Source/cmLocalVisualStudio7Generator.cxx | 38 | ||||
-rw-r--r-- | Source/cmMakefile.cxx | 10 | ||||
-rw-r--r-- | Source/cmState.cxx | 36 | ||||
-rw-r--r-- | Source/cmState.h | 14 | ||||
-rw-r--r-- | Source/cmVisualStudio10TargetGenerator.cxx | 178 | ||||
-rw-r--r-- | Source/cmVisualStudio10TargetGenerator.h | 3 | ||||
-rw-r--r-- | Source/cmXMLWriter.cxx | 29 | ||||
-rw-r--r-- | Source/cmXMLWriter.h | 3 | ||||
-rw-r--r-- | Source/cmake.cxx | 96 | ||||
-rw-r--r-- | Source/cmake.h | 10 |
22 files changed, 741 insertions, 154 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/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 0f1a352..7ea071b 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 11) -set(CMake_VERSION_PATCH 20180404) +set(CMake_VERSION_PATCH 20180406) #set(CMake_VERSION_RC 1) diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 90b943b..0d31070 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -758,7 +758,11 @@ bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args, } std::vector<std::string> files; + bool configureDepends = false; + bool warnConfigureLate = false; bool warnFollowedSymlinks = false; + const cmake::WorkingMode workingMode = + this->Makefile->GetCMakeInstance()->GetWorkingMode(); while (i != args.end()) { if (*i == "LIST_DIRECTORIES") { ++i; @@ -807,6 +811,27 @@ bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args, this->SetError("GLOB requires a glob expression after the directory."); return false; } + } else if (*i == "CONFIGURE_DEPENDS") { + // Generated build system depends on glob results + if (!configureDepends && warnConfigureLate) { + this->Makefile->IssueMessage( + cmake::AUTHOR_WARNING, + "CONFIGURE_DEPENDS flag was given after a glob expression was " + "already evaluated."); + } + if (workingMode != cmake::NORMAL_MODE) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "CONFIGURE_DEPENDS is invalid for script and find package modes."); + return false; + } + configureDepends = true; + ++i; + if (i == args.end()) { + this->SetError( + "GLOB requires a glob expression after CONFIGURE_DEPENDS."); + return false; + } } else { std::string expr = *i; if (!cmsys::SystemTools::FileIsFullPath(*i)) { @@ -849,6 +874,19 @@ bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args, std::vector<std::string>& foundFiles = g.GetFiles(); files.insert(files.end(), foundFiles.begin(), foundFiles.end()); + + if (configureDepends) { + std::sort(foundFiles.begin(), foundFiles.end()); + foundFiles.erase(std::unique(foundFiles.begin(), foundFiles.end()), + foundFiles.end()); + this->Makefile->GetCMakeInstance()->AddGlobCacheEntry( + recurse, (recurse ? g.GetRecurseListDirs() : g.GetListDirs()), + (recurse ? g.GetRecurseThroughSymlinks() : false), + (g.GetRelative() ? g.GetRelative() : ""), expr, foundFiles, variable, + this->Makefile->GetBacktrace()); + } else { + warnConfigureLate = true; + } ++i; } } 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/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 55a403e..d562df7 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -475,6 +475,7 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm) , PolicyCMP0058(cmPolicies::WARN) , NinjaSupportsConsolePool(false) , NinjaSupportsImplicitOuts(false) + , NinjaSupportsManifestRestat(false) , NinjaSupportsDyndeps(0) { #ifdef _WIN32 @@ -597,6 +598,9 @@ void cmGlobalNinjaGenerator::CheckNinjaFeatures() this->NinjaSupportsImplicitOuts = !cmSystemTools::VersionCompare( cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), this->RequiredNinjaVersionForImplicitOuts().c_str()); + this->NinjaSupportsManifestRestat = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + RequiredNinjaVersionForManifestRestat().c_str()); { // Our ninja branch adds ".dyndep-#" to its version number, // where '#' is a feature-specific version number. Extract it. @@ -1361,6 +1365,7 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) /*generator=*/true); cmNinjaDeps implicitDeps; + cmNinjaDeps explicitDeps; for (cmLocalGenerator* localGen : this->LocalGenerators) { std::vector<std::string> const& lf = localGen->GetMakefile()->GetListFiles(); @@ -1370,10 +1375,6 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) } implicitDeps.push_back(this->CMakeCacheFile); - std::sort(implicitDeps.begin(), implicitDeps.end()); - implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()), - implicitDeps.end()); - cmNinjaVars variables; // Use 'console' pool to get non buffered output of the CMake re-run call // Available since Ninja 1.5 @@ -1381,12 +1382,71 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) variables["pool"] = "console"; } + cmake* cm = this->GetCMakeInstance(); + if (this->SupportsManifestRestat() && cm->DoWriteGlobVerifyTarget()) { + std::ostringstream verify_cmd; + verify_cmd << lg->ConvertToOutputFormat(cmSystemTools::GetCMakeCommand(), + cmOutputConverter::SHELL) + << " -P " + << lg->ConvertToOutputFormat(cm->GetGlobVerifyScript(), + cmOutputConverter::SHELL); + + WriteRule(*this->RulesFileStream, "VERIFY_GLOBS", verify_cmd.str(), + "Re-checking globbed directories...", + "Rule for re-checking globbed directories.", + /*depfile=*/"", + /*deptype=*/"", + /*rspfile=*/"", + /*rspcontent*/ "", + /*restat=*/"", + /*generator=*/true); + + std::string verifyForce = cm->GetGlobVerifyScript() + "_force"; + cmNinjaDeps verifyForceDeps(1, this->NinjaOutputPath(verifyForce)); + + this->WritePhonyBuild(os, "Phony target to force glob verification run.", + verifyForceDeps, cmNinjaDeps()); + + variables["restat"] = "1"; + std::string const verifyScriptFile = + this->NinjaOutputPath(cm->GetGlobVerifyScript()); + std::string const verifyStampFile = + this->NinjaOutputPath(cm->GetGlobVerifyStamp()); + this->WriteBuild(os, + "Re-run CMake to check if globbed directories changed.", + "VERIFY_GLOBS", + /*outputs=*/cmNinjaDeps(1, verifyStampFile), + /*implicitOuts=*/cmNinjaDeps(), + /*explicitDeps=*/cmNinjaDeps(), + /*implicitDeps=*/verifyForceDeps, + /*orderOnlyDeps=*/cmNinjaDeps(), variables); + + variables.erase("restat"); + implicitDeps.push_back(verifyScriptFile); + explicitDeps.push_back(verifyStampFile); + } else if (!this->SupportsManifestRestat() && + cm->DoWriteGlobVerifyTarget()) { + std::ostringstream msg; + msg << "The detected version of Ninja:\n" + << " " << this->NinjaVersion << "\n" + << "is less than the version of Ninja required by CMake for adding " + "restat dependencies to the build.ninja manifest regeneration " + "target:\n" + << " " << this->RequiredNinjaVersionForManifestRestat() << "\n"; + msg << "Any pre-check scripts, such as those generated for file(GLOB " + "CONFIGURE_DEPENDS), will not be run by Ninja."; + this->GetCMakeInstance()->IssueMessage(cmake::AUTHOR_WARNING, msg.str()); + } + + std::sort(implicitDeps.begin(), implicitDeps.end()); + implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()), + implicitDeps.end()); + std::string const ninjaBuildFile = this->NinjaOutputPath(NINJA_BUILD_FILE); this->WriteBuild(os, "Re-run CMake if any of its inputs changed.", "RERUN_CMAKE", /*outputs=*/cmNinjaDeps(1, ninjaBuildFile), - /*implicitOuts=*/cmNinjaDeps(), - /*explicitDeps=*/cmNinjaDeps(), implicitDeps, + /*implicitOuts=*/cmNinjaDeps(), explicitDeps, implicitDeps, /*orderOnlyDeps=*/cmNinjaDeps(), variables); cmNinjaDeps missingInputs; @@ -1419,6 +1479,11 @@ bool cmGlobalNinjaGenerator::SupportsImplicitOuts() const return this->NinjaSupportsImplicitOuts; } +bool cmGlobalNinjaGenerator::SupportsManifestRestat() const +{ + return this->NinjaSupportsManifestRestat; +} + void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) { WriteRule(*this->RulesFileStream, "CLEAN", ninjaCmd() + " -t clean", diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 7f80d08..a779919 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -346,8 +346,10 @@ public: static std::string RequiredNinjaVersion() { return "1.3"; } static std::string RequiredNinjaVersionForConsolePool() { return "1.5"; } static std::string RequiredNinjaVersionForImplicitOuts() { return "1.7"; } + static std::string RequiredNinjaVersionForManifestRestat() { return "1.8"; } bool SupportsConsolePool() const; bool SupportsImplicitOuts() const; + bool SupportsManifestRestat() const; std::string NinjaOutputPath(std::string const& path) const; bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); } @@ -460,6 +462,7 @@ private: std::string NinjaVersion; bool NinjaSupportsConsolePool; bool NinjaSupportsImplicitOuts; + bool NinjaSupportsManifestRestat; unsigned long NinjaSupportsDyndeps; private: diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index c92df55..a005885 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -301,6 +301,13 @@ void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile() lfiles.insert(lfiles.end(), lg->GetMakefile()->GetListFiles().begin(), lg->GetMakefile()->GetListFiles().end()); } + + cmake* cm = this->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + lfiles.push_back(cm->GetGlobVerifyScript()); + lfiles.push_back(cm->GetGlobVerifyStamp()); + } + // Sort the list and remove duplicates. std::sort(lfiles.begin(), lfiles.end(), std::less<std::string>()); #if !defined(__VMS) // The Compaq STL on VMS crashes, so accept duplicates. diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index ad72f5e..117d051 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -87,18 +87,18 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() { // Add a special target on which all other targets depend that // checks the build system and optionally re-runs CMake. - const char* no_working_directory = 0; + // Skip the target if no regeneration is to be done. + if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { + return false; + } + + const char* no_working_directory = nullptr; std::vector<std::string> no_depends; std::vector<cmLocalGenerator*> const& generators = this->LocalGenerators; cmLocalVisualStudio7Generator* lg = static_cast<cmLocalVisualStudio7Generator*>(generators[0]); cmMakefile* mf = lg->GetMakefile(); - // Skip the target if no regeneration is to be done. - if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { - return false; - } - cmCustomCommandLines noCommandLines; cmTarget* tgt = mf->AddUtilityCommand( CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator, @@ -144,6 +144,30 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() listFiles.insert(listFiles.end(), lmf->GetListFiles().begin(), lmf->GetListFiles().end()); } + + // Add a custom prebuild target to run the VerifyGlobs script. + cmake* cm = this->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + cmCustomCommandLine verifyCommandLine; + verifyCommandLine.push_back(cmSystemTools::GetCMakeCommand()); + verifyCommandLine.push_back("-P"); + verifyCommandLine.push_back(cm->GetGlobVerifyScript()); + cmCustomCommandLines verifyCommandLines; + verifyCommandLines.push_back(verifyCommandLine); + std::vector<std::string> byproducts; + byproducts.push_back(cm->GetGlobVerifyStamp()); + + mf->AddCustomCommandToTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET, byproducts, + no_depends, verifyCommandLines, + cmTarget::PRE_BUILD, "Checking File Globs", + no_working_directory, false); + + // Ensure ZERO_CHECK always runs in Visual Studio using MSBuild, + // otherwise the prebuild command will not be run. + tgt->SetProperty("VS_GLOBAL_DisableFastUpToDateCheck", "true"); + listFiles.push_back(cm->GetGlobVerifyStamp()); + } + // Sort the list of input files and remove duplicates. std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>()); std::vector<std::string>::iterator new_end = @@ -151,8 +175,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() listFiles.erase(new_end, listFiles.end()); // Create a rule to re-run CMake. - std::string stampName = cmake::GetCMakeFilesDirectoryPostSlash(); - stampName += "generate.stamp"; cmCustomCommandLine commandLine; commandLine.push_back(cmSystemTools::GetCMakeCommand()); std::string argH = "-H"; diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index f8597af..df671c2 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -537,6 +537,12 @@ void cmGlobalXCodeGenerator::CreateReRunCMakeFile( std::vector<std::string>::iterator new_end = std::unique(lfiles.begin(), lfiles.end()); lfiles.erase(new_end, lfiles.end()); + + cmake* cm = this->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + lfiles.emplace_back(cm->GetGlobVerifyStamp()); + } + this->CurrentReRunCMakeMakefile = root->GetCurrentBinaryDirectory(); this->CurrentReRunCMakeMakefile += "/CMakeScripts"; cmSystemTools::MakeDirectory(this->CurrentReRunCMakeMakefile.c_str()); @@ -555,14 +561,28 @@ void cmGlobalXCodeGenerator::CreateReRunCMakeFile( makefileStream << "TARGETS += $(subst $(space),$(spaceplus),$(wildcard " << this->ConvertToRelativeForMake(lfile) << "))\n"; } + makefileStream << "\n"; std::string checkCache = root->GetBinaryDirectory(); checkCache += "/"; checkCache += cmake::GetCMakeFilesDirectoryPostSlash(); checkCache += "cmake.check_cache"; - makefileStream << "\n" - << this->ConvertToRelativeForMake(checkCache) + if (cm->DoWriteGlobVerifyTarget()) { + makefileStream << ".NOTPARALLEL:\n\n"; + makefileStream << ".PHONY: all VERIFY_GLOBS\n\n"; + makefileStream << "all: VERIFY_GLOBS " + << this->ConvertToRelativeForMake(checkCache) << "\n\n"; + makefileStream << "VERIFY_GLOBS:\n"; + makefileStream << "\t" + << this->ConvertToRelativeForMake( + cmSystemTools::GetCMakeCommand()) + << " -P " + << this->ConvertToRelativeForMake(cm->GetGlobVerifyScript()) + << "\n\n"; + } + + makefileStream << this->ConvertToRelativeForMake(checkCache) << ": $(TARGETS)\n"; makefileStream << "\t" << this->ConvertToRelativeForMake( diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index c1af92f..c714299 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -197,6 +197,16 @@ void cmLocalNinjaGenerator::WriteNinjaRequiredVersion(std::ostream& os) this->GetGlobalNinjaGenerator()->RequiredNinjaVersionForConsolePool(); } + // The Ninja generator writes rules which require support for restat + // when rebuilding build.ninja manifest (>= 1.8) + if (this->GetGlobalNinjaGenerator()->SupportsManifestRestat() && + this->GetCMakeInstance()->DoWriteGlobVerifyTarget() && + !this->GetGlobalNinjaGenerator()->GlobalSettingIsOn( + "CMAKE_SUPPRESS_REGENERATION")) { + requiredVersion = + this->GetGlobalNinjaGenerator()->RequiredNinjaVersionForManifestRestat(); + } + cmGlobalNinjaGenerator::WriteComment( os, "Minimal version of Ninja required by this file"); os << "ninja_required_version = " << requiredVersion << std::endl diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index c9237a8..cf2a7b9 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -763,6 +763,14 @@ void cmLocalUnixMakefileGenerator3::WriteSpecialTargetsBottom( if (!this->GlobalGenerator->GlobalSettingIsOn( "CMAKE_SUPPRESS_REGENERATION")) { // Build command to run CMake to check if anything needs regenerating. + std::vector<std::string> commands; + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + std::string rescanRule = "$(CMAKE_COMMAND) -P "; + rescanRule += this->ConvertToOutputFormat(cm->GetGlobVerifyScript(), + cmOutputConverter::SHELL); + commands.push_back(rescanRule); + } std::string cmakefileName = cmake::GetCMakeFilesDirectoryPostSlash(); cmakefileName += "Makefile.cmake"; std::string runRule = @@ -773,7 +781,6 @@ void cmLocalUnixMakefileGenerator3::WriteSpecialTargetsBottom( runRule += " 0"; std::vector<std::string> no_depends; - std::vector<std::string> commands; commands.push_back(std::move(runRule)); if (!this->IsRootMakefile()) { this->CreateCDCommand(commands, this->GetBinaryDirectory(), @@ -1666,6 +1673,13 @@ void cmLocalUnixMakefileGenerator3::WriteLocalAllRules( // write the depend rule, really a recompute depends rule depends.clear(); commands.clear(); + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + std::string rescanRule = "$(CMAKE_COMMAND) -P "; + rescanRule += this->ConvertToOutputFormat(cm->GetGlobVerifyScript(), + cmOutputConverter::SHELL); + commands.push_back(rescanRule); + } std::string cmakefileName = cmake::GetCMakeFilesDirectoryPostSlash(); cmakefileName += "Makefile.cmake"; { diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 91ee09f..acb5921 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -160,10 +160,21 @@ void cmLocalVisualStudio7Generator::WriteStampFiles() depName += ".depend"; cmsys::ofstream depFile(depName.c_str()); depFile << "# CMake generation dependency list for this directory.\n"; - std::vector<std::string> const& listFiles = this->Makefile->GetListFiles(); - for (std::vector<std::string>::const_iterator lf = listFiles.begin(); - lf != listFiles.end(); ++lf) { - depFile << *lf << std::endl; + + std::vector<std::string> listFiles(this->Makefile->GetListFiles()); + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + listFiles.push_back(cm->GetGlobVerifyStamp()); + } + + // Sort the list of input files and remove duplicates. + std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>()); + std::vector<std::string>::iterator new_end = + std::unique(listFiles.begin(), listFiles.end()); + listFiles.erase(new_end, listFiles.end()); + + for (const std::string& lf : listFiles) { + depFile << lf << "\n"; } } @@ -228,6 +239,18 @@ cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule() return nullptr; } + std::vector<std::string> listFiles = this->Makefile->GetListFiles(); + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + if (cm->DoWriteGlobVerifyTarget()) { + listFiles.push_back(cm->GetGlobVerifyStamp()); + } + + // Sort the list of input files and remove duplicates. + std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>()); + std::vector<std::string>::iterator new_end = + std::unique(listFiles.begin(), listFiles.end()); + listFiles.erase(new_end, listFiles.end()); + std::string stampName = this->GetCurrentBinaryDirectory(); stampName += "/"; stampName += cmake::GetCMakeFilesDirectoryPostSlash(); @@ -245,17 +268,14 @@ cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule() commandLine.push_back(args); commandLine.push_back("--check-stamp-file"); commandLine.push_back(stampName); - - std::vector<std::string> const& listFiles = this->Makefile->GetListFiles(); - cmCustomCommandLines commandLines; commandLines.push_back(commandLine); const char* no_working_directory = 0; std::string fullpathStampName = cmSystemTools::CollapseFullPath(stampName.c_str()); this->Makefile->AddCustomCommandToOutput( - fullpathStampName.c_str(), listFiles, makefileIn.c_str(), commandLines, - comment.c_str(), no_working_directory, true, false); + fullpathStampName, listFiles, makefileIn, commandLines, comment.c_str(), + no_working_directory, true, false); if (cmSourceFile* file = this->Makefile->GetSource(makefileIn.c_str())) { // Finalize the source file path now since we're adding this after // the generator validated all project-named sources. diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 3ecd7eb..bbe6cc9 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -1836,12 +1836,10 @@ void cmMakefile::AddGlobalLinkInformation(cmTarget& target) std::vector<std::string> linkDirs; cmSystemTools::ExpandListArgument(linkDirsProp, linkDirs); - for (std::string const& linkDir : linkDirs) { - std::string newdir = linkDir; - // remove trailing slashes - if (*linkDir.rbegin() == '/') { - newdir = linkDir.substr(0, linkDir.size() - 1); - } + for (std::string& linkDir : linkDirs) { + // Sanitize the path the same way the link_directories command does + // in case projects set the LINK_DIRECTORIES property directly. + cmSystemTools::ConvertToUnixSlashes(linkDir); target.AddLinkDirectory(linkDir); } } 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/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 28e8b24..7c1d948 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -33,6 +33,7 @@ struct cmVisualStudio10TargetGenerator::Elem cmGeneratedFileStream& S; int Indent; bool HasElements = false; + const char* Tag = nullptr; Elem(cmGeneratedFileStream& s, int i) : S(s) @@ -46,25 +47,45 @@ struct cmVisualStudio10TargetGenerator::Elem { par.SetHasElements(); } + Elem(Elem& par, const char* tag) + : S(par.S) + , Indent(par.Indent + 1) + { + par.SetHasElements(); + this->StartElement(tag); + } void SetHasElements() { if (!HasElements) { - S << ">\n"; + this->S << ">\n"; HasElements = true; } } + cmGeneratedFileStream& WriteString(const char* line); + void StartElement(const char* tag) + { + this->Tag = tag; + this->WriteString("<") << tag; + } + template <typename T> + void WriteElem(const char* tag, const T& val) + { + this->WriteString("<") << tag << ">" << val << "</" << tag << ">\n"; + } + template <typename T> + void Attr(const char* an, const T& av) + { + this->S << " " << an << "=\"" << av << "\""; + } void WriteEndTag(const char* tag) { if (HasElements) { - S.fill(' '); - S.width(Indent * 2); - // write an empty string to get the fill level indent to print - S << ""; - S << "</" << tag << ">\n"; + this->WriteString("</") << tag << ">\n"; } else { - S << " />\n"; + this->S << " />\n"; } } + void EndElement() { this->WriteEndTag(this->Tag); } }; class cmVS10GeneratorOptions : public cmVisualStudioGeneratorOptions @@ -102,16 +123,14 @@ inline void cmVisualStudio10TargetGenerator::WriteElem(const char* tag, const char* val, int indentLevel) { - this->WriteString("<", indentLevel); - (*this->BuildFileStream) << tag << ">" << val << "</" << tag << ">\n"; + Elem(*this->BuildFileStream, indentLevel).WriteElem(tag, val); } inline void cmVisualStudio10TargetGenerator::WriteElem(const char* tag, std::string const& val, int indentLevel) { - this->WriteString("<", indentLevel); - (*this->BuildFileStream) << tag << ">" << val << "</" << tag << ">\n"; + Elem(*this->BuildFileStream, indentLevel).WriteElem(tag, val); } inline void cmVisualStudio10TargetGenerator::WriteElemEscapeXML( @@ -247,14 +266,21 @@ void cmVisualStudio10TargetGenerator::WritePlatformConfigTag( } } +cmGeneratedFileStream& cmVisualStudio10TargetGenerator::Elem::WriteString( + const char* line) +{ + this->S.fill(' '); + this->S.width(this->Indent * 2); + // write an empty string to get the fill level indent to print + this->S << ""; + this->S << line; + return this->S; +} + void cmVisualStudio10TargetGenerator::WriteString(const char* line, int indentLevel) { - this->BuildFileStream->fill(' '); - this->BuildFileStream->width(indentLevel * 2); - // write an empty string to get the fill level indent to print - (*this->BuildFileStream) << ""; - (*this->BuildFileStream) << line; + Elem(*this->BuildFileStream, indentLevel).WriteString(line); } #define VS10_CXX_DEFAULT_PROPS "$(VCTargetsPath)\\Microsoft.Cpp.Default.props" @@ -1373,71 +1399,72 @@ void cmVisualStudio10TargetGenerator::WriteGroups() fout.SetCopyIfDifferent(true); char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; fout.write(magic, 3); - cmGeneratedFileStream* save = this->BuildFileStream; - this->BuildFileStream = &fout; // get the tools version to use const std::string toolsVer(this->GlobalGenerator->GetToolsVersion()); - std::string project_defaults = "<?xml version=\"1.0\" encoding=\"" + - this->GlobalGenerator->Encoding() + "\"?>\n"; - project_defaults.append("<Project ToolsVersion=\""); - project_defaults.append(toolsVer + "\" "); - project_defaults.append( - "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n"); - this->WriteString(project_defaults.c_str(), 0); + fout << "<?xml version=\"1.0\" encoding=\"" + << this->GlobalGenerator->Encoding() << "\"?>\n"; + + Elem e0(fout, 0); + e0.StartElement("Project"); + e0.Attr("ToolsVersion", toolsVer); + e0.Attr("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"); + e0.SetHasElements(); for (auto const& ti : this->Tools) { - this->WriteGroupSources(ti.first.c_str(), ti.second, sourceGroups); + this->WriteGroupSources(e0, ti.first, ti.second, sourceGroups); } // Added files are images and the manifest. if (!this->AddedFiles.empty()) { - this->WriteString("<ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); + e1.SetHasElements(); for (std::string const& oi : this->AddedFiles) { std::string fileName = cmSystemTools::LowerCase(cmSystemTools::GetFilenameName(oi)); if (fileName == "wmappmanifest.xml") { - this->WriteString("<XML Include=\"", 2); - (*this->BuildFileStream) << oi << "\">\n"; - this->WriteElem("Filter", "Resource Files", 3); - this->WriteString("</XML>\n", 2); + Elem e2(e1, "XML"); + e2.Attr("Include", oi); + Elem(e2).WriteElem("Filter", "Resource Files"); + e2.EndElement(); } else if (cmSystemTools::GetFilenameExtension(fileName) == ".appxmanifest") { - this->WriteString("<AppxManifest Include=\"", 2); - (*this->BuildFileStream) << oi << "\">\n"; - this->WriteElem("Filter", "Resource Files", 3); - this->WriteString("</AppxManifest>\n", 2); + Elem e2(e1, "AppxManifest"); + e2.Attr("Include", oi); + Elem(e2).WriteElem("Filter", "Resource Files"); + e2.EndElement(); } else if (cmSystemTools::GetFilenameExtension(fileName) == ".pfx") { - this->WriteString("<None Include=\"", 2); - (*this->BuildFileStream) << oi << "\">\n"; - this->WriteElem("Filter", "Resource Files", 3); - this->WriteString("</None>\n", 2); + Elem e2(e1, "None"); + e2.Attr("Include", oi); + Elem(e2).WriteElem("Filter", "Resource Files"); + e2.EndElement(); } else { - this->WriteString("<Image Include=\"", 2); - (*this->BuildFileStream) << oi << "\">\n"; - this->WriteElem("Filter", "Resource Files", 3); - this->WriteString("</Image>\n", 2); + Elem e2(e1, "Image"); + e2.Attr("Include", oi); + Elem(e2).WriteElem("Filter", "Resource Files"); + e2.EndElement(); } } - this->WriteString("</ItemGroup>\n", 1); + e1.EndElement(); } std::vector<cmSourceFile const*> resxObjs; this->GeneratorTarget->GetResxSources(resxObjs, ""); if (!resxObjs.empty()) { - this->WriteString("<ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); for (cmSourceFile const* oi : resxObjs) { std::string obj = oi->GetFullPath(); - this->WriteString("<EmbeddedResource Include=\"", 2); ConvertToWindowsSlash(obj); - (*this->BuildFileStream) << cmVS10EscapeXML(obj) << "\">\n"; - this->WriteElem("Filter", "Resource Files", 3); - this->WriteString("</EmbeddedResource>\n", 2); + Elem e2(e1, "EmbeddedResource"); + e2.Attr("Include", cmVS10EscapeXML(obj)); + Elem(e2).WriteElem("Filter", "Resource Files"); + e2.EndElement(); } - this->WriteString("</ItemGroup>\n", 1); + e1.EndElement(); } - this->WriteString("<ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); + e1.SetHasElements(); std::vector<cmSourceGroup*> groupsVec(groupsUsed.begin(), groupsUsed.end()); std::sort(groupsVec.begin(), groupsVec.end(), [](cmSourceGroup* l, cmSourceGroup* r) { @@ -1446,31 +1473,29 @@ void cmVisualStudio10TargetGenerator::WriteGroups() for (cmSourceGroup* sg : groupsVec) { std::string const& name = sg->GetFullName(); if (!name.empty()) { - this->WriteString("<Filter Include=\"", 2); - (*this->BuildFileStream) << name << "\">\n"; - std::string guidName = "SG_Filter_"; - guidName += name; + std::string guidName = "SG_Filter_" + name; std::string guid = this->GlobalGenerator->GetGUID(guidName); - this->WriteElem("UniqueIdentifier", "{" + guid + "}", 3); - this->WriteString("</Filter>\n", 2); + Elem e2(e1, "Filter"); + e2.Attr("Include", name); + Elem(e2).WriteElem("UniqueIdentifier", "{" + guid + "}"); + e2.EndElement(); } } if (!resxObjs.empty() || !this->AddedFiles.empty()) { - this->WriteString("<Filter Include=\"Resource Files\">\n", 2); std::string guidName = "SG_Filter_Resource Files"; std::string guid = this->GlobalGenerator->GetGUID(guidName); - this->WriteElem("UniqueIdentifier", "{" + guid + "}", 3); - this->WriteString("<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;", 3); - (*this->BuildFileStream) << "gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;"; - (*this->BuildFileStream) << "mfcribbon-ms</Extensions>\n"; - this->WriteString("</Filter>\n", 2); + Elem e2(e1, "Filter"); + e2.Attr("Include", "Resource Files"); + Elem(e2).WriteElem("UniqueIdentifier", "{" + guid + "}"); + Elem(e2).WriteElem("Extensions", + "rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;" + "gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms"); + e2.EndElement(); } - this->WriteString("</ItemGroup>\n", 1); - this->WriteString("</Project>\n", 0); - // restore stream pointer - this->BuildFileStream = save; + e1.EndElement(); + e0.EndElement(); if (fout.Close()) { this->GlobalGenerator->FileReplacedDuringGenerate(path); @@ -1515,30 +1540,27 @@ void cmVisualStudio10TargetGenerator::AddMissingSourceGroups( } void cmVisualStudio10TargetGenerator::WriteGroupSources( - std::string const& name, ToolSources const& sources, + Elem& e0, std::string const& name, ToolSources const& sources, std::vector<cmSourceGroup>& sourceGroups) { - this->WriteString("<ItemGroup>\n", 1); + Elem e1(e0, "ItemGroup"); + e1.SetHasElements(); for (ToolSource const& s : sources) { cmSourceFile const* sf = s.SourceFile; std::string const& source = sf->GetFullPath(); cmSourceGroup* sourceGroup = this->Makefile->FindSourceGroup(source, sourceGroups); std::string const& filter = sourceGroup->GetFullName(); - this->WriteString("<", 2); std::string path = this->ConvertPath(source, s.RelativePath); ConvertToWindowsSlash(path); - (*this->BuildFileStream) << name << " Include=\"" << cmVS10EscapeXML(path); + Elem e2(e1, name.c_str()); + e2.Attr("Include", cmVS10EscapeXML(path)); if (!filter.empty()) { - (*this->BuildFileStream) << "\">\n"; - this->WriteElem("Filter", filter, 3); - this->WriteString("</", 2); - (*this->BuildFileStream) << name << ">\n"; - } else { - (*this->BuildFileStream) << "\" />\n"; + Elem(e2).WriteElem("Filter", filter); } + e2.EndElement(); } - this->WriteString("</ItemGroup>\n", 1); + e1.EndElement(); } void cmVisualStudio10TargetGenerator::WriteHeaderSource(cmSourceFile const* sf) diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index de3a8a6..3c53d1b 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -158,7 +158,8 @@ private: void WriteEvent(const char* name, std::vector<cmCustomCommand> const& commands, std::string const& configName); - void WriteGroupSources(std::string const& name, ToolSources const& sources, + void WriteGroupSources(Elem& e0, std::string const& name, + ToolSources const& sources, std::vector<cmSourceGroup>&); void AddMissingSourceGroups(std::set<cmSourceGroup*>& groupsUsed, const std::vector<cmSourceGroup>& allGroups); diff --git a/Source/cmXMLWriter.cxx b/Source/cmXMLWriter.cxx index 3cbc70d..9d2a3c4 100644 --- a/Source/cmXMLWriter.cxx +++ b/Source/cmXMLWriter.cxx @@ -9,6 +9,7 @@ cmXMLWriter::cmXMLWriter(std::ostream& output, std::size_t level) : Output(output) , IndentationElement(1, '\t') , Level(level) + , Indent(0) , ElementOpen(false) , BreakAttrib(false) , IsContent(false) @@ -17,7 +18,7 @@ cmXMLWriter::cmXMLWriter(std::ostream& output, std::size_t level) cmXMLWriter::~cmXMLWriter() { - assert(this->Elements.empty()); + assert(this->Indent == 0); } void cmXMLWriter::StartDocument(const char* encoding) @@ -27,27 +28,29 @@ void cmXMLWriter::StartDocument(const char* encoding) void cmXMLWriter::EndDocument() { - assert(this->Elements.empty()); + assert(this->Indent == 0); this->Output << '\n'; } void cmXMLWriter::StartElement(std::string const& name) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << '<' << name; this->Elements.push(name); + ++this->Indent; this->ElementOpen = true; this->BreakAttrib = false; } void cmXMLWriter::EndElement() { - assert(!this->Elements.empty()); + assert(this->Indent > 0); + --this->Indent; if (this->ElementOpen) { this->Output << "/>"; } else { - this->ConditionalLineBreak(!this->IsContent, this->Elements.size() - 1); + this->ConditionalLineBreak(!this->IsContent); this->IsContent = false; this->Output << "</" << this->Elements.top() << '>'; } @@ -58,7 +61,7 @@ void cmXMLWriter::EndElement() void cmXMLWriter::Element(const char* name) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << '<' << name << "/>"; } @@ -70,7 +73,7 @@ void cmXMLWriter::BreakAttributes() void cmXMLWriter::Comment(const char* comment) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << "<!-- " << comment << " -->"; } @@ -83,14 +86,14 @@ void cmXMLWriter::CData(std::string const& data) void cmXMLWriter::Doctype(const char* doctype) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << "<!DOCTYPE " << doctype << ">"; } void cmXMLWriter::ProcessingInstruction(const char* target, const char* data) { this->CloseStartElement(); - this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->ConditionalLineBreak(!this->IsContent); this->Output << "<?" << target << ' ' << data << "?>"; } @@ -106,11 +109,11 @@ void cmXMLWriter::SetIndentationElement(std::string const& element) this->IndentationElement = element; } -void cmXMLWriter::ConditionalLineBreak(bool condition, std::size_t indent) +void cmXMLWriter::ConditionalLineBreak(bool condition) { if (condition) { this->Output << '\n'; - for (std::size_t i = 0; i < indent + this->Level; ++i) { + for (std::size_t i = 0; i < this->Indent + this->Level; ++i) { this->Output << this->IndentationElement; } } @@ -119,7 +122,7 @@ void cmXMLWriter::ConditionalLineBreak(bool condition, std::size_t indent) void cmXMLWriter::PreAttribute() { assert(this->ElementOpen); - this->ConditionalLineBreak(this->BreakAttrib, this->Elements.size()); + this->ConditionalLineBreak(this->BreakAttrib); if (!this->BreakAttrib) { this->Output << ' '; } @@ -134,7 +137,7 @@ void cmXMLWriter::PreContent() void cmXMLWriter::CloseStartElement() { if (this->ElementOpen) { - this->ConditionalLineBreak(this->BreakAttrib, this->Elements.size()); + this->ConditionalLineBreak(this->BreakAttrib); this->Output << '>'; this->ElementOpen = false; } diff --git a/Source/cmXMLWriter.h b/Source/cmXMLWriter.h index 80940ee..ff0df18 100644 --- a/Source/cmXMLWriter.h +++ b/Source/cmXMLWriter.h @@ -67,7 +67,7 @@ public: void SetIndentationElement(std::string const& element); private: - void ConditionalLineBreak(bool condition, std::size_t indent); + void ConditionalLineBreak(bool condition); void PreAttribute(); void PreContent(); @@ -128,6 +128,7 @@ private: std::stack<std::string, std::vector<std::string>> Elements; std::string IndentationElement; std::size_t Level; + std::size_t Indent; bool ElementOpen; bool BreakAttrib; bool IsContent; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 83c5384..5bae4e7 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -1433,6 +1433,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()) { @@ -1647,6 +1648,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('.'); @@ -2434,37 +2462,49 @@ int cmake::Build(const std::string& dir, const std::string& target, cmGlobalVisualStudio9Generator::GetGenerateStampList(); // Note that the stampList file only exists for VS generators. - if (cmSystemTools::FileExists(stampList) && - !cmakeCheckStampList(stampList.c_str(), false)) { - - // Correctly initialize the home (=source) and home output (=binary) - // directories, which is required for running the generation step. - std::string homeOrig = this->GetHomeDirectory(); - std::string homeOutputOrig = this->GetHomeOutputDirectory(); - this->SetDirectoriesFromFile(cachePath.c_str()); + if (cmSystemTools::FileExists(stampList)) { + // Check if running for Visual Studio 9 - we need to explicitly run + // the glob verification script before starting the build this->AddScriptingCommands(); - this->AddProjectCommands(); + if (this->GlobalGenerator->MatchesGeneratorName("Visual Studio 9 2008")) { + std::string const globVerifyScript = cachePath + "/" + + GetCMakeFilesDirectoryPostSlash() + "VerifyGlobs.cmake"; + if (cmSystemTools::FileExists(globVerifyScript)) { + std::vector<std::string> args; + this->ReadListFile(args, globVerifyScript.c_str()); + } + } - int ret = this->Configure(); - if (ret) { - cmSystemTools::Message("CMake Configure step failed. " - "Build files cannot be regenerated correctly."); - return ret; - } - ret = this->Generate(); - if (ret) { - cmSystemTools::Message("CMake Generate step failed. " - "Build files cannot be regenerated correctly."); - return ret; - } - std::string message = "Build files have been written to: "; - message += this->GetHomeOutputDirectory(); - this->UpdateProgress(message.c_str(), -1); - - // Restore the previously set directories to their original value. - this->SetHomeDirectory(homeOrig); - this->SetHomeOutputDirectory(homeOutputOrig); + if (!cmakeCheckStampList(stampList.c_str(), false)) { + // Correctly initialize the home (=source) and home output (=binary) + // directories, which is required for running the generation step. + std::string homeOrig = this->GetHomeDirectory(); + std::string homeOutputOrig = this->GetHomeOutputDirectory(); + this->SetDirectoriesFromFile(cachePath.c_str()); + + this->AddProjectCommands(); + + int ret = this->Configure(); + if (ret) { + cmSystemTools::Message("CMake Configure step failed. " + "Build files cannot be regenerated correctly."); + return ret; + } + ret = this->Generate(); + if (ret) { + cmSystemTools::Message("CMake Generate step failed. " + "Build files cannot be regenerated correctly."); + return ret; + } + std::string message = "Build files have been written to: "; + message += this->GetHomeOutputDirectory(); + this->UpdateProgress(message.c_str(), -1); + + // Restore the previously set directories to their original value. + this->SetHomeDirectory(homeOrig); + this->SetHomeOutputDirectory(homeOutputOrig); + } } #endif diff --git a/Source/cmake.h b/Source/cmake.h index cc56a07..63dbe9f 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 */ |