// 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
namespace {
+class SharedStream {
+public:
+ SharedStream(raw_ostream &OS) : OS(OS) {}
+ void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) {
+ std::unique_lock<std::mutex> 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<DependencyOutputOptions> 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<DependencyOutputOptions> 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<CompilerInvocation> Invocation,
FileManager *FileMgr,
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(
+ std::make_shared<DependencyPrinter>(std::move(Opts), OS));
+
auto Action = llvm::make_unique<PreprocessOnlyAction>();
const bool Result = Compiler.ExecuteAction(*Action);
FileMgr->clearStatCache();
private:
StringRef WorkingDirectory;
+ SharedStream &OS;
};
/// A proxy file system that doesn't call `chdir` when changing the working
///
/// \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<PCHContainerOperations>();
BaseFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem());
}
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<PCHContainerOperations> PCHContainerOps;
/// The real filesystem used as a base for all the operations performed by the
/// tool.
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<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, DependencyOS));
std::vector<std::thread> WorkerThreads;
std::atomic<bool> HadErrors(false);