Updated VS build system. Patch provided by Cedric Venet:
[oota-llvm.git] / tools / llvmc2 / CompilationGraph.cpp
index 310413b9f0a5a4b41d0b2740c0897d831278e850..acf391a29060f79ebf3ab9fbb59aa49a364e8130 100644 (file)
@@ -11,6 +11,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "Error.h"
 #include "CompilationGraph.h"
 
 #include "llvm/ADT/STLExtras.h"
@@ -31,11 +32,25 @@ extern cl::list<std::string> InputFilenames;
 extern cl::opt<std::string> OutputFilename;
 extern cl::list<std::string> Languages;
 
+namespace llvmc {
+  /// ExtsToLangs - Map from file extensions to language names.
+  LanguageMap GlobalLanguageMap;
+
+  /// GetLanguage -  Find the language name corresponding to the given file.
+  const std::string& GetLanguage(const sys::Path& File) {
+    LanguageMap::const_iterator Lang = GlobalLanguageMap.find(File.getSuffix());
+    if (Lang == GlobalLanguageMap.end())
+      throw std::runtime_error("Unknown suffix: " + File.getSuffix());
+    return Lang->second;
+  }
+}
+
 namespace {
 
-  // Return the edge with the maximum weight.
+  /// ChooseEdge - Return the edge with the maximum weight.
   template <class C>
   const Edge* ChooseEdge(const C& EdgesContainer,
+                         const InputLanguagesSet& InLangs,
                          const std::string& NodeName = "root") {
     const Edge* MaxEdge = 0;
     unsigned MaxWeight = 0;
@@ -44,13 +59,12 @@ namespace {
     for (typename C::const_iterator B = EdgesContainer.begin(),
            E = EdgesContainer.end(); B != E; ++B) {
       const Edge* E = B->getPtr();
-      unsigned EW = E->Weight();
+      unsigned EW = E->Weight(InLangs);
       if (EW > MaxWeight) {
         MaxEdge = E;
         MaxWeight = EW;
         SingleMax = true;
-      }
-      else if (EW == MaxWeight) {
+      } else if (EW == MaxWeight) {
         SingleMax = false;
       }
     }
@@ -86,20 +100,14 @@ const Node& CompilationGraph::getNode(const std::string& ToolName) const {
   return I->second;
 }
 
-const std::string& CompilationGraph::getLanguage(const sys::Path& File) const {
-  LanguageMap::const_iterator Lang = ExtsToLangs.find(File.getSuffix());
-  if (Lang == ExtsToLangs.end())
-    throw std::runtime_error("Unknown suffix: " + File.getSuffix() + '!');
-  return Lang->second;
-}
-
+// Find the tools list corresponding to the given language name.
 const CompilationGraph::tools_vector_type&
 CompilationGraph::getToolsVector(const std::string& LangName) const
 {
   tools_map_type::const_iterator I = ToolsMap.find(LangName);
   if (I == ToolsMap.end())
     throw std::runtime_error("No tool corresponding to the language "
-                             + LangName + "found!");
+                             + LangName + " found");
   return I->second;
 }
 
@@ -112,16 +120,17 @@ void CompilationGraph::insertNode(Tool* V) {
   }
 }
 
-void CompilationGraph::insertEdge(const std::string& A, Edge* E) {
-  Node& B = getNode(E->ToolName());
+void CompilationGraph::insertEdge(const std::string& A, Edge* Edg) {
+  Node& B = getNode(Edg->ToolName());
   if (A == "root") {
-    const std::string& InputLanguage = B.ToolPtr->InputLanguage();
-    ToolsMap[InputLanguage].push_back(IntrusiveRefCntPtr<Edge>(E));
-    NodesMap["root"].AddEdge(E);
+    const char** InLangs = B.ToolPtr->InputLanguages();
+    for (;*InLangs; ++InLangs)
+      ToolsMap[*InLangs].push_back(IntrusiveRefCntPtr<Edge>(Edg));
+    NodesMap["root"].AddEdge(Edg);
   }
   else {
     Node& N = getNode(A);
-    N.AddEdge(E);
+    N.AddEdge(Edg);
   }
   // Increase the inward edge counter.
   B.IncrInEdges();
@@ -130,9 +139,22 @@ void CompilationGraph::insertEdge(const std::string& A, Edge* E) {
 namespace {
   sys::Path MakeTempFile(const sys::Path& TempDir, const std::string& BaseName,
                          const std::string& Suffix) {
-    sys::Path Out = TempDir;
-    Out.appendComponent(BaseName);
+    sys::Path Out;
+
+    // Make sure we don't end up with path names like '/file.o' if the
+    // TempDir is empty.
+    if (TempDir.empty()) {
+      Out.set(BaseName);
+    }
+    else {
+      Out = TempDir;
+      Out.appendComponent(BaseName);
+    }
     Out.appendSuffix(Suffix);
+    // NOTE: makeUnique always *creates* a unique temporary file,
+    // which is good, since there will be no races. However, some
+    // tools do not like it when the output file already exists, so
+    // they have to be placated with -f or something like that.
     Out.makeUnique(true, NULL);
     return Out;
   }
@@ -142,6 +164,7 @@ namespace {
 // a node that says that it is the last.
 void CompilationGraph::PassThroughGraph (const sys::Path& InFile,
                                          const Node* StartNode,
+                                         const InputLanguagesSet& InLangs,
                                          const sys::Path& TempDir) const {
   bool Last = false;
   sys::Path In = InFile;
@@ -173,69 +196,45 @@ void CompilationGraph::PassThroughGraph (const sys::Path& InFile,
       Out = MakeTempFile(TempDir, In.getBasename(), CurTool->OutputSuffix());
     }
 
-    if (CurTool->GenerateAction(In, Out).Execute() != 0)
-      throw std::runtime_error("Tool returned error code!");
+    if (int ret = CurTool->GenerateAction(In, Out, InLangs).Execute())
+      throw error_code(ret);
 
     if (Last)
       return;
 
     CurNode = &getNode(ChooseEdge(CurNode->OutEdges,
+                                  InLangs,
                                   CurNode->Name())->ToolName());
     In = Out; Out.clear();
   }
 }
 
-// Sort the nodes in topological order.
-void CompilationGraph::TopologicalSort(std::vector<const Node*>& Out) {
-  std::queue<const Node*> Q;
-  Q.push(&getNode("root"));
-
-  while (!Q.empty()) {
-    const Node* A = Q.front();
-    Q.pop();
-    Out.push_back(A);
-    for (Node::const_iterator EB = A->EdgesBegin(), EE = A->EdgesEnd();
-         EB != EE; ++EB) {
-      Node* B = &getNode((*EB)->ToolName());
-      B->DecrInEdges();
-      if (B->HasNoInEdges())
-        Q.push(B);
-    }
-  }
-}
+// Find the head of the toolchain corresponding to the given file.
+// Also, insert an input language into InLangs.
+const Node* CompilationGraph::
+FindToolChain(const sys::Path& In, const std::string* forceLanguage,
+              InputLanguagesSet& InLangs) const {
 
-namespace {
-  bool NotJoinNode(const Node* N) {
-    return N->ToolPtr ? !N->ToolPtr->IsJoin() : true;
-  }
-}
+  // Determine the input language.
+  const std::string& InLanguage =
+    forceLanguage ? *forceLanguage : GetLanguage(In);
 
-// Call TopologicalSort and filter the resulting list to include
-// only Join nodes.
-void CompilationGraph::
-TopologicalSortFilterJoinNodes(std::vector<const Node*>& Out) {
-  std::vector<const Node*> TopSorted;
-  TopologicalSort(TopSorted);
-  std::remove_copy_if(TopSorted.begin(), TopSorted.end(),
-                      std::back_inserter(Out), NotJoinNode);
-}
+  // Add the current input language to the input language set.
+  InLangs.insert(InLanguage);
 
-// Find head of the toolchain corresponding to the given file.
-const Node* CompilationGraph::
-FindToolChain(const sys::Path& In, const std::string* forceLanguage) const {
-  const std::string& InLanguage =
-    forceLanguage ? *forceLanguage : getLanguage(In);
+  // Find the toolchain for the input language.
   const tools_vector_type& TV = getToolsVector(InLanguage);
   if (TV.empty())
-    throw std::runtime_error("No toolchain corresponding to language"
-                             + InLanguage + " found!");
-  return &getNode(ChooseEdge(TV)->ToolName());
+    throw std::runtime_error("No toolchain corresponding to language "
+                             + InLanguage + " found");
+  return &getNode(ChooseEdge(TV, InLangs)->ToolName());
 }
 
-// Build the targets. Command-line options are passed through
-// temporary variables.
-int CompilationGraph::Build (const sys::Path& TempDir) {
-
+// Helper function used by Build().
+// Traverses initial portions of the toolchains (up to the first Join node).
+// This function is also responsible for handling the -x option.
+void CompilationGraph::BuildInitial (InputLanguagesSet& InLangs,
+                                     const sys::Path& TempDir) {
   // This is related to -x option handling.
   cl::list<std::string>::const_iterator xIter = Languages.begin(),
     xBegin = xIter, xEnd = Languages.end();
@@ -284,10 +283,53 @@ int CompilationGraph::Build (const sys::Path& TempDir) {
     }
 
     // Find the toolchain corresponding to this file.
-    const Node* N = FindToolChain(In, xLanguage);
+    const Node* N = FindToolChain(In, xLanguage, InLangs);
     // Pass file through the chain starting at head.
-    PassThroughGraph(In, N, TempDir);
+    PassThroughGraph(In, N, InLangs, TempDir);
   }
+}
+
+// Sort the nodes in topological order.
+void CompilationGraph::TopologicalSort(std::vector<const Node*>& Out) {
+  std::queue<const Node*> Q;
+  Q.push(&getNode("root"));
+
+  while (!Q.empty()) {
+    const Node* A = Q.front();
+    Q.pop();
+    Out.push_back(A);
+    for (Node::const_iterator EB = A->EdgesBegin(), EE = A->EdgesEnd();
+         EB != EE; ++EB) {
+      Node* B = &getNode((*EB)->ToolName());
+      B->DecrInEdges();
+      if (B->HasNoInEdges())
+        Q.push(B);
+    }
+  }
+}
+
+namespace {
+  bool NotJoinNode(const Node* N) {
+    return N->ToolPtr ? !N->ToolPtr->IsJoin() : true;
+  }
+}
+
+// Call TopologicalSort and filter the resulting list to include
+// only Join nodes.
+void CompilationGraph::
+TopologicalSortFilterJoinNodes(std::vector<const Node*>& Out) {
+  std::vector<const Node*> TopSorted;
+  TopologicalSort(TopSorted);
+  std::remove_copy_if(TopSorted.begin(), TopSorted.end(),
+                      std::back_inserter(Out), NotJoinNode);
+}
+
+int CompilationGraph::Build (const sys::Path& TempDir) {
+
+  InputLanguagesSet InLangs;
+
+  // Traverse initial parts of the toolchains and fill in InLangs.
+  BuildInitial(InLangs, TempDir);
 
   std::vector<const Node*> JTV;
   TopologicalSortFilterJoinNodes(JTV);
@@ -301,12 +343,12 @@ int CompilationGraph::Build (const sys::Path& TempDir) {
     JoinTool* JT = &dynamic_cast<JoinTool&>(*CurNode->ToolPtr.getPtr());
     bool IsLast = false;
 
-    // Are there any files to be joined?
+    // Are there any files in the join list?
     if (JT->JoinListEmpty())
       continue;
 
-    // Is this the last tool in the chain?
-    // NOTE: we can process several chains in parallel.
+    // Is this the last tool in the toolchain?
+    // NOTE: we can process several toolchains in parallel.
     if (!CurNode->HasChildren() || JT->IsLast()) {
       if (OutputFilename.empty()) {
         Out.set("a");
@@ -320,13 +362,14 @@ int CompilationGraph::Build (const sys::Path& TempDir) {
       Out = MakeTempFile(TempDir, "tmp", JT->OutputSuffix());
     }
 
-    if (JT->GenerateAction(Out).Execute() != 0)
-      throw std::runtime_error("Tool returned error code!");
+    if (int ret = JT->GenerateAction(Out, InLangs).Execute())
+      throw error_code(ret);
 
     if (!IsLast) {
-      const Node* NextNode = &getNode(ChooseEdge(CurNode->OutEdges,
-                                                 CurNode->Name())->ToolName());
-      PassThroughGraph(Out, NextNode, TempDir);
+      const Node* NextNode =
+        &getNode(ChooseEdge(CurNode->OutEdges, InLangs,
+                            CurNode->Name())->ToolName());
+      PassThroughGraph(Out, NextNode, InLangs, TempDir);
     }
   }
 
@@ -357,24 +400,40 @@ namespace llvm {
 
     template<typename EdgeIter>
     static std::string getEdgeSourceLabel(const Node* N, EdgeIter I) {
-      if (N->ToolPtr)
+      if (N->ToolPtr) {
         return N->ToolPtr->OutputLanguage();
-      else
-        return I->ToolPtr->InputLanguage();
+      }
+      else {
+        const char** InLangs = I->ToolPtr->InputLanguages();
+        std::string ret;
+
+        for (; *InLangs; ++InLangs) {
+          if (*(InLangs + 1)) {
+            ret += *InLangs;
+            ret +=  ", ";
+          }
+          else {
+            ret += *InLangs;
+          }
+        }
+
+        return ret;
+      }
     }
   };
 
 }
 
 void CompilationGraph::writeGraph() {
-  std::ofstream O("CompilationGraph.dot");
+  std::ofstream O("compilation-graph.dot");
 
   if (O.good()) {
     llvm::WriteGraph(this, "compilation-graph");
     O.close();
   }
   else {
-    throw std::runtime_error("");
+    throw std::runtime_error("Error opening file 'compilation-graph.dot'"
+                             " for writing!");
   }
 }