diff options
Diffstat (limited to 'clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp')
-rw-r--r-- | clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp | 279 |
1 files changed, 229 insertions, 50 deletions
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp index 010380d..e1f4d0d 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp @@ -9,8 +9,10 @@ #include "DependencyScannerImpl.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/DiagnosticSerialization.h" +#include "clang/Driver/Driver.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" +#include "llvm/TargetParser/Host.h" using namespace clang; using namespace tooling; @@ -332,11 +334,9 @@ public: return DepFS->getDirectiveTokens(File.getName()); } }; -} // namespace /// Sanitize diagnostic options for dependency scan. -void clang::tooling::dependencies::sanitizeDiagOpts( - DiagnosticOptions &DiagOpts) { +void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) { // Don't print 'X warnings and Y errors generated'. DiagOpts.ShowCarets = false; // Don't write out diagnostic file. @@ -355,44 +355,146 @@ void clang::tooling::dependencies::sanitizeDiagOpts( .Default(true); }); } +} // namespace -bool DependencyScanningAction::runInvocation( - std::shared_ptr<CompilerInvocation> Invocation, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - std::shared_ptr<PCHContainerOperations> PCHContainerOps, - DiagnosticConsumer *DiagConsumer) { - // Making sure that we canonicalize the defines before we create the deep - // copy to avoid unnecessary variants in the scanner and in the resulting - // explicit command lines. - if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros)) - canonicalizeDefines(Invocation->getPreprocessorOpts()); +namespace clang::tooling::dependencies { +std::unique_ptr<DiagnosticOptions> +createDiagOptions(ArrayRef<std::string> CommandLine) { + std::vector<const char *> CLI; + for (const std::string &Arg : CommandLine) + CLI.push_back(Arg.c_str()); + auto DiagOpts = CreateAndPopulateDiagOpts(CLI); + sanitizeDiagOpts(*DiagOpts); + return DiagOpts; +} - // Make a deep copy of the original Clang invocation. - CompilerInvocation OriginalInvocation(*Invocation); +DignosticsEngineWithDiagOpts::DignosticsEngineWithDiagOpts( + ArrayRef<std::string> CommandLine, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC) { + std::vector<const char *> CCommandLine(CommandLine.size(), nullptr); + llvm::transform(CommandLine, CCommandLine.begin(), + [](const std::string &Str) { return Str.c_str(); }); + DiagOpts = CreateAndPopulateDiagOpts(CCommandLine); + sanitizeDiagOpts(*DiagOpts); + DiagEngine = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DC, + /*ShouldOwnClient=*/false); +} - 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; +std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>> +buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) { + SmallVector<const char *, 256> Argv; + Argv.reserve(ArgStrs.size()); + for (const std::string &Arg : ArgStrs) + Argv.push_back(Arg.c_str()); + + 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.get())) { + Diags.Report(diag::err_drv_expand_response_file) + << llvm::toString(std::move(E)); + return std::make_pair(nullptr, nullptr); } - Scanned = true; + std::unique_ptr<driver::Compilation> Compilation( + Driver->BuildCompilation(Argv)); + if (!Compilation) + return std::make_pair(nullptr, nullptr); - // Create a compiler instance to handle the actual work. - auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries()); - ScanInstanceStorage.emplace(std::move(Invocation), std::move(PCHContainerOps), - ModCache.get()); - CompilerInstance &ScanInstance = *ScanInstanceStorage; + if (Compilation->containsError()) + return std::make_pair(nullptr, nullptr); + + return std::make_pair(std::move(Driver), std::move(Compilation)); +} + +std::unique_ptr<CompilerInvocation> +createCompilerInvocation(ArrayRef<std::string> CommandLine, + DiagnosticsEngine &Diags) { + llvm::opt::ArgStringList Argv; + for (const std::string &Str : ArrayRef(CommandLine).drop_front()) + Argv.push_back(Str.c_str()); + + auto Invocation = std::make_unique<CompilerInvocation>(); + if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) { + // FIXME: Should we just go on like cc1_main does? + return nullptr; + } + return Invocation; +} + +std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>> +initVFSForTUBuferScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, + ArrayRef<std::string> CommandLine, + StringRef WorkingDirectory, + llvm::MemoryBufferRef TUBuffer) { + // Reset what might have been modified in the previous worker invocation. + BaseFS->setCurrentWorkingDirectory(WorkingDirectory); + + IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS; + auto OverlayFS = + llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); + auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); + InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); + auto InputPath = TUBuffer.getBufferIdentifier(); + InMemoryFS->addFile( + InputPath, 0, llvm::MemoryBuffer::getMemBufferCopy(TUBuffer.getBuffer())); + IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS; + + OverlayFS->pushOverlay(InMemoryOverlay); + ModifiedFS = OverlayFS; + std::vector<std::string> ModifiedCommandLine(CommandLine); + ModifiedCommandLine.emplace_back(InputPath); + + return std::make_pair(ModifiedFS, ModifiedCommandLine); +} + +std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>> +initVFSForByNameScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, + ArrayRef<std::string> CommandLine, + StringRef WorkingDirectory, StringRef ModuleName) { + // Reset what might have been modified in the previous worker invocation. + BaseFS->setCurrentWorkingDirectory(WorkingDirectory); + + // 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. + auto OverlayFS = + llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); + auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); + InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); + 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("")); + IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS; + OverlayFS->pushOverlay(InMemoryOverlay); + + std::vector<std::string> ModifiedCommandLine(CommandLine); + ModifiedCommandLine.emplace_back(FakeInputPath); + + return std::make_pair(OverlayFS, ModifiedCommandLine); +} + +bool initializeScanCompilerInstance( + CompilerInstance &ScanInstance, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, + DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service, + IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS) { ScanInstance.setBuildingModule(false); ScanInstance.createVirtualFileSystem(FS, DiagConsumer); // Create the compiler's actual diagnostics engine. sanitizeDiagOpts(ScanInstance.getDiagnosticOpts()); - assert(!DiagConsumerFinished && "attempt to reuse finished consumer"); ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false); if (!ScanInstance.hasDiagnostics()) return false; @@ -435,6 +537,26 @@ bool DependencyScanningAction::runInvocation( ScanInstance.createSourceManager(); + // Consider different header search and diagnostic options to create + // different modules. This avoids the unsound aliasing of module PCMs. + // + // TODO: Implement diagnostic bucketing to reduce the impact of strict + // context hashing. + ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true; + ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true; + ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true; + ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true; + ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true; + ScanInstance.getHeaderSearchOpts().ModulesForceValidateUserHeaders = false; + + // Avoid some checks and module map parsing when loading PCM files. + ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false; + + return true; +} + +llvm::SmallVector<StringRef> +getInitialStableDirs(const CompilerInstance &ScanInstance) { // Create a collection of stable directories derived from the ScanInstance // for determining whether module dependencies would fully resolve from // those directories. @@ -442,7 +564,12 @@ bool DependencyScanningAction::runInvocation( const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot; if (!Sysroot.empty() && (llvm::sys::path::root_directory(Sysroot) != Sysroot)) StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir}; + return StableDirs; +} +std::optional<PrebuiltModulesAttrsMap> +computePrebuiltModulesASTMap(CompilerInstance &ScanInstance, + llvm::SmallVector<StringRef> &StableDirs) { // Store a mapping of prebuilt module files and their properties like header // search options. This will prevent the implicit build to create duplicate // modules and will force reuse of the existing prebuilt module files @@ -454,12 +581,14 @@ bool DependencyScanningAction::runInvocation( ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance, ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles, PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs)) - return false; + return {}; - // Create the dependency collector that will collect the produced - // dependencies. - // - // This also moves the existing dependency output options from the + return PrebuiltModulesASTMap; +} + +std::unique_ptr<DependencyOutputOptions> +takeDependencyOutputOptionsFrom(CompilerInstance &ScanInstance) { + // This function moves the existing dependency output options from the // invocation to the collector. The options in the invocation are reset, // which ensures that the compiler won't create new dependency collectors, // and thus won't write out the extra '.d' files to disk. @@ -472,35 +601,85 @@ bool DependencyScanningAction::runInvocation( ScanInstance.getFrontendOpts().Inputs)}; Opts->IncludeSystemHeaders = true; + return Opts; +} + +std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector( + CompilerInstance &ScanInstance, + std::unique_ptr<DependencyOutputOptions> DepOutputOpts, + StringRef WorkingDirectory, DependencyConsumer &Consumer, + DependencyScanningService &Service, CompilerInvocation &Inv, + DependencyActionController &Controller, + PrebuiltModulesAttrsMap PrebuiltModulesASTMap, + llvm::SmallVector<StringRef> &StableDirs) { + std::shared_ptr<ModuleDepCollector> MDC; switch (Service.getFormat()) { case ScanningOutputFormat::Make: ScanInstance.addDependencyCollector( std::make_shared<DependencyConsumerForwarder>( - std::move(Opts), WorkingDirectory, Consumer)); + std::move(DepOutputOpts), WorkingDirectory, Consumer)); break; case ScanningOutputFormat::P1689: case ScanningOutputFormat::Full: MDC = std::make_shared<ModuleDepCollector>( - Service, std::move(Opts), ScanInstance, Consumer, Controller, - OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs); + Service, std::move(DepOutputOpts), ScanInstance, Consumer, Controller, + Inv, std::move(PrebuiltModulesASTMap), StableDirs); 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 to reduce the impact of strict - // context hashing. - ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true; - ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true; - ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true; - ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true; - ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true; - ScanInstance.getHeaderSearchOpts().ModulesForceValidateUserHeaders = false; + return MDC; +} +} // namespace clang::tooling::dependencies - // Avoid some checks and module map parsing when loading PCM files. - ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false; +bool DependencyScanningAction::runInvocation( + std::unique_ptr<CompilerInvocation> Invocation, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + DiagnosticConsumer *DiagConsumer) { + // Making sure that we canonicalize the defines before we create the deep + // copy to avoid unnecessary variants in the scanner and in the resulting + // explicit command lines. + if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros)) + canonicalizeDefines(Invocation->getPreprocessorOpts()); + + // Make a deep copy of the original Clang invocation. + CompilerInvocation OriginalInvocation(*Invocation); + + 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. + auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries()); + ScanInstanceStorage.emplace(std::move(Invocation), std::move(PCHContainerOps), + ModCache.get()); + CompilerInstance &ScanInstance = *ScanInstanceStorage; + + assert(!DiagConsumerFinished && "attempt to reuse finished consumer"); + if (!initializeScanCompilerInstance(ScanInstance, FS, DiagConsumer, Service, + DepFS)) + return false; + + llvm::SmallVector<StringRef> StableDirs = getInitialStableDirs(ScanInstance); + auto MaybePrebuiltModulesASTMap = + computePrebuiltModulesASTMap(ScanInstance, StableDirs); + if (!MaybePrebuiltModulesASTMap) + return false; + + auto DepOutputOpts = takeDependencyOutputOptionsFrom(ScanInstance); + + MDC = initializeScanInstanceDependencyCollector( + ScanInstance, std::move(DepOutputOpts), WorkingDirectory, Consumer, + Service, OriginalInvocation, Controller, *MaybePrebuiltModulesASTMap, + StableDirs); std::unique_ptr<FrontendAction> Action; |