blob: 4868f2663796002283244709661be5d842c72c66 [file] [log] [blame]
//===- DependencyScanningWorker.cpp - clang-scan-deps worker --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/Tooling/Tooling.h"
using namespace clang;
using namespace tooling;
using namespace dependencies;
namespace {
/// Prints out all of the gathered dependencies into a string.
class DependencyPrinter : public DependencyFileGenerator {
public:
DependencyPrinter(std::unique_ptr<DependencyOutputOptions> Opts,
std::string &S)
: DependencyFileGenerator(*Opts), Opts(std::move(Opts)), S(S) {}
void finishedMainFile(DiagnosticsEngine &Diags) override {
llvm::raw_string_ostream OS(S);
outputDependencyFile(OS);
}
private:
std::unique_ptr<DependencyOutputOptions> Opts;
std::string &S;
};
/// A proxy file system that doesn't call `chdir` when changing the working
/// directory of a clang tool.
class ProxyFileSystemWithoutChdir : public llvm::vfs::ProxyFileSystem {
public:
ProxyFileSystemWithoutChdir(
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
: ProxyFileSystem(std::move(FS)) {}
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
assert(!CWD.empty() && "empty CWD");
return CWD;
}
std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
CWD = Path.str();
return {};
}
private:
std::string CWD;
};
/// A clang tool that runs the preprocessor in a mode that's optimized for
/// dependency scanning for the given compiler invocation.
class DependencyScanningAction : public tooling::ToolAction {
public:
DependencyScanningAction(StringRef WorkingDirectory,
std::string &DependencyFileContents)
: WorkingDirectory(WorkingDirectory),
DependencyFileContents(DependencyFileContents) {}
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
FileManager *FileMgr,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagConsumer) override {
// Create a compiler instance to handle the actual work.
CompilerInstance Compiler(std::move(PCHContainerOps));
Compiler.setInvocation(std::move(Invocation));
FileMgr->getFileSystemOpts().WorkingDir = WorkingDirectory;
Compiler.setFileManager(FileMgr);
// Don't print 'X warnings and Y errors generated'.
Compiler.getDiagnosticOpts().ShowCarets = false;
// Create the compiler's actual diagnostics engine.
Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
if (!Compiler.hasDiagnostics())
return false;
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), DependencyFileContents));
auto Action = llvm::make_unique<PreprocessOnlyAction>();
const bool Result = Compiler.ExecuteAction(*Action);
FileMgr->clearStatCache();
return Result;
}
private:
StringRef WorkingDirectory;
/// The dependency file will be written to this string.
std::string &DependencyFileContents;
};
} // end anonymous namespace
DependencyScanningWorker::DependencyScanningWorker() {
DiagOpts = new DiagnosticOptions();
PCHContainerOps = std::make_shared<PCHContainerOperations>();
/// FIXME: Use the shared file system from the service for fast scanning
/// mode.
WorkerFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem());
}
llvm::Expected<std::string>
DependencyScanningWorker::getDependencyFile(const std::string &Input,
StringRef WorkingDirectory,
const CompilationDatabase &CDB) {
// 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());
WorkerFS->setCurrentWorkingDirectory(WorkingDirectory);
tooling::ClangTool Tool(CDB, Input, PCHContainerOps, WorkerFS);
Tool.clearArgumentsAdjusters();
Tool.setRestoreWorkingDir(false);
Tool.setPrintErrorMessage(false);
Tool.setDiagnosticConsumer(&DiagPrinter);
std::string Output;
DependencyScanningAction Action(WorkingDirectory, Output);
if (Tool.run(&Action)) {
return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
llvm::inconvertibleErrorCode());
}
return Output;
}