diff options
author | Brad King <brad.king@kitware.com> | 2018-04-06 14:24:57 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2018-04-06 14:25:08 (GMT) |
commit | d308e9442eb90db6fbd385374574ba5818001509 (patch) | |
tree | 81e7a05c00d785dcd994fb86b5748b17880dd3bd | |
parent | 2e49bb643450d4b27788b6f86d58f13ca1fbf917 (diff) | |
parent | 6c4f8b4596fb48f5eeea905b7fbdd9350b9e7838 (diff) | |
download | CMake-d308e9442eb90db6fbd385374574ba5818001509.zip CMake-d308e9442eb90db6fbd385374574ba5818001509.tar.gz CMake-d308e9442eb90db6fbd385374574ba5818001509.tar.bz2 |
Merge topic 'glob_configure_depends'
6c4f8b4596 Adjust help documentation for file(GLOB), add topic notes
20612978c8 Add tests for `file(GLOB)` CONFIGURE_DEPENDS flag
3f4b81f540 Add glob verify support to XCode, VS, Ninja, and Makefile generators
ca0befc2e1 Add `CONFIGURE_DEPENDS` flag support to cmFileCommand::HandleGlobCommand
599c93e22d Add cmGlobVerificationManager class, integrate with cmake and cmState
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !1767
38 files changed, 793 insertions, 56 deletions
diff --git a/Help/command/file.rst b/Help/command/file.rst index 5e18077..43ce3d9 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -98,10 +98,10 @@ command. :: file(GLOB <variable> - [LIST_DIRECTORIES true|false] [RELATIVE <path>] + [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS] [<globbing-expressions>...]) file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS] - [LIST_DIRECTORIES true|false] [RELATIVE <path>] + [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS] [<globbing-expressions>...]) Generate a list of files that match the ``<globbing-expressions>`` and @@ -110,6 +110,11 @@ regular expressions, but much simpler. If ``RELATIVE`` flag is specified, the results will be returned as relative paths to the given path. The results will be ordered lexicographically. +If the ``CONFIGURE_DEPENDS`` flag is specified, CMake will add logic +to the main build system check target to rerun the flagged ``GLOB`` commands +at build time. If any of the outputs change, CMake will regenerate the build +system. + By default ``GLOB`` lists directories - directories are omitted in result if ``LIST_DIRECTORIES`` is set to false. @@ -118,6 +123,10 @@ By default ``GLOB`` lists directories - directories are omitted in result if your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate. + The ``CONFIGURE_DEPENDS`` flag may not work reliably on all generators, or if + a new generator is added in the future that cannot support it, projects using + it will be stuck. Even if ``CONFIGURE_DEPENDS`` works reliably, there is + still a cost to perform the check on every rebuild. Examples of globbing expressions include:: diff --git a/Help/release/dev/glob_configure_depends.rst b/Help/release/dev/glob_configure_depends.rst new file mode 100644 index 0000000..147e44a --- /dev/null +++ b/Help/release/dev/glob_configure_depends.rst @@ -0,0 +1,6 @@ +glob_configure_depends +---------------------- + +* The :command:`file(GLOB)` and :command:`file(GLOB_RECURSE)` commands + learned a new flag ``CONFIGURE_DEPENDS`` which enables expression of + build system dependency on globbed directory's contents. 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/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/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 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 */ diff --git a/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-build-stdout.txt b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-build-stdout.txt new file mode 100644 index 0000000..71ab721 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-build-stdout.txt @@ -0,0 +1 @@ +.*b9fbdd8803c036dbe9f5ea6b74db4b9670c78a72 diff --git a/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-rebuild_first-stdout.txt b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-rebuild_first-stdout.txt new file mode 100644 index 0000000..ff90f9c --- /dev/null +++ b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-rebuild_first-stdout.txt @@ -0,0 +1,2 @@ +.*Running CMake on GLOB-CONFIGURE_DEPENDS-RerunCMake +.*6bc141b40c0f851d20fa9a1fe5fbdae94acc5de0 diff --git a/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-rebuild_second-stdout.txt b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-rebuild_second-stdout.txt new file mode 100644 index 0000000..cf2a5af --- /dev/null +++ b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-rebuild_second-stdout.txt @@ -0,0 +1,2 @@ +.*Running CMake on GLOB-CONFIGURE_DEPENDS-RerunCMake +.*0c3ceab9daa7914fde7410c34cae4049e140aa51 diff --git a/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-stdout.txt b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-stdout.txt new file mode 100644 index 0000000..66b6c44 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake-stdout.txt @@ -0,0 +1 @@ +.*Running CMake on GLOB-CONFIGURE_DEPENDS-RerunCMake diff --git a/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake.cmake b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake.cmake new file mode 100644 index 0000000..fe87c78 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-CONFIGURE_DEPENDS-RerunCMake.cmake @@ -0,0 +1,10 @@ +message(STATUS "Running CMake on GLOB-CONFIGURE_DEPENDS-RerunCMake") +file(GLOB_RECURSE + CONTENT_LIST + CONFIGURE_DEPENDS + LIST_DIRECTORIES false + RELATIVE "${CMAKE_CURRENT_BINARY_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}/test/*" + ) +string(SHA1 CONTENT_LIST_HASH "${CONTENT_LIST}") +add_custom_target(CONTENT_ECHO ALL ${CMAKE_COMMAND} -E echo ${CONTENT_LIST_HASH}) diff --git a/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE-result.txt b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE-stderr.txt b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE-stderr.txt new file mode 100644 index 0000000..40083c1 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE-stderr.txt @@ -0,0 +1 @@ +.*CONFIGURE_DEPENDS is invalid for script and find package modes\. diff --git a/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE.cmake b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE.cmake new file mode 100644 index 0000000..9dc0f03 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE.cmake @@ -0,0 +1 @@ +file(GLOB CONTENT_LIST CONFIGURE_DEPENDS) diff --git a/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-modified-result.txt b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-modified-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-modified-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-modified-stderr.txt b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-modified-stderr.txt new file mode 100644 index 0000000..d7b36eb --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-modified-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error: The glob expression +.* at GLOB-error-CONFIGURE_DEPENDS-modified\.cmake:[0-9]+ \(file\) +was already present in the glob cache but the directory +contents have changed during the configuration run. +Matching glob expressions: + CONTENT_LIST_1 at GLOB-error-CONFIGURE_DEPENDS-modified\.cmake:[0-9]+ \(file\) + CONTENT_LIST_2 at GLOB-error-CONFIGURE_DEPENDS-modified\.cmake:[0-9]+ \(file\)$ diff --git a/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-modified.cmake b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-modified.cmake new file mode 100644 index 0000000..8d74dea --- /dev/null +++ b/Tests/RunCMake/file/GLOB-error-CONFIGURE_DEPENDS-modified.cmake @@ -0,0 +1,21 @@ +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/first") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/second") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/third") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/first/one" "one") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/second/two" "two") +file(GLOB_RECURSE CONTENT_LIST_1 + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/test/*" + ) + +file(GLOB_RECURSE CONTENT_LIST_2 + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/test/*" + ) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/third/three" "three") +file(GLOB_RECURSE CONTENT_LIST_3 + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/test/*" + ) diff --git a/Tests/RunCMake/file/GLOB-noexp-CONFIGURE_DEPENDS-result.txt b/Tests/RunCMake/file/GLOB-noexp-CONFIGURE_DEPENDS-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/file/GLOB-noexp-CONFIGURE_DEPENDS-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file/GLOB-noexp-CONFIGURE_DEPENDS-stderr.txt b/Tests/RunCMake/file/GLOB-noexp-CONFIGURE_DEPENDS-stderr.txt new file mode 100644 index 0000000..01d204f --- /dev/null +++ b/Tests/RunCMake/file/GLOB-noexp-CONFIGURE_DEPENDS-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at GLOB-noexp-CONFIGURE_DEPENDS\.cmake:[0-9]+ \(file\): + file GLOB requires a glob expression after CONFIGURE_DEPENDS\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/file/GLOB-noexp-CONFIGURE_DEPENDS.cmake b/Tests/RunCMake/file/GLOB-noexp-CONFIGURE_DEPENDS.cmake new file mode 100644 index 0000000..9dc0f03 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-noexp-CONFIGURE_DEPENDS.cmake @@ -0,0 +1 @@ +file(GLOB CONTENT_LIST CONFIGURE_DEPENDS) diff --git a/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late-stderr.txt b/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late-stderr.txt new file mode 100644 index 0000000..af722a4 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late-stderr.txt @@ -0,0 +1,6 @@ +^CMake Warning \(dev\) at GLOB-warn-CONFIGURE_DEPENDS-late\.cmake:[0-9]+ \(file\): + CONFIGURE_DEPENDS flag was given after a glob expression was already + evaluated\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late.cmake b/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late.cmake new file mode 100644 index 0000000..0b69552 --- /dev/null +++ b/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late.cmake @@ -0,0 +1,11 @@ +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/first") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/second") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/first/one" "Hi, Mom!") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/second/two" "Love you!") + +file(GLOB CONTENT_LIST + "${CMAKE_CURRENT_BINARY_DIR}/test/first/*" + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/test/second/*" + ) diff --git a/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version-stderr.txt b/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version-stderr.txt new file mode 100644 index 0000000..8d22332 --- /dev/null +++ b/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version-stderr.txt @@ -0,0 +1,13 @@ +^CMake Warning \(dev\): + The detected version of Ninja: + + .* + + is less than the version of Ninja required by CMake for adding restat + dependencies to the build\.ninja manifest regeneration target: + + 1\.8 + + Any pre-check scripts, such as those generated for file\(GLOB + CONFIGURE_DEPENDS\), will not be run by Ninja\. +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version.cmake b/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version.cmake new file mode 100644 index 0000000..8e80895 --- /dev/null +++ b/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version.cmake @@ -0,0 +1,6 @@ +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test/first") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/first/one" "one") +file(GLOB_RECURSE CONTENT_LIST + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/test/*" + ) diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index 4aab32d..342606b 100644 --- a/Tests/RunCMake/file/RunCMakeTest.cmake +++ b/Tests/RunCMake/file/RunCMakeTest.cmake @@ -43,10 +43,79 @@ run_cmake(GLOB-error-FOLLOW_SYMLINKS) run_cmake(GLOB-error-LIST_DIRECTORIES-not-boolean) run_cmake(GLOB-error-LIST_DIRECTORIES-no-arg) run_cmake(GLOB-error-RELATIVE-no-arg) +run_cmake(GLOB-error-CONFIGURE_DEPENDS-modified) +run_cmake(GLOB-noexp-CONFIGURE_DEPENDS) run_cmake(GLOB-noexp-LIST_DIRECTORIES) run_cmake(GLOB-noexp-RELATIVE) +run_cmake_command(GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE ${CMAKE_COMMAND} -P + ${RunCMake_SOURCE_DIR}/GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE.cmake) if(NOT WIN32 OR CYGWIN) run_cmake(GLOB_RECURSE-cyclic-recursion) run_cmake(INSTALL-SYMLINK) endif() + +if(RunCMake_GENERATOR STREQUAL "Ninja") + # Detect ninja version so we know what tests can be supported. + execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" --version + OUTPUT_VARIABLE ninja_out + ERROR_VARIABLE ninja_out + RESULT_VARIABLE ninja_res + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(ninja_res EQUAL 0 AND "x${ninja_out}" MATCHES "^x[0-9]+\\.[0-9]+") + set(ninja_version "${ninja_out}") + message(STATUS "ninja version: ${ninja_version}") + else() + message(FATAL_ERROR "'ninja --version' reported:\n${ninja_out}") + endif() + + if("${ninja_version}" VERSION_LESS 1.8) + message(STATUS "Ninja is too old for GLOB CONFIGURE_DEPENDS; expect a warning.") + endif() +endif() + +if(RunCMake_GENERATOR STREQUAL "Ninja" AND "${ninja_version}" VERSION_LESS 1.8) + run_cmake(GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version) +else() + run_cmake(GLOB-warn-CONFIGURE_DEPENDS-late) + + # Use a single build tree for a few tests without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GLOB-CONFIGURE_DEPENDS-RerunCMake-build) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_DEFAULT_stderr ".*") + if(RunCMake_GENERATOR STREQUAL "Borland Makefiles" OR + RunCMake_GENERATOR STREQUAL "Watcom WMake") + set(fs_delay 3) + else() + set(fs_delay 1.125) + endif() + + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/test") + set(tf_1 "${RunCMake_TEST_BINARY_DIR}/test/1.txt") + file(WRITE "${tf_1}" "1") + + message(STATUS "GLOB-RerunCMake: first configuration...") + run_cmake(GLOB-CONFIGURE_DEPENDS-RerunCMake) + run_cmake_command(GLOB-CONFIGURE_DEPENDS-RerunCMake-build ${CMAKE_COMMAND} --build .) + + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${fs_delay}) + message(STATUS "GLOB-CONFIGURE_DEPENDS-RerunCMake: add another file...") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/test/sub") + set(tf_2 "${RunCMake_TEST_BINARY_DIR}/test/sub/2.txt") + file(WRITE "${tf_2}" "2") + run_cmake_command(GLOB-CONFIGURE_DEPENDS-RerunCMake-rebuild_first ${CMAKE_COMMAND} --build .) + run_cmake_command(GLOB-CONFIGURE_DEPENDS-RerunCMake-nowork ${CMAKE_COMMAND} --build .) + + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${fs_delay}) + message(STATUS "GLOB-CONFIGURE_DEPENDS-RerunCMake: remove first test file...") + file(REMOVE "${RunCMake_TEST_BINARY_DIR}/test/1.txt") + run_cmake_command(GLOB-CONFIGURE_DEPENDS-RerunCMake-rebuild_second ${CMAKE_COMMAND} --build .) + run_cmake_command(GLOB-CONFIGURE_DEPENDS-RerunCMake-nowork ${CMAKE_COMMAND} --build .) + + unset(RunCMake_TEST_BINARY_DIR) + unset(RunCMake_TEST_NO_CLEAN) + unset(RunCMake_DEFAULT_stderr) +endif() @@ -332,6 +332,7 @@ CMAKE_CXX_SOURCES="\ cmGlobalCommonGenerator \ cmGlobalGenerator \ cmGlobalUnixMakefileGenerator3 \ + cmGlobVerificationManager \ cmHexFileConverter \ cmIfCommand \ cmIncludeCommand \ |