arphaman created this revision.
arphaman added reviewers: Bigcheese, aganea.
Herald added subscribers: tschuett, dexonsmith, jkorous.
Herald added a project: clang.

The gathered dependencies are now printed to the STDOUT instead of being 
written to disk. The tool also doesn't need the dependency output options as 
well to produce the dependencies.


Repository:
  rC Clang

https://reviews.llvm.org/D63579

Files:
  clang/include/clang/Frontend/Utils.h
  clang/lib/Frontend/DependencyFile.cpp
  clang/test/ClangScanDeps/Inputs/regular_cdb.json
  clang/test/ClangScanDeps/regular_cdb.cpp
  clang/tools/clang-scan-deps/ClangScanDeps.cpp

Index: clang/tools/clang-scan-deps/ClangScanDeps.cpp
===================================================================
--- clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -29,12 +29,59 @@
 
 namespace {
 
+class DependencyCollectorFactory {
+public:
+  virtual ~DependencyCollectorFactory() {}
+
+  virtual std::shared_ptr<DependencyCollector> createDependencyCollector(
+      const std::unique_ptr<DependencyOutputOptions> Opts) = 0;
+};
+
+/// Prints out all of the gathered dependencies into one output stream instead
+/// of using the output dependency file.
+class DependencyPrinter : public DependencyFileGenerator {
+public:
+  class Factory : public DependencyCollectorFactory {
+  public:
+    Factory(raw_ostream &OS) : OS(OS) {}
+
+    std::shared_ptr<DependencyCollector> createDependencyCollector(
+        std::unique_ptr<DependencyOutputOptions> Opts) override {
+      std::unique_lock<std::mutex> LockGuard(SharedLock);
+      return std::make_shared<DependencyPrinter>(std::move(Opts), SharedLock,
+                                                 OS);
+    }
+
+  private:
+    std::mutex SharedLock;
+    raw_ostream &OS;
+  };
+
+  DependencyPrinter(std::unique_ptr<DependencyOutputOptions> Opts,
+                    std::mutex &Lock, raw_ostream &OS)
+      : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), Lock(Lock),
+        OS(OS) {}
+
+  void finishedMainFile(DiagnosticsEngine &Diags) override {
+    std::unique_lock<std::mutex> LockGuard(Lock);
+    outputDependencyFile(OS);
+    OS.flush();
+  }
+
+private:
+  std::unique_ptr<DependencyOutputOptions> Opts;
+  std::mutex &Lock;
+  raw_ostream &OS;
+};
+
 /// A clang tool that runs the preprocessor only for the given compiler
 /// invocation.
 class PreprocessorOnlyTool : public tooling::ToolAction {
 public:
-  PreprocessorOnlyTool(StringRef WorkingDirectory)
-      : WorkingDirectory(WorkingDirectory) {}
+  PreprocessorOnlyTool(StringRef WorkingDirectory,
+                       DependencyCollectorFactory &DepCollectorFactory)
+      : WorkingDirectory(WorkingDirectory),
+        DepCollectorFactory(DepCollectorFactory) {}
 
   bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
                      FileManager *FileMgr,
@@ -53,6 +100,21 @@
 
     Compiler.createSourceManager(*FileMgr);
 
+    // Create the dependency collector that will collect the produced
+    // dependencies.
+    //
+    // This also moves the existing dependency output options from the
+    // invocation to the collector. The options in the invocation are reset,
+    // which ensures that the compiler won't create new dependency collectors,
+    // and thus won't write out the extra '.d' files to disk.
+    auto Opts = llvm::make_unique<DependencyOutputOptions>(
+        std::move(Compiler.getInvocation().getDependencyOutputOpts()));
+    // We need at least one -MT equivalent for the generator to work.
+    if (Opts->Targets.empty())
+      Opts->Targets = {"clang-scan-deps dependency"};
+    Compiler.addDependencyCollector(
+        DepCollectorFactory.createDependencyCollector(std::move(Opts)));
+
     auto Action = llvm::make_unique<PreprocessOnlyAction>();
     const bool Result = Compiler.ExecuteAction(*Action);
     FileMgr->clearStatCache();
@@ -61,6 +123,7 @@
 
 private:
   StringRef WorkingDirectory;
+  DependencyCollectorFactory &DepCollectorFactory;
 };
 
 /// A proxy file system that doesn't call `chdir` when changing the working
@@ -93,8 +156,9 @@
   ///
   /// \param Compilations     The reference to the compilation database that's
   /// used by the clang tool.
-  DependencyScanningTool(const tooling::CompilationDatabase &Compilations)
-      : Compilations(Compilations) {
+  DependencyScanningTool(const tooling::CompilationDatabase &Compilations,
+                         DependencyCollectorFactory &DepCollectorFactory)
+      : Compilations(Compilations), DepCollectorFactory(DepCollectorFactory) {
     PCHContainerOps = std::make_shared<PCHContainerOperations>();
     BaseFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem());
   }
@@ -107,12 +171,13 @@
     tooling::ClangTool Tool(Compilations, Input, PCHContainerOps, BaseFS);
     Tool.clearArgumentsAdjusters();
     Tool.setRestoreWorkingDir(false);
-    PreprocessorOnlyTool Action(CWD);
+    PreprocessorOnlyTool Action(CWD, DepCollectorFactory);
     return Tool.run(&Action);
   }
 
 private:
   const tooling::CompilationDatabase &Compilations;
+  DependencyCollectorFactory &DepCollectorFactory;
   std::shared_ptr<PCHContainerOperations> PCHContainerOps;
   /// The real filesystem used as a base for all the operations performed by the
   /// tool.
@@ -176,12 +241,14 @@
         return AdjustedArgs;
       });
 
