1//===- DependencyScanningWorker.cpp - Thread-Safe Scanning Worker ---------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/DependencyScanning/DependencyScanningWorker.h"
10#include "clang/Basic/Diagnostic.h"
11#include "clang/Basic/DiagnosticFrontend.h"
12#include "clang/DependencyScanning/DependencyScannerImpl.h"
13#include "clang/Serialization/ObjectFilePCHContainerReader.h"
14#include "llvm/ADT/IntrusiveRefCntPtr.h"
15#include "llvm/Support/VirtualFileSystem.h"
16
17using namespace clang;
18using namespace dependencies;
19
20DependencyScanningWorker::DependencyScanningWorker(
21 DependencyScanningService &Service)
22 : Service(Service) {
23 PCHContainerOps = std::make_shared<PCHContainerOperations>();
24 // We need to read object files from PCH built outside the scanner.
25 PCHContainerOps->registerReader(
26 Reader: std::make_unique<ObjectFilePCHContainerReader>());
27 // The scanner itself writes only raw ast files.
28 PCHContainerOps->registerWriter(Writer: std::make_unique<RawPCHContainerWriter>());
29
30 auto BaseFS = Service.getOpts().MakeVFS();
31
32 if (Service.getOpts().TraceVFS)
33 BaseFS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(
34 A: std::move(BaseFS));
35
36 DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
37 A&: Service.getSharedCache(), A: std::move(BaseFS));
38}
39
40DependencyScanningWorker::~DependencyScanningWorker() = default;
41DependencyActionController::~DependencyActionController() = default;
42
43static bool createAndRunToolInvocation(
44 ArrayRef<std::string> CommandLine, DependencyScanningAction &Action,
45 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
46 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
47 DiagnosticsEngine &Diags) {
48 auto Invocation = createCompilerInvocation(CommandLine, Diags);
49 if (!Invocation)
50 return false;
51
52 return Action.runInvocation(Executable: CommandLine[0], Invocation: std::move(Invocation),
53 FS: std::move(FS), PCHContainerOps,
54 DiagConsumer: Diags.getClient());
55}
56
57bool DependencyScanningWorker::computeDependencies(
58 StringRef WorkingDirectory, ArrayRef<std::string> CommandLine,
59 DependencyConsumer &DepConsumer, DependencyActionController &Controller,
60 DiagnosticConsumer &DiagConsumer,
61 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
62 return computeDependencies(WorkingDirectory,
63 CommandLines: ArrayRef<ArrayRef<std::string>>(CommandLine),
64 DepConsumer, Controller, DiagConsumer, OverlayFS);
65}
66
67bool DependencyScanningWorker::computeDependencies(
68 StringRef WorkingDirectory, ArrayRef<ArrayRef<std::string>> CommandLines,
69 DependencyConsumer &DepConsumer, DependencyActionController &Controller,
70 DiagnosticConsumer &DiagConsumer,
71 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
72 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = nullptr;
73 if (OverlayFS) {
74#ifndef NDEBUG
75 bool SawDepFS = false;
76 OverlayFS->visit(
77 [&](llvm::vfs::FileSystem &VFS) { SawDepFS |= &VFS == DepFS.get(); });
78 assert(SawDepFS && "OverlayFS not based on DepFS");
79#endif
80 FS = std::move(OverlayFS);
81 } else {
82 FS = DepFS;
83 FS->setCurrentWorkingDirectory(WorkingDirectory);
84 }
85
86 DependencyScanningAction Action(Service, WorkingDirectory, DepConsumer,
87 Controller, DepFS);
88
89 const bool Success = llvm::all_of(Range&: CommandLines, P: [&](const auto &Cmd) {
90 if (StringRef(Cmd[1]) != "-cc1") {
91 // Non-clang command. Just pass through to the dependency consumer.
92 DepConsumer.handleBuildCommand(
93 Cmd: {Cmd.front(), {Cmd.begin() + 1, Cmd.end()}});
94 return true;
95 }
96
97 auto DiagEngineWithDiagOpts =
98 DiagnosticsEngineWithDiagOpts(Cmd, FS, DiagConsumer);
99 auto &Diags = *DiagEngineWithDiagOpts.DiagEngine;
100
101 // Create an invocation that uses the underlying file system to ensure that
102 // any file system requests that are made by the driver do not go through
103 // the dependency scanning filesystem.
104 return createAndRunToolInvocation(Cmd, Action, FS, PCHContainerOps, Diags);
105 });
106
107 return Success && Action.hasScanned();
108}
109