27#include "llvm/ADT/ScopeExit.h"
28#include "llvm/Support/Allocator.h"
29#include "llvm/Support/Error.h"
30#include "llvm/TargetParser/Host.h"
34using namespace tooling;
35using namespace dependencies;
42 DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
45 Opts(
std::move(Opts)),
C(
C) {}
48 C.handleDependencyOutputOpts(*Opts);
52 llvm::sys::path::remove_dots(CanonPath,
true);
53 llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);
54 C.handleFileDependency(CanonPath);
59 StringRef WorkingDirectory;
60 std::unique_ptr<DependencyOutputOptions> Opts;
68 if (LangOpts.Modules) {
71 Diags->
Report(diag::warn_pch_vfsoverlay_mismatch);
73 if (VFSOverlays.empty()) {
74 Diags->
Report(diag::note_pch_vfsoverlay_empty) <<
Type;
76 std::string Files = llvm::join(VFSOverlays,
"\n");
77 Diags->
Report(diag::note_pch_vfsoverlay_files) <<
Type << Files;
94 PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
99 : PrebuiltModuleFiles(PrebuiltModuleFiles),
100 NewModuleFiles(NewModuleFiles),
101 PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), ExistingHSOpts(HSOpts),
102 ExistingLangOpts(LangOpts), Diags(Diags) {}
107 if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
108 NewModuleFiles.push_back(
Filename.str());
117 bool Complain)
override {
119 PrebuiltModuleVFSMap.insert(
120 {CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});
121 return checkHeaderSearchPaths(
122 HSOpts, ExistingHSOpts, Complain ? &Diags :
nullptr, ExistingLangOpts);
126 PrebuiltModuleFilesT &PrebuiltModuleFiles;
132 std::string CurrentFile;
137static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
139 PrebuiltModuleFilesT &ModuleFiles,
144 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,
148 Listener.visitModuleFile(PrebuiltModuleFilename,
157 while (!Worklist.empty()) {
170static std::string makeObjFileName(StringRef
FileName) {
172 llvm::sys::path::replace_extension(ObjFileName,
"o");
173 return std::string(ObjFileName);
178deduceDepTarget(
const std::string &OutputFile,
180 if (OutputFile !=
"-")
183 if (InputFiles.empty() || !InputFiles.front().isFile())
184 return "clang-scan-deps\\ dependency";
186 return makeObjFileName(InputFiles.front().getFile());
192 DiagOpts.ShowCarets =
false;
203 return llvm::StringSwitch<bool>(Warning)
204 .Cases(
"pch-vfs-diff",
"error=pch-vfs-diff", false)
205 .StartsWith(
"no-error=", false)
220static std::optional<StringRef> getSimpleMacroName(StringRef Macro) {
221 StringRef Name =
Macro.split(
"=").first.ltrim(
" \t");
224 auto FinishName = [&]() -> std::optional<StringRef> {
225 StringRef SimpleName = Name.slice(0, I);
226 if (SimpleName.empty())
231 for (; I != Name.size(); ++I) {
240 if (llvm::isAlnum(Name[I]))
249 using MacroOpt = std::pair<StringRef, std::size_t>;
250 std::vector<MacroOpt> SimpleNames;
251 SimpleNames.reserve(PPOpts.
Macros.size());
252 std::size_t Index = 0;
253 for (
const auto &M : PPOpts.
Macros) {
254 auto SName = getSimpleMacroName(M.first);
258 SimpleNames.emplace_back(*SName, Index);
262 llvm::stable_sort(SimpleNames, llvm::less_first());
264 auto NewEnd = std::unique(
265 SimpleNames.rbegin(), SimpleNames.rend(),
266 [](
const MacroOpt &A,
const MacroOpt &B) { return A.first == B.first; });
267 SimpleNames.erase(SimpleNames.begin(), NewEnd.base());
270 decltype(PPOpts.
Macros) NewMacros;
271 NewMacros.reserve(SimpleNames.size());
272 for (std::size_t I = 0,
E = SimpleNames.size(); I !=
E; ++I) {
273 std::size_t OriginalIndex = SimpleNames[I].second;
275 NewMacros.push_back(std::move(PPOpts.
Macros[OriginalIndex]));
277 std::swap(PPOpts.
Macros, NewMacros);
284 DependencyScanningAction(
289 bool EagerLoadModules,
bool DisableFree,
290 std::optional<StringRef> ModuleName = std::nullopt)
291 : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
292 Controller(Controller), DepFS(
std::move(DepFS)), Format(Format),
293 OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),
294 DisableFree(DisableFree), ModuleName(ModuleName) {}
296 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
298 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
303 OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;
304 if (
any(OptimizeArgs & ScanningOptimizations::Macros))
305 canonicalizeDefines(OriginalInvocation.getPreprocessorOpts());
312 setLastCC1Arguments(std::move(OriginalInvocation));
319 ScanInstanceStorage.emplace(std::move(PCHContainerOps));
326 DiagConsumer,
false);
331 auto DiagConsumerFinisher =
332 llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->
finish(); });
344 any(OptimizeArgs & ScanningOptimizations::VFS);
353 StringRef ModulesCachePath =
356 DepFS->resetBypassedPathPrefix();
357 if (!ModulesCachePath.empty())
358 DepFS->setBypassedPathPrefix(ModulesCachePath);
363 if (llvm::ErrorOr<EntryRef> Entry =
364 LocalDepFS->getOrCreateFileSystemEntry(
File.getName()))
365 if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
366 return Entry->getDirectiveTokens();
380 if (visitPrebuiltModule(
394 auto Opts = std::make_unique<DependencyOutputOptions>();
398 if (Opts->Targets.empty())
402 Opts->IncludeSystemHeaders =
true;
405 case ScanningOutputFormat::Make:
407 std::make_shared<DependencyConsumerForwarder>(
408 std::move(Opts), WorkingDirectory, Consumer));
410 case ScanningOutputFormat::P1689:
411 case ScanningOutputFormat::Full:
412 MDC = std::make_shared<ModuleDepCollector>(
413 std::move(Opts), ScanInstance, Consumer, Controller,
414 OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,
415 EagerLoadModules, Format == ScanningOutputFormat::P1689);
435 std::unique_ptr<FrontendAction> Action;
437 if (Format == ScanningOutputFormat::P1689)
438 Action = std::make_unique<PreprocessOnlyAction>();
440 Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
442 Action = std::make_unique<ReadPCHAndPreprocessAction>();
448 DiagConsumerFinisher.release();
452 setLastCC1Arguments(std::move(OriginalInvocation));
460 bool hasScanned()
const {
return Scanned; }
465 std::vector<std::string> takeLastCC1Arguments() {
466 std::vector<std::string>
Result;
467 std::swap(Result, LastCC1Arguments);
474 MDC->applyDiscoveredDependencies(CI);
475 LastCC1Arguments = CI.getCC1CommandLine();
479 StringRef WorkingDirectory;
485 bool EagerLoadModules;
487 std::optional<StringRef> ModuleName;
488 std::optional<CompilerInstance> ScanInstanceStorage;
489 std::shared_ptr<ModuleDepCollector> MDC;
490 std::vector<std::string> LastCC1Arguments;
491 bool Scanned =
false;
499 : Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),
500 EagerLoadModules(Service.shouldEagerLoadModules()) {
501 PCHContainerOps = std::make_shared<PCHContainerOperations>();
503 PCHContainerOps->registerReader(
504 std::make_unique<ObjectFilePCHContainerReader>());
506 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
509 FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));
525 StringRef WorkingDirectory,
const std::vector<std::string> &CommandLine,
527 std::optional<StringRef> ModuleName) {
528 std::vector<const char *> CLI;
529 for (
const std::string &Arg : CommandLine)
530 CLI.push_back(Arg.c_str());
532 sanitizeDiagOpts(*DiagOpts);
536 std::string DiagnosticOutput;
537 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
541 DiagPrinter, ModuleName))
542 return llvm::Error::success();
543 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
544 llvm::inconvertibleErrorCode());
551 Argv.reserve(ArgStrs.size());
552 for (
const std::string &Arg : ArgStrs)
553 Argv.push_back(Arg.c_str());
557 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
558 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
559 "clang LLVM compiler", FS);
560 Driver->setTitle(
"clang_based_tool");
562 llvm::BumpPtrAllocator Alloc;
567 Diags.
Report(diag::err_drv_expand_response_file)
568 << llvm::toString(std::move(
E));
572 const std::unique_ptr<driver::Compilation> Compilation(
577 if (Compilation->containsError())
588 std::vector<std::string> CommandLine, DependencyScanningAction &Action,
590 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
594 std::string Executable = CommandLine[0];
599 if (!Invocation.
run())
602 std::vector<std::string> Args = Action.takeLastCC1Arguments();
608 StringRef WorkingDirectory,
const std::vector<std::string> &CommandLine,
612 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
614 std::optional<std::vector<std::string>> ModifiedCommandLine;
622 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
624 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
625 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
626 OverlayFS->pushOverlay(InMemoryFS);
627 ModifiedFS = OverlayFS;
631 llvm::sys::fs::createUniquePath(*ModuleName +
"-%%%%%%%%.input",
634 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(
""));
636 ModifiedCommandLine = CommandLine;
637 ModifiedCommandLine->emplace_back(FakeInputPath);
640 const std::vector<std::string> &FinalCommandLine =
641 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
642 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
647 std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(),
nullptr);
648 llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
649 [](
const std::string &Str) { return Str.c_str(); });
652 sanitizeDiagOpts(*DiagOpts);
660 Diags->setSourceManager(&SrcMgr);
664 bool DisableFree =
true;
665 DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,
666 Format, OptimizeArgs, EagerLoadModules,
667 DisableFree, ModuleName);
670 if (FinalCommandLine[1] ==
"-cc1") {
672 PCHContainerOps, *Diags, Consumer);
676 if (StringRef(
Cmd.getCreator().getName()) !=
"clang") {
679 Consumer.handleBuildCommand(
680 {Cmd.getExecutable(),
681 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
686 std::vector<std::string> Argv;
687 Argv.push_back(
Cmd.getExecutable());
688 Argv.insert(Argv.end(),
Cmd.getArguments().begin(),
689 Cmd.getArguments().end());
696 PCHContainerOps, *Diags, Consumer);
700 if (
Success && !Action.hasScanned())
701 Diags->
Report(diag::err_fe_expected_compiler_job)
702 << llvm::join(FinalCommandLine,
" ");
703 return Success && Action.hasScanned();
static bool forEachDriverJob(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, FileManager &FM, llvm::function_ref< bool(const driver::Command &Cmd)> Callback)
static bool createAndRunToolInvocation(std::vector< std::string > CommandLine, DependencyScanningAction &Action, FileManager &FM, std::shared_ptr< clang::PCHContainerOperations > &PCHContainerOps, DiagnosticsEngine &Diags, DependencyConsumer &Consumer)
Abstract interface for callback invocations by the ASTReader.
virtual bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts, bool Complain)
Receives the header search paths.
virtual void visitImport(StringRef ModuleName, StringRef Filename)
If needsImportVisitation returns true, this is called for each AST file imported by this AST file.
virtual void visitModuleFile(StringRef Filename, serialization::ModuleKind Kind)
This is called for each AST file loaded.
virtual bool needsImportVisitation() const
Returns true if this ASTReaderListener wants to receive the imports of the AST file via visitImport,...
@ ARR_OutOfDate
The client can handle an AST file that cannot load because it is out-of-date relative to its input fi...
static bool readASTFileControlBlock(StringRef Filename, FileManager &FileMgr, const InMemoryModuleCache &ModuleCache, const PCHContainerReader &PCHContainerRdr, bool FindModuleFileExtensions, ASTReaderListener &Listener, bool ValidateDiagnosticOptions, unsigned ClientLoadCapabilities=ARR_ConfigurationMismatch|ARR_OutOfDate)
Read the control block for the named AST file.
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
void createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
FileManager * createFileManager(IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS=nullptr)
Create the file manager and replace any existing one with it.
const PCHContainerReader & getPCHContainerReader() const
Return the appropriate PCHContainerReader depending on the current CodeGenOptions.
DiagnosticsEngine & getDiagnostics() const
Get the current diagnostics engine.
void createDiagnostics(llvm::vfs::FileSystem &VFS, DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
FileManager & getFileManager() const
Return the current file manager to the caller.
InMemoryModuleCache & getModuleCache() const
void addDependencyCollector(std::shared_ptr< DependencyCollector > Listener)
FrontendOptions & getFrontendOpts()
bool hasDiagnostics() const
HeaderSearchOptions & getHeaderSearchOpts()
CompilerInvocation & getInvocation()
PreprocessorOptions & getPreprocessorOpts()
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
DiagnosticOptions & getDiagnosticOpts()
LangOptions & getLangOpts()
Helper class for holding the data necessary to invoke the compiler.
DependencyOutputOptions & getDependencyOutputOpts()
ArrayRef< std::string > getDependencies() const
Builds a dependency file when attached to a Preprocessor (for includes) and ASTReader (for module imp...
void finishedMainFile(DiagnosticsEngine &Diags) override
Called when the end of the main file is reached.
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.
Options for controlling the compiler diagnostics engine.
std::vector< std::string > Warnings
The list of -W... options used to alter the diagnostic mappings, with the prefixes removed.
std::string DiagnosticSerializationFile
The file to serialize diagnostics to (non-appending).
Concrete class used by the front-end to report problems and issues.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
bool hasErrorOccurred() const
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
DiagnosticConsumer * getClient()
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
Implements support for file system lookup, file system caching, and directory search management.
void AddStats(const FileManager &Other)
Import statistics from a child FileManager and add them to this current FileManager.
llvm::vfs::FileSystem & getVirtualFileSystem() const
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() const
Keeps track of options that affect how file operations are performed.
unsigned ModulesShareFileManager
Whether to share the FileManager when building modules.
std::string OutputFile
The output file, if any.
unsigned GenerateGlobalModuleIndex
Whether we can generate the global module index if needed.
SmallVector< FrontendInputFile, 0 > Inputs
The input files and their types.
unsigned UseGlobalModuleIndex
Whether we can use the global module index if available.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
PreprocessorOptions - This class is used for passing the various options used in preprocessor initial...
bool ModulesCheckRelocated
Perform extra checks when loading PCM files for mutable file systems.
std::string ImplicitPCHInclude
The implicit PCH included at the start of the translation unit, or empty.
bool AllowPCHWithDifferentModulesCachePath
When true, a PCH with modules cache path different to the current compilation will not be rejected.
std::vector< std::pair< std::string, bool > > Macros
std::function< std::optional< ArrayRef< dependency_directives_scan::Directive > >(FileEntryRef)> DependencyDirectivesForFile
Function for getting the dependency preprocessor directives of a file.
This class handles loading and caching of source files into memory.
The base class of the type hierarchy.
Command - An executable path/name and argument vector to execute.
llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)
Returns the driver mode option's value, i.e.
llvm::Error expandResponseFiles(SmallVectorImpl< const char * > &Args, bool ClangCLMode, llvm::BumpPtrAllocator &Alloc, llvm::vfs::FileSystem *FS=nullptr)
Expand response files from a clang driver or cc1 invocation.
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
ModuleKind
Specifies the kind of module that has been loaded.
@ MK_ExplicitModule
File is an explicitly-loaded module.
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
@ Result
The result type of a method or function.
IntrusiveRefCntPtr< llvm::vfs::FileSystem > createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags)
@ Success
Template argument deduction was successful.
int __ovld __cnfn any(char)
Returns 1 if the most significant bit in any component of x is set; otherwise returns 0.