#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.
///
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;
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
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,
// 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);
private:
StringRef WorkingDirectory;
- /// The dependency file will be written to this string.
- std::string &DependencyFileContents;
+ DependencyConsumer &Consumer;
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
};
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);
+ });
}
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) {