Browse Source

ENH: Split dependency scanning and checking into separate cmDepends superclass with language-specific subclasses such as cmDependsC.

Brad King 21 years ago
parent
commit
195cdf172e

+ 5 - 0
Source/CMakeLists.txt

@@ -46,6 +46,11 @@ SET(SRCS
   cmake.h
   cmakewizard.cxx
   cmakewizard.h
+
+  cmDepends.h
+  cmDepends.cxx
+  cmDependsC.h
+  cmDependsC.cxx
   )
 
 

+ 96 - 0
Source/cmDepends.cxx

@@ -0,0 +1,96 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "cmDepends.h"
+
+#include "cmGeneratedFileStream.h"
+#include "cmSystemTools.h"
+
+//----------------------------------------------------------------------------
+cmDepends::cmDepends(const char* dir, const char* targetFile):
+  m_Directory(dir),
+  m_TargetFile(targetFile),
+  m_DependsMakeFile(dir),
+  m_DependsMarkFile(dir)
+{
+  // Construct the path to the make and mark files.  Append
+  // appropriate extensions to their names.
+  m_DependsMakeFile += "/";
+  m_DependsMarkFile += "/";
+  m_DependsMakeFile += m_TargetFile;
+  m_DependsMarkFile += m_TargetFile;
+  m_DependsMakeFile += ".depends.make";
+  m_DependsMarkFile += ".depends";
+}
+
+//----------------------------------------------------------------------------
+cmDepends::~cmDepends()
+{
+}
+
+//----------------------------------------------------------------------------
+void cmDepends::Write()
+{
+  // Try to generate dependencies for the target file.
+  cmGeneratedFileStream fout(m_DependsMakeFile.c_str());
+  fout << "# Dependencies for " << m_TargetFile.c_str() << std::endl;
+  if(this->WriteDependencies(fout) && fout)
+    {
+    // Dependencies were generated.  Touch the mark file.
+    std::ofstream fmark(m_DependsMarkFile.c_str());
+    fmark << "Dependencies updated for " << m_TargetFile.c_str() << std::endl;
+    }
+}
+
+//----------------------------------------------------------------------------
+void cmDepends::Check()
+{
+  // Check whether dependencies must be regenerated.
+  std::ifstream fin(m_DependsMakeFile.c_str());
+  if(!(fin && this->CheckDependencies(fin)))
+    {
+    // Clear all dependencies so they will be regenerated.
+    this->Clear();
+    }
+}
+
+//----------------------------------------------------------------------------
+void cmDepends::Clear()
+{
+  // Remove the dependency mark file to be sure dependencies will be
+  // regenerated.
+  cmSystemTools::RemoveFile(m_DependsMarkFile.c_str());
+
+  // Write an empty dependency file.
+  cmGeneratedFileStream depFileStream(m_DependsMakeFile.c_str());
+  depFileStream
+    << "# Empty dependencies file for " << m_TargetFile.c_str() << ".\n"
+    << "# This may be replaced when dependencies are built." << std::endl;
+}
+
+//----------------------------------------------------------------------------
+const char* cmDepends::GetMakeFileName()
+{
+  // Skip over the directory part of the name.
+  return m_DependsMakeFile.c_str() + m_Directory.length() + 1;
+}
+
+//----------------------------------------------------------------------------
+const char* cmDepends::GetMarkFileName()
+{
+  // Skip over the directory part of the name.
+  return m_DependsMarkFile.c_str() + m_Directory.length() + 1;
+}

+ 82 - 0
Source/cmDepends.h

