From c564cde3d099571c4ab7a9b779277a658e8cb432 Mon Sep 17 00:00:00 2001
From: Michael Wild <themiwi@users.sourceforge.net>
Date: Sat, 12 Dec 2009 21:23:28 +0100
Subject: [PATCH] FIX: Preserve implicit dependencies and collect all dependencies into a single entry

Signed-off-by: Michael Wild <themiwi@users.sourceforge.net>
---
 Source/cmAddCustomCommandCommand.h                 |   13 +-
 Source/cmDepends.cxx                               |   19 +-
 Source/cmDepends.h                                 |    2 +-
 Source/cmDependsC.cxx                              |  291 ++++++++++----------
 Source/cmDependsC.h                                |    4 +-
 Source/cmLocalUnixMakefileGenerator3.cxx           |   10 +-
 Source/cmLocalUnixMakefileGenerator3.h             |    2 +-
 Tests/BuildDepends/Project/CMakeLists.txt          |    3 +-
 Tests/BuildDepends/Project/dep_custom1.cxx         |    1 +
 .../Project/{dep_custom.cxx => dep_custom2.cxx}    |    0
 10 files changed, 182 insertions(+), 163 deletions(-)
 create mode 100644 Tests/BuildDepends/Project/dep_custom1.cxx
 rename Tests/BuildDepends/Project/{dep_custom.cxx => dep_custom2.cxx} (100%)

diff --git a/Source/cmAddCustomCommandCommand.h b/Source/cmAddCustomCommandCommand.h
index c67caa5..bcd3024 100644
--- a/Source/cmAddCustomCommandCommand.h
+++ b/Source/cmAddCustomCommandCommand.h
@@ -67,7 +67,7 @@ public:
       "                     [COMMAND command2 [ARGS] [args2...] ...]\n"
       "                     [MAIN_DEPENDENCY depend]\n"
       "                     [DEPENDS [depends...]]\n"
-      "                     [IMPLICIT_DEPENDS <lang1> depend1 ...]\n"
+      "                     [IMPLICIT_DEPENDS <lang1> depend1 [<lang2> depend2] ...]\n"
       "                     [WORKING_DIRECTORY dir]\n"
       "                     [COMMENT comment] [VERBATIM] [APPEND])\n"
       "This defines a command to generate specified OUTPUT file(s).  "
@@ -135,11 +135,12 @@ public:
       "dependencies of an input file.  The language given specifies the "
       "programming language whose corresponding dependency scanner should "
       "be used.  Currently only C and CXX language scanners are supported. "
-      "Dependencies discovered from the scanning are added to those of "
-      "the custom command at build time.  Note that the IMPLICIT_DEPENDS "
-      "option is currently supported only for Makefile generators and "
-      "will be ignored by other generators."
-      "\n"
+      "The language has to be specified for every file listed in the "
+      "IMPLICIT_DEPENDS list. Dependencies discovered from the scanning are "
+      "added to those of the custom command at build time. Note that the "
+      "IMPLICIT_DEPENDS option is currently supported only for Makefile "
+      "generators and will be ignored by other generators.\n"
+
       "If COMMAND specifies an executable target (created by "
       "ADD_EXECUTABLE) it will automatically be replaced by the location "
       "of the executable created at build time.  Additionally a "
diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx
index 69ff858..f72932e 100644
--- a/Source/cmDepends.cxx
+++ b/Source/cmDepends.cxx
@@ -50,6 +50,7 @@ bool cmDepends::Write(std::ostream &makeDepends,
   std::vector<std::string> pairs;
   cmSystemTools::ExpandListArgument(srcStr, pairs);
 
+  std::map<std::string, std::set<std::string> > dependencies;
   for(std::vector<std::string>::iterator si = pairs.begin();
       si != pairs.end();)
     {
@@ -62,9 +63,16 @@ bool cmDepends::Write(std::ostream &makeDepends,
     obj = this->LocalGenerator->Convert(obj.c_str(),
                                         cmLocalGenerator::HOME_OUTPUT,
                                         cmLocalGenerator::MAKEFILE);
+    dependencies[obj].insert(src);
+    }
+  std::map<std::string, std::set<std::string> >::const_iterator
+    it = dependencies.begin(),
+    e  = dependencies.end();
+  for(; it != e; ++it)
+    {
 
     // Write the dependencies for this pair.
-    if(!this->WriteDependencies(src.c_str(), obj.c_str(),
+    if(!this->WriteDependencies(it->second, it->first,
                                 makeDepends, internalDepends))
       {
       return false;
@@ -134,7 +142,7 @@ void cmDepends::Clear(const char *file)
 }
 
 //----------------------------------------------------------------------------
-bool cmDepends::WriteDependencies(const char*, const char*,
+bool cmDepends::WriteDependencies(const std::set<std::string>&, const std::string&,
                                   std::ostream&, std::ostream&)
 {
   // This should be implemented by the subclass.
@@ -173,8 +181,11 @@ bool cmDepends::CheckDependencies(std::istream& internalDepends,
       // kdelibs/khtml this reduces the number of calls from 184k down to 92k,
       // or the time for cmake -E cmake_depends from 0.3 s down to 0.21 s.
       dependerExists = cmSystemTools::FileExists(this->Depender);
-      DependencyVector tmp;
-      validDeps[this->Depender] = tmp;
+      // If we erase validDeps[this->Depender] by overwriting it with an empty
+      // vector, we lose dependencies for dependers that have multiple entries.
+      //
+      //DependencyVector tmp;
+      //validDeps[this->Depender] = tmp;
       currentDependencies = &validDeps[this->Depender];
       continue;
       }
diff --git a/Source/cmDepends.h b/Source/cmDepends.h
index 087da64..0104088 100644
--- a/Source/cmDepends.h
+++ b/Source/cmDepends.h
@@ -76,7 +76,7 @@ protected:
 
   // Write dependencies for the target file to the given stream.
   // Return true for success and false for failure.
-  virtual bool WriteDependencies(const char *src, const char* obj,
+  virtual bool WriteDependencies(const std::set<std::string>&, const std::string&,
     std::ostream& makeDepends, std::ostream& internalDepends);
 
   // Check dependencies for the target file in the given stream.
diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx
index 942cb3f..c3b1b3c 100644
--- a/Source/cmDependsC.cxx
+++ b/Source/cmDependsC.cxx
@@ -98,187 +98,189 @@ cmDependsC::~cmDependsC()
 }
 
 //----------------------------------------------------------------------------
-bool cmDependsC::WriteDependencies(const char *src, const char *obj,
+bool cmDependsC::WriteDependencies(
+  const std::set<std::string>& sources, const std::string& obj,
   std::ostream& makeDepends, std::ostream& internalDepends)
 {
-  // Make sure this is a scanning instance.
-  if(!src || src[0] == '\0')
+  std::set<std::string> dependencies;
+  bool okay = true;
+  for(std::set<std::string>::const_iterator it = sources.begin();
+      it != sources.end(); ++it)
     {
-    cmSystemTools::Error("Cannot scan dependencies without a source file.");
-    return false;
-    }
-  if(!obj || obj[0] == '\0')
-    {
-    cmSystemTools::Error("Cannot scan dependencies without an object file.");
-    return false;
-    }
-
-  if (this->ValidDeps != 0)
-    {
-    std::map<std::string, DependencyVector>::const_iterator tmpIt =
-                                                    this->ValidDeps->find(obj);
-    if (tmpIt!= this->ValidDeps->end())
+    bool done = false;
+    const std::string& src = *it;
+    // Make sure this is a scanning instance.
+    if(src.empty())
       {
-      // Write the dependencies to the output stream.  Makefile rules
-      // written by the original local generator for this directory
-      // convert the dependencies to paths relative to the home output
-      // directory.  We must do the same here.
-      internalDepends << obj << std::endl;
-      for(DependencyVector::const_iterator i=tmpIt->second.begin();
-         i != tmpIt->second.end(); ++i)
-        {
-        makeDepends << obj << ": " <<
-           this->LocalGenerator->Convert(i->c_str(),
-                                         cmLocalGenerator::HOME_OUTPUT,
-                                         cmLocalGenerator::MAKEFILE)
-           << std::endl;
-        internalDepends << " " << i->c_str() << std::endl;
-        }
-      makeDepends << std::endl;
-      return true;
+      cmSystemTools::Error("Cannot scan dependencies without a source file.");
+      return false;
+      }
+    if(obj.empty())
+      {
+      cmSystemTools::Error("Cannot scan dependencies without an object file.");
+      return false;
       }
-    }
 
-  // Walk the dependency graph starting with the source file.
-  bool first = true;
-  UnscannedEntry root;
-  root.FileName = src;
-  this->Unscanned.push(root);
-  this->Encountered.clear();
-  this->Encountered.insert(src);
-  std::set<cmStdString> dependencies;
-  std::set<cmStdString> scanned;
-
-  // Use reserve to allocate enough memory for tempPathStr
-  // so that during the loops no memory is allocated or freed
-  std::string tempPathStr;
-  tempPathStr.reserve(4*1024);
-
-  while(!this->Unscanned.empty())
-    {
-    // Get the next file to scan.
-    UnscannedEntry current = this->Unscanned.front();
-    this->Unscanned.pop();
+    dependencies.insert(src);
 
-    // If not a full path, find the file in the include path.
-    std::string fullName;
-    if(first || cmSystemTools::FileIsFullPath(current.FileName.c_str()))
+    if (this->ValidDeps != 0)
       {
-      if(cmSystemTools::FileExists(current.FileName.c_str(), true))
+      std::map<std::string, DependencyVector>::const_iterator tmpIt =
+                                                      this->ValidDeps->find(obj);
+      if (tmpIt!= this->ValidDeps->end())
         {
-        fullName = current.FileName;
+        for(DependencyVector::const_iterator i=tmpIt->second.begin();
+           i != tmpIt->second.end(); ++i)
+          {
+            dependencies.insert(*i);
+            done = true;
+          }
         }
       }
-    else if(!current.QuotedLocation.empty() &&
-            cmSystemTools::FileExists(current.QuotedLocation.c_str(), true))
-      {
-      // The include statement producing this entry was a double-quote
-      // include and the included file is present in the directory of
-      // the source containing the include statement.
-      fullName = current.QuotedLocation;
-      }
-    else
+
+    if(!done)
       {
-      std::map<cmStdString, cmStdString>::iterator
-        headerLocationIt=this->HeaderLocationCache.find(current.FileName);
-      if (headerLocationIt!=this->HeaderLocationCache.end())
+      // Walk the dependency graph starting with the source file.
+      bool first = true;
+      UnscannedEntry root;
+      root.FileName = src;
+      this->Unscanned.push(root);
+      this->Encountered.clear();
+      this->Encountered.insert(src);
+      std::set<cmStdString> scanned;
+
+      // Use reserve to allocate enough memory for tempPathStr
+      // so that during the loops no memory is allocated or freed
+      std::string tempPathStr;
+      tempPathStr.reserve(4*1024);
+
+      while(!this->Unscanned.empty())
         {
-        fullName=headerLocationIt->second;
-        }
-      else for(std::vector<std::string>::const_iterator i =
-            this->IncludePath.begin(); i != this->IncludePath.end(); ++i)
-        {
-        // Construct the name of the file as if it were in the current
-        // include directory.  Avoid using a leading "./".
+        // Get the next file to scan.
+        UnscannedEntry current = this->Unscanned.front();
+        this->Unscanned.pop();
 
-        tempPathStr = "";
-        if((*i) == ".")
+        // If not a full path, find the file in the include path.
+        std::string fullName;
+        if(first || cmSystemTools::FileIsFullPath(current.FileName.c_str()))
+          {
+          if(cmSystemTools::FileExists(current.FileName.c_str(), true))
+            {
+            fullName = current.FileName;
+            }
+          }
+        else if(!current.QuotedLocation.empty() &&
+                cmSystemTools::FileExists(current.QuotedLocation.c_str(), true))
           {
-          tempPathStr += current.FileName;
+          // The include statement producing this entry was a double-quote
+          // include and the included file is present in the directory of
+          // the source containing the include statement.
+          fullName = current.QuotedLocation;
           }
         else
           {
-          tempPathStr += *i;
-          tempPathStr+="/";
-          tempPathStr+=current.FileName;
+          std::map<cmStdString, cmStdString>::iterator
+            headerLocationIt=this->HeaderLocationCache.find(current.FileName);
+          if (headerLocationIt!=this->HeaderLocationCache.end())
+            {
+            fullName=headerLocationIt->second;
+            }
+          else for(std::vector<std::string>::const_iterator i =
+                this->IncludePath.begin(); i != this->IncludePath.end(); ++i)
+            {
+            // Construct the name of the file as if it were in the current
+            // include directory.  Avoid using a leading "./".
+
+            tempPathStr = "";
+            if((*i) == ".")
+              {
+              tempPathStr += current.FileName;
+              }
+            else
+              {
+              tempPathStr += *i;
+              tempPathStr+="/";
+              tempPathStr+=current.FileName;
+              }
+
+            // Look for the file in this location.
+            if(cmSystemTools::FileExists(tempPathStr.c_str(), true))
+              {
+              fullName = tempPathStr;
+              HeaderLocationCache[current.FileName]=fullName;
+              break;
+              }
+            }
           }
 
-        // Look for the file in this location.
-        if(cmSystemTools::FileExists(tempPathStr.c_str(), true))
+        // Complain if the file cannot be found and matches the complain
+        // regex.
+        if(fullName.empty() &&
+           this->IncludeRegexComplain.find(current.FileName.c_str()))
           {
-          fullName = tempPathStr;
-          HeaderLocationCache[current.FileName]=fullName;
-          break;
+          cmSystemTools::Error("Cannot find file \"",
+                               current.FileName.c_str(), "\".");
+          return false;
           }
-        }
-      }
-
-    // Complain if the file cannot be found and matches the complain
-    // regex.
-    if(fullName.empty() &&
-       this->IncludeRegexComplain.find(current.FileName.c_str()))
-      {
-      cmSystemTools::Error("Cannot find file \"",
-                           current.FileName.c_str(), "\".");
-      return false;
-      }
-
-    // Scan the file if it was found and has not been scanned already.
-    if(!fullName.empty() && (scanned.find(fullName) == scanned.end()))
-      {
-      // Record scanned files.
-      scanned.insert(fullName);
 
-      // Check whether this file is already in the cache
-      std::map<cmStdString, cmIncludeLines*>::iterator fileIt=
-        this->FileCache.find(fullName);
-      if (fileIt!=this->FileCache.end())
-        {
-        fileIt->second->Used=true;
-        dependencies.insert(fullName);
-        for (std::vector<UnscannedEntry>::const_iterator incIt=
-               fileIt->second->UnscannedEntries.begin(); 
-             incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
+        // Scan the file if it was found and has not been scanned already.
+        if(!fullName.empty() && (scanned.find(fullName) == scanned.end()))
           {
-          if (this->Encountered.find(incIt->FileName) == 
-              this->Encountered.end())
+          // Record scanned files.
+          scanned.insert(fullName);
+
+          // Check whether this file is already in the cache
+          std::map<cmStdString, cmIncludeLines*>::iterator fileIt=
+            this->FileCache.find(fullName);
+          if (fileIt!=this->FileCache.end())
             {
-            this->Encountered.insert(incIt->FileName);
-            this->Unscanned.push(*incIt);
+            fileIt->second->Used=true;
+            dependencies.insert(fullName);
+            for (std::vector<UnscannedEntry>::const_iterator incIt=
+                   fileIt->second->UnscannedEntries.begin();
+                 incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
+              {
+              if (this->Encountered.find(incIt->FileName) ==
+                  this->Encountered.end())
+                {
+                this->Encountered.insert(incIt->FileName);
+                this->Unscanned.push(*incIt);
+                }
+              }
             }
-          }
-        }
-      else
-        {
-
-        // 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);
+          else
+            {
 
-          // Scan this file for new dependencies.  Pass the directory
-          // containing the file to handle double-quote includes.
-          std::string dir = cmSystemTools::GetFilenamePath(fullName);
-          this->Scan(fin, dir.c_str(), 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.  Pass the directory
+              // containing the file to handle double-quote includes.
+              std::string dir = cmSystemTools::GetFilenamePath(fullName);
+              this->Scan(fin, dir.c_str(), fullName);
+              }
+            }
           }
+          first = false;
         }
+        done = true;
       }
-
-    first = false;
+      okay = okay && done;
     }
-
   // Write the dependencies to the output stream.  Makefile rules
   // written by the original local generator for this directory
   // convert the dependencies to paths relative to the home output
   // directory.  We must do the same here.
   internalDepends << obj << std::endl;
-  for(std::set<cmStdString>::iterator i=dependencies.begin();
+  for(std::set<std::string>::iterator i=dependencies.begin();
       i != dependencies.end(); ++i)
     {
-    makeDepends << obj << ": " << 
+    makeDepends << obj << ": " <<
       this->LocalGenerator->Convert(i->c_str(),
                                     cmLocalGenerator::HOME_OUTPUT,
                                     cmLocalGenerator::MAKEFILE)
@@ -286,8 +288,7 @@ bool cmDependsC::WriteDependencies(const char *src, const char *obj,
     internalDepends << " " << i->c_str() << std::endl;
     }
   makeDepends << std::endl;
-
-  return true;
+  return okay;
 }
 
 //----------------------------------------------------------------------------
diff --git a/Source/cmDependsC.h b/Source/cmDependsC.h
index bd9a4b7..331d291 100644
--- a/Source/cmDependsC.h
+++ b/Source/cmDependsC.h
@@ -35,8 +35,8 @@ protected:
   typedef std::vector<char> t_CharBuffer;
 
   // Implement writing/checking methods required by superclass.
-  virtual bool WriteDependencies(const char *src,
-                                 const char *file,
+  virtual bool WriteDependencies(const std::set<std::string>& sources,
+                                 const std::string&      obj,
                                  std::ostream& makeDepends,
                                  std::ostream& internalDepends);
 
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index fce5a9c..3ad24d6 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -1876,8 +1876,12 @@ void cmLocalUnixMakefileGenerator3
     for(ImplicitDependFileMap::const_iterator pi = implicitPairs.begin();
         pi != implicitPairs.end(); ++pi)
       {
-      cmakefileStream << "  \"" << pi->second << "\" ";
-      cmakefileStream << "\"" << pi->first << "\"\n";
+      for(cmDepends::DependencyVector::const_iterator di = pi->second.begin();
+          di != pi->second.end(); ++ di)
+        {
+        cmakefileStream << "  \"" << *di << "\" ";
+        cmakefileStream << "\"" << pi->first << "\"\n";
+        }
       }
     cmakefileStream << "  )\n";
 
@@ -2180,7 +2184,7 @@ cmLocalUnixMakefileGenerator3::AddImplicitDepends(cmTarget const& tgt,
                                                   const char* obj,
                                                   const char* src)
 {
-  this->ImplicitDepends[tgt.GetName()][lang][obj] = src;
+  this->ImplicitDepends[tgt.GetName()][lang][obj].push_back(src);
 }
 
 //----------------------------------------------------------------------------
diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h
index 790eff1..dea242b 100644
--- a/Source/cmLocalUnixMakefileGenerator3.h
+++ b/Source/cmLocalUnixMakefileGenerator3.h
@@ -217,7 +217,7 @@ public:
 
   // File pairs for implicit dependency scanning.  The key of the map
   // is the depender and the value is the explicit dependee.
-  struct ImplicitDependFileMap: public std::map<cmStdString, cmStdString> {};
+  struct ImplicitDependFileMap: public std::map<cmStdString, cmDepends::DependencyVector> {};
   struct ImplicitDependLanguageMap:
     public std::map<cmStdString, ImplicitDependFileMap> {};
   struct ImplicitDependTargetMap:
diff --git a/Tests/BuildDepends/Project/CMakeLists.txt b/Tests/BuildDepends/Project/CMakeLists.txt
index e9d1296..68d5c33 100644
--- a/Tests/BuildDepends/Project/CMakeLists.txt
+++ b/Tests/BuildDepends/Project/CMakeLists.txt
@@ -42,7 +42,8 @@ IF("${CMAKE_GENERATOR}" MATCHES "Make")
   # Test the IMPLICIT_DEPENDS feature.
   SET(ZOT_DEPENDS IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep.cxx)
   SET(ZOT_CUSTOM_DEP
-    IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep_custom.cxx)
+    IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep_custom1.cxx
+    CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep_custom2.cxx)
 ELSE("${CMAKE_GENERATOR}" MATCHES "Make")
   # No IMPLICIT_DEPENDS...just depend directly.
   SET(ZOT_DEPENDS DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/zot.hxx.in)
diff --git a/Tests/BuildDepends/Project/dep_custom1.cxx b/Tests/BuildDepends/Project/dep_custom1.cxx
new file mode 100644
index 0000000..55024c4
--- /dev/null
+++ b/Tests/BuildDepends/Project/dep_custom1.cxx
@@ -0,0 +1 @@
+// some comment
diff --git a/Tests/BuildDepends/Project/dep_custom.cxx b/Tests/BuildDepends/Project/dep_custom2.cxx
similarity index 100%
rename from Tests/BuildDepends/Project/dep_custom.cxx
rename to Tests/BuildDepends/Project/dep_custom2.cxx
-- 
1.6.5.2

