aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp508
1 files changed, 353 insertions, 155 deletions
diff --git a/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index d651ff23b387..7ab4a699af6d 100644
--- a/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -7,7 +7,13 @@
//===----------------------------------------------------------------------===//
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
+#include "clang/Basic/DiagnosticDriver.h"
+#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/Job.h"
+#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
@@ -17,6 +23,10 @@
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Error.h"
+#include "llvm/TargetParser/Host.h"
+#include <optional>
using namespace clang;
using namespace tooling;
@@ -28,8 +38,9 @@ namespace {
class DependencyConsumerForwarder : public DependencyFileGenerator {
public:
DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
- DependencyConsumer &C)
- : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), C(C) {}
+ StringRef WorkingDirectory, DependencyConsumer &C)
+ : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),
+ Opts(std::move(Opts)), C(C) {}
void finishedMainFile(DiagnosticsEngine &Diags) override {
C.handleDependencyOutputOpts(*Opts);
@@ -37,87 +48,62 @@ public:
for (const auto &File : getDependencies()) {
CanonPath = File;
llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
+ llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);
C.handleFileDependency(CanonPath);
}
}
private:
+ StringRef WorkingDirectory;
std::unique_ptr<DependencyOutputOptions> Opts;
DependencyConsumer &C;
};
+using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
+
/// A listener that collects the imported modules and optionally the input
/// files.
class PrebuiltModuleListener : public ASTReaderListener {
public:
- PrebuiltModuleListener(llvm::StringMap<std::string> &PrebuiltModuleFiles,
- llvm::StringSet<> &InputFiles, bool VisitInputFiles)
- : PrebuiltModuleFiles(PrebuiltModuleFiles), InputFiles(InputFiles),
- VisitInputFiles(VisitInputFiles) {}
+ PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
+ llvm::SmallVector<std::string> &NewModuleFiles)
+ : PrebuiltModuleFiles(PrebuiltModuleFiles),
+ NewModuleFiles(NewModuleFiles) {}
bool needsImportVisitation() const override { return true; }
- bool needsInputFileVisitation() override { return VisitInputFiles; }
- bool needsSystemInputFileVisitation() override { return VisitInputFiles; }
void visitImport(StringRef ModuleName, StringRef Filename) override {
- PrebuiltModuleFiles.insert({ModuleName, Filename.str()});
- }
-
- bool visitInputFile(StringRef Filename, bool isSystem, bool isOverridden,
- bool isExplicitModule) override {
- InputFiles.insert(Filename);
- return true;
+ if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
+ NewModuleFiles.push_back(Filename.str());
}
private:
- llvm::StringMap<std::string> &PrebuiltModuleFiles;
- llvm::StringSet<> &InputFiles;
- bool VisitInputFiles;
+ PrebuiltModuleFilesT &PrebuiltModuleFiles;
+ llvm::SmallVector<std::string> &NewModuleFiles;
};
-using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
-
/// Visit the given prebuilt module and collect all of the modules it
/// transitively imports and contributing input files.
static void visitPrebuiltModule(StringRef PrebuiltModuleFilename,
CompilerInstance &CI,
- PrebuiltModuleFilesT &ModuleFiles,
- llvm::StringSet<> &InputFiles,
- bool VisitInputFiles) {
- // Maps the names of modules that weren't yet visited to their PCM path.
- llvm::StringMap<std::string> ModuleFilesWorklist;
- // Contains PCM paths of all visited modules.
- llvm::StringSet<> VisitedModuleFiles;
-
- PrebuiltModuleListener Listener(ModuleFilesWorklist, InputFiles,
- VisitInputFiles);
-
- auto GatherModuleFileInfo = [&](StringRef ASTFile) {
+ PrebuiltModuleFilesT &ModuleFiles) {
+ // List of module files to be processed.
+ llvm::SmallVector<std::string> Worklist{PrebuiltModuleFilename.str()};
+ PrebuiltModuleListener Listener(ModuleFiles, Worklist);
+
+ while (!Worklist.empty())
ASTReader::readASTFileControlBlock(
- ASTFile, CI.getFileManager(), CI.getPCHContainerReader(),
+ Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
+ CI.getPCHContainerReader(),
/*FindModuleFileExtensions=*/false, Listener,
/*ValidateDiagnosticOptions=*/false);
- };
-
- GatherModuleFileInfo(PrebuiltModuleFilename);
- while (!ModuleFilesWorklist.empty()) {
- auto WorklistItemIt = ModuleFilesWorklist.begin();
-
- if (!VisitedModuleFiles.contains(WorklistItemIt->getValue())) {
- VisitedModuleFiles.insert(WorklistItemIt->getValue());
- GatherModuleFileInfo(WorklistItemIt->getValue());
- ModuleFiles[WorklistItemIt->getKey().str()] = WorklistItemIt->getValue();
- }
-
- ModuleFilesWorklist.erase(WorklistItemIt);
- }
}
/// Transform arbitrary file name into an object-like file name.
static std::string makeObjFileName(StringRef FileName) {
SmallString<128> ObjFileName(FileName);
llvm::sys::path::replace_extension(ObjFileName, "o");
- return std::string(ObjFileName.str());
+ return std::string(ObjFileName);
}
/// Deduce the dependency target based on the output file and input files.
@@ -133,18 +119,31 @@ deduceDepTarget(const std::string &OutputFile,
return makeObjFileName(InputFiles.front().getFile());
}
+/// Sanitize diagnostic options for dependency scan.
+static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
+ // Don't print 'X warnings and Y errors generated'.
+ DiagOpts.ShowCarets = false;
+ // Don't write out diagnostic file.
+ DiagOpts.DiagnosticSerializationFile.clear();
+ // Don't emit warnings as errors (and all other warnings too).
+ DiagOpts.IgnoreWarnings = true;
+}
+
/// 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, DependencyConsumer &Consumer,
+ DependencyActionController &Controller,
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
- ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings,
- ScanningOutputFormat Format)
+ ScanningOutputFormat Format, ScanningOptimizations OptimizeArgs,
+ bool EagerLoadModules, bool DisableFree,
+ std::optional<StringRef> ModuleName = std::nullopt)
: WorkingDirectory(WorkingDirectory), Consumer(Consumer),
- DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings),
- Format(Format) {}
+ Controller(Controller), DepFS(std::move(DepFS)), Format(Format),
+ OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),
+ DisableFree(DisableFree), ModuleName(ModuleName) {}
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
FileManager *FileMgr,
@@ -152,66 +151,67 @@ public:
DiagnosticConsumer *DiagConsumer) override {
// Make a deep copy of the original Clang invocation.
CompilerInvocation OriginalInvocation(*Invocation);
+ // Restore the value of DisableFree, which may be modified by Tooling.
+ OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;
+
+ if (Scanned) {
+ // Scanning runs once for the first -cc1 invocation in a chain of driver
+ // jobs. For any dependent jobs, reuse the scanning result and just
+ // update the LastCC1Arguments to correspond to the new invocation.
+ // FIXME: to support multi-arch builds, each arch requires a separate scan
+ setLastCC1Arguments(std::move(OriginalInvocation));
+ return true;
+ }
+
+ Scanned = true;
// Create a compiler instance to handle the actual work.
- CompilerInstance Compiler(std::move(PCHContainerOps));
- Compiler.setInvocation(std::move(Invocation));
-
- // Don't print 'X warnings and Y errors generated'.
- Compiler.getDiagnosticOpts().ShowCarets = false;
- // Don't write out diagnostic file.
- Compiler.getDiagnosticOpts().DiagnosticSerializationFile.clear();
- // Don't treat warnings as errors.
- Compiler.getDiagnosticOpts().Warnings.push_back("no-error");
+ ScanInstanceStorage.emplace(std::move(PCHContainerOps));
+ CompilerInstance &ScanInstance = *ScanInstanceStorage;
+ ScanInstance.setInvocation(std::move(Invocation));
+
// Create the compiler's actual diagnostics engine.
- Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
- if (!Compiler.hasDiagnostics())
+ sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
+ ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
+ if (!ScanInstance.hasDiagnostics())
return false;
- Compiler.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = true;
+ ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
+ true;
- FileMgr->getFileSystemOpts().WorkingDir = std::string(WorkingDirectory);
- Compiler.setFileManager(FileMgr);
- Compiler.createSourceManager(*FileMgr);
+ ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
+ ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
+ ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
+ ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
+
+ ScanInstance.setFileManager(FileMgr);
+ // Support for virtual file system overlays.
+ FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation(
+ ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
+ FileMgr->getVirtualFileSystemPtr()));
+
+ ScanInstance.createSourceManager(*FileMgr);
- llvm::StringSet<> PrebuiltModulesInputFiles;
// Store the list of prebuilt module files into header search options. This
// will prevent the implicit build to create duplicate modules and will
// force reuse of the existing prebuilt module files instead.
- if (!Compiler.getPreprocessorOpts().ImplicitPCHInclude.empty())
+ if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
visitPrebuiltModule(
- Compiler.getPreprocessorOpts().ImplicitPCHInclude, Compiler,
- Compiler.getHeaderSearchOpts().PrebuiltModuleFiles,
- PrebuiltModulesInputFiles, /*VisitInputFiles=*/DepFS != nullptr);
+ ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance,
+ ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles);
// Use the dependency scanning optimized file system if requested to do so.
if (DepFS) {
- const CompilerInvocation &CI = Compiler.getInvocation();
- DepFS->clearIgnoredFiles();
- // Ignore any files that contributed to prebuilt modules. The implicit
- // build validates the modules by comparing the reported sizes of their
- // inputs to the current state of the filesystem. Minimization would throw
- // this mechanism off.
- for (const auto &File : PrebuiltModulesInputFiles)
- DepFS->ignoreFile(File.getKey());
- // Add any filenames that were explicity passed in the build settings and
- // that might be opened, as we want to ensure we don't run source
- // minimization on them.
- for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries)
- DepFS->ignoreFile(Entry.Path);
- for (const auto &Entry : CI.getHeaderSearchOpts().VFSOverlayFiles)
- DepFS->ignoreFile(Entry);
-
- // Support for virtual file system overlays on top of the caching
- // filesystem.
- FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation(
- CI, Compiler.getDiagnostics(), DepFS));
-
- // Pass the skip mappings which should speed up excluded conditional block
- // skipping in the preprocessor.
- if (PPSkipMappings)
- Compiler.getPreprocessorOpts()
- .ExcludedConditionalDirectiveSkipMappings = PPSkipMappings;
+ llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> LocalDepFS =
+ DepFS;
+ ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
+ [LocalDepFS = std::move(LocalDepFS)](FileEntryRef File)
+ -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
+ if (llvm::ErrorOr<EntryRef> Entry =
+ LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
+ return Entry->getDirectiveTokens();
+ return std::nullopt;
+ };
}
// Create the dependency collector that will collect the produced
@@ -222,104 +222,302 @@ public:
// 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 = std::make_unique<DependencyOutputOptions>();
- std::swap(*Opts, Compiler.getInvocation().getDependencyOutputOpts());
+ std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
// We need at least one -MT equivalent for the generator of make dependency
// files to work.
if (Opts->Targets.empty())
- Opts->Targets = {deduceDepTarget(Compiler.getFrontendOpts().OutputFile,
- Compiler.getFrontendOpts().Inputs)};
+ Opts->Targets = {
+ deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
+ ScanInstance.getFrontendOpts().Inputs)};
Opts->IncludeSystemHeaders = true;
switch (Format) {
case ScanningOutputFormat::Make:
- Compiler.addDependencyCollector(
- std::make_shared<DependencyConsumerForwarder>(std::move(Opts),
- Consumer));
+ ScanInstance.addDependencyCollector(
+ std::make_shared<DependencyConsumerForwarder>(
+ std::move(Opts), WorkingDirectory, Consumer));
break;
+ case ScanningOutputFormat::P1689:
case ScanningOutputFormat::Full:
- Compiler.addDependencyCollector(std::make_shared<ModuleDepCollector>(
- std::move(Opts), Compiler, Consumer, std::move(OriginalInvocation)));
+ MDC = std::make_shared<ModuleDepCollector>(
+ std::move(Opts), ScanInstance, Consumer, Controller,
+ OriginalInvocation, OptimizeArgs, EagerLoadModules,
+ Format == ScanningOutputFormat::P1689);
+ ScanInstance.addDependencyCollector(MDC);
break;
}
// Consider different header search and diagnostic options to create
// different modules. This avoids the unsound aliasing of module PCMs.
//
- // TODO: Implement diagnostic bucketing and header search pruning to reduce
- // the impact of strict context hashing.
- Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true;
-
- auto Action = std::make_unique<ReadPCHAndPreprocessAction>();
- const bool Result = Compiler.ExecuteAction(*Action);
- if (!DepFS)
- FileMgr->clearStatCache();
+ // TODO: Implement diagnostic bucketing to reduce the impact of strict
+ // context hashing.
+ ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
+ ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
+ ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
+ ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings =
+ true;
+
+ // Avoid some checks and module map parsing when loading PCM files.
+ ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
+
+ std::unique_ptr<FrontendAction> Action;
+
+ if (ModuleName)
+ Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
+ else
+ Action = std::make_unique<ReadPCHAndPreprocessAction>();
+
+ const bool Result = ScanInstance.ExecuteAction(*Action);
+
+ if (Result)
+ setLastCC1Arguments(std::move(OriginalInvocation));
+
+ return Result;
+ }
+
+ bool hasScanned() const { return Scanned; }
+
+ /// Take the cc1 arguments corresponding to the most recent invocation used
+ /// with this action. Any modifications implied by the discovered dependencies
+ /// will have already been applied.
+ std::vector<std::string> takeLastCC1Arguments() {
+ std::vector<std::string> Result;
+ std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
return Result;
}
private:
+ void setLastCC1Arguments(CompilerInvocation &&CI) {
+ if (MDC)
+ MDC->applyDiscoveredDependencies(CI);
+ LastCC1Arguments = CI.getCC1CommandLine();
+ }
+
+private:
StringRef WorkingDirectory;
DependencyConsumer &Consumer;
+ DependencyActionController &Controller;
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
- ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
ScanningOutputFormat Format;
+ ScanningOptimizations OptimizeArgs;
+ bool EagerLoadModules;
+ bool DisableFree;
+ std::optional<StringRef> ModuleName;
+ std::optional<CompilerInstance> ScanInstanceStorage;
+ std::shared_ptr<ModuleDepCollector> MDC;
+ std::vector<std::string> LastCC1Arguments;
+ bool Scanned = false;
};
} // end anonymous namespace
DependencyScanningWorker::DependencyScanningWorker(
- DependencyScanningService &Service)
- : Format(Service.getFormat()) {
- DiagOpts = new DiagnosticOptions();
-
+ DependencyScanningService &Service,
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
+ : Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),
+ EagerLoadModules(Service.shouldEagerLoadModules()) {
PCHContainerOps = std::make_shared<PCHContainerOperations>();
+ // We need to read object files from PCH built outside the scanner.
PCHContainerOps->registerReader(
std::make_unique<ObjectFilePCHContainerReader>());
- // We don't need to write object files, but the current PCH implementation
- // requires the writer to be registered as well.
- PCHContainerOps->registerWriter(
- std::make_unique<ObjectFilePCHContainerWriter>());
-
- RealFS = llvm::vfs::createPhysicalFileSystem();
- if (Service.canSkipExcludedPPRanges())
- PPSkipMappings =
- std::make_unique<ExcludedPreprocessorDirectiveSkipMapping>();
- if (Service.getMode() == ScanningMode::MinimizedSourcePreprocessing)
- DepFS = new DependencyScanningWorkerFilesystem(
- Service.getSharedCache(), RealFS, PPSkipMappings.get());
- if (Service.canReuseFileManager())
- Files = new FileManager(FileSystemOptions(), RealFS);
+ // The scanner itself writes only raw ast files.
+ PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
+
+ switch (Service.getMode()) {
+ case ScanningMode::DependencyDirectivesScan:
+ DepFS =
+ new DependencyScanningWorkerFilesystem(Service.getSharedCache(), FS);
+ BaseFS = DepFS;
+ break;
+ case ScanningMode::CanonicalPreprocessing:
+ DepFS = nullptr;
+ BaseFS = FS;
+ break;
+ }
}
-static llvm::Error runWithDiags(
- DiagnosticOptions *DiagOpts,
- llvm::function_ref<bool(DiagnosticConsumer &DC)> BodyShouldSucceed) {
+llvm::Error DependencyScanningWorker::computeDependencies(
+ StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
+ DependencyConsumer &Consumer, DependencyActionController &Controller,
+ std::optional<StringRef> ModuleName) {
+ std::vector<const char *> CLI;
+ for (const std::string &Arg : CommandLine)
+ CLI.push_back(Arg.c_str());
+ auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
+ sanitizeDiagOpts(*DiagOpts);
+
// 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);
+ TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
- if (BodyShouldSucceed(DiagPrinter))
+ if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
+ DiagPrinter, ModuleName))
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);
- 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,
- PPSkipMappings.get(), Format);
- return !Tool.run(&Action);
- });
+static bool forEachDriverJob(
+ ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, FileManager &FM,
+ llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
+ SmallVector<const char *, 256> Argv;
+ Argv.reserve(ArgStrs.size());
+ for (const std::string &Arg : ArgStrs)
+ Argv.push_back(Arg.c_str());
+
+ llvm::vfs::FileSystem *FS = &FM.getVirtualFileSystem();
+
+ std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
+ Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
+ "clang LLVM compiler", FS);
+ Driver->setTitle("clang_based_tool");
+
+ llvm::BumpPtrAllocator Alloc;
+ bool CLMode = driver::IsClangCL(
+ driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
+
+ if (llvm::Error E = driver::expandResponseFiles(Argv, CLMode, Alloc, FS)) {
+ Diags.Report(diag::err_drv_expand_response_file)
+ << llvm::toString(std::move(E));
+ return false;
+ }
+
+ const std::unique_ptr<driver::Compilation> Compilation(
+ Driver->BuildCompilation(llvm::ArrayRef(Argv)));
+ if (!Compilation)
+ return false;
+
+ if (Compilation->containsError())
+ return false;
+
+ for (const driver::Command &Job : Compilation->getJobs()) {
+ if (!Callback(Job))
+ return false;
+ }
+ return true;
}
+
+static bool createAndRunToolInvocation(
+ std::vector<std::string> CommandLine, DependencyScanningAction &Action,
+ FileManager &FM,
+ std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
+ DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
+
+ // Save executable path before providing CommandLine to ToolInvocation
+ std::string Executable = CommandLine[0];
+ ToolInvocation Invocation(std::move(CommandLine), &Action, &FM,
+ PCHContainerOps);
+ Invocation.setDiagnosticConsumer(Diags.getClient());
+ Invocation.setDiagnosticOptions(&Diags.getDiagnosticOptions());
+ if (!Invocation.run())
+ return false;
+
+ std::vector<std::string> Args = Action.takeLastCC1Arguments();
+ Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});
+ return true;
+}
+
+bool DependencyScanningWorker::computeDependencies(
+ StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
+ DependencyConsumer &Consumer, DependencyActionController &Controller,
+ DiagnosticConsumer &DC, std::optional<StringRef> ModuleName) {
+ // Reset what might have been modified in the previous worker invocation.
+ BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
+
+ std::optional<std::vector<std::string>> ModifiedCommandLine;
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
+
+ // If we're scanning based on a module name alone, we don't expect the client
+ // to provide us with an input file. However, the driver really wants to have
+ // one. Let's just make it up to make the driver happy.
+ if (ModuleName) {
+ auto OverlayFS =
+ llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
+ auto InMemoryFS =
+ llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+ InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
+ OverlayFS->pushOverlay(InMemoryFS);
+ ModifiedFS = OverlayFS;
+
+ SmallString<128> FakeInputPath;
+ // TODO: We should retry the creation if the path already exists.
+ llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",
+ FakeInputPath,
+ /*MakeAbsolute=*/false);
+ InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
+
+ ModifiedCommandLine = CommandLine;
+ ModifiedCommandLine->emplace_back(FakeInputPath);
+ }
+
+ const std::vector<std::string> &FinalCommandLine =
+ ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
+ auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
+
+ FileSystemOptions FSOpts;
+ FSOpts.WorkingDir = WorkingDirectory.str();
+ auto FileMgr = llvm::makeIntrusiveRefCnt<FileManager>(FSOpts, FinalFS);
+
+ std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);
+ llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
+ [](const std::string &Str) { return Str.c_str(); });
+
+ auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);
+ sanitizeDiagOpts(*DiagOpts);
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ CompilerInstance::createDiagnostics(DiagOpts.release(), &DC,
+ /*ShouldOwnClient=*/false);
+
+ // Although `Diagnostics` are used only for command-line parsing, the
+ // custom `DiagConsumer` might expect a `SourceManager` to be present.
+ SourceManager SrcMgr(*Diags, *FileMgr);
+ Diags->setSourceManager(&SrcMgr);
+ // DisableFree is modified by Tooling for running
+ // in-process; preserve the original value, which is
+ // always true for a driver invocation.
+ bool DisableFree = true;
+ DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,
+ Format, OptimizeArgs, EagerLoadModules,
+ DisableFree, ModuleName);
+
+ bool Success = false;
+ if (FinalCommandLine[1] == "-cc1") {
+ Success = createAndRunToolInvocation(FinalCommandLine, Action, *FileMgr,
+ PCHContainerOps, *Diags, Consumer);
+ } else {
+ Success = forEachDriverJob(
+ FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
+ if (StringRef(Cmd.getCreator().getName()) != "clang") {
+ // Non-clang command. Just pass through to the dependency
+ // consumer.
+ Consumer.handleBuildCommand(
+ {Cmd.getExecutable(),
+ {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
+ return true;
+ }
+
+ // Insert -cc1 comand line options into Argv
+ std::vector<std::string> Argv;
+ Argv.push_back(Cmd.getExecutable());
+ Argv.insert(Argv.end(), Cmd.getArguments().begin(),
+ Cmd.getArguments().end());
+
+ // Create an invocation 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.
+ return createAndRunToolInvocation(std::move(Argv), Action, *FileMgr,
+ PCHContainerOps, *Diags, Consumer);
+ });
+ }
+
+ if (Success && !Action.hasScanned())
+ Diags->Report(diag::err_fe_expected_compiler_job)
+ << llvm::join(FinalCommandLine, " ");
+ return Success && Action.hasScanned();
+}
+
+DependencyActionController::~DependencyActionController() {}