@@ -0,0 +1,82 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef cmDepends_h
+#define cmDepends_h
+
+#include "cmStandardIncludes.h"
+
+/** \class cmDepends
+ * \brief Dependency scanner superclass.
+ *
+ * This class is responsible for maintaining a .depends.make file in
+ * the build tree corresponding to an object file.  Subclasses help it
+ * maintain dependencies for particular languages.
+ */
+class cmDepends
+{
+public:
+  /** Instances need to know the build directory name and the relative
+      path from the build directory to the target file.  */
+  cmDepends(const char* dir, const char* targetFile);
+
+  /** Virtual destructor to cleanup subclasses properly.  */
+  virtual ~cmDepends();
+
+  /** Write dependencies for the target file.  */
+  void Write();
+
+  /** Check dependencies for the target file.  */
+  void Check();
+
+  /** Clear dependencies for the target file so they will be regenerated.  */
+  void Clear();
+
+  /** Get the name of the dependency make file.  */
+  const char* GetMakeFileName();
+
+  /** Get the name of the dependency mark file.  */
+  const char* GetMarkFileName();
+
+protected:
+
+  // Write dependencies for the target file to the given stream.
+  // Return true for success and false for failure.
+  virtual bool WriteDependencies(std::ostream& os)=0;
+
+  // Check dependencies for the target file in the given stream.
+  // Return false if dependencies must be regenerated and true
+  // otherwise.
+  virtual bool CheckDependencies(std::istream& is)=0;
+
+  // The directory in which the build rule for the target file is executed.
+  std::string m_Directory;
+
+  // The name of the target file for which dependencies are maintained.
+  std::string m_TargetFile;
+
+  // The name of the .depends.make file corresponding to the target.
+  std::string m_DependsMakeFile;
+
+  // The name of the .depends file marking when dependencies were generated.
+  std::string m_DependsMarkFile;
+
+private:
+  cmDepends(cmDepends const&); // Purposely not implemented.
+  void operator=(cmDepends const&); // Purposely not implemented.
+};
+
+#endif

+ 247 - 0
Source/cmDependsC.cxx

