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
1517using namespace clang ;
1618using 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