diff options
3 files changed, 117 insertions, 43 deletions
diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h index d56f5395da1..5b42741ecb6 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h @@ -19,12 +19,25 @@ #include <string> namespace clang { + +class DependencyOutputOptions; + namespace tooling { namespace dependencies { class DependencyScanningService; class DependencyScanningWorkerFilesystem; +class DependencyConsumer { +public: + virtual ~DependencyConsumer() {} + + virtual void handleFileDependency(const DependencyOutputOptions &Opts, + StringRef Filename) = 0; + + // FIXME: Add support for reporting modular dependencies. +}; + /// An individual dependency scanning worker that is able to run on its own /// thread. /// @@ -35,15 +48,16 @@ class DependencyScanningWorker { public: DependencyScanningWorker(DependencyScanningService &Service); - /// Print out the dependency information into a string using the dependency - /// file format that is specified in the options (-MD is the default) and - /// return it. + /// Run the dependency scanning tool for a given clang driver invocation (as + /// specified for the given Input in the CDB), and report the discovered + /// dependencies to the provided consumer. /// /// \returns A \c StringError with the diagnostic output if clang errors - /// occurred, dependency file contents otherwise. - llvm::Expected<std::string> getDependencyFile(const std::string &Input, - StringRef WorkingDirectory, - const CompilationDatabase &CDB); + /// occurred, success otherwise. + llvm::Error computeDependencies(const std::string &Input, + StringRef WorkingDirectory, + const CompilationDatabase &CDB, + DependencyConsumer &Consumer); private: IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index 2d49e0d794a..f6033de4618 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -21,21 +21,21 @@ using namespace dependencies; namespace { -/// Prints out all of the gathered dependencies into a string. -class DependencyPrinter : public DependencyFileGenerator { +/// Forwards the gatherered dependencies to the consumer. +class DependencyConsumerForwarder : public DependencyFileGenerator { public: - DependencyPrinter(std::unique_ptr<DependencyOutputOptions> Opts, - std::string &S) - : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), S(S) {} + DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts, + DependencyConsumer &C) + : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), C(C) {} void finishedMainFile(DiagnosticsEngine &Diags) override { - llvm::raw_string_ostream OS(S); - outputDependencyFile(OS); + for (const auto &File : getDependencies()) + C.handleFileDependency(*Opts, File); } private: std::unique_ptr<DependencyOutputOptions> Opts; - std::string &S; + DependencyConsumer &C; }; /// A proxy file system that doesn't call `chdir` when changing the working @@ -65,10 +65,9 @@ private: class DependencyScanningAction : public tooling::ToolAction { public: DependencyScanningAction( - StringRef WorkingDirectory, std::string &DependencyFileContents, + StringRef WorkingDirectory, DependencyConsumer &Consumer, llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS) - : WorkingDirectory(WorkingDirectory), - DependencyFileContents(DependencyFileContents), + : WorkingDirectory(WorkingDirectory), Consumer(Consumer), DepFS(std::move(DepFS)) {} bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation, @@ -121,8 +120,9 @@ public: // 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), DependencyFileContents)); + Compiler.addDependencyCollector( + std::make_shared<DependencyConsumerForwarder>(std::move(Opts), + Consumer)); auto Action = std::make_unique<PreprocessOnlyAction>(); const bool Result = Compiler.ExecuteAction(*Action); @@ -133,8 +133,7 @@ public: private: StringRef WorkingDirectory; - /// The dependency file will be written to this string. - std::string &DependencyFileContents; + DependencyConsumer &Consumer; llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS; }; @@ -152,30 +151,35 @@ DependencyScanningWorker::DependencyScanningWorker( Files = new FileManager(FileSystemOptions(), RealFS); } -llvm::Expected<std::string> -DependencyScanningWorker::getDependencyFile(const std::string &Input, - StringRef WorkingDirectory, - const CompilationDatabase &CDB) { +static llvm::Error runWithDiags( + DiagnosticOptions *DiagOpts, + llvm::function_ref<bool(DiagnosticConsumer &DC)> BodyShouldSucceed) { // Capture the emitted diagnostics and report them to the client // in the case of a failure. std::string DiagnosticOutput; llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); - TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.get()); + TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts); + if (BodyShouldSucceed(DiagPrinter)) + return llvm::Error::success(); + return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(), + llvm::inconvertibleErrorCode()); +} + +llvm::Error DependencyScanningWorker::computeDependencies( + const std::string &Input, StringRef WorkingDirectory, + const CompilationDatabase &CDB, DependencyConsumer &Consumer) { RealFS->setCurrentWorkingDirectory(WorkingDirectory); - /// Create the tool that uses the underlying file system to ensure that any - /// file system requests that are made by the driver do not go through the - /// dependency scanning filesystem. - tooling::ClangTool Tool(CDB, Input, PCHContainerOps, RealFS, Files); - Tool.clearArgumentsAdjusters(); - Tool.setRestoreWorkingDir(false); - Tool.setPrintErrorMessage(false); - Tool.setDiagnosticConsumer(&DiagPrinter); - std::string Output; - DependencyScanningAction Action(WorkingDirectory, Output, DepFS); - if (Tool.run(&Action)) { - return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(), - llvm::inconvertibleErrorCode()); - } - return Output; + return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) { + /// Create the tool that uses the underlying file system to ensure that any + /// file system requests that are made by the driver do not go through the + /// dependency scanning filesystem. + tooling::ClangTool Tool(CDB, Input, PCHContainerOps, RealFS, Files); + Tool.clearArgumentsAdjusters(); + Tool.setRestoreWorkingDir(false); + Tool.setPrintErrorMessage(false); + Tool.setDiagnosticConsumer(&DC); + DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS); + return !Tool.run(&Action); + }); } diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index 5e567fef9da..bee4b479b2d 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -51,11 +51,67 @@ public: SharedStream &OS, SharedStream &Errs) : Worker(Service), Compilations(Compilations), OS(OS), Errs(Errs) {} + /// Print out the dependency information into a string using the dependency + /// file format that is specified in the options (-MD is the default) and + /// return it. + /// + /// \returns A \c StringError with the diagnostic output if clang errors + /// occurred, dependency file contents otherwise. + llvm::Expected<std::string> getDependencyFile(const std::string &Input, + StringRef CWD) { + /// Prints out all of the gathered dependencies into a string. + class DependencyPrinterConsumer : public DependencyConsumer { + public: + void handleFileDependency(const DependencyOutputOptions &Opts, + StringRef File) override { + if (!this->Opts) + this->Opts = std::make_unique<DependencyOutputOptions>(Opts); + Dependencies.push_back(File); + } + + void printDependencies(std::string &S) { + if (!Opts) + return; + + class DependencyPrinter : public DependencyFileGenerator { + public: + DependencyPrinter(DependencyOutputOptions &Opts, + ArrayRef<std::string> Dependencies) + : DependencyFileGenerator(Opts) { + for (const auto &Dep : Dependencies) + addDependency(Dep); + } + + void printDependencies(std::string &S) { + llvm::raw_string_ostream OS(S); + outputDependencyFile(OS); + } + }; + + DependencyPrinter Generator(*Opts, Dependencies); + Generator.printDependencies(S); + } + + private: + std::unique_ptr<DependencyOutputOptions> Opts; + std::vector<std::string> Dependencies; + }; + + DependencyPrinterConsumer Consumer; + auto Result = + Worker.computeDependencies(Input, CWD, Compilations, Consumer); + if (Result) + return std::move(Result); + std::string Output; + Consumer.printDependencies(Output); + return Output; + } + /// Computes the dependencies for the given file and prints them out. /// /// \returns True on error. bool runOnFile(const std::string &Input, StringRef CWD) { - auto MaybeFile = Worker.getDependencyFile(Input, CWD, Compilations); + auto MaybeFile = getDependencyFile(Input, CWD); if (!MaybeFile) { llvm::handleAllErrors( MaybeFile.takeError(), [this, &Input](llvm::StringError &Err) { |