@@ -0,0 +1,247 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "cmDependsC.h"
+
+#include "cmSystemTools.h"
+
+//----------------------------------------------------------------------------
+cmDependsC::cmDependsC(const char* dir, const char* targetFile):
+  cmDepends(dir, targetFile),
+  m_SourceFile(),
+  m_IncludePath(0),
+  m_IncludeLineRegex()
+{
+}
+
+//----------------------------------------------------------------------------
+cmDependsC::cmDependsC(const char* dir, const char* targetFile,
+                       const char* sourceFile,
+                       std::vector<std::string> const& includes):
+  cmDepends(dir, targetFile),
+  m_SourceFile(sourceFile),
+  m_IncludePath(&includes),
+  m_IncludeLineRegex("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]")
+{
+}
+
+//----------------------------------------------------------------------------
+cmDependsC::~cmDependsC()
+{
+}
+
+//----------------------------------------------------------------------------
+bool cmDependsC::WriteDependencies(std::ostream& os)
+{
+  // Make sure this is a scanning instance.
+  if(m_SourceFile == "")
+    {
+    cmSystemTools::Error("Cannot scan dependencies without an source file.");
+    return false;
+    }
+  if(!m_IncludePath)
+    {
+    cmSystemTools::Error("Cannot scan dependencies without an include path.");
+    return false;
+    }
+
+  // Walk the dependency graph starting with the source file.
+  bool first = true;
+  m_Unscanned.push(m_SourceFile);
+  m_Encountered.insert(m_SourceFile);
+  std::set<cmStdString> dependencies;
+  std::set<cmStdString> scanned;
+  while(!m_Unscanned.empty())
+    {
+    // Get the next file to scan.
+    std::string fname = m_Unscanned.front();
+    m_Unscanned.pop();
+
+    // If not a full path, find the file in the include path.
+    std::string fullName;
+    if(first || cmSystemTools::FileIsFullPath(fname.c_str()))
+      {
+      fullName = fname;
+      }
+    else
+      {
+      for(std::vector<std::string>::const_iterator i = m_IncludePath->begin();
+          i != m_IncludePath->end(); ++i)
+        {
+        std::string temp = *i;
+        temp += "/";
+        temp += fname;
+        if(cmSystemTools::FileExists(temp.c_str()))
+          {
+          fullName = temp;
+          break;
+          }
+        }
+      }
+
+    // Scan the file if it was found and has not been scanned already.
+    if(fullName.size() && (scanned.find(fullName) == scanned.end()))
+      {
+      // Record scanned files.
+      scanned.insert(fullName);
+
+      // Try to scan the file.  Just leave it out if we cannot find
+      // it.
+      std::ifstream fin(fullName.c_str());
+      if(fin)
+        {
+        // Add this file as a dependency.
+        dependencies.insert(fullName);
+
+        // Scan this file for new dependencies.
+        this->Scan(fin);
+        }
+      }
+
+    first = false;
+    }
+  m_Encountered.clear();
+
+  // Write the dependencies to the output stream.
+  for(std::set<cmStdString>::iterator i=dependencies.begin();
+      i != dependencies.end(); ++i)
+    {
+    os << m_TargetFile.c_str() << ": "
+       << cmSystemTools::ConvertToOutputPath(i->c_str()).c_str()
+       << std::endl;
+    }
+  os << std::endl;
+
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmDependsC::CheckDependencies(std::istream& is)
+{
+  // Parse dependencies from the stream.  If any dependee is missing
+  // or newer than the depender then dependencies should be
+  // regenerated.
+  bool okay = true;
+  std::string line;
+  std::string depender;
+  std::string dependee;
+  while(cmSystemTools::GetLineFromStream(is, 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() < 3)
+      {
+      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(),
+                                                 m_Directory.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(),
+                                                 m_Directory.c_str());
+      }
+
+    // Dependencies must be regenerated if the dependee does not exist
+    // or if the depender exists and is older than the dependee.
+    int result = 0;
+    if(!cmSystemTools::FileExists(dependee.c_str()) ||
+       (cmSystemTools::FileExists(depender.c_str()) &&
+         (!cmSystemTools::FileTimeCompare(depender.c_str(), dependee.c_str(),
+                                          &result) || result < 0)))
+      {
+      // Dependencies must be regenerated.
+      okay = false;
+
+      // Remove the depender to be sure it is rebuilt.
+      cmSystemTools::RemoveFile(depender.c_str());
+      }
+    }
+
+  return okay;
+}
+
+//----------------------------------------------------------------------------
+void cmDependsC::Scan(std::istream& is)
+{
+  // Read one line at a time.
+  std::string line;
+  while(cmSystemTools::GetLineFromStream(is, line))
+    {
+    // Match include directives.  TODO: Support include regex and
+    // ignore regex.  Possibly also support directory-based inclusion
+    // in dependencies.
+    if(m_IncludeLineRegex.find(line.c_str()))
+      {
+      // Get the file being included.
+      std::string includeFile = m_IncludeLineRegex.match(1);
+
+      // Queue the file if it has not yet been encountered.
+      if(m_Encountered.find(includeFile) == m_Encountered.end())
+        {
+        m_Encountered.insert(includeFile);
+        m_Unscanned.push(includeFile);
+        }
+      }
+    }
+}

+ 70 - 0
Source/cmDependsC.h

