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/DependencyConsumer.h"
13#include "clang/DependencyScanning/DependencyScannerImpl.h"
14#include "clang/Serialization/ObjectFilePCHContainerReader.h"
15#include "llvm/ADT/IntrusiveRefCntPtr.h"
16#include "llvm/ADT/ScopeExit.h"
17#include "llvm/Support/VirtualFileSystem.h"
18
19using namespace clang;
20using namespace dependencies;
21
22DependencyScanningWorker::DependencyScanningWorker(
23 DependencyScanningService &Service)
24 : Service(Service) {
25 PCHContainerOps = std::make_shared<PCHContainerOperations>();
26 // We need to read object files from PCH built outside the scanner.
27 PCHContainerOps->registerReader(
28 Reader: std::make_unique<ObjectFilePCHContainerReader>());
29 // The scanner itself writes only raw ast files.
30 PCHContainerOps->registerWriter(Writer: std::make_unique<RawPCHContainerWriter>());
31
32 auto BaseFS = Service.getOpts().MakeVFS();
33
34 if (Service.getOpts().TraceVFS) {
35 TracingFS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(
36 A: std::move(BaseFS));
37 BaseFS = TracingFS;
38 }
39
40 DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
41 A&: Service, A: std::move(BaseFS));
42}
43
44DependencyScanningWorker::~DependencyScanningWorker() = default;
45
46static bool createAndRunToolInvocation(
47 ArrayRef<std::string> CommandLine, DependencyScanningAction &Action,
48 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
49 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
50 DiagnosticsEngine &Diags) {
51 auto Invocation = createCompilerInvocation(CommandLine, Diags);
52 if (!Invocation)
53 return false;
54
55 return Action.runInvocation(Executable: CommandLine[0], Invocation: std::move(Invocation),
56 FS: std::move(FS), PCHContainerOps,
57 DiagConsumer: Diags.getClient());
58}
59
60IntrusiveRefCntPtr<llvm::vfs::FileSystem>
61DependencyScanningWorker::makeEffectiveVFS(
62 StringRef WorkingDirectory,
63 IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS) const {
64 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = DepFS;
65 if (OverlayFS) {
66 auto NewFS =
67 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(A: std::move(FS));
68 NewFS->pushOverlay(FS: std::move(OverlayFS));
69 FS = std::move(NewFS);
70 }
71 FS->setCurrentWorkingDirectory(WorkingDirectory);
72 return FS;
73}
74
75bool DependencyScanningWorker::computeDependencies(
76 StringRef WorkingDirectory, ArrayRef<ArrayRef<std::string>> CommandLines,
77 DependencyConsumer &DepConsumer, DependencyActionController &Controller,
78 DiagnosticConsumer &DiagConsumer,
79 IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS) {
80 auto FS = makeEffectiveVFS(WorkingDirectory, OverlayFS: std::move(OverlayFS));
81
82 DependencyScanningAction Action(Service, WorkingDirectory, DepConsumer,
83 Controller, DepFS);
84
85 const bool Success = llvm::all_of(Range&: CommandLines, P: [&](const auto &Cmd) {
86 if (StringRef(Cmd[1]) != "-cc1") {
87 // Non-clang command. Just pass through to the dependency consumer.
88 DepConsumer.handleBuildCommand(
89 Cmd: {Cmd.front(), {Cmd.begin() + 1, Cmd.end()}});
90 return true;
91 }
92
93 Service.getLogger().log().logArray("starting scanning command:", " ", Cmd);
94 llvm::scope_exit ExitLogging([&] {
95 Service.getLogger().log().logArray("finished scanning command:", " ",
96 Cmd);
97 });
98
99 auto DiagEngineWithDiagOpts =
100 DiagnosticsEngineWithDiagOpts(Cmd, FS, DiagConsumer);
101 auto &Diags = *DiagEngineWithDiagOpts.DiagEngine;
102
103 // Create an invocation that uses the underlying file system to ensure that
104 // any file system requests that are made by the driver do not go through
105 // the dependency scanning filesystem.
106 return createAndRunToolInvocation(Cmd, Action, FS, PCHContainerOps, Diags);
107 });
108
109 return Success && Action.hasScanned();
110}
111