27 #include "llvm/ADT/ScopeExit.h"
28 #include "llvm/Support/Allocator.h"
29 #include "llvm/Support/Error.h"
30 #include "llvm/TargetParser/Host.h"
33 using namespace clang;
34 using namespace tooling;
35 using namespace dependencies;
42 DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
45 Opts(
std::move(Opts)),
C(
C) {}
48 C.handleDependencyOutputOpts(*Opts);
50 for (
const auto &File : getDependencies()) {
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) {}
104 bool needsImportVisitation()
const override {
return true; }
106 void visitImport(StringRef ModuleName, StringRef
Filename)
override {
107 if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
108 NewModuleFiles.push_back(
Filename.str());
111 void visitModuleFile(StringRef
Filename,
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;
137 static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
139 PrebuiltModuleFilesT &ModuleFiles,
144 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,
148 Listener.visitModuleFile(PrebuiltModuleFilename,
157 while (!Worklist.empty()) {
170 static std::string makeObjFileName(StringRef FileName) {
172 llvm::sys::path::replace_extension(ObjFileName,
"o");
173 return std::string(ObjFileName);
178 deduceDepTarget(
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;
202 llvm::erase_if(DiagOpts.
Warnings, [](StringRef Warning) {
203 return llvm::StringSwitch<bool>(Warning)
204 .Cases(
"pch-vfs-diff",
"error=pch-vfs-diff", false)
205 .StartsWith(
"no-error=", false)
220 static 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());
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) {
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;
305 canonicalizeDefines(OriginalInvocation.getPreprocessorOpts());
312 setLastCC1Arguments(std::move(OriginalInvocation));
319 ScanInstanceStorage.emplace(std::move(PCHContainerOps));
330 auto DiagConsumerFinisher =
331 llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->
finish(); });
350 StringRef ModulesCachePath =
353 DepFS->resetBypassedPathPrefix();
354 if (!ModulesCachePath.empty())
355 DepFS->setBypassedPathPrefix(ModulesCachePath);
360 if (llvm::ErrorOr<EntryRef> Entry =
361 LocalDepFS->getOrCreateFileSystemEntry(
File.getName()))
362 if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
363 return Entry->getDirectiveTokens();
377 if (visitPrebuiltModule(
391 auto Opts = std::make_unique<DependencyOutputOptions>();
395 if (Opts->Targets.empty())
399 Opts->IncludeSystemHeaders =
true;
404 std::make_shared<DependencyConsumerForwarder>(
405 std::move(Opts), WorkingDirectory, Consumer));
409 MDC = std::make_shared<ModuleDepCollector>(
410 std::move(Opts), ScanInstance, Consumer, Controller,
411 OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,
431 std::unique_ptr<FrontendAction> Action;
434 Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
436 Action = std::make_unique<ReadPCHAndPreprocessAction>();
442 DiagConsumerFinisher.release();
446 setLastCC1Arguments(std::move(OriginalInvocation));
454 bool hasScanned()
const {
return Scanned; }
459 std::vector<std::string> takeLastCC1Arguments() {
460 std::vector<std::string> Result;
461 std::swap(Result, LastCC1Arguments);
468 MDC->applyDiscoveredDependencies(CI);
469 LastCC1Arguments = CI.getCC1CommandLine();
473 StringRef WorkingDirectory;
479 bool EagerLoadModules;
481 std::optional<StringRef> ModuleName;
482 std::optional<CompilerInstance> ScanInstanceStorage;
483 std::shared_ptr<ModuleDepCollector> MDC;
484 std::vector<std::string> LastCC1Arguments;
485 bool Scanned =
false;
493 : Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),
494 EagerLoadModules(Service.shouldEagerLoadModules()) {
495 PCHContainerOps = std::make_shared<PCHContainerOperations>();
497 PCHContainerOps->registerReader(
498 std::make_unique<ObjectFilePCHContainerReader>());
500 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
516 StringRef WorkingDirectory,
const std::vector<std::string> &CommandLine,
518 std::optional<StringRef> ModuleName) {
519 std::vector<const char *> CLI;
520 for (
const std::string &Arg : CommandLine)
521 CLI.push_back(Arg.c_str());
523 sanitizeDiagOpts(*DiagOpts);
527 std::string DiagnosticOutput;
528 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
532 DiagPrinter, ModuleName))
533 return llvm::Error::success();
534 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
535 llvm::inconvertibleErrorCode());
542 Argv.reserve(ArgStrs.size());
543 for (
const std::string &Arg : ArgStrs)
544 Argv.push_back(Arg.c_str());
548 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
549 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
550 "clang LLVM compiler", FS);
551 Driver->setTitle(
"clang_based_tool");
553 llvm::BumpPtrAllocator
Alloc;
558 Diags.
Report(diag::err_drv_expand_response_file)
563 const std::unique_ptr<driver::Compilation> Compilation(
568 if (Compilation->containsError())
579 std::vector<std::string> CommandLine, DependencyScanningAction &Action,
581 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
585 std::string Executable = CommandLine[0];
590 if (!Invocation.
run())
593 std::vector<std::string> Args = Action.takeLastCC1Arguments();
599 StringRef WorkingDirectory,
const std::vector<std::string> &CommandLine,
603 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
605 std::optional<std::vector<std::string>> ModifiedCommandLine;
613 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
615 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
616 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
617 OverlayFS->pushOverlay(InMemoryFS);
618 ModifiedFS = OverlayFS;
622 llvm::sys::fs::createUniquePath(*ModuleName +
"-%%%%%%%%.input",
625 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(
""));
627 ModifiedCommandLine = CommandLine;
628 ModifiedCommandLine->emplace_back(FakeInputPath);
631 const std::vector<std::string> &FinalCommandLine =
632 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
633 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
638 std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(),
nullptr);
639 llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
640 [](
const std::string &Str) { return Str.c_str(); });
643 sanitizeDiagOpts(*DiagOpts);
651 Diags->setSourceManager(&SrcMgr);
655 bool DisableFree =
true;
656 DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,
657 Format, OptimizeArgs, EagerLoadModules,
658 DisableFree, ModuleName);
660 bool Success =
false;
661 if (FinalCommandLine[1] ==
"-cc1") {
663 PCHContainerOps, *Diags, Consumer);
667 if (StringRef(
Cmd.getCreator().getName()) !=
"clang") {
670 Consumer.handleBuildCommand(
671 {Cmd.getExecutable(),
672 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
677 std::vector<std::string> Argv;
678 Argv.push_back(
Cmd.getExecutable());
679 Argv.insert(Argv.end(),
Cmd.getArguments().begin(),
680 Cmd.getArguments().end());
687 PCHContainerOps, *Diags, Consumer);
691 if (Success && !Action.hasScanned())
692 Diags->
Report(diag::err_fe_expected_compiler_job)
693 << llvm::join(FinalCommandLine,
" ");
694 return Success && Action.hasScanned();
697 DependencyActionController::~DependencyActionController() {}
enum clang::sema::@1659::IndirectLocalPathEntry::EntryKind Kind
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.
@ 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.
CompilerInvocation & getInvocation()
void createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
FileManager * createFileManager(IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS=nullptr)
Create the file manager and replace any existing one with it.
FrontendOptions & getFrontendOpts()
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
PreprocessorOptions & getPreprocessorOpts()
InMemoryModuleCache & getModuleCache() const
void addDependencyCollector(std::shared_ptr< DependencyCollector > Listener)
bool hasDiagnostics() const
FileManager & getFileManager() const
Return the current file manager to the caller.
HeaderSearchOptions & getHeaderSearchOpts()
DiagnosticsEngine & getDiagnostics() const
Get the current diagnostics engine.
LangOptions & getLangOpts()
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
DiagnosticOptions & getDiagnosticOpts()
const PCHContainerReader & getPCHContainerReader() const
Return the appropriate PCHContainerReader depending on the current CodeGenOptions.
Helper class for holding the data necessary to invoke the compiler.
DependencyOutputOptions & getDependencyOutputOpts()
Builds a dependency file when attached to a Preprocessor (for includes) and ASTReader (for module imp...
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...
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...
std::function< std::optional< ArrayRef< dependency_directives_scan::Directive > > FileEntryRef)> DependencyDirectivesForFile
Function for getting the dependency preprocessor directives of a file.
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
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.
bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc)
ModuleKind
Specifies the kind of module that has been loaded.
@ MK_ExplicitModule
File is an explicitly-loaded module.
std::string toString(const til::SExpr *E)
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
IntrusiveRefCntPtr< llvm::vfs::FileSystem > createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags)
for(const auto &A :T->param_types())