@@ -0,0 +1,70 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef cmDependsC_h
+#define cmDependsC_h
+
+#include "cmDepends.h"
+#include <cmsys/RegularExpression.hxx>
+#include <queue>
+
+/** \class cmDependsC
+ * \brief Dependency scanner for C and C++ object files.
+ */
+class cmDependsC: public cmDepends
+{
+public:
+  /** Checking instances need to know the build directory name and the
+      relative path from the build directory to the target file.  */
+  cmDependsC(const char* dir, const char* targetFile);
+
+  /** Scanning need to know the build directory name, the relative
+      path from the build directory to the target file, the source
+      file from which to start scanning, and the include file search
+      path.  */
+  cmDependsC(const char* dir, const char* targetFile,
+             const char* sourceFile, std::vector<std::string> const& includes);
+
+  /** Virtual destructor to cleanup subclasses properly.  */
+  virtual ~cmDependsC();
+
+protected:
+  // Implement writing/checking methods required by superclass.
+  virtual bool WriteDependencies(std::ostream& os);
+  virtual bool CheckDependencies(std::istream& is);
+
+  // Method to scan a single file.
+  void Scan(std::istream& is);
+
+  // The source file from which to start scanning.
+  std::string m_SourceFile;
+
+  // The include file search path.
+  std::vector<std::string> const* m_IncludePath;
+
+  // Regular expression to identify C preprocessor include directives.
+  cmsys::RegularExpression m_IncludeLineRegex;
+
+  // Data structures for dependency graph walk.
+  std::set<cmStdString> m_Encountered;
+  std::queue<cmStdString> m_Unscanned;
+
+private:
+  cmDependsC(cmDependsC const&); // Purposely not implemented.
+  void operator=(cmDependsC const&); // Purposely not implemented.
+};
+
+#endif

+ 101 - 315
Source/cmLocalUnixMakefileGenerator2.cxx

@@ -16,17 +16,20 @@
 =========================================================================*/
 #include "cmLocalUnixMakefileGenerator2.h"
 
+#include "cmDepends.h"
+#include "cmDependsC.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
 
+#include <memory> // auto_ptr
 #include <queue>
 
 #include <assert.h>
 
 // Quick-switch for generating old makefiles.
-#if 0
+#if 1
 # define CMLUMG_MAKEFILE_NAME "Makefile"
 #else
 # define CMLUMG_WRITE_OLD_MAKEFILE
@@ -237,16 +240,33 @@ void cmLocalUnixMakefileGenerator2::GenerateCMakefile()
 
   // 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)
+    << "# The set of files whose dependency integrity should be checked:\n";
+  cmakefileStream
+    << "SET(CMAKE_DEPENDS_LANGUAGES\n";
+  for(std::map<cmStdString, IntegrityCheckSet>::const_iterator
+        l = m_CheckDependFiles.begin();
+      l != m_CheckDependFiles.end(); ++l)
     {
     cmakefileStream
-      << "  \"" << this->ConvertToRelativePath(i->c_str()).c_str() << "\"\n";
+      << "  \"" << l->first.c_str() << "\"\n";
     }
   cmakefileStream
     << "  )\n";
+  for(std::map<cmStdString, IntegrityCheckSet>::const_iterator
+        l = m_CheckDependFiles.begin();
+      l != m_CheckDependFiles.end(); ++l)
+    {
+    cmakefileStream
+      << "SET(CMAKE_DEPENDS_CHECK_" << l->first.c_str() << "\n";
+    for(std::set<cmStdString>::const_iterator i = l->second.begin();
+        i != l->second.end(); ++i)
+      {
+      cmakefileStream
+        << "  \"" << this->ConvertToRelativePath(i->c_str()).c_str() << "\"\n";
+      }
+    cmakefileStream
+      << "  )\n";
+    }
 }
 
 //----------------------------------------------------------------------------
@@ -278,7 +298,6 @@ cmLocalUnixMakefileGenerator2
   std::string depBase = dir;
   depBase += "/";
   depBase += target.GetName();
-  std::string depMakeFile = this->GenerateDependsMakeFile(depBase.c_str());
 
   // Construct the rule file name.
   std::string ruleFileName = dir;
@@ -302,13 +321,6 @@ cmLocalUnixMakefileGenerator2
   ruleFileStream
     << "# Rule file for target " << target.GetName() << ".\n\n";
 
-  // Include the dependencies for the target.
-  ruleFileStream
-    << "# Include any dependencies generated for this rule.\n"
-    << m_IncludeDirective << " "
-    << this->ConvertToOutputForExisting(depMakeFile.c_str()).c_str()
-    << "\n\n";
-
   // Include the rule file for each object.
   if(!objects.empty())
     {
@@ -371,19 +383,27 @@ cmLocalUnixMakefileGenerator2
   // Get the full path name of the object file.
   std::string obj = this->GetObjectFileName(target, source);
 
-  // Save this in the target's list of object files.
-  objects.push_back(obj);
-
-  // 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());
 
   // Generate the build-time dependencies file for this object file.
-  std::string depMakeFile = this->GenerateDependsMakeFile(obj.c_str());
+  std::string depMakeFile;
+  std::string depMarkFile;
+  if(!this->GenerateDependsMakeFile(lang, obj.c_str(),
+                                    depMakeFile, depMarkFile))
+    {
+    cmSystemTools::Error("No dependency checker available for language \"",
+                         lang, "\".");
+    return;
+    }
+
+  // Save this in the target's list of object files.
+  objects.push_back(obj);
+
+  // The object file should be checked for dependency integrity.
+  m_CheckDependFiles[lang].insert(obj);
 
   // Open the rule file for writing.  This should be copy-if-different
   // because the rules may depend on this file itself.
@@ -424,8 +444,6 @@ cmLocalUnixMakefileGenerator2
   depends.push_back(ruleFileName);
 
   // Write the dependency generation rule.
-  std::string depTarget = obj;
-  depTarget += ".depends";
   {
   std::string depEcho = "Scanning ";
   depEcho += lang;
@@ -453,7 +471,7 @@ cmLocalUnixMakefileGenerator2
 
   // Write the rule.
   this->WriteMakeRule(ruleFileStream, 0, depEcho.c_str(),
-                      depTarget.c_str(), depends, commands);
+                      depMarkFile.c_str(), depends, commands);
   }
 
   // Write the build rule.
@@ -676,30 +694,28 @@ cmLocalUnixMakefileGenerator2
 }
 
 //----------------------------------------------------------------------------
-std::string
+bool
 cmLocalUnixMakefileGenerator2
-::GenerateDependsMakeFile(const char* file)
+::GenerateDependsMakeFile(const std::string& lang, const char* objFile,
+                          std::string& depMakeFile, std::string& depMarkFile)
 {
-  // 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
+  // Construct a checker for the given language.
+  std::auto_ptr<cmDepends>
+    checker(this->GetDependsChecker(lang,
+                                    m_Makefile->GetStartOutputDirectory(),
+                                    objFile));
+  if(checker.get())
     {
-    // 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());
+    // Save the make and mark file names.
+    depMakeFile = checker->GetMakeFileName();
+    depMarkFile = checker->GetMarkFileName();
+
+    // Check the dependencies.
+    checker->Check();
+
+    return true;
     }
-  return depMakeFile;
+  return false;
 }
 
 //----------------------------------------------------------------------------
@@ -2686,6 +2702,19 @@ cmLocalUnixMakefileGenerator2
     }
 }
 
+//----------------------------------------------------------------------------
+cmDepends*
+cmLocalUnixMakefileGenerator2::GetDependsChecker(const std::string& lang,
+                                                 const char* dir,
+                                                 const char* objFile)
+{
+  if(lang == "C" || lang == "CXX" || lang == "RC")
+    {
+    return new cmDependsC(dir, objFile);
+    }
+  return 0;
+}
+
 //----------------------------------------------------------------------------
 bool
 cmLocalUnixMakefileGenerator2
@@ -2718,292 +2747,49 @@ cmLocalUnixMakefileGenerator2
   if(lang == "C" || lang == "CXX" || lang == "RC")
     {
     // TODO: Handle RC (resource files) dependencies correctly.
-    return cmLocalUnixMakefileGenerator2::ScanDependenciesC(objFile, srcFile,
-                                                            includes);
+    cmDependsC scanner(".", objFile, srcFile, includes);
+    scanner.Write();
+    return true;
     }
   return false;
 }
 
 //----------------------------------------------------------------------------
-void
-cmLocalUnixMakefileGenerator2ScanDependenciesC(
-  std::ifstream& fin,
-  std::set<cmStdString>& encountered,
-  std::queue<cmStdString>& unscanned)
+void cmLocalUnixMakefileGenerator2::CheckDependencies(cmMakefile* mf)
 {
-  // Regular expression to identify C preprocessor include directives.
-  cmsys::RegularExpression
-    includeLine("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]");
-
-  // Read one line at a time.
-  std::string line;
-  while(cmSystemTools::GetLineFromStream(fin, line))
-    {
-    // 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.
-      std::string includeFile = includeLine.match(1);
-
-      // Queue the file if it has not yet been encountered.
-      if(encountered.find(includeFile) == encountered.end())
-        {
-        encountered.insert(includeFile);
-        unscanned.push(includeFile);
-        }
-      }
-    }
-}
-
-//----------------------------------------------------------------------------
-bool
-cmLocalUnixMakefileGenerator2
-::ScanDependenciesC(const char* objFile, const char* srcFile,
-                    std::vector<std::string> const& includes)
-{
-  // Walk the dependency graph starting with the source file.
-  std::set<cmStdString> dependencies;
-  std::set<cmStdString> encountered;
-  std::set<cmStdString> scanned;
-  std::queue<cmStdString> unscanned;
-  unscanned.push(srcFile);
-  encountered.insert(srcFile);
-  while(!unscanned.empty())
-    {
-    // Get the next file to scan.
-    std::string fname = unscanned.front();
-    unscanned.pop();
-
-    // If not a full path, find the file in the include path.
-    std::string fullName;
-    if(cmSystemTools::FileIsFullPath(fname.c_str()))
-      {
-      fullName = fname;
-      }
-    else
-      {
-      for(std::vector<std::string>::const_iterator i = includes.begin();
-          i != includes.end(); ++i)
-        {
-        std::string temp = *i;
-        temp += "/";
-        temp += fname;
-        if(cmSystemTools::FileExists(temp.c_str()))
-          {
-          fullName = temp;
-          break;
-          }
-        }
-      }
-
-    // Scan the file if it was found and has not been scanned already.
-    if(fullName.size() && (scanned.find(fullName) == scanned.end()))
-      {
-      // Record scanned files.
-      scanned.insert(fullName);
-
-      // Try to scan the file.  Just leave it out if we cannot find
-      // it.
-      std::ifstream fin(fullName.c_str());
-      if(fin)
-        {
-        // Add this file as a dependency.
-        dependencies.insert(fullName);
-
-        // Scan this file for new dependencies.
-        cmLocalUnixMakefileGenerator2ScanDependenciesC(fin, encountered,
-                                                       unscanned);
-        }
-      }
-    }
-
-  // Write the dependencies to the output file.
-  std::string depMarkFile = objFile;
-  std::string depMakeFile = objFile;
-  depMarkFile += ".depends";
-  depMakeFile += ".depends.make";
-  cmGeneratedFileStream fout(depMakeFile.c_str());
-  fout << "# Dependencies for " << objFile << std::endl;
-  for(std::set<cmStdString>::iterator i=dependencies.begin();
-      i != dependencies.end(); ++i)
+  // Get the list of languages that may have sources to check.
+  const char* langDef = mf->GetDefinition("CMAKE_DEPENDS_LANGUAGES");
+  if(!langDef)
     {
-    fout << objFile << ": "
-         << cmSystemTools::ConvertToOutputPath(i->c_str()).c_str()
-         << std::endl;
-    }
-  fout << std::endl;
-  fout << "# Dependencies for " << depMarkFile.c_str() << std::endl;
-  for(std::set<cmStdString>::iterator i=dependencies.begin();
-      i != dependencies.end(); ++i)
-    {
-    fout << depMarkFile.c_str() << ": "
-         << cmSystemTools::ConvertToOutputPath(i->c_str()).c_str()
-         << std::endl;
+    return;
     }
+  std::vector<std::string> languages;
+  cmSystemTools::ExpandListArgument(langDef, languages);
 
-  // If we could write the dependencies, touch the corresponding
-  // depends file to mark dependencies up to date.
-  if(fout)
+  // For each language get the set of files to check.
+  for(std::vector<std::string>::iterator l = languages.begin();
+      l != languages.end(); ++l)
     {
-    std::ofstream fmark(depMarkFile.c_str());
-    fmark << "Dependencies updated for " << objFile << "\n";
-    }
-
-  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))
+    std::string depCheck = "CMAKE_DEPENDS_CHECK_";
+    depCheck += *l;
+    if(const char* fileDef = mf->GetDefinition(depCheck.c_str()))
       {
-      // 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() < 3)
+      // Check each file.  The current working directory is already
+      // correct.
+      std::vector<std::string> files;
+      cmSystemTools::ExpandListArgument(fileDef, files);
+      for(std::vector<std::string>::iterator f = files.begin();
+          f != files.end(); ++f)
         {
-        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)
+        // Construct a checker for the given language.
+        std::auto_ptr<cmDepends>
+          checker(cmLocalUnixMakefileGenerator2
+                  ::GetDependsChecker(*l, ".", f->c_str()));
+        if(checker.get())
           {
-          depender = depender.substr(0, pos+1);
+          checker->Check();
           }
-
-        // 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.
-  cmGeneratedFileStream depFileStream(depMakeFileFull);
-  depFileStream
-    << "# Empty dependencies file for " << file << ".\n"
-    << "# This may be replaced when dependencies are built.\n";
 }

+ 15 - 10
Source/cmLocalUnixMakefileGenerator2.h

@@ -21,6 +21,7 @@
 
 class cmCustomCommand;
 class cmDependInformation;
+class cmDepends;
 class cmMakeDepend;
 class cmTarget;
 class cmSourceFile;
@@ -57,7 +58,7 @@ public:
   static bool ScanDependencies(std::vector<std::string> const& args);
 
   /** Called from command-line hook to check dependencies.  */
-  static void CheckDependencies(const char* depCheck);
+  static void CheckDependencies(cmMakefile* mf);
 
 protected:
 
@@ -69,7 +70,10 @@ protected:
                               std::vector<std::string>& objects);
   void GenerateCustomRuleFile(const cmCustomCommand& cc);
   void GenerateUtilityRuleFile(const cmTarget& target);
-  std::string GenerateDependsMakeFile(const char* file);
+  bool GenerateDependsMakeFile(const std::string& lang,
+                               const char* objFile,
+                               std::string& depMakeFile,
+                               std::string& depMarkFile);
   void WriteMakeRule(std::ostream& os,
                      const char* comment,
                      const char* preEcho,
@@ -163,12 +167,10 @@ protected:
   std::string GetRecursiveMakeCall(const char* tgt);
   void WriteJumpAndBuildRules(std::ostream& makefileStream);
 
-  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);
+  static cmDepends* GetDependsChecker(const std::string& lang,
+                                      const char* dir,
+                                      const char* objFile);
+
 private:
   // Map from target name to build directory containing it for
   // jump-and-build targets.
@@ -179,8 +181,11 @@ private:
   };
   std::map<cmStdString, RemoteTarget> m_JumpAndBuild;
 
-  // List the files for which to check dependency integrity.
-  std::set<cmStdString> m_CheckDependFiles;
+  // List the files for which to check dependency integrity.  Each
+  // language has its own list because integrity may be checked
+  // differently.
+  struct IntegrityCheckSet: public std::set<cmStdString> {};
+  std::map<cmStdString, IntegrityCheckSet> m_CheckDependFiles;
 
   // Command used when a rule has no dependencies or commands.
   std::vector<std::string> m_EmptyCommands;

+ 1 - 4
Source/cmake.cxx

@@ -1635,10 +1635,7 @@ int cmake::CheckBuildSystem()
 
 #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);
-    }
+  cmLocalUnixMakefileGenerator2::CheckDependencies(mf);
 #endif
 
   // No need to rerun.