clang 22.0.0git
DependencyScanningWorker.cpp
Go to the documentation of this file.
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
13#include "clang/Driver/Driver.h"
14#include "clang/Driver/Tool.h"
16#include "llvm/Support/VirtualFileSystem.h"
17
18using namespace clang;
19using namespace dependencies;
20
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 std::make_unique<ObjectFilePCHContainerReader>());
29 // The scanner itself writes only raw ast files.
30 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
31
32 if (Service.shouldTraceVFS())
33 BaseFS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(
34 std::move(BaseFS));
35
36 DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
37 Service.getSharedCache(), std::move(BaseFS));
38}
39
42
43static bool forEachDriverJob(
46 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
47 // Compilation holds a non-owning a reference to the Driver, hence we need to
48 // keep the Driver alive when we use Compilation. Arguments to commands may be
49 // owned by Alloc when expanded from response files.
50 llvm::BumpPtrAllocator Alloc;
51 auto [Driver, Compilation] = buildCompilation(ArgStrs, Diags, FS, Alloc);
52 if (!Compilation)
53 return false;
54 for (const driver::Command &Job : Compilation->getJobs()) {
55 if (!Callback(Job))
56 return false;
57 }
58 return true;
59}
60
64 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
65 DiagnosticsEngine &Diags) {
66 auto Invocation = createCompilerInvocation(CommandLine, Diags);
67 if (!Invocation)
68 return false;
69
70 return Action.runInvocation(CommandLine[0], std::move(Invocation),
71 std::move(FS), PCHContainerOps,
72 Diags.getClient());
73}
74
75bool DependencyScanningWorker::scanDependencies(
76 StringRef WorkingDirectory, ArrayRef<std::string> CommandLine,
78 DiagnosticConsumer &DC,
79 IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS) {
80 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = DepFS;
81 if (OverlayFS) {
82#ifndef NDEBUG
83 bool SawDepFS = false;
84 OverlayFS->visit(
85 [&](llvm::vfs::FileSystem &VFS) { SawDepFS |= &VFS == DepFS.get(); });
86 assert(SawDepFS && "OverlayFS not based on DepFS");
87#endif
88 FS = std::move(OverlayFS);
89 }
90
91 DiagnosticsEngineWithDiagOpts DiagEngineWithCmdAndOpts(CommandLine, FS, DC);
92 DependencyScanningAction Action(Service, WorkingDirectory, Consumer,
93 Controller, DepFS);
94
95 bool Success = false;
96 if (CommandLine[1] == "-cc1") {
97 Success =
98 createAndRunToolInvocation(CommandLine, Action, FS, PCHContainerOps,
99 *DiagEngineWithCmdAndOpts.DiagEngine);
100 } else {
102 CommandLine, *DiagEngineWithCmdAndOpts.DiagEngine, FS,
103 [&](const driver::Command &Cmd) {
104 if (StringRef(Cmd.getCreator().getName()) != "clang") {
105 // Non-clang command. Just pass through to the dependency
106 // consumer.
107 Consumer.handleBuildCommand(
108 {Cmd.getExecutable(),
109 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
110 return true;
111 }
112
113 // Insert -cc1 command line options into Argv
114 std::vector<std::string> Argv;
115 Argv.push_back(Cmd.getExecutable());
116 llvm::append_range(Argv, Cmd.getArguments());
117
118 // Create an invocation that uses the underlying file
119 // system to ensure that any file system requests that
120 // are made by the driver do not go through the
121 // dependency scanning filesystem.
123 std::move(Argv), Action, FS, PCHContainerOps,
124 *DiagEngineWithCmdAndOpts.DiagEngine);
125 });
126 }
127
128 if (Success && !Action.hasScanned())
129 DiagEngineWithCmdAndOpts.DiagEngine->Report(
130 diag::err_fe_expected_compiler_job)
131 << llvm::join(CommandLine, " ");
132
133 // Ensure finish() is called even if we never reached ExecuteAction().
134 if (!Action.hasDiagConsumerFinished())
135 DC.finish();
136
137 return Success && Action.hasScanned();
138}
139
141 StringRef WorkingDirectory, ArrayRef<std::string> CommandLine,
142 DependencyConsumer &Consumer, DependencyActionController &Controller,
143 DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) {
144 if (TUBuffer) {
145 auto [FinalFS, FinalCommandLine] = initVFSForTUBufferScanning(
146 DepFS, CommandLine, WorkingDirectory, *TUBuffer);
147 return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,
148 Controller, DC, FinalFS);
149 }
150
151 DepFS->setCurrentWorkingDirectory(WorkingDirectory);
152 return scanDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
153 DC);
154}
155
157 StringRef CWD, ArrayRef<std::string> CommandLine, DiagnosticConsumer &DC) {
158 auto [OverlayFS, ModifiedCommandLine] =
159 initVFSForByNameScanning(DepFS, CommandLine, CWD, "ScanningByName");
160 auto DiagEngineWithCmdAndOpts =
161 std::make_unique<DiagnosticsEngineWithDiagOpts>(ModifiedCommandLine,
162 OverlayFS, DC);
164 CWD, ModifiedCommandLine, std::move(DiagEngineWithCmdAndOpts), OverlayFS);
165}
166
168 StringRef CWD, ArrayRef<std::string> CommandLine,
169 std::unique_ptr<DiagnosticsEngineWithDiagOpts> DiagEngineWithDiagOpts,
171 CIWithContext =
172 std::make_unique<CompilerInstanceWithContext>(*this, CWD, CommandLine);
173 return CIWithContext->initialize(std::move(DiagEngineWithDiagOpts),
174 OverlayFS);
175}
176
178 StringRef ModuleName, DependencyConsumer &Consumer,
179 DependencyActionController &Controller) {
180 assert(CIWithContext && "CompilerInstance with context required!");
181 return CIWithContext->computeDependencies(ModuleName, Consumer, Controller);
182}
183
185 return CIWithContext->finalize();
186}
Defines the Diagnostic-related interfaces.
static bool createAndRunToolInvocation(ArrayRef< std::string > CommandLine, DependencyScanningAction &Action, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, std::shared_ptr< clang::PCHContainerOperations > &PCHContainerOps, DiagnosticsEngine &Diags)
static bool forEachDriverJob(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, llvm::function_ref< bool(const driver::Command &Cmd)> Callback)
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
virtual void finish()
Callback to inform the diagnostic client that processing of all source files has ended.
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:232
DiagnosticConsumer * getClient()
Definition Diagnostic.h:607
Dependency scanner callbacks that are used during scanning to influence the behaviour of the scan - f...
bool runInvocation(std::string Executable, std::unique_ptr< CompilerInvocation > Invocation, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, std::shared_ptr< PCHContainerOperations > PCHContainerOps, DiagnosticConsumer *DiagConsumer)
The dependency scanning service contains shared configuration and state that is used by the individua...
bool computeDependenciesByNameWithContext(StringRef ModuleName, DependencyConsumer &Consumer, DependencyActionController &Controller)
Performaces dependency scanning for the module whose name is specified.
DependencyScanningWorker(DependencyScanningService &Service, IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS)
Construct a dependency scanning worker.
bool computeDependencies(StringRef WorkingDirectory, ArrayRef< std::string > CommandLine, DependencyConsumer &DepConsumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, std::optional< llvm::MemoryBufferRef > TUBuffer=std::nullopt)
Run the dependency scanning tool for a given clang driver command-line, and report the discovered dep...
bool initializeCompilerInstanceWithContext(StringRef CWD, ArrayRef< std::string > CommandLine, DiagnosticConsumer &DC)
The three method below implements a new interface for by name dependency scanning.
bool finalizeCompilerInstanceWithContext()
Finalizes the diagnostics engine and deletes the compiler instance.
Command - An executable path/name and argument vector to execute.
Definition Job.h:106
const llvm::opt::ArgStringList & getArguments() const
Definition Job.h:224
const char * getExecutable() const
Definition Job.h:222
@ VFS
Remove unused -ivfsoverlay arguments.
std::pair< IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem >, std::vector< std::string > > initVFSForTUBufferScanning(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS, ArrayRef< std::string > CommandLine, StringRef WorkingDirectory, llvm::MemoryBufferRef TUBuffer)
std::unique_ptr< CompilerInvocation > createCompilerInvocation(ArrayRef< std::string > CommandLine, DiagnosticsEngine &Diags)
std::pair< IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem >, std::vector< std::string > > initVFSForByNameScanning(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS, ArrayRef< std::string > CommandLine, StringRef WorkingDirectory, StringRef ModuleName)
std::pair< std::unique_ptr< driver::Driver >, std::unique_ptr< driver::Compilation > > buildCompilation(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, llvm::BumpPtrAllocator &Alloc)
The JSON file list parser is used to communicate input to InstallAPI.
@ Success
Annotation was successful.
Definition Parser.h:65