Browse Source

ENH: Added build system integrity check to cmLocalUnixMakefileGenerator2. This now uses a special --check-build-system flag to cmake which replaces --check-rerun. Integrity of dependencies is also checked during generation.

Brad King 21 years ago
parent
commit
81bbae1fb2
4 changed files with 261 additions and 54 deletions
  1. 219 39
      Source/cmLocalUnixMakefileGenerator2.cxx
  2. 11 0
      Source/cmLocalUnixMakefileGenerator2.h
  3. 24 13
      Source/cmake.cxx
  4. 7 2
      Source/cmake.h

+ 219 - 39
Source/cmLocalUnixMakefileGenerator2.cxx

@@ -149,6 +149,19 @@ void cmLocalUnixMakefileGenerator2::GenerateCMakefile()
     << "# The corresponding makefile is:\n"
     << "SET(CMAKE_MAKEFILE_OUTPUTS\n"
     << "  \"" << makefileName.c_str() << "\"\n"
+    << "  )\n\n";
+
+  // Set the set of files to check for dependency integrity.
+  cmakefileStream
+    << "# The set of files whose dependency integrity should be checked:\n"
+    << "SET(CMAKE_DEPENDS_CHECK\n";
+  for(std::set<cmStdString>::const_iterator i = m_CheckDependFiles.begin();
+      i != m_CheckDependFiles.end(); ++i)
+    {
+    cmakefileStream
+      << "  \"" << i->c_str() << "\"\n";
+    }
+  cmakefileStream
     << "  )\n";
 }
 
@@ -180,20 +193,11 @@ cmLocalUnixMakefileGenerator2
       }
     }
 
-  // If there is no dependencies file, create an empty one.
-  std::string depFileName = dir;
-  depFileName += "/";
-  depFileName += target.GetName();
-  depFileName += ".depends.make";
-  std::string depFileNameFull = this->ConvertToFullPath(depFileName);
-  if(!cmSystemTools::FileExists(depFileNameFull.c_str()))
-    {
-    std::ofstream depFileStream(depFileNameFull.c_str());
-    this->WriteDisclaimer(depFileStream);
-    depFileStream
-      << "# Empty dependencies file for target " << target.GetName() << ".\n"
-      << "# This may be replaced when dependencies are built.\n";
-    }
+  // Generate the build-time dependencies file for this target.
+  std::string depBase = dir;
+  depBase += "/";
+  depBase += target.GetName();
+  std::string depMakeFile = this->GenerateDependsMakeFile(depBase.c_str());
 
   // Open the rule file.  This should be copy-if-different because the
   // rules may depend on this file itself.
@@ -218,7 +222,7 @@ cmLocalUnixMakefileGenerator2
   ruleFileStream
     << "# Include any dependencies generated for this rule.\n"
     << m_IncludeDirective << " "
-    << this->ConvertToOutputForExisting(depFileName.c_str()).c_str()
+    << this->ConvertToOutputForExisting(depMakeFile.c_str()).c_str()
     << "\n\n";
 
   // Include the rule file for each object.
@@ -243,7 +247,7 @@ cmLocalUnixMakefileGenerator2
   // Write the dependency generation rule.
   {
   std::vector<std::string> depends;
-  std::vector<std::string> commands;
+  std::vector<std::string> no_commands;
   std::string depEcho = "Building dependencies for ";
   depEcho += target.GetName();
   depEcho += "...";
@@ -258,7 +262,7 @@ cmLocalUnixMakefileGenerator2
     }
   depends.push_back(ruleFileName);
   this->WriteMakeRule(ruleFileStream, 0, depEcho.c_str(),
-                      depTarget.c_str(), depends, commands);
+                      depTarget.c_str(), depends, no_commands);
   }
 
   // Write the build rule.
@@ -303,23 +307,16 @@ cmLocalUnixMakefileGenerator2
   // Get the full path name of the object file.
   std::string obj = this->GetObjectFileName(target, source);
 
+  // The object file should be checked for dependency integrity.
+  m_CheckDependFiles.insert(obj);
+
   // Create the directory containing the object file.  This may be a
   // subdirectory under the target's directory.
   std::string dir = cmSystemTools::GetFilenamePath(obj.c_str());
   cmSystemTools::MakeDirectory(this->ConvertToFullPath(dir).c_str());
 
-  // If there is no dependencies file, create an empty one.
-  std::string depFileName = obj;
-  depFileName += ".depends.make";
-  std::string depFileNameFull = this->ConvertToFullPath(depFileName);
-  if(!cmSystemTools::FileExists(depFileNameFull.c_str()))
-    {
-    std::ofstream depFileStream(depFileNameFull.c_str());
-    this->WriteDisclaimer(depFileStream);
-    depFileStream
-      << "# Empty dependencies file for object file " << obj.c_str() << ".\n"
-      << "# This may be replaced when dependencies are built.\n";
-    }
+  // Generate the build-time dependencies file for this object file.
+  std::string depMakeFile = this->GenerateDependsMakeFile(obj.c_str());
 
   // Open the rule file for writing.  This should be copy-if-different
   // because the rules may depend on this file itself.
@@ -342,7 +339,7 @@ cmLocalUnixMakefileGenerator2
   ruleFileStream
     << "# Include any dependencies generated for this rule.\n"
     << m_IncludeDirective << " "
-    << this->ConvertToOutputForExisting(depFileName.c_str()).c_str()
+    << this->ConvertToOutputForExisting(depMakeFile.c_str()).c_str()
     << "\n\n";
 
   // Create the list of dependencies known at cmake time.  These are
@@ -469,6 +466,33 @@ cmLocalUnixMakefileGenerator2
   }
 }
 
+//----------------------------------------------------------------------------
+std::string
+cmLocalUnixMakefileGenerator2
+::GenerateDependsMakeFile(const char* file)
+{
+  // Check if the build-time dependencies file exists.
+  std::string depMarkFile = file;
+  depMarkFile += ".depends";
+  std::string depMakeFile = depMarkFile;
+  depMakeFile += ".make";
+  std::string depMakeFileFull = this->ConvertToFullPath(depMakeFile);
+  if(cmSystemTools::FileExists(depMakeFileFull.c_str()))
+    {
+    // The build-time dependencies file already exists.  Check it.
+    this->CheckDependencies(m_Makefile->GetStartOutputDirectory(), file);
+    }
+  else
+    {
+    // The build-time dependencies file does not exist.  Create an
+    // empty one.
+    std::string depMarkFileFull = this->ConvertToFullPath(depMarkFile);
+    this->WriteEmptyDependMakeFile(file, depMarkFileFull.c_str(),
+                                   depMakeFileFull.c_str());
+    }
+  return depMakeFile;
+}
+
 //----------------------------------------------------------------------------
 void
 cmLocalUnixMakefileGenerator2
@@ -496,7 +520,7 @@ cmLocalUnixMakefileGenerator2
     m_Makefile->ExpandVariablesInString(replace);
     std::string::size_type lpos = 0;
     std::string::size_type rpos;
-    while((rpos = replace.find(lpos, '\n')) != std::string::npos)
+    while((rpos = replace.find('\n', lpos)) != std::string::npos)
       {
       os << "# " << replace.substr(lpos, rpos-lpos);
       lpos = rpos+1;
@@ -667,7 +691,7 @@ cmLocalUnixMakefileGenerator2
   cmakefileName += "/Makefile2.cmake";
   std::string runRule =
     "@$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)";
-  runRule += " --check-rerun ";
+  runRule += " --check-build-system ";
   runRule += this->ConvertToRelativeOutputPath(cmakefileName.c_str());
 
   // Write the main entry point target.  This must be the VERY first
@@ -676,7 +700,7 @@ cmLocalUnixMakefileGenerator2
   std::vector<std::string> depends;
   std::vector<std::string> commands;
   // Check the build system in this directory.
-  depends.push_back("cmake_check_rerun");
+  depends.push_back("cmake_check_build_system");
 
   // Recursively build dependencies.
   commands.push_back(this->GetRecursiveMakeCall("all.depends"));
@@ -699,8 +723,8 @@ cmLocalUnixMakefileGenerator2
                       postEcho.c_str());
   }
 
-  // Write special "cmake_check_rerun" target to run cmake with the
-  // --check-rerun flag.
+  // Write special "cmake_check_build_system" target to run cmake with
+  // the --check-build-system flag.
   {
   std::vector<std::string> no_depends;
   std::vector<std::string> commands;
@@ -709,7 +733,7 @@ cmLocalUnixMakefileGenerator2
                       "Special rule to run CMake to check the build system "
                       "integrity.",
                       "Checking build system integrity...",
-                      "cmake_check_rerun",
+                      "cmake_check_build_system",
                       no_depends,
                       commands);
   }
@@ -1633,7 +1657,7 @@ cmLocalUnixMakefileGenerator2
       commands.push_back(cmd);
 
       // Check the build system in destination directory.
-      commands.push_back(this->GetRecursiveMakeCall("cmake_check_rerun"));
+      commands.push_back(this->GetRecursiveMakeCall("cmake_check_build_system"));
 
       // Build the targets's dependencies.
       commands.push_back(this->GetRecursiveMakeCall(dep.c_str()));
@@ -1655,7 +1679,7 @@ cmLocalUnixMakefileGenerator2
 
       // Check the build system in destination directory.
       cmd += " && ";
-      cmd += this->GetRecursiveMakeCall("cmake_check_rerun");
+      cmd += this->GetRecursiveMakeCall("cmake_check_build_system");
 
       // Build the targets's dependencies.
       cmd += " && ";
@@ -1736,7 +1760,9 @@ cmLocalUnixMakefileGenerator2ScanDependenciesC(
   std::string line;
   while(cmSystemTools::GetLineFromStream(fin, line))
     {
-    // Match include directives.
+    // Match include directives.  TODO: Support include regex and
+    // ignore regex.  Possibly also support directory-based inclusion
+    // in dependencies.
     if(includeLine.find(line.c_str()))
       {
       // Get the file being included.
@@ -1848,3 +1874,157 @@ cmLocalUnixMakefileGenerator2
 
   return true;
 }
+
+//----------------------------------------------------------------------------
+void
+cmLocalUnixMakefileGenerator2
+::CheckDependencies(const char* depCheck)
+{
+  // Get the list of files to scan.  This is given through the command
+  // line hook cmake file.
+  std::vector<std::string> files;
+  cmSystemTools::ExpandListArgument(depCheck, files);
+
+  // Check each file.  The current working directory is already
+  // correct.
+  for(std::vector<std::string>::iterator f = files.begin();
+      f != files.end(); ++f)
+    {
+    cmLocalUnixMakefileGenerator2::CheckDependencies(".", f->c_str());
+    }
+}
+
+//----------------------------------------------------------------------------
+void
+cmLocalUnixMakefileGenerator2
+::CheckDependencies(const char* dir, const char* file)
+{
+  // Check the dependencies associated with the given file whose path
+  // is specified relative to the given directory.  If any dependency
+  // is missing then dependencies should be regenerated.
+  bool regenerate = false;
+
+  // Construct the names of the mark and make files.
+  std::string depMarkFileFull = dir;
+  depMarkFileFull += "/";
+  depMarkFileFull += file;
+  depMarkFileFull += ".depends";
+  std::string depMakeFileFull = depMarkFileFull;
+  depMakeFileFull += ".make";
+
+  // Open the dependency makefile.
+  std::ifstream fin(depMakeFileFull.c_str());
+  if(fin)
+    {
+    // Parse dependencies.
+    std::string line;
+    std::string depender;
+    std::string dependee;
+    while(cmSystemTools::GetLineFromStream(fin, line))
+      {
+      // Skip empty lines and comments.
+      std::string::size_type pos = line.find_first_not_of(" \t\r\n");
+      if(pos == std::string::npos || line[pos] == '#')
+        {
+        continue;
+        }
+
+      // Strip leading whitespace.
+      if(pos > 0)
+        {
+        line = line.substr(pos);
+        }
+
+      // Skip lines too short to have a dependency.
+      if(line.size() < 2)
+        {
+        continue;
+        }
+
+      // Find the colon on the line.  Skip the first two characters to
+      // avoid finding the colon in a drive letter on Windows.  Ignore
+      // the line if a colon cannot be found.
+      if((pos = line.find(':', 2)) == std::string::npos)
+        {
+        continue;
+        }
+
+      // Split the line into depender and dependee.
+      depender = line.substr(0, pos);
+      dependee = line.substr(pos+1);
+
+      // Strip whitespace from the dependee.
+      if((pos = dependee.find_first_not_of(" \t\r\n")) != std::string::npos &&
+         pos > 0)
+        {
+        dependee = dependee.substr(pos);
+        }
+      if((pos = dependee.find_last_not_of(" \t\r\n")) != std::string::npos)
+        {
+        dependee = dependee.substr(0, pos+1);
+        }
+
+      // Convert dependee to a full path.
+      if(!cmSystemTools::FileIsFullPath(dependee.c_str()))
+        {
+        dependee = cmSystemTools::CollapseFullPath(dependee.c_str(), dir);
+        }
+
+      // If the dependee does not exist, we need to regenerate
+      // dependencies and the depender should be removed.
+      if(!cmSystemTools::FileExists(dependee.c_str()))
+        {
+        // Strip whitespace from the depender.
+        if((pos = depender.find_last_not_of(" \t\r\n")) != std::string::npos)
+          {
+          depender = depender.substr(0, pos+1);
+          }
+
+        // Convert depender to a full path.
+        if(!cmSystemTools::FileIsFullPath(depender.c_str()))
+          {
+          depender = cmSystemTools::CollapseFullPath(depender.c_str(), dir);
+          }
+
+        // Remove the depender.
+        cmSystemTools::RemoveFile(depender.c_str());
+
+        // Mark the need for regeneration.
+        regenerate = true;
+        }
+      }
+    }
+  else
+    {
+    // Could not open the dependencies file.  It needs to be
+    // regenerated.
+    regenerate = true;
+    }
+
+  // If the dependencies file needs to be regenerated, create an empty
+  // one and delete the mark file.
+  if(regenerate)
+    {
+    cmLocalUnixMakefileGenerator2
+      ::WriteEmptyDependMakeFile(file, depMarkFileFull.c_str(),
+                                 depMakeFileFull.c_str());
+    }
+}
+
+//----------------------------------------------------------------------------
+void
+cmLocalUnixMakefileGenerator2
+::WriteEmptyDependMakeFile(const char* file,
+                           const char* depMarkFileFull,
+                           const char* depMakeFileFull)
+{
+  // Remove the dependency mark file to be sure dependencies will be
+  // regenerated.
+  cmSystemTools::RemoveFile(depMarkFileFull);
+
+  // Write an empty dependency file.
+  std::ofstream depFileStream(depMakeFileFull);
+  depFileStream
+    << "# Empty dependencies file for " << file << ".\n"
+    << "# This may be replaced when dependencies are built.\n";
+}

+ 11 - 0
Source/cmLocalUnixMakefileGenerator2.h

@@ -50,6 +50,9 @@ public:
   /** Called from command-line hook to scan dependencies.  */
   static bool ScanDependencies(std::vector<std::string> const& args);
 
+  /** Called from command-line hook to check dependencies.  */
+  static void CheckDependencies(const char* depCheck);
+
 protected:
 
   void GenerateMakefile();
@@ -57,6 +60,7 @@ protected:
   void GenerateTargetRuleFile(const cmTarget& target);
   void GenerateObjectRuleFile(const cmTarget& target,
                               const cmSourceFile& source);
+  std::string GenerateDependsMakeFile(const char* file);
   void WriteMakeRule(std::ostream& os,
                      const char* comment,
                      const char* preEcho,
@@ -112,6 +116,10 @@ protected:
 
   static bool ScanDependenciesC(const char* objFile, const char* srcFile,
                                 std::vector<std::string> const& includes);
+  static void CheckDependencies(const char* dir, const char* file);
+  static void WriteEmptyDependMakeFile(const char* file,
+                                       const char* depMarkFileFull,
+                                       const char* depMakeFileFull);
 private:
   // Map from target name to build directory containing it for
   // jump-and-build targets.
@@ -121,6 +129,9 @@ private:
     std::string m_FilePath;
   };
   std::map<cmStdString, RemoteTarget> m_JumpAndBuild;
+
+  // List the files for which to check dependency integrity.
+  std::set<cmStdString> m_CheckDependFiles;
 };
 
 #endif

+ 24 - 13
Source/cmake.cxx

@@ -25,11 +25,7 @@
 #if defined(CMAKE_BUILD_WITH_CMAKE)
 # include "cmVariableWatch.h"
 # include "cmVersion.h"
-#endif
-
-
-#if defined(CMAKE_BUILD_WITH_CMAKE)
-#include "cmLocalUnixMakefileGenerator2.h"
+# include "cmLocalUnixMakefileGenerator2.h"
 #endif
 
 // only build kdevelop generator on non-windows platforms
@@ -317,9 +313,9 @@ void cmake::SetArgs(const std::vector<std::string>& args)
       cmSystemTools::ConvertToUnixSlashes(path);
       this->SetHomeOutputDirectory(path.c_str());
       }
-    else if((i < args.size()-1) && (arg.find("--check-rerun",0) == 0))
+    else if((i < args.size()-1) && (arg.find("--check-build-system",0) == 0))
       {
-      m_CheckRerun = args[++i];
+      m_CheckBuildSystem = args[++i];
       }
     else if(arg.find("-V",0) == 0)
       {
@@ -1268,8 +1264,8 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure)
   if(m_ScriptMode || !m_Local || !this->CacheVersionMatches() ||
      !cmSystemTools::FileExists(systemFile.c_str()) )
     {
-    // Check whether we should really do a generate.
-    if(!this->CheckRerun())
+    // Check the state of the build system to see if we need to regenerate.
+    if(!this->CheckBuildSystem())
       {
       return 0;
       }
@@ -1572,16 +1568,23 @@ void cmake::UpdateConversionPathTable()
     }
 }
 
-int cmake::CheckRerun()
+//----------------------------------------------------------------------------
+int cmake::CheckBuildSystem()
 {
+  // This method will check the integrity of the build system if the
+  // option was given on the command line.  It reads the given file to
+  // determine whether CMake should rerun.  If it does rerun then the
+  // generation step will check the integrity of dependencies.  If it
+  // does not then we need to check the integrity here.
+
   // If no file is provided for the check, we have to rerun.
-  if(m_CheckRerun.size() == 0)
+  if(m_CheckBuildSystem.size() == 0)
     {
     return 1;
     }
 
   // If the file provided does not exist, we have to rerun.
-  if(!cmSystemTools::FileExists(m_CheckRerun.c_str()))
+  if(!cmSystemTools::FileExists(m_CheckBuildSystem.c_str()))
     {
     return 1;
     }
@@ -1594,7 +1597,7 @@ int cmake::CheckRerun()
   std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator());
   lg->SetGlobalGenerator(&gg);
   cmMakefile* mf = lg->GetMakefile();
-  if(!mf->ReadListFile(0, m_CheckRerun.c_str()) ||
+  if(!mf->ReadListFile(0, m_CheckBuildSystem.c_str()) ||
      cmSystemTools::GetErrorOccuredFlag())
     {
     // There was an error reading the file.  Just rerun.
@@ -1630,6 +1633,14 @@ int cmake::CheckRerun()
       }
     }
 
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  // We do not need to rerun CMake.  Check dependency integrity.
+  if(const char* depCheck = mf->GetDefinition("CMAKE_DEPENDS_CHECK"))
+    {
+    cmLocalUnixMakefileGenerator2::CheckDependencies(depCheck);
+    }
+#endif
+
   // No need to rerun.
   return 0;
 }

+ 7 - 2
Source/cmake.h

@@ -286,7 +286,12 @@ protected:
   
   ///! used by Run
   int LocalGenerate();
-  int CheckRerun();
+
+  /**
+   * Method called to check build system integrity at build time.
+   * Returns 1 if CMake should rerun and 0 otherwise.
+   */
+  int CheckBuildSystem();
 
   /**
    * Generate CMAKE_ROOT and CMAKE_COMMAND cache entries
@@ -307,7 +312,7 @@ private:
   std::string m_CMakeCommand;
   std::string m_CXXEnvironment;
   std::string m_CCEnvironment;
-  std::string m_CheckRerun;
+  std::string m_CheckBuildSystem;
   bool m_DebugTryCompile;
   
   void UpdateConversionPathTable();