| //===- DependencyScanningWorker.cpp - Thread-Safe Scanning Worker ---------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/DependencyScanning/DependencyScanningWorker.h" |
| #include "clang/Basic/Diagnostic.h" |
| #include "clang/Basic/DiagnosticFrontend.h" |
| #include "clang/DependencyScanning/DependencyConsumer.h" |
| #include "clang/DependencyScanning/DependencyScannerImpl.h" |
| #include "clang/Serialization/ObjectFilePCHContainerReader.h" |
| #include "llvm/ADT/IntrusiveRefCntPtr.h" |
| #include "llvm/Support/VirtualFileSystem.h" |
| |
| using namespace clang; |
| using namespace dependencies; |
| |
| DependencyScanningWorker::DependencyScanningWorker( |
| DependencyScanningService &Service) |
| : Service(Service) { |
| PCHContainerOps = std::make_shared<PCHContainerOperations>(); |
| // We need to read object files from PCH built outside the scanner. |
| PCHContainerOps->registerReader( |
| std::make_unique<ObjectFilePCHContainerReader>()); |
| // The scanner itself writes only raw ast files. |
| PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>()); |
| |
| auto BaseFS = Service.getOpts().MakeVFS(); |
| |
| if (Service.getOpts().TraceVFS) { |
| TracingFS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>( |
| std::move(BaseFS)); |
| BaseFS = TracingFS; |
| } |
| |
| DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>( |
| Service.getSharedCache(), std::move(BaseFS)); |
| } |
| |
| DependencyScanningWorker::~DependencyScanningWorker() = default; |
| |
| static bool createAndRunToolInvocation( |
| ArrayRef<std::string> CommandLine, DependencyScanningAction &Action, |
| IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, |
| std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps, |
| DiagnosticsEngine &Diags) { |
| auto Invocation = createCompilerInvocation(CommandLine, Diags); |
| if (!Invocation) |
| return false; |
| |
| return Action.runInvocation(CommandLine[0], std::move(Invocation), |
| std::move(FS), PCHContainerOps, |
| Diags.getClient()); |
| } |
| |
| bool DependencyScanningWorker::computeDependencies( |
| StringRef WorkingDirectory, ArrayRef<ArrayRef<std::string>> CommandLines, |
| DependencyConsumer &DepConsumer, DependencyActionController &Controller, |
| DiagnosticConsumer &DiagConsumer, |
| llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) { |
| IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = nullptr; |
| if (OverlayFS) { |
| #ifndef NDEBUG |
| bool SawDepFS = false; |
| OverlayFS->visit( |
| [&](llvm::vfs::FileSystem &VFS) { SawDepFS |= &VFS == DepFS.get(); }); |
| assert(SawDepFS && "OverlayFS not based on DepFS"); |
| #endif |
| FS = std::move(OverlayFS); |
| } else { |
| FS = DepFS; |
| FS->setCurrentWorkingDirectory(WorkingDirectory); |
| } |
| |
| DependencyScanningAction Action(Service, WorkingDirectory, DepConsumer, |
| Controller, DepFS); |
| |
| const bool Success = llvm::all_of(CommandLines, [&](const auto &Cmd) { |
| if (StringRef(Cmd[1]) != "-cc1") { |
| // Non-clang command. Just pass through to the dependency consumer. |
| DepConsumer.handleBuildCommand( |
| {Cmd.front(), {Cmd.begin() + 1, Cmd.end()}}); |
| return true; |
| } |
| |
| auto DiagEngineWithDiagOpts = |
| DiagnosticsEngineWithDiagOpts(Cmd, FS, DiagConsumer); |
| auto &Diags = *DiagEngineWithDiagOpts.DiagEngine; |
| |
| // Create an invocation that uses the underlying file system to ensure that |
| // any file system requests that are made by the driver do not go through |
| // the dependency scanning filesystem. |
| return createAndRunToolInvocation(Cmd, Action, FS, PCHContainerOps, Diags); |
| }); |
| |
| return Success && Action.hasScanned(); |
| } |