+  // Print out the dependency results to STDOUT by default.
+  DependencyPrinter::Factory DepPrintFactory(llvm::outs());
   unsigned NumWorkers =
       NumThreads == 0 ? llvm::hardware_concurrency() : NumThreads;
   std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools;
   for (unsigned I = 0; I < NumWorkers; ++I)
-    WorkerTools.push_back(
-        llvm::make_unique<DependencyScanningTool>(*AdjustingCompilations));
+    WorkerTools.push_back(llvm::make_unique<DependencyScanningTool>(
+        *AdjustingCompilations, DepPrintFactory));
 
   std::vector<std::thread> WorkerThreads;
   std::atomic<bool> HadErrors(false);
Index: clang/test/ClangScanDeps/regular_cdb.cpp
===================================================================
--- clang/test/ClangScanDeps/regular_cdb.cpp
+++ clang/test/ClangScanDeps/regular_cdb.cpp
@@ -2,26 +2,35 @@
 // RUN: rm -rf %t.cdb
 // RUN: mkdir -p %t.dir
 // RUN: cp %s %t.dir/regular_cdb.cpp
+// RUN: cp %s %t.dir/regular_cdb2.cpp
 // RUN: mkdir %t.dir/Inputs
 // RUN: cp %S/Inputs/header.h %t.dir/Inputs/header.h
 // RUN: cp %S/Inputs/header2.h %t.dir/Inputs/header2.h
 // RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/regular_cdb.json > %t.cdb
 //
-// RUN: clang-scan-deps -compilation-database %t.cdb -j 1
-// RUN: cat %t.dir/regular_cdb.d | FileCheck %s
-// RUN: cat %t.dir/regular_cdb2.d | FileCheck --check-prefix=CHECK2 %s
-// RUN: rm -rf %t.dir/regular_cdb.d %t.dir/regular_cdb2.d
+// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 | \
+// RUN:   FileCheck --check-prefixes=CHECK1,CHECK2,CHECK2NO %s
 //
-// RUN: clang-scan-deps -compilation-database %t.cdb -j 2
-// RUN: cat %t.dir/regular_cdb.d | FileCheck %s
-// RUN: cat %t.dir/regular_cdb2.d | FileCheck --check-prefix=CHECK2 %s
+// Make sure we didn't produce any dependency files!
+// RUN: not cat %t.dir/regular_cdb.d
+// RUN: not cat %t.dir/regular_cdb2.d
+//
+// The output order is non-deterministic when using more than one thread,
+// so check the output using two runs. Note that the 'NOT' check is not used
+// as it might fail if the results for `regular_cdb.cpp` are reported before
+// `regular_cdb2.cpp`.
+//
+// RUN: clang-scan-deps -compilation-database %t.cdb -j 2 | \
+// RUN:   FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-scan-deps -compilation-database %t.cdb -j 2 | \
+// RUN:   FileCheck --check-prefix=CHECK2 %s
 
 #include "header.h"
 
-// CHECK: regular_cdb.cpp
-// CHECK-NEXT: Inputs{{/|\\}}header.h
-// CHECK-NOT: header2
+// CHECK1: regular_cdb2.cpp
+// CHECK1-NEXT: Inputs{{/|\\}}header.h
+// CHECK1-NEXT: Inputs{{/|\\}}header2.h
 
 // CHECK2: regular_cdb.cpp
 // CHECK2-NEXT: Inputs{{/|\\}}header.h
-// CHECK2-NEXT: Inputs{{/|\\}}header2.h
+// CHECK2NO-NOT: header2
Index: clang/test/ClangScanDeps/Inputs/regular_cdb.json
===================================================================
--- clang/test/ClangScanDeps/Inputs/regular_cdb.json
+++ clang/test/ClangScanDeps/Inputs/regular_cdb.json
@@ -1,12 +1,12 @@
 [
 {
   "directory": "DIR",
-  "command": "clang -E -fsyntax-only DIR/regular_cdb.cpp -IInputs -MD -MF DIR/regular_cdb.d",
-  "file": "DIR/regular_cdb.cpp"
+  "command": "clang -E -fsyntax-only DIR/regular_cdb2.cpp -IInputs -D INCLUDE_HEADER2 -MD -MF DIR/regular_cdb2.d",
+  "file": "DIR/regular_cdb2.cpp"
 },
 {
   "directory": "DIR",
-  "command": "clang -E -fsyntax-only DIR/regular_cdb.cpp -IInputs -D INCLUDE_HEADER2 -MD -MF DIR/regular_cdb2.d",
+  "command": "clang -E -fsyntax-only DIR/regular_cdb.cpp -IInputs",
   "file": "DIR/regular_cdb.cpp"
 }
 ]
Index: clang/lib/Frontend/DependencyFile.cpp
===================================================================
--- clang/lib/Frontend/DependencyFile.cpp
+++ clang/lib/Frontend/DependencyFile.cpp
@@ -322,6 +322,10 @@
     return;
   }
 
+  outputDependencyFile(OS);
+}
+
+void DependencyFileGenerator::outputDependencyFile(llvm::raw_ostream &OS) {
   // Write out the dependency targets, trying to avoid overly long
   // lines when possible. We try our best to emit exactly the same
   // dependency file as GCC (4.2), assuming the included files are the
Index: clang/include/clang/Frontend/Utils.h
===================================================================
--- clang/include/clang/Frontend/Utils.h
+++ clang/include/clang/Frontend/Utils.h
@@ -132,6 +132,9 @@
   bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem,
                      bool IsModuleFile, bool IsMissing) final override;
 
+protected:
+  void outputDependencyFile(llvm::raw_ostream &OS);
+
 private:
   void outputDependencyFile(DiagnosticsEngine &Diags);
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to