1e5dd7070Spatrick //===- DependencyScanningWorker.cpp - clang-scan-deps worker --------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick
9e5dd7070Spatrick #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
10*12c85518Srobert #include "clang/Basic/DiagnosticFrontend.h"
11a9ac8606Spatrick #include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
12*12c85518Srobert #include "clang/Driver/Compilation.h"
13*12c85518Srobert #include "clang/Driver/Driver.h"
14*12c85518Srobert #include "clang/Driver/Job.h"
15*12c85518Srobert #include "clang/Driver/Tool.h"
16e5dd7070Spatrick #include "clang/Frontend/CompilerInstance.h"
17e5dd7070Spatrick #include "clang/Frontend/CompilerInvocation.h"
18e5dd7070Spatrick #include "clang/Frontend/FrontendActions.h"
19e5dd7070Spatrick #include "clang/Frontend/TextDiagnosticPrinter.h"
20e5dd7070Spatrick #include "clang/Frontend/Utils.h"
21e5dd7070Spatrick #include "clang/Lex/PreprocessorOptions.h"
22e5dd7070Spatrick #include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
23e5dd7070Spatrick #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
24e5dd7070Spatrick #include "clang/Tooling/Tooling.h"
25*12c85518Srobert #include "llvm/Support/Host.h"
26*12c85518Srobert #include <optional>
27e5dd7070Spatrick
28e5dd7070Spatrick using namespace clang;
29e5dd7070Spatrick using namespace tooling;
30e5dd7070Spatrick using namespace dependencies;
31e5dd7070Spatrick
32e5dd7070Spatrick namespace {
33e5dd7070Spatrick
34e5dd7070Spatrick /// Forwards the gatherered dependencies to the consumer.
35e5dd7070Spatrick class DependencyConsumerForwarder : public DependencyFileGenerator {
36e5dd7070Spatrick public:
DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,StringRef WorkingDirectory,DependencyConsumer & C)37e5dd7070Spatrick DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
38*12c85518Srobert StringRef WorkingDirectory, DependencyConsumer &C)
39*12c85518Srobert : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),
40*12c85518Srobert Opts(std::move(Opts)), C(C) {}
41e5dd7070Spatrick
finishedMainFile(DiagnosticsEngine & Diags)42e5dd7070Spatrick void finishedMainFile(DiagnosticsEngine &Diags) override {
43a9ac8606Spatrick C.handleDependencyOutputOpts(*Opts);
44e5dd7070Spatrick llvm::SmallString<256> CanonPath;
45e5dd7070Spatrick for (const auto &File : getDependencies()) {
46e5dd7070Spatrick CanonPath = File;
47e5dd7070Spatrick llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
48*12c85518Srobert llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);
49a9ac8606Spatrick C.handleFileDependency(CanonPath);
50e5dd7070Spatrick }
51e5dd7070Spatrick }
52e5dd7070Spatrick
53e5dd7070Spatrick private:
54*12c85518Srobert StringRef WorkingDirectory;
55e5dd7070Spatrick std::unique_ptr<DependencyOutputOptions> Opts;
56e5dd7070Spatrick DependencyConsumer &C;
57e5dd7070Spatrick };
58e5dd7070Spatrick
59*12c85518Srobert using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
60*12c85518Srobert
61a9ac8606Spatrick /// A listener that collects the imported modules and optionally the input
62a9ac8606Spatrick /// files.
63a9ac8606Spatrick class PrebuiltModuleListener : public ASTReaderListener {
64e5dd7070Spatrick public:
PrebuiltModuleListener(PrebuiltModuleFilesT & PrebuiltModuleFiles,llvm::StringSet<> & InputFiles,bool VisitInputFiles,llvm::SmallVector<std::string> & NewModuleFiles)65*12c85518Srobert PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
66*12c85518Srobert llvm::StringSet<> &InputFiles, bool VisitInputFiles,
67*12c85518Srobert llvm::SmallVector<std::string> &NewModuleFiles)
68a9ac8606Spatrick : PrebuiltModuleFiles(PrebuiltModuleFiles), InputFiles(InputFiles),
69*12c85518Srobert VisitInputFiles(VisitInputFiles), NewModuleFiles(NewModuleFiles) {}
70e5dd7070Spatrick
needsImportVisitation() const71a9ac8606Spatrick bool needsImportVisitation() const override { return true; }
needsInputFileVisitation()72a9ac8606Spatrick bool needsInputFileVisitation() override { return VisitInputFiles; }
needsSystemInputFileVisitation()73a9ac8606Spatrick bool needsSystemInputFileVisitation() override { return VisitInputFiles; }
74a9ac8606Spatrick
visitImport(StringRef ModuleName,StringRef Filename)75a9ac8606Spatrick void visitImport(StringRef ModuleName, StringRef Filename) override {
76*12c85518Srobert if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
77*12c85518Srobert NewModuleFiles.push_back(Filename.str());
78e5dd7070Spatrick }
79e5dd7070Spatrick
visitInputFile(StringRef Filename,bool isSystem,bool isOverridden,bool isExplicitModule)80a9ac8606Spatrick bool visitInputFile(StringRef Filename, bool isSystem, bool isOverridden,
81a9ac8606Spatrick bool isExplicitModule) override {
82a9ac8606Spatrick InputFiles.insert(Filename);
83a9ac8606Spatrick return true;
84e5dd7070Spatrick }
85e5dd7070Spatrick
86e5dd7070Spatrick private:
87*12c85518Srobert PrebuiltModuleFilesT &PrebuiltModuleFiles;
88a9ac8606Spatrick llvm::StringSet<> &InputFiles;
89a9ac8606Spatrick bool VisitInputFiles;
90*12c85518Srobert llvm::SmallVector<std::string> &NewModuleFiles;
91e5dd7070Spatrick };
92e5dd7070Spatrick
93a9ac8606Spatrick /// Visit the given prebuilt module and collect all of the modules it
94a9ac8606Spatrick /// transitively imports and contributing input files.
visitPrebuiltModule(StringRef PrebuiltModuleFilename,CompilerInstance & CI,PrebuiltModuleFilesT & ModuleFiles,llvm::StringSet<> & InputFiles,bool VisitInputFiles)95a9ac8606Spatrick static void visitPrebuiltModule(StringRef PrebuiltModuleFilename,
96a9ac8606Spatrick CompilerInstance &CI,
97a9ac8606Spatrick PrebuiltModuleFilesT &ModuleFiles,
98a9ac8606Spatrick llvm::StringSet<> &InputFiles,
99a9ac8606Spatrick bool VisitInputFiles) {
100*12c85518Srobert // List of module files to be processed.
101*12c85518Srobert llvm::SmallVector<std::string> Worklist{PrebuiltModuleFilename.str()};
102*12c85518Srobert PrebuiltModuleListener Listener(ModuleFiles, InputFiles, VisitInputFiles,
103*12c85518Srobert Worklist);
104a9ac8606Spatrick
105*12c85518Srobert while (!Worklist.empty())
106a9ac8606Spatrick ASTReader::readASTFileControlBlock(
107*12c85518Srobert Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
108*12c85518Srobert CI.getPCHContainerReader(),
109a9ac8606Spatrick /*FindModuleFileExtensions=*/false, Listener,
110a9ac8606Spatrick /*ValidateDiagnosticOptions=*/false);
111a9ac8606Spatrick }
112a9ac8606Spatrick
113a9ac8606Spatrick /// Transform arbitrary file name into an object-like file name.
makeObjFileName(StringRef FileName)114a9ac8606Spatrick static std::string makeObjFileName(StringRef FileName) {
115a9ac8606Spatrick SmallString<128> ObjFileName(FileName);
116a9ac8606Spatrick llvm::sys::path::replace_extension(ObjFileName, "o");
117a9ac8606Spatrick return std::string(ObjFileName.str());
118a9ac8606Spatrick }
119a9ac8606Spatrick
120a9ac8606Spatrick /// Deduce the dependency target based on the output file and input files.
121a9ac8606Spatrick static std::string
deduceDepTarget(const std::string & OutputFile,const SmallVectorImpl<FrontendInputFile> & InputFiles)122a9ac8606Spatrick deduceDepTarget(const std::string &OutputFile,
123a9ac8606Spatrick const SmallVectorImpl<FrontendInputFile> &InputFiles) {
124a9ac8606Spatrick if (OutputFile != "-")
125a9ac8606Spatrick return OutputFile;
126a9ac8606Spatrick
127a9ac8606Spatrick if (InputFiles.empty() || !InputFiles.front().isFile())
128a9ac8606Spatrick return "clang-scan-deps\\ dependency";
129a9ac8606Spatrick
130a9ac8606Spatrick return makeObjFileName(InputFiles.front().getFile());
131a9ac8606Spatrick }
132a9ac8606Spatrick
133*12c85518Srobert /// Sanitize diagnostic options for dependency scan.
sanitizeDiagOpts(DiagnosticOptions & DiagOpts)134*12c85518Srobert static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
135*12c85518Srobert // Don't print 'X warnings and Y errors generated'.
136*12c85518Srobert DiagOpts.ShowCarets = false;
137*12c85518Srobert // Don't write out diagnostic file.
138*12c85518Srobert DiagOpts.DiagnosticSerializationFile.clear();
139*12c85518Srobert // Don't emit warnings as errors (and all other warnings too).
140*12c85518Srobert DiagOpts.IgnoreWarnings = true;
141*12c85518Srobert }
142*12c85518Srobert
143e5dd7070Spatrick /// A clang tool that runs the preprocessor in a mode that's optimized for
144e5dd7070Spatrick /// dependency scanning for the given compiler invocation.
145e5dd7070Spatrick class DependencyScanningAction : public tooling::ToolAction {
146e5dd7070Spatrick public:
DependencyScanningAction(StringRef WorkingDirectory,DependencyConsumer & Consumer,llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,ScanningOutputFormat Format,bool OptimizeArgs,bool EagerLoadModules,bool DisableFree,std::optional<StringRef> ModuleName=std::nullopt)147e5dd7070Spatrick DependencyScanningAction(
148e5dd7070Spatrick StringRef WorkingDirectory, DependencyConsumer &Consumer,
149e5dd7070Spatrick llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
150*12c85518Srobert ScanningOutputFormat Format, bool OptimizeArgs, bool EagerLoadModules,
151*12c85518Srobert bool DisableFree, std::optional<StringRef> ModuleName = std::nullopt)
152e5dd7070Spatrick : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
153*12c85518Srobert DepFS(std::move(DepFS)), Format(Format), OptimizeArgs(OptimizeArgs),
154*12c85518Srobert EagerLoadModules(EagerLoadModules), DisableFree(DisableFree),
155*12c85518Srobert ModuleName(ModuleName) {}
156e5dd7070Spatrick
runInvocation(std::shared_ptr<CompilerInvocation> Invocation,FileManager * FileMgr,std::shared_ptr<PCHContainerOperations> PCHContainerOps,DiagnosticConsumer * DiagConsumer)157e5dd7070Spatrick bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
158e5dd7070Spatrick FileManager *FileMgr,
159e5dd7070Spatrick std::shared_ptr<PCHContainerOperations> PCHContainerOps,
160e5dd7070Spatrick DiagnosticConsumer *DiagConsumer) override {
161a9ac8606Spatrick // Make a deep copy of the original Clang invocation.
162a9ac8606Spatrick CompilerInvocation OriginalInvocation(*Invocation);
163*12c85518Srobert // Restore the value of DisableFree, which may be modified by Tooling.
164*12c85518Srobert OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;
165*12c85518Srobert
166*12c85518Srobert if (Scanned) {
167*12c85518Srobert // Scanning runs once for the first -cc1 invocation in a chain of driver
168*12c85518Srobert // jobs. For any dependent jobs, reuse the scanning result and just
169*12c85518Srobert // update the LastCC1Arguments to correspond to the new invocation.
170*12c85518Srobert // FIXME: to support multi-arch builds, each arch requires a separate scan
171*12c85518Srobert setLastCC1Arguments(std::move(OriginalInvocation));
172*12c85518Srobert return true;
173*12c85518Srobert }
174*12c85518Srobert
175*12c85518Srobert Scanned = true;
176a9ac8606Spatrick
177e5dd7070Spatrick // Create a compiler instance to handle the actual work.
178*12c85518Srobert ScanInstanceStorage.emplace(std::move(PCHContainerOps));
179*12c85518Srobert CompilerInstance &ScanInstance = *ScanInstanceStorage;
180*12c85518Srobert ScanInstance.setInvocation(std::move(Invocation));
181e5dd7070Spatrick
182e5dd7070Spatrick // Create the compiler's actual diagnostics engine.
183*12c85518Srobert sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
184*12c85518Srobert ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
185*12c85518Srobert if (!ScanInstance.hasDiagnostics())
186e5dd7070Spatrick return false;
187e5dd7070Spatrick
188*12c85518Srobert ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
189*12c85518Srobert true;
190a9ac8606Spatrick
191*12c85518Srobert ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
192*12c85518Srobert ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
193*12c85518Srobert ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
194*12c85518Srobert
195*12c85518Srobert ScanInstance.setFileManager(FileMgr);
196*12c85518Srobert // Support for virtual file system overlays.
197*12c85518Srobert FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation(
198*12c85518Srobert ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
199*12c85518Srobert FileMgr->getVirtualFileSystemPtr()));
200*12c85518Srobert
201*12c85518Srobert ScanInstance.createSourceManager(*FileMgr);
202a9ac8606Spatrick
203a9ac8606Spatrick llvm::StringSet<> PrebuiltModulesInputFiles;
204a9ac8606Spatrick // Store the list of prebuilt module files into header search options. This
205a9ac8606Spatrick // will prevent the implicit build to create duplicate modules and will
206a9ac8606Spatrick // force reuse of the existing prebuilt module files instead.
207*12c85518Srobert if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
208a9ac8606Spatrick visitPrebuiltModule(
209*12c85518Srobert ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance,
210*12c85518Srobert ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
211a9ac8606Spatrick PrebuiltModulesInputFiles, /*VisitInputFiles=*/DepFS != nullptr);
212a9ac8606Spatrick
213a9ac8606Spatrick // Use the dependency scanning optimized file system if requested to do so.
214e5dd7070Spatrick if (DepFS) {
215*12c85518Srobert llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> LocalDepFS =
216*12c85518Srobert DepFS;
217*12c85518Srobert ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
218*12c85518Srobert [LocalDepFS = std::move(LocalDepFS)](FileEntryRef File)
219*12c85518Srobert -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
220*12c85518Srobert if (llvm::ErrorOr<EntryRef> Entry =
221*12c85518Srobert LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
222*12c85518Srobert return Entry->getDirectiveTokens();
223*12c85518Srobert return std::nullopt;
224*12c85518Srobert };
225e5dd7070Spatrick }
226e5dd7070Spatrick
227e5dd7070Spatrick // Create the dependency collector that will collect the produced
228e5dd7070Spatrick // dependencies.
229e5dd7070Spatrick //
230e5dd7070Spatrick // This also moves the existing dependency output options from the
231e5dd7070Spatrick // invocation to the collector. The options in the invocation are reset,
232e5dd7070Spatrick // which ensures that the compiler won't create new dependency collectors,
233e5dd7070Spatrick // and thus won't write out the extra '.d' files to disk.
234a9ac8606Spatrick auto Opts = std::make_unique<DependencyOutputOptions>();
235*12c85518Srobert std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
236a9ac8606Spatrick // We need at least one -MT equivalent for the generator of make dependency
237a9ac8606Spatrick // files to work.
238e5dd7070Spatrick if (Opts->Targets.empty())
239*12c85518Srobert Opts->Targets = {
240*12c85518Srobert deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
241*12c85518Srobert ScanInstance.getFrontendOpts().Inputs)};
242a9ac8606Spatrick Opts->IncludeSystemHeaders = true;
243e5dd7070Spatrick
244e5dd7070Spatrick switch (Format) {
245e5dd7070Spatrick case ScanningOutputFormat::Make:
246*12c85518Srobert ScanInstance.addDependencyCollector(
247*12c85518Srobert std::make_shared<DependencyConsumerForwarder>(
248*12c85518Srobert std::move(Opts), WorkingDirectory, Consumer));
249e5dd7070Spatrick break;
250*12c85518Srobert case ScanningOutputFormat::P1689:
251e5dd7070Spatrick case ScanningOutputFormat::Full:
252*12c85518Srobert MDC = std::make_shared<ModuleDepCollector>(
253*12c85518Srobert std::move(Opts), ScanInstance, Consumer, OriginalInvocation,
254*12c85518Srobert OptimizeArgs, EagerLoadModules,
255*12c85518Srobert Format == ScanningOutputFormat::P1689);
256*12c85518Srobert ScanInstance.addDependencyCollector(MDC);
257e5dd7070Spatrick break;
258e5dd7070Spatrick }
259e5dd7070Spatrick
260ec727ea7Spatrick // Consider different header search and diagnostic options to create
261ec727ea7Spatrick // different modules. This avoids the unsound aliasing of module PCMs.
262ec727ea7Spatrick //
263*12c85518Srobert // TODO: Implement diagnostic bucketing to reduce the impact of strict
264*12c85518Srobert // context hashing.
265*12c85518Srobert ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
266e5dd7070Spatrick
267*12c85518Srobert std::unique_ptr<FrontendAction> Action;
268*12c85518Srobert
269*12c85518Srobert if (ModuleName)
270*12c85518Srobert Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
271*12c85518Srobert else
272*12c85518Srobert Action = std::make_unique<ReadPCHAndPreprocessAction>();
273*12c85518Srobert
274*12c85518Srobert const bool Result = ScanInstance.ExecuteAction(*Action);
275*12c85518Srobert
276*12c85518Srobert if (Result)
277*12c85518Srobert setLastCC1Arguments(std::move(OriginalInvocation));
278*12c85518Srobert
279e5dd7070Spatrick return Result;
280e5dd7070Spatrick }
281e5dd7070Spatrick
hasScanned() const282*12c85518Srobert bool hasScanned() const { return Scanned; }
283*12c85518Srobert
284*12c85518Srobert /// Take the cc1 arguments corresponding to the most recent invocation used
285*12c85518Srobert /// with this action. Any modifications implied by the discovered dependencies
286*12c85518Srobert /// will have already been applied.
takeLastCC1Arguments()287*12c85518Srobert std::vector<std::string> takeLastCC1Arguments() {
288*12c85518Srobert std::vector<std::string> Result;
289*12c85518Srobert std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
290*12c85518Srobert return Result;
291*12c85518Srobert }
292*12c85518Srobert
293*12c85518Srobert private:
setLastCC1Arguments(CompilerInvocation && CI)294*12c85518Srobert void setLastCC1Arguments(CompilerInvocation &&CI) {
295*12c85518Srobert if (MDC)
296*12c85518Srobert MDC->applyDiscoveredDependencies(CI);
297*12c85518Srobert LastCC1Arguments = CI.getCC1CommandLine();
298*12c85518Srobert }
299*12c85518Srobert
300e5dd7070Spatrick private:
301e5dd7070Spatrick StringRef WorkingDirectory;
302e5dd7070Spatrick DependencyConsumer &Consumer;
303e5dd7070Spatrick llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
304e5dd7070Spatrick ScanningOutputFormat Format;
305*12c85518Srobert bool OptimizeArgs;
306*12c85518Srobert bool EagerLoadModules;
307*12c85518Srobert bool DisableFree;
308*12c85518Srobert std::optional<StringRef> ModuleName;
309*12c85518Srobert std::optional<CompilerInstance> ScanInstanceStorage;
310*12c85518Srobert std::shared_ptr<ModuleDepCollector> MDC;
311*12c85518Srobert std::vector<std::string> LastCC1Arguments;
312*12c85518Srobert bool Scanned = false;
313e5dd7070Spatrick };
314e5dd7070Spatrick
315e5dd7070Spatrick } // end anonymous namespace
316e5dd7070Spatrick
DependencyScanningWorker(DependencyScanningService & Service,llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)317e5dd7070Spatrick DependencyScanningWorker::DependencyScanningWorker(
318*12c85518Srobert DependencyScanningService &Service,
319*12c85518Srobert llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
320*12c85518Srobert : Format(Service.getFormat()), OptimizeArgs(Service.canOptimizeArgs()),
321*12c85518Srobert EagerLoadModules(Service.shouldEagerLoadModules()) {
322e5dd7070Spatrick PCHContainerOps = std::make_shared<PCHContainerOperations>();
323a9ac8606Spatrick PCHContainerOps->registerReader(
324a9ac8606Spatrick std::make_unique<ObjectFilePCHContainerReader>());
325a9ac8606Spatrick // We don't need to write object files, but the current PCH implementation
326a9ac8606Spatrick // requires the writer to be registered as well.
327a9ac8606Spatrick PCHContainerOps->registerWriter(
328a9ac8606Spatrick std::make_unique<ObjectFilePCHContainerWriter>());
329a9ac8606Spatrick
330*12c85518Srobert switch (Service.getMode()) {
331*12c85518Srobert case ScanningMode::DependencyDirectivesScan:
332*12c85518Srobert DepFS =
333*12c85518Srobert new DependencyScanningWorkerFilesystem(Service.getSharedCache(), FS);
334*12c85518Srobert BaseFS = DepFS;
335*12c85518Srobert break;
336*12c85518Srobert case ScanningMode::CanonicalPreprocessing:
337*12c85518Srobert DepFS = nullptr;
338*12c85518Srobert BaseFS = FS;
339*12c85518Srobert break;
340*12c85518Srobert }
341e5dd7070Spatrick }
342e5dd7070Spatrick
computeDependencies(StringRef WorkingDirectory,const std::vector<std::string> & CommandLine,DependencyConsumer & Consumer,std::optional<StringRef> ModuleName)343*12c85518Srobert llvm::Error DependencyScanningWorker::computeDependencies(
344*12c85518Srobert StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
345*12c85518Srobert DependencyConsumer &Consumer, std::optional<StringRef> ModuleName) {
346*12c85518Srobert std::vector<const char *> CLI;
347*12c85518Srobert for (const std::string &Arg : CommandLine)
348*12c85518Srobert CLI.push_back(Arg.c_str());
349*12c85518Srobert auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
350*12c85518Srobert sanitizeDiagOpts(*DiagOpts);
351*12c85518Srobert
352e5dd7070Spatrick // Capture the emitted diagnostics and report them to the client
353e5dd7070Spatrick // in the case of a failure.
354e5dd7070Spatrick std::string DiagnosticOutput;
355e5dd7070Spatrick llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
356*12c85518Srobert TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
357e5dd7070Spatrick
358*12c85518Srobert if (computeDependencies(WorkingDirectory, CommandLine, Consumer, DiagPrinter,
359*12c85518Srobert ModuleName))
360e5dd7070Spatrick return llvm::Error::success();
361e5dd7070Spatrick return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
362e5dd7070Spatrick llvm::inconvertibleErrorCode());
363e5dd7070Spatrick }
364e5dd7070Spatrick
forEachDriverJob(ArrayRef<std::string> Args,DiagnosticsEngine & Diags,FileManager & FM,llvm::function_ref<bool (const driver::Command & Cmd)> Callback)365*12c85518Srobert static bool forEachDriverJob(
366*12c85518Srobert ArrayRef<std::string> Args, DiagnosticsEngine &Diags, FileManager &FM,
367*12c85518Srobert llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
368*12c85518Srobert std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
369*12c85518Srobert Args[0], llvm::sys::getDefaultTargetTriple(), Diags,
370*12c85518Srobert "clang LLVM compiler", &FM.getVirtualFileSystem());
371*12c85518Srobert Driver->setTitle("clang_based_tool");
372*12c85518Srobert
373*12c85518Srobert std::vector<const char *> Argv;
374*12c85518Srobert for (const std::string &Arg : Args)
375*12c85518Srobert Argv.push_back(Arg.c_str());
376*12c85518Srobert
377*12c85518Srobert const std::unique_ptr<driver::Compilation> Compilation(
378*12c85518Srobert Driver->BuildCompilation(llvm::ArrayRef(Argv)));
379*12c85518Srobert if (!Compilation)
380*12c85518Srobert return false;
381*12c85518Srobert
382*12c85518Srobert for (const driver::Command &Job : Compilation->getJobs()) {
383*12c85518Srobert if (!Callback(Job))
384*12c85518Srobert return false;
385*12c85518Srobert }
386*12c85518Srobert return true;
387*12c85518Srobert }
388*12c85518Srobert
computeDependencies(StringRef WorkingDirectory,const std::vector<std::string> & CommandLine,DependencyConsumer & Consumer,DiagnosticConsumer & DC,std::optional<StringRef> ModuleName)389*12c85518Srobert bool DependencyScanningWorker::computeDependencies(
390*12c85518Srobert StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
391*12c85518Srobert DependencyConsumer &Consumer, DiagnosticConsumer &DC,
392*12c85518Srobert std::optional<StringRef> ModuleName) {
393*12c85518Srobert // Reset what might have been modified in the previous worker invocation.
394*12c85518Srobert BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
395*12c85518Srobert
396*12c85518Srobert std::optional<std::vector<std::string>> ModifiedCommandLine;
397*12c85518Srobert llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
398*12c85518Srobert if (ModuleName) {
399*12c85518Srobert ModifiedCommandLine = CommandLine;
400*12c85518Srobert ModifiedCommandLine->emplace_back(*ModuleName);
401*12c85518Srobert
402*12c85518Srobert auto OverlayFS =
403*12c85518Srobert llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
404*12c85518Srobert auto InMemoryFS =
405*12c85518Srobert llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
406*12c85518Srobert InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
407*12c85518Srobert InMemoryFS->addFile(*ModuleName, 0, llvm::MemoryBuffer::getMemBuffer(""));
408*12c85518Srobert OverlayFS->pushOverlay(InMemoryFS);
409*12c85518Srobert ModifiedFS = OverlayFS;
410*12c85518Srobert }
411*12c85518Srobert
412*12c85518Srobert const std::vector<std::string> &FinalCommandLine =
413*12c85518Srobert ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
414*12c85518Srobert
415*12c85518Srobert FileSystemOptions FSOpts;
416*12c85518Srobert FSOpts.WorkingDir = WorkingDirectory.str();
417*12c85518Srobert auto FileMgr = llvm::makeIntrusiveRefCnt<FileManager>(
418*12c85518Srobert FSOpts, ModifiedFS ? ModifiedFS : BaseFS);
419*12c85518Srobert
420*12c85518Srobert std::vector<const char *> FinalCCommandLine(CommandLine.size(), nullptr);
421*12c85518Srobert llvm::transform(CommandLine, FinalCCommandLine.begin(),
422*12c85518Srobert [](const std::string &Str) { return Str.c_str(); });
423*12c85518Srobert
424*12c85518Srobert auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);
425*12c85518Srobert sanitizeDiagOpts(*DiagOpts);
426*12c85518Srobert IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
427*12c85518Srobert CompilerInstance::createDiagnostics(DiagOpts.release(), &DC,
428*12c85518Srobert /*ShouldOwnClient=*/false);
429*12c85518Srobert
430*12c85518Srobert // Although `Diagnostics` are used only for command-line parsing, the
431*12c85518Srobert // custom `DiagConsumer` might expect a `SourceManager` to be present.
432*12c85518Srobert SourceManager SrcMgr(*Diags, *FileMgr);
433*12c85518Srobert Diags->setSourceManager(&SrcMgr);
434*12c85518Srobert // DisableFree is modified by Tooling for running
435*12c85518Srobert // in-process; preserve the original value, which is
436*12c85518Srobert // always true for a driver invocation.
437*12c85518Srobert bool DisableFree = true;
438*12c85518Srobert DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS, Format,
439*12c85518Srobert OptimizeArgs, EagerLoadModules, DisableFree,
440*12c85518Srobert ModuleName);
441*12c85518Srobert bool Success = forEachDriverJob(
442*12c85518Srobert FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
443*12c85518Srobert if (StringRef(Cmd.getCreator().getName()) != "clang") {
444*12c85518Srobert // Non-clang command. Just pass through to the dependency
445*12c85518Srobert // consumer.
446*12c85518Srobert Consumer.handleBuildCommand(
447*12c85518Srobert {Cmd.getExecutable(),
448*12c85518Srobert {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
449*12c85518Srobert return true;
450*12c85518Srobert }
451*12c85518Srobert
452*12c85518Srobert std::vector<std::string> Argv;
453*12c85518Srobert Argv.push_back(Cmd.getExecutable());
454*12c85518Srobert Argv.insert(Argv.end(), Cmd.getArguments().begin(),
455*12c85518Srobert Cmd.getArguments().end());
456*12c85518Srobert
457*12c85518Srobert // Create an invocation that uses the underlying file
458*12c85518Srobert // system to ensure that any file system requests that
459*12c85518Srobert // are made by the driver do not go through the
460*12c85518Srobert // dependency scanning filesystem.
461*12c85518Srobert ToolInvocation Invocation(std::move(Argv), &Action, &*FileMgr,
462*12c85518Srobert PCHContainerOps);
463*12c85518Srobert Invocation.setDiagnosticConsumer(Diags->getClient());
464*12c85518Srobert Invocation.setDiagnosticOptions(&Diags->getDiagnosticOptions());
465*12c85518Srobert if (!Invocation.run())
466*12c85518Srobert return false;
467*12c85518Srobert
468*12c85518Srobert std::vector<std::string> Args = Action.takeLastCC1Arguments();
469*12c85518Srobert Consumer.handleBuildCommand({Cmd.getExecutable(), std::move(Args)});
470*12c85518Srobert return true;
471e5dd7070Spatrick });
472*12c85518Srobert
473*12c85518Srobert if (Success && !Action.hasScanned())
474*12c85518Srobert Diags->Report(diag::err_fe_expected_compiler_job)
475*12c85518Srobert << llvm::join(FinalCommandLine, " ");
476*12c85518Srobert return Success && Action.hasScanned();
477e5dd7070Spatrick }
478