9
9
#include " DependencyScannerImpl.h"
10
10
#include " clang/Basic/DiagnosticFrontend.h"
11
11
#include " clang/Basic/DiagnosticSerialization.h"
12
+ #include " clang/Driver/Driver.h"
12
13
#include " clang/Frontend/FrontendActions.h"
13
14
#include " clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
15
+ #include " llvm/TargetParser/Host.h"
14
16
15
17
using namespace clang ;
16
18
using namespace tooling ;
@@ -332,11 +334,9 @@ class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter {
332
334
return DepFS->getDirectiveTokens (File.getName ());
333
335
}
334
336
};
335
- } // namespace
336
337
337
338
// / Sanitize diagnostic options for dependency scan.
338
- void clang::tooling::dependencies::sanitizeDiagOpts (
339
- DiagnosticOptions &DiagOpts) {
339
+ void sanitizeDiagOpts (DiagnosticOptions &DiagOpts) {
340
340
// Don't print 'X warnings and Y errors generated'.
341
341
DiagOpts.ShowCarets = false ;
342
342
// Don't write out diagnostic file.
@@ -355,44 +355,146 @@ void clang::tooling::dependencies::sanitizeDiagOpts(
355
355
.Default (true );
356
356
});
357
357
}
358
+ } // namespace
358
359
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
+ }
369
370
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
+ }
372
382
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 );
380
405
}
381
406
382
- Scanned = true ;
407
+ std::unique_ptr<driver::Compilation> Compilation (
408
+ Driver->BuildCompilation (Argv));
409
+ if (!Compilation)
410
+ return std::make_pair (nullptr , nullptr );
383
411
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) {
389
492
ScanInstance.setBuildingModule (false );
390
493
391
494
ScanInstance.createVirtualFileSystem (FS, DiagConsumer);
392
495
393
496
// Create the compiler's actual diagnostics engine.
394
497
sanitizeDiagOpts (ScanInstance.getDiagnosticOpts ());
395
- assert (!DiagConsumerFinished && " attempt to reuse finished consumer" );
396
498
ScanInstance.createDiagnostics (DiagConsumer, /* ShouldOwnClient=*/ false );
397
499
if (!ScanInstance.hasDiagnostics ())
398
500
return false ;
@@ -435,14 +537,39 @@ bool DependencyScanningAction::runInvocation(
435
537
436
538
ScanInstance.createSourceManager ();
437
539
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) {
438
560
// Create a collection of stable directories derived from the ScanInstance
439
561
// for determining whether module dependencies would fully resolve from
440
562
// those directories.
441
563
llvm::SmallVector<StringRef> StableDirs;
442
564
const StringRef Sysroot = ScanInstance.getHeaderSearchOpts ().Sysroot ;
443
565
if (!Sysroot.empty () && (llvm::sys::path::root_directory (Sysroot) != Sysroot))
444
566
StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts ().ResourceDir };
567
+ return StableDirs;
568
+ }
445
569
570
+ std::optional<PrebuiltModulesAttrsMap>
571
+ computePrebuiltModulesASTMap (CompilerInstance &ScanInstance,
572
+ llvm::SmallVector<StringRef> &StableDirs) {
446
573
// Store a mapping of prebuilt module files and their properties like header
447
574
// search options. This will prevent the implicit build to create duplicate
448
575
// modules and will force reuse of the existing prebuilt module files
@@ -454,12 +581,14 @@ bool DependencyScanningAction::runInvocation(
454
581
ScanInstance.getPreprocessorOpts ().ImplicitPCHInclude , ScanInstance,
455
582
ScanInstance.getHeaderSearchOpts ().PrebuiltModuleFiles ,
456
583
PrebuiltModulesASTMap, ScanInstance.getDiagnostics (), StableDirs))
457
- return false ;
584
+ return {} ;
458
585
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
463
592
// invocation to the collector. The options in the invocation are reset,
464
593
// which ensures that the compiler won't create new dependency collectors,
465
594
// and thus won't write out the extra '.d' files to disk.
@@ -472,35 +601,85 @@ bool DependencyScanningAction::runInvocation(
472
601
ScanInstance.getFrontendOpts ().Inputs )};
473
602
Opts->IncludeSystemHeaders = true ;
474
603
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;
475
616
switch (Service.getFormat ()) {
476
617
case ScanningOutputFormat::Make:
477
618
ScanInstance.addDependencyCollector (
478
619
std::make_shared<DependencyConsumerForwarder>(
479
- std::move (Opts ), WorkingDirectory, Consumer));
620
+ std::move (DepOutputOpts ), WorkingDirectory, Consumer));
480
621
break ;
481
622
case ScanningOutputFormat::P1689:
482
623
case ScanningOutputFormat::Full:
483
624
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);
486
627
ScanInstance.addDependencyCollector (MDC);
487
628
break ;
488
629
}
489
630
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
501
634
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);
504
683
505
684
std::unique_ptr<FrontendAction> Action;
506
685
0 commit comments