Skip to content

Commit 4368616

Browse files
authored
[clang][Dependency Scanning] Refactor Scanning Compiler Instance Initialization (#161300)
This PR follows #160795, and it is the second of a series of planned PRs to land #160207 in smaller pieces. The initialization steps before and within `DependencyScanningAction::runInvocation` are broken up in to several helper functions. The intention is to reuse the helper functions in a followup PR to initialize the `CompilerInstanceWithContext`. Part of work for rdar://136303612.
1 parent c2c2e4e commit 4368616

File tree

4 files changed

+346
-207
lines changed

4 files changed

+346
-207
lines changed

clang/lib/Tooling/DependencyScanning/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,5 @@ add_clang_library(clangDependencyScanning
2424
clangFrontend
2525
clangLex
2626
clangSerialization
27-
clangTooling
2827
${LLVM_PTHREAD_LIB}
2928
)

clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp

Lines changed: 229 additions & 50 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;
@@ -332,11 +334,9 @@ class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter {
332334
return DepFS->getDirectiveTokens(File.getName());
333335
}
334336
};
335-
} // namespace
336337

337338
/// Sanitize diagnostic options for dependency scan.
338-
void clang::tooling::dependencies::sanitizeDiagOpts(
339-
DiagnosticOptions &DiagOpts) {
339+
void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
340340
// Don't print 'X warnings and Y errors generated'.
341341
DiagOpts.ShowCarets = false;
342342
// Don't write out diagnostic file.
@@ -355,44 +355,146 @@ void clang::tooling::dependencies::sanitizeDiagOpts(
355355
.Default(true);
356356
});
357357
}
358+
} // namespace
358359

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());
360+
namespace clang::tooling::dependencies {
361+
std::unique_ptr<DiagnosticOptions>
362+
createDiagOptions(ArrayRef<std::string> CommandLine) {
363+
std::vector<const char *> CLI;
364+
for (const std::string &Arg : CommandLine)
365+
CLI.push_back(Arg.c_str());
366+
auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
367+
sanitizeDiagOpts(*DiagOpts);
368+
return DiagOpts;
369+
}
369370

370-
// Make a deep copy of the original Clang invocation.
371-
CompilerInvocation OriginalInvocation(*Invocation);
371+
DignosticsEngineWithDiagOpts::DignosticsEngineWithDiagOpts(
372+
ArrayRef<std::string> CommandLine,
373+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC) {
374+
std::vector<const char *> CCommandLine(CommandLine.size(), nullptr);
375+
llvm::transform(CommandLine, CCommandLine.begin(),
376+
[](const std::string &Str) { return Str.c_str(); });
377+
DiagOpts = CreateAndPopulateDiagOpts(CCommandLine);
378+
sanitizeDiagOpts(*DiagOpts);
379+
DiagEngine = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DC,
380+
/*ShouldOwnClient=*/false);
381+
}
372382

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;
383+
std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>>
384+
buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags,
385+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
386+
SmallVector<const char *, 256> Argv;
387+
Argv.reserve(ArgStrs.size());
388+
for (const std::string &Arg : ArgStrs)
389+
Argv.push_back(Arg.c_str());
390+
391+
std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
392+
Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
393+
"clang LLVM compiler", FS);
394+
Driver->setTitle("clang_based_tool");
395+
396+
llvm::BumpPtrAllocator Alloc;
397+
bool CLMode = driver::IsClangCL(
398+
driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
399+
400+
if (llvm::Error E =
401+
driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) {
402+
Diags.Report(diag::err_drv_expand_response_file)
403+
<< llvm::toString(std::move(E));
404+
return std::make_pair(nullptr, nullptr);
380405
}
381406

382-
Scanned = true;
407+
std::unique_ptr<driver::Compilation> Compilation(
408+
Driver->BuildCompilation(Argv));
409+
if (!Compilation)
410+
return std::make_pair(nullptr, nullptr);
383411

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;
412+
if (Compilation->containsError())
413+
return std::make_pair(nullptr, nullptr);
414+
415+
return std::make_pair(std::move(Driver), std::move(Compilation));
416+
}
417+
418+
std::unique_ptr<CompilerInvocation>
419+
createCompilerInvocation(ArrayRef<std::string> CommandLine,
420+
DiagnosticsEngine &Diags) {
421+
llvm::opt::ArgStringList Argv;
422+
for (const std::string &Str : ArrayRef(CommandLine).drop_front())
423+
Argv.push_back(Str.c_str());
424+
425+
auto Invocation = std::make_unique<CompilerInvocation>();
426+
if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) {
427+
// FIXME: Should we just go on like cc1_main does?
428+
return nullptr;
429+
}
430+
return Invocation;
431+
}
432+
433+
std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>>
434+
initVFSForTUBuferScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
435+
ArrayRef<std::string> CommandLine,
436+
StringRef WorkingDirectory,
437+
llvm::MemoryBufferRef TUBuffer) {
438+
// Reset what might have been modified in the previous worker invocation.
439+
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
440+
441+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
442+
auto OverlayFS =
443+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
444+
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
445+
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
446+
auto InputPath = TUBuffer.getBufferIdentifier();
447+
InMemoryFS->addFile(
448+
InputPath, 0, llvm::MemoryBuffer::getMemBufferCopy(TUBuffer.getBuffer()));
449+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
450+
451+
OverlayFS->pushOverlay(InMemoryOverlay);
452+
ModifiedFS = OverlayFS;
453+
std::vector<std::string> ModifiedCommandLine(CommandLine);
454+
ModifiedCommandLine.emplace_back(InputPath);
455+
456+
return std::make_pair(ModifiedFS, ModifiedCommandLine);
457+
}
458+
459+
std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>>
460+
initVFSForByNameScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
461+
ArrayRef<std::string> CommandLine,
462+
StringRef WorkingDirectory, StringRef ModuleName) {
463+
// Reset what might have been modified in the previous worker invocation.
464+
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
465+
466+
// If we're scanning based on a module name alone, we don't expect the client
467+
// to provide us with an input file. However, the driver really wants to have
468+
// one. Let's just make it up to make the driver happy.
469+
auto OverlayFS =
470+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
471+
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
472+
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
473+
SmallString<128> FakeInputPath;
474+
// TODO: We should retry the creation if the path already exists.
475+
llvm::sys::fs::createUniquePath(ModuleName + "-%%%%%%%%.input", FakeInputPath,
476+
/*MakeAbsolute=*/false);
477+
InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
478+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
479+
OverlayFS->pushOverlay(InMemoryOverlay);
480+
481+
std::vector<std::string> ModifiedCommandLine(CommandLine);
482+
ModifiedCommandLine.emplace_back(FakeInputPath);
483+
484+
return std::make_pair(OverlayFS, ModifiedCommandLine);
485+
}
486+
487+
bool initializeScanCompilerInstance(
488+
CompilerInstance &ScanInstance,
489+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
490+
DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service,
491+
IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS) {
389492
ScanInstance.setBuildingModule(false);
390493

391494
ScanInstance.createVirtualFileSystem(FS, DiagConsumer);
392495

393496
// Create the compiler's actual diagnostics engine.
394497
sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
395-
assert(!DiagConsumerFinished && "attempt to reuse finished consumer");
396498
ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
397499
if (!ScanInstance.hasDiagnostics())
398500
return false;
@@ -435,14 +537,39 @@ bool DependencyScanningAction::runInvocation(
435537

436538
ScanInstance.createSourceManager();
437539

540+
// Consider different header search and diagnostic options to create
541+
// different modules. This avoids the unsound aliasing of module PCMs.
542+
//
543+
// TODO: Implement diagnostic bucketing to reduce the impact of strict
544+
// context hashing.
545+
ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
546+
ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true;
547+
ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
548+
ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
549+
ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true;
550+
ScanInstance.getHeaderSearchOpts().ModulesForceValidateUserHeaders = false;
551+
552+
// Avoid some checks and module map parsing when loading PCM files.
553+
ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
554+
555+
return true;
556+
}
557+
558+
llvm::SmallVector<StringRef>
559+
getInitialStableDirs(const CompilerInstance &ScanInstance) {
438560
// Create a collection of stable directories derived from the ScanInstance
439561
// for determining whether module dependencies would fully resolve from
440562
// those directories.
441563
llvm::SmallVector<StringRef> StableDirs;
442564
const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot;
443565
if (!Sysroot.empty() && (llvm::sys::path::root_directory(Sysroot) != Sysroot))
444566
StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir};
567+
return StableDirs;
568+
}
445569

570+
std::optional<PrebuiltModulesAttrsMap>
571+
computePrebuiltModulesASTMap(CompilerInstance &ScanInstance,
572+
llvm::SmallVector<StringRef> &StableDirs) {
446573
// Store a mapping of prebuilt module files and their properties like header
447574
// search options. This will prevent the implicit build to create duplicate
448575
// modules and will force reuse of the existing prebuilt module files
@@ -454,12 +581,14 @@ bool DependencyScanningAction::runInvocation(
454581
ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance,
455582
ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
456583
PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs))
457-
return false;
584+
return {};
458585

459-
// Create the dependency collector that will collect the produced
460-
// dependencies.
461-
//
462-
// This also moves the existing dependency output options from the
586+
return PrebuiltModulesASTMap;
587+
}
588+
589+
std::unique_ptr<DependencyOutputOptions>
590+
takeDependencyOutputOptionsFrom(CompilerInstance &ScanInstance) {
591+
// This function moves the existing dependency output options from the
463592
// invocation to the collector. The options in the invocation are reset,
464593
// which ensures that the compiler won't create new dependency collectors,
465594
// and thus won't write out the extra '.d' files to disk.
@@ -472,35 +601,85 @@ bool DependencyScanningAction::runInvocation(
472601
ScanInstance.getFrontendOpts().Inputs)};
473602
Opts->IncludeSystemHeaders = true;
474603

604+
return Opts;
605+
}
606+
607+
std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector(
608+
CompilerInstance &ScanInstance,
609+
std::unique_ptr<DependencyOutputOptions> DepOutputOpts,
610+
StringRef WorkingDirectory, DependencyConsumer &Consumer,
611+
DependencyScanningService &Service, CompilerInvocation &Inv,
612+
DependencyActionController &Controller,
613+
PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
614+
llvm::SmallVector<StringRef> &StableDirs) {
615+
std::shared_ptr<ModuleDepCollector> MDC;
475616
switch (Service.getFormat()) {
476617
case ScanningOutputFormat::Make:
477618
ScanInstance.addDependencyCollector(
478619
std::make_shared<DependencyConsumerForwarder>(
479-
std::move(Opts), WorkingDirectory, Consumer));
620+
std::move(DepOutputOpts), WorkingDirectory, Consumer));
480621
break;
481622
case ScanningOutputFormat::P1689:
482623
case ScanningOutputFormat::Full:
483624
MDC = std::make_shared<ModuleDepCollector>(
484-
Service, std::move(Opts), ScanInstance, Consumer, Controller,
485-
OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs);
625+
Service, std::move(DepOutputOpts), ScanInstance, Consumer, Controller,
626+
Inv, std::move(PrebuiltModulesASTMap), StableDirs);
486627
ScanInstance.addDependencyCollector(MDC);
487628
break;
488629
}
489630

490-
// Consider different header search and diagnostic options to create
491-
// different modules. This avoids the unsound aliasing of module PCMs.
492-
//
493-
// TODO: Implement diagnostic bucketing to reduce the impact of strict
494-
// context hashing.
495-
ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
496-
ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true;
497-
ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
498-
ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
499-
ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true;
500-
ScanInstance.getHeaderSearchOpts().ModulesForceValidateUserHeaders = false;
631+
return MDC;
632+
}
633+
} // namespace clang::tooling::dependencies
501634

502-
// Avoid some checks and module map parsing when loading PCM files.
503-
ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
635+
bool DependencyScanningAction::runInvocation(
636+
std::unique_ptr<CompilerInvocation> Invocation,
637+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
638+
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
639+
DiagnosticConsumer *DiagConsumer) {
640+
// Making sure that we canonicalize the defines before we create the deep
641+
// copy to avoid unnecessary variants in the scanner and in the resulting
642+
// explicit command lines.
643+
if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros))
644+
canonicalizeDefines(Invocation->getPreprocessorOpts());
645+
646+
// Make a deep copy of the original Clang invocation.
647+
CompilerInvocation OriginalInvocation(*Invocation);
648+
649+
if (Scanned) {
650+
// Scanning runs once for the first -cc1 invocation in a chain of driver
651+
// jobs. For any dependent jobs, reuse the scanning result and just
652+
// update the LastCC1Arguments to correspond to the new invocation.
653+
// FIXME: to support multi-arch builds, each arch requires a separate scan
654+
setLastCC1Arguments(std::move(OriginalInvocation));
655+
return true;
656+
}
657+
658+
Scanned = true;
659+
660+
// Create a compiler instance to handle the actual work.
661+
auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries());
662+
ScanInstanceStorage.emplace(std::move(Invocation), std::move(PCHContainerOps),
663+
ModCache.get());
664+
CompilerInstance &ScanInstance = *ScanInstanceStorage;
665+
666+
assert(!DiagConsumerFinished && "attempt to reuse finished consumer");
667+
if (!initializeScanCompilerInstance(ScanInstance, FS, DiagConsumer, Service,
668+
DepFS))
669+
return false;
670+
671+
llvm::SmallVector<StringRef> StableDirs = getInitialStableDirs(ScanInstance);
672+
auto MaybePrebuiltModulesASTMap =
673+
computePrebuiltModulesASTMap(ScanInstance, StableDirs);
674+
if (!MaybePrebuiltModulesASTMap)
675+
return false;
676+
677+
auto DepOutputOpts = takeDependencyOutputOptionsFrom(ScanInstance);
678+
679+
MDC = initializeScanInstanceDependencyCollector(
680+
ScanInstance, std::move(DepOutputOpts), WorkingDirectory, Consumer,
681+
Service, OriginalInvocation, Controller, *MaybePrebuiltModulesASTMap,
682+
StableDirs);
504683

505684
std::unique_ptr<FrontendAction> Action;
506685

0 commit comments

Comments
 (0)