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); 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/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/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/tools/clang-scan-deps/ClangScanDeps.cpp =================================================================== --- clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -29,12 +29,43 @@ namespace { +class SharedStream { +public: + SharedStream(raw_ostream &OS) : OS(OS) {} + void applyLocked(llvm::function_ref Fn) { + std::unique_lock LockGuard(Lock); + Fn(OS); + OS.flush(); + } + +private: + std::mutex Lock; + raw_ostream &OS; +}; + +/// Prints out all of the gathered dependencies into one output stream instead +/// of using the output dependency file. +class DependencyPrinter : public DependencyFileGenerator { +public: + DependencyPrinter(std::unique_ptr Opts, + SharedStream &OS) + : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), OS(OS) {} + + void finishedMainFile(DiagnosticsEngine &Diags) override { + OS.applyLocked([this](raw_ostream &OS) { outputDependencyFile(OS); }); + } + +private: + std::unique_ptr Opts; + SharedStream &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, SharedStream &OS) + : WorkingDirectory(WorkingDirectory), OS(OS) {} bool runInvocation(std::shared_ptr Invocation, FileManager *FileMgr, @@ -53,6 +84,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( + 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( + std::make_shared(std::move(Opts), OS)); + auto Action = llvm::make_unique(); const bool Result = Compiler.ExecuteAction(*Action); FileMgr->clearStatCache(); @@ -61,6 +107,7 @@ private: StringRef WorkingDirectory; + SharedStream &OS; }; /// A proxy file system that doesn't call `chdir` when changing the working @@ -93,8 +140,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, + SharedStream &OS) + : Compilations(Compilations), OS(OS) { PCHContainerOps = std::make_shared(); BaseFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem()); } @@ -107,12 +155,13 @@ tooling::ClangTool Tool(Compilations, Input, PCHContainerOps, BaseFS); Tool.clearArgumentsAdjusters(); Tool.setRestoreWorkingDir(false); - PreprocessorOnlyTool Action(CWD); + PreprocessorOnlyTool Action(CWD, OS); return Tool.run(&Action); } private: const tooling::CompilationDatabase &Compilations; + SharedStream &OS; std::shared_ptr PCHContainerOps; /// The real filesystem used as a base for all the operations performed by the /// tool. @@ -176,12 +225,14 @@ return AdjustedArgs; }); + // Print out the dependency results to STDOUT by default. + SharedStream DependencyOS(llvm::outs()); unsigned NumWorkers = NumThreads == 0 ? llvm::hardware_concurrency() : NumThreads; std::vector> WorkerTools; for (unsigned I = 0; I < NumWorkers; ++I) - WorkerTools.push_back( - llvm::make_unique(*AdjustingCompilations)); + WorkerTools.push_back(llvm::make_unique( + *AdjustingCompilations, DependencyOS)); std::vector WorkerThreads; std::atomic HadErrors(false);