Przeglądaj źródła

Add glob verify support to XCode, VS, Ninja, and Makefile generators

Shane Parris 7 lat temu
rodzic
commit
3f4b81f540

+ 71 - 6
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",

+ 3 - 0
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:

+ 7 - 0
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.

+ 30 - 8
Source/cmGlobalVisualStudio8Generator.cxx

@@ -209,18 +209,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,
@@ -266,6 +266,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 =
@@ -273,8 +297,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";

+ 22 - 2
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(

+ 10 - 0
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

+ 15 - 1
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";
     {

+ 29 - 9
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.

+ 40 - 28
Source/cmake.cxx

@@ -2465,37 +2465,49 @@ int cmake::Build(const std::string& dir, const std::string& target,
     cmGlobalVisualStudio8Generator::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