From ca4216abde786cf0335dcc8982a8c914a5763b83 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Fri, 30 Aug 2019 01:25:57 +0000 Subject: [PATCH] [clang-scan-deps] NFC, refactor the DependencyScanningWorker to use a consumer to report the dependencies to the client This will allow the scanner to report modular dependencies to the consumer. This will also allow the scanner to accept regular cc1 clang invocations, e.g. in an implementation of a libclang C API for clang-scan-deps, that I will add follow-up patches for in the future. llvm-svn: 370425 --- .../DependencyScanningWorker.h | 28 +++++-- .../DependencyScanningWorker.cpp | 74 ++++++++++--------- clang/tools/clang-scan-deps/ClangScanDeps.cpp | 58 ++++++++++++++- 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 d56f5395da16..5b42741ecb61 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h @@ -19,12 +19,25 @@ #include 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 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 DiagOpts; diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index 2d49e0d794af..f6033de4618d 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 Opts, - std::string &S) - : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), S(S) {} + DependencyConsumerForwarder(std::unique_ptr 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 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 DepFS) - : WorkingDirectory(WorkingDirectory), - DependencyFileContents(DependencyFileContents), + : WorkingDirectory(WorkingDirectory), Consumer(Consumer), DepFS(std::move(DepFS)) {} bool runInvocation(std::shared_ptr 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( - std::move(Opts), DependencyFileContents)); + Compiler.addDependencyCollector( + std::make_shared(std::move(Opts), + Consumer)); auto Action = std::make_unique(); 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 DepFS; }; @@ -152,30 +151,35 @@ DependencyScanningWorker::DependencyScanningWorker( Files = new FileManager(FileSystemOptions(), RealFS); } -llvm::Expected -DependencyScanningWorker::getDependencyFile(const std::string &Input, - StringRef WorkingDirectory, - const CompilationDatabase &CDB) { +static llvm::Error runWithDiags( + DiagnosticOptions *DiagOpts, + llvm::function_ref 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(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(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 5e567fef9dae..bee4b479b2d1 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 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(Opts); + Dependencies.push_back(File); + } + + void printDependencies(std::string &S) { + if (!Opts) + return; + + class DependencyPrinter : public DependencyFileGenerator { + public: + DependencyPrinter(DependencyOutputOptions &Opts, + ArrayRef 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 Opts; + std::vector 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) { -- 2.34.1