xref: /openbsd-src/gnu/llvm/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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