Skip to content

Commit 554613b

Browse files
committed
Extract dependency scanning compiler instance initialization stepts into functions.
1 parent 0e35f56 commit 554613b

File tree

3 files changed

+293
-155
lines changed

3 files changed

+293
-155
lines changed

clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp

Lines changed: 215 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
#include "DependencyScannerImpl.h"
1010
#include "clang/Basic/DiagnosticFrontend.h"
1111
#include "clang/Basic/DiagnosticSerialization.h"
12+
#include "clang/Driver/Driver.h"
1213
#include "clang/Frontend/FrontendActions.h"
1314
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
15+
#include "llvm/TargetParser/Host.h"
1416

1517
using namespace clang;
1618
using namespace tooling;
@@ -334,6 +336,17 @@ class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter {
334336
};
335337
} // namespace
336338

339+
std::unique_ptr<DiagnosticOptions>
340+
clang::tooling::dependencies::createDiagOptions(
341+
const std::vector<std::string> &CommandLine) {
342+
std::vector<const char *> CLI;
343+
for (const std::string &Arg : CommandLine)
344+
CLI.push_back(Arg.c_str());
345+
auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
346+
sanitizeDiagOpts(*DiagOpts);
347+
return DiagOpts;
348+
}
349+
337350
/// Sanitize diagnostic options for dependency scan.
338351
void clang::tooling::dependencies::sanitizeDiagOpts(
339352
DiagnosticOptions &DiagOpts) {
@@ -356,43 +369,131 @@ void clang::tooling::dependencies::sanitizeDiagOpts(
356369
});
357370
}
358371

359-
bool DependencyScanningAction::runInvocation(
360-
std::shared_ptr<CompilerInvocation> Invocation,
361-
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
362-
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
363-
DiagnosticConsumer *DiagConsumer) {
364-
// Making sure that we canonicalize the defines before we create the deep
365-
// copy to avoid unnecessary variants in the scanner and in the resulting
366-
// explicit command lines.
367-
if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros))
368-
canonicalizeDefines(Invocation->getPreprocessorOpts());
372+
std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>>
373+
clang::tooling::dependencies::buildCompilation(
374+
ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags,
375+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
376+
SmallVector<const char *, 256> Argv;
377+
Argv.reserve(ArgStrs.size());
378+
for (const std::string &Arg : ArgStrs)
379+
Argv.push_back(Arg.c_str());
380+
381+
std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
382+
Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
383+
"clang LLVM compiler", FS);
384+
Driver->setTitle("clang_based_tool");
385+
386+
llvm::BumpPtrAllocator Alloc;
387+
bool CLMode = driver::IsClangCL(
388+
driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
389+
390+
if (llvm::Error E =
391+
driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) {
392+
Diags.Report(diag::err_drv_expand_response_file)
393+
<< llvm::toString(std::move(E));
394+
return std::make_pair(nullptr, nullptr);
395+
}
369396

370-
// Make a deep copy of the original Clang invocation.
371-
CompilerInvocation OriginalInvocation(*Invocation);
397+
std::unique_ptr<driver::Compilation> Compilation(
398+
Driver->BuildCompilation(llvm::ArrayRef(Argv)));
399+
if (!Compilation)
400+
return std::make_pair(nullptr, nullptr);
372401

373-
if (Scanned) {
374-
// Scanning runs once for the first -cc1 invocation in a chain of driver
375-
// jobs. For any dependent jobs, reuse the scanning result and just
376-
// update the LastCC1Arguments to correspond to the new invocation.
377-
// FIXME: to support multi-arch builds, each arch requires a separate scan
378-
setLastCC1Arguments(std::move(OriginalInvocation));
379-
return true;
402+
if (Compilation->containsError())
403+
return std::make_pair(nullptr, nullptr);
404+
405+
return std::make_pair(std::move(Driver), std::move(Compilation));
406+
}
407+
408+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
409+
clang::tooling::dependencies::initVFSForTUBufferScanning(
410+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
411+
std::optional<std::vector<std::string>> &ModifiedCommandLine,
412+
const std::vector<std::string> &CommandLine, StringRef WorkingDirectory,
413+
std::optional<llvm::MemoryBufferRef> TUBuffer) {
414+
// Reset what might have been modified in the previous worker invocation.
415+
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
416+
417+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
418+
if (TUBuffer) {
419+
auto OverlayFS =
420+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
421+
auto InMemoryFS =
422+
llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
423+
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
424+
auto InputPath = TUBuffer->getBufferIdentifier();
425+
InMemoryFS->addFile(
426+
InputPath, 0,
427+
llvm::MemoryBuffer::getMemBufferCopy(TUBuffer->getBuffer()));
428+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay =
429+
InMemoryFS;
430+
431+
OverlayFS->pushOverlay(InMemoryOverlay);
432+
ModifiedFS = OverlayFS;
433+
ModifiedCommandLine = CommandLine;
434+
ModifiedCommandLine->emplace_back(InputPath);
435+
436+
return ModifiedFS;
380437
}
381438

382-
Scanned = true;
439+
return BaseFS;
440+
}
383441

384-
// Create a compiler instance to handle the actual work.
385-
auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries());
386-
ScanInstanceStorage.emplace(std::move(Invocation), std::move(PCHContainerOps),
387-
ModCache.get());
388-
CompilerInstance &ScanInstance = *ScanInstanceStorage;
442+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
443+
clang::tooling::dependencies::initVFSForByNameScanning(
444+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
445+
std::vector<std::string> &CommandLine, StringRef WorkingDirectory,
446+
StringRef FakeFileNamePrefix) {
447+
// Reset what might have been modified in the previous worker invocation.
448+
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
449+
450+
// If we're scanning based on a module name alone, we don't expect the client
451+
// to provide us with an input file. However, the driver really wants to have
452+
// one. Let's just make it up to make the driver happy.
453+
auto OverlayFS =
454+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
455+
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
456+
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
457+
SmallString<128> FakeInputPath;
458+
// TODO: We should retry the creation if the path already exists.
459+
llvm::sys::fs::createUniquePath(FakeFileNamePrefix + "-%%%%%%%%.input",
460+
FakeInputPath,
461+
/*MakeAbsolute=*/false);
462+
InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
463+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
464+
OverlayFS->pushOverlay(InMemoryOverlay);
465+
466+
CommandLine.emplace_back(FakeInputPath);
467+
468+
return OverlayFS;
469+
}
470+
471+
std::unique_ptr<CompilerInvocation>
472+
clang::tooling::dependencies::createCompilerInvocation(
473+
const std::vector<std::string> &CommandLine, DiagnosticsEngine &Diags) {
474+
llvm::opt::ArgStringList Argv;
475+
for (const std::string &Str : ArrayRef(CommandLine).drop_front())
476+
Argv.push_back(Str.c_str());
477+
478+
auto Invocation = std::make_unique<CompilerInvocation>();
479+
if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) {
480+
// FIXME: Should we just go on like cc1_main does?
481+
return nullptr;
482+
}
483+
return Invocation;
484+
}
485+
486+
bool clang::tooling::dependencies::initializeScanCompilerInstance(
487+
CompilerInstance &ScanInstance,
488+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
489+
DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service,
490+
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS) {
389491
ScanInstance.setBuildingModule(false);
390492

391493
ScanInstance.createVirtualFileSystem(FS, DiagConsumer);
392494

393495
// Create the compiler's actual diagnostics engine.
394496
sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
395-
assert(!DiagConsumerFinished && "attempt to reuse finished consumer");
396497
ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
397498
if (!ScanInstance.hasDiagnostics())
398499
return false;
@@ -434,14 +535,39 @@ bool DependencyScanningAction::runInvocation(
434535

435536
ScanInstance.createSourceManager(*FileMgr);
436537

538+
// Consider different header search and diagnostic options to create
539+
// different modules. This avoids the unsound aliasing of module PCMs.
540+
//
541+
// TODO: Implement diagnostic bucketing to reduce the impact of strict
542+
// context hashing.
543+
ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
544+
ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true;
545+
ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
546+
ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
547+
ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true;
548+
ScanInstance.getHeaderSearchOpts().ModulesForceValidateUserHeaders = false;
549+
550+
// Avoid some checks and module map parsing when loading PCM files.
551+
ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
552+
553+
return true;
554+
}
555+
556+
llvm::SmallVector<StringRef> clang::tooling::dependencies::computeStableDirs(
557+
CompilerInstance &ScanInstance) {
437558
// Create a collection of stable directories derived from the ScanInstance
438559
// for determining whether module dependencies would fully resolve from
439560
// those directories.
440561
llvm::SmallVector<StringRef> StableDirs;
441562
const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot;
442563
if (!Sysroot.empty() && (llvm::sys::path::root_directory(Sysroot) != Sysroot))
443564
StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir};
565+
return StableDirs;
566+
}
444567

568+
std::optional<PrebuiltModulesAttrsMap>
569+
clang::tooling::dependencies::computePrebuiltModulesASTMap(
570+
CompilerInstance &ScanInstance, llvm::SmallVector<StringRef> &StableDirs) {
445571
// Store a mapping of prebuilt module files and their properties like header
446572
// search options. This will prevent the implicit build to create duplicate
447573
// modules and will force reuse of the existing prebuilt module files
@@ -453,8 +579,18 @@ bool DependencyScanningAction::runInvocation(
453579
ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance,
454580
ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
455581
PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs))
456-
return false;
582+
return {};
457583

584+
return PrebuiltModulesASTMap;
585+
}
586+
587+
void clang::tooling::dependencies::initializeModuleDepCollector(
588+
CompilerInstance &ScanInstance, std::shared_ptr<ModuleDepCollector> &MDC,
589+
StringRef WorkingDirectory, DependencyConsumer &Consumer,
590+
DependencyScanningService &Service, CompilerInvocation &Inv,
591+
DependencyActionController &Controller,
592+
PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
593+
llvm::SmallVector<StringRef> &StableDirs) {
458594
// Create the dependency collector that will collect the produced
459595
// dependencies.
460596
//
@@ -463,6 +599,11 @@ bool DependencyScanningAction::runInvocation(
463599
// which ensures that the compiler won't create new dependency collectors,
464600
// and thus won't write out the extra '.d' files to disk.
465601
auto Opts = std::make_unique<DependencyOutputOptions>();
602+
603+
// We rely on the behaviour that that ScanInstance's Invocation instance's
604+
// dependency output opts are cleared here.
605+
// TODO: we will need to preserve and recover the original dependency output
606+
// opts if we want to reuse ScanInstance.
466607
std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
467608
// We need at least one -MT equivalent for the generator of make dependency
468609
// files to work.
@@ -480,26 +621,58 @@ bool DependencyScanningAction::runInvocation(
480621
case ScanningOutputFormat::P1689:
481622
case ScanningOutputFormat::Full:
482623
MDC = std::make_shared<ModuleDepCollector>(
483-
Service, std::move(Opts), ScanInstance, Consumer, Controller,
484-
OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs);
624+
Service, std::move(Opts), ScanInstance, Consumer, Controller, Inv,
625+
std::move(PrebuiltModulesASTMap), StableDirs);
485626
ScanInstance.addDependencyCollector(MDC);
486627
break;
487628
}
629+
}
488630

489-
// Consider different header search and diagnostic options to create
490-
// different modules. This avoids the unsound aliasing of module PCMs.
491-
//
492-
// TODO: Implement diagnostic bucketing to reduce the impact of strict
493-
// context hashing.
494-
ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
495-
ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true;
496-
ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
497-
ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
498-
ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true;
499-
ScanInstance.getHeaderSearchOpts().ModulesForceValidateUserHeaders = false;
631+
bool DependencyScanningAction::runInvocation(
632+
std::unique_ptr<CompilerInvocation> Invocation,
633+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
634+
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
635+
DiagnosticConsumer *DiagConsumer) {
636+
// Making sure that we canonicalize the defines before we create the deep
637+
// copy to avoid unnecessary variants in the scanner and in the resulting
638+
// explicit command lines.
639+
if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros))
640+
canonicalizeDefines(Invocation->getPreprocessorOpts());
500641

501-
// Avoid some checks and module map parsing when loading PCM files.
502-
ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
642+
// Make a deep copy of the original Clang invocation.
643+
CompilerInvocation OriginalInvocation(*Invocation);
644+
645+
if (Scanned) {
646+
// Scanning runs once for the first -cc1 invocation in a chain of driver
647+
// jobs. For any dependent jobs, reuse the scanning result and just
648+
// update the LastCC1Arguments to correspond to the new invocation.
649+
// FIXME: to support multi-arch builds, each arch requires a separate scan
650+
setLastCC1Arguments(std::move(OriginalInvocation));
651+
return true;
652+
}
653+
654+
Scanned = true;
655+
656+
// Create a compiler instance to handle the actual work.
657+
auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries());
658+
ScanInstanceStorage.emplace(std::move(Invocation), std::move(PCHContainerOps),
659+
ModCache.get());
660+
CompilerInstance &ScanInstance = *ScanInstanceStorage;
661+
662+
assert(!DiagConsumerFinished && "attempt to reuse finished consumer");
663+
if (!initializeScanCompilerInstance(ScanInstance, FS, DiagConsumer, Service,
664+
DepFS))
665+
return false;
666+
667+
llvm::SmallVector<StringRef> StableDirs = computeStableDirs(ScanInstance);
668+
auto MaybePrebuiltModulesASTMap =
669+
computePrebuiltModulesASTMap(ScanInstance, StableDirs);
670+
if (!MaybePrebuiltModulesASTMap)
671+
return false;
672+
673+
initializeModuleDepCollector(ScanInstance, MDC, WorkingDirectory, Consumer,
674+
Service, OriginalInvocation, Controller,
675+
*MaybePrebuiltModulesASTMap, StableDirs);
503676

504677
std::unique_ptr<FrontendAction> Action;
505678

0 commit comments

Comments
 (0)