27#include "llvm/ADT/IntrusiveRefCntPtr.h"
28#include "llvm/Support/Allocator.h"
29#include "llvm/Support/Error.h"
30#include "llvm/Support/MemoryBuffer.h"
31#include "llvm/TargetParser/Host.h"
35using namespace tooling;
36using namespace dependencies;
43 DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
46 Opts(
std::move(Opts)),
C(
C) {}
49 C.handleDependencyOutputOpts(*Opts);
53 llvm::sys::path::remove_dots(CanonPath,
true);
54 llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);
55 C.handleFileDependency(CanonPath);
60 StringRef WorkingDirectory;
61 std::unique_ptr<DependencyOutputOptions> Opts;
69 if (LangOpts.Modules) {
72 Diags->
Report(diag::warn_pch_vfsoverlay_mismatch);
74 if (VFSOverlays.empty()) {
75 Diags->
Report(diag::note_pch_vfsoverlay_empty) <<
Type;
77 std::string Files = llvm::join(VFSOverlays,
"\n");
78 Diags->
Report(diag::note_pch_vfsoverlay_files) <<
Type << Files;
96 PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
102 : PrebuiltModuleFiles(PrebuiltModuleFiles),
103 NewModuleFiles(NewModuleFiles),
104 PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts),
105 ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {}
114 if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
115 NewModuleFiles.push_back(
Filename.str());
117 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(
Filename);
119 if (PrebuiltMapEntry.second)
122 if (
auto It = PrebuiltModulesASTMap.find(CurrentFile);
123 It != PrebuiltModulesASTMap.end() && CurrentFile !=
Filename)
131 bool isSystem,
bool isOverridden,
132 bool isExplicitModule)
override {
133 if (StableDirs.empty())
135 auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
136 if ((PrebuiltEntryIt == PrebuiltModulesASTMap.end()) ||
137 (!PrebuiltEntryIt->second.isInStableDir()))
140 PrebuiltEntryIt->second.setInStableDir(
142 return PrebuiltEntryIt->second.isInStableDir();
150 auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
151 if ((PrebuiltEntryIt != PrebuiltModulesASTMap.end()) &&
152 !PrebuiltEntryIt->second.isInStableDir())
153 PrebuiltEntryIt->second.updateDependentsNotInStableDirs(
154 PrebuiltModulesASTMap);
161 StringRef ModuleFilename,
162 StringRef SpecificModuleCachePath,
163 bool Complain)
override {
165 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
167 if (PrebuiltMapEntry.second)
178 bool Complain)
override {
180 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
182 if (PrebuiltMapEntry.second)
188 return checkHeaderSearchPaths(
189 HSOpts, ExistingHSOpts, Complain ? &Diags :
nullptr, ExistingLangOpts);
193 PrebuiltModuleFilesT &PrebuiltModuleFiles;
199 std::string CurrentFile;
205static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
207 PrebuiltModuleFilesT &ModuleFiles,
214 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap,
218 Listener.visitModuleFile(PrebuiltModuleFilename,
227 while (!Worklist.empty()) {
240static std::string makeObjFileName(StringRef
FileName) {
242 llvm::sys::path::replace_extension(ObjFileName,
"o");
243 return std::string(ObjFileName);
248deduceDepTarget(
const std::string &OutputFile,
250 if (OutputFile !=
"-")
253 if (InputFiles.empty() || !InputFiles.front().isFile())
254 return "clang-scan-deps\\ dependency";
256 return makeObjFileName(InputFiles.front().getFile());
262 DiagOpts.ShowCarets =
false;
273 return llvm::StringSwitch<bool>(Warning)
274 .Cases(
"pch-vfs-diff",
"error=pch-vfs-diff", false)
275 .StartsWith(
"no-error=", false)
290static std::optional<StringRef> getSimpleMacroName(StringRef
Macro) {
291 StringRef Name =
Macro.split(
"=").first.ltrim(
" \t");
294 auto FinishName = [&]() -> std::optional<StringRef> {
295 StringRef SimpleName = Name.slice(0, I);
296 if (SimpleName.empty())
301 for (; I != Name.size(); ++I) {
310 if (llvm::isAlnum(Name[I]))
319 using MacroOpt = std::pair<StringRef, std::size_t>;
320 std::vector<MacroOpt> SimpleNames;
321 SimpleNames.reserve(PPOpts.
Macros.size());
322 std::size_t Index = 0;
323 for (
const auto &M : PPOpts.
Macros) {
324 auto SName = getSimpleMacroName(M.first);
328 SimpleNames.emplace_back(*SName, Index);
332 llvm::stable_sort(SimpleNames, llvm::less_first());
334 auto NewEnd = std::unique(
335 SimpleNames.rbegin(), SimpleNames.rend(),
336 [](
const MacroOpt &A,
const MacroOpt &B) { return A.first == B.first; });
337 SimpleNames.erase(SimpleNames.begin(), NewEnd.base());
340 decltype(PPOpts.
Macros) NewMacros;
341 NewMacros.reserve(SimpleNames.size());
342 for (std::size_t I = 0,
E = SimpleNames.size(); I !=
E; ++I) {
343 std::size_t OriginalIndex = SimpleNames[I].second;
345 NewMacros.push_back(std::move(PPOpts.
Macros[OriginalIndex]));
347 std::swap(PPOpts.
Macros, NewMacros);
354 ScanningDependencyDirectivesGetter(
FileManager &FileMgr) : DepFS(nullptr) {
356 auto *
DFS = llvm::dyn_cast<DependencyScanningWorkerFilesystem>(&FS);
358 assert(!DepFS &&
"Found multiple scanning VFSs");
362 assert(DepFS &&
"Did not find scanning VFS");
365 std::unique_ptr<DependencyDirectivesGetter>
367 return std::make_unique<ScanningDependencyDirectivesGetter>(FileMgr);
370 std::optional<ArrayRef<dependency_directives_scan::Directive>>
378class DependencyScanningAction {
380 DependencyScanningAction(
384 std::optional<StringRef> ModuleName = std::nullopt)
385 : Service(Service), WorkingDirectory(WorkingDirectory),
386 Consumer(Consumer), Controller(Controller), DepFS(
std::move(DepFS)),
387 ModuleName(ModuleName) {}
389 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
391 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
395 if (
any(Service.getOptimizeArgs() & ScanningOptimizations::Macros))
396 canonicalizeDefines(OriginalInvocation.getPreprocessorOpts());
403 setLastCC1Arguments(std::move(OriginalInvocation));
411 ScanInstanceStorage.emplace(std::move(Invocation),
412 std::move(PCHContainerOps), ModCache.get());
418 assert(!DiagConsumerFinished &&
"attempt to reuse finished consumer");
429 Service.getBuildSessionTimestamp();
439 any(Service.getOptimizeArgs() & ScanningOptimizations::VFS);
451 StringRef ModulesCachePath =
455 if (!ModulesCachePath.empty())
459 std::make_unique<ScanningDependencyDirectivesGetter>(*FileMgr));
469 if (!Sysroot.empty() &&
470 (llvm::sys::path::root_directory(Sysroot) != Sysroot))
480 if (visitPrebuiltModule(
484 PrebuiltModulesASTMap, ScanInstance.
getDiagnostics(), StableDirs))
494 auto Opts = std::make_unique<DependencyOutputOptions>();
498 if (Opts->Targets.empty())
502 Opts->IncludeSystemHeaders =
true;
504 switch (Service.getFormat()) {
505 case ScanningOutputFormat::Make:
507 std::make_shared<DependencyConsumerForwarder>(
508 std::move(Opts), WorkingDirectory, Consumer));
510 case ScanningOutputFormat::P1689:
511 case ScanningOutputFormat::Full:
512 MDC = std::make_shared<ModuleDepCollector>(
513 Service, std::move(Opts), ScanInstance, Consumer, Controller,
514 OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs);
535 std::unique_ptr<FrontendAction> Action;
537 if (Service.getFormat() == ScanningOutputFormat::P1689)
538 Action = std::make_unique<PreprocessOnlyAction>();
540 Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
542 Action = std::make_unique<ReadPCHAndPreprocessAction>();
550 DiagConsumerFinished =
true;
553 setLastCC1Arguments(std::move(OriginalInvocation));
558 bool hasScanned()
const {
return Scanned; }
559 bool hasDiagConsumerFinished()
const {
return DiagConsumerFinished; }
564 std::vector<std::string> takeLastCC1Arguments() {
565 std::vector<std::string> Result;
566 std::swap(Result, LastCC1Arguments);
573 MDC->applyDiscoveredDependencies(CI);
574 LastCC1Arguments = CI.getCC1CommandLine();
578 StringRef WorkingDirectory;
582 std::optional<StringRef> ModuleName;
583 std::optional<CompilerInstance> ScanInstanceStorage;
584 std::shared_ptr<ModuleDepCollector> MDC;
585 std::vector<std::string> LastCC1Arguments;
586 bool Scanned =
false;
587 bool DiagConsumerFinished =
false;
596 PCHContainerOps = std::make_shared<PCHContainerOperations>();
598 PCHContainerOps->registerReader(
599 std::make_unique<ObjectFilePCHContainerReader>());
601 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
604 FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));
608 DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
619static std::unique_ptr<DiagnosticOptions>
621 std::vector<const char *> CLI;
622 for (
const std::string &Arg : CommandLine)
623 CLI.push_back(Arg.c_str());
625 sanitizeDiagOpts(*DiagOpts);
630 StringRef WorkingDirectory,
const std::vector<std::string> &CommandLine,
632 std::optional<llvm::MemoryBufferRef> TUBuffer) {
635 std::string DiagnosticOutput;
636 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
641 DiagPrinter, TUBuffer))
642 return llvm::Error::success();
643 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
644 llvm::inconvertibleErrorCode());
648 StringRef WorkingDirectory,
const std::vector<std::string> &CommandLine,
650 StringRef ModuleName) {
653 std::string DiagnosticOutput;
654 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
659 DiagPrinter, ModuleName))
660 return llvm::Error::success();
661 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
662 llvm::inconvertibleErrorCode());
670 Argv.reserve(ArgStrs.size());
671 for (
const std::string &Arg : ArgStrs)
672 Argv.push_back(Arg.c_str());
674 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
675 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
676 "clang LLVM compiler", FS);
677 Driver->setTitle(
"clang_based_tool");
679 llvm::BumpPtrAllocator Alloc;
685 Diags.
Report(diag::err_drv_expand_response_file)
686 << llvm::toString(std::move(
E));
690 const std::unique_ptr<driver::Compilation> Compilation(
695 if (Compilation->containsError())
706 std::vector<std::string> CommandLine, DependencyScanningAction &Action,
708 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
712 std::string Executable = CommandLine[0];
714 llvm::opt::ArgStringList Argv;
715 for (
const std::string &Str :
ArrayRef(CommandLine).drop_front())
716 Argv.push_back(Str.c_str());
718 auto Invocation = std::make_shared<CompilerInvocation>();
724 if (!Action.runInvocation(std::move(Invocation), std::move(FS),
728 std::vector<std::string> Args = Action.takeLastCC1Arguments();
733bool DependencyScanningWorker::scanDependencies(
734 StringRef WorkingDirectory,
const std::vector<std::string> &CommandLine,
737 std::optional<StringRef> ModuleName) {
738 std::vector<const char *> CCommandLine(CommandLine.size(),
nullptr);
739 llvm::transform(CommandLine, CCommandLine.begin(),
740 [](
const std::string &Str) { return Str.c_str(); });
742 sanitizeDiagOpts(*DiagOpts);
746 DependencyScanningAction Action(Service, WorkingDirectory, Consumer,
747 Controller, DepFS, ModuleName);
750 if (CommandLine[1] ==
"-cc1") {
752 PCHContainerOps, *Diags, Consumer);
756 if (StringRef(
Cmd.getCreator().getName()) !=
"clang") {
759 Consumer.handleBuildCommand(
760 {Cmd.getExecutable(),
761 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
766 std::vector<std::string> Argv;
767 Argv.push_back(
Cmd.getExecutable());
768 llvm::append_range(Argv,
Cmd.getArguments());
775 PCHContainerOps, *Diags, Consumer);
779 if (
Success && !Action.hasScanned())
780 Diags->
Report(diag::err_fe_expected_compiler_job)
781 << llvm::join(CommandLine,
" ");
784 if (!Action.hasDiagConsumerFinished())
787 return Success && Action.hasScanned();
791 StringRef WorkingDirectory,
const std::vector<std::string> &CommandLine,
795 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
797 std::optional<std::vector<std::string>> ModifiedCommandLine;
805 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
807 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
808 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
809 auto InputPath = TUBuffer->getBufferIdentifier();
812 llvm::MemoryBuffer::getMemBufferCopy(TUBuffer->getBuffer()));
816 OverlayFS->pushOverlay(InMemoryOverlay);
817 ModifiedFS = OverlayFS;
818 ModifiedCommandLine = CommandLine;
819 ModifiedCommandLine->emplace_back(InputPath);
822 const std::vector<std::string> &FinalCommandLine =
823 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
824 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
826 return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,
827 Controller, DC, FinalFS, std::nullopt);
831 StringRef WorkingDirectory,
const std::vector<std::string> &CommandLine,
835 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
841 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
842 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
843 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
846 llvm::sys::fs::createUniquePath(ModuleName +
"-%%%%%%%%.input", FakeInputPath,
848 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(
""));
851 OverlayFS->pushOverlay(InMemoryOverlay);
852 auto ModifiedCommandLine = CommandLine;
853 ModifiedCommandLine.emplace_back(FakeInputPath);
855 return scanDependencies(WorkingDirectory, ModifiedCommandLine, Consumer,
856 Controller, DC, OverlayFS, ModuleName);
static bool createAndRunToolInvocation(std::vector< std::string > CommandLine, DependencyScanningAction &Action, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, std::shared_ptr< clang::PCHContainerOperations > &PCHContainerOps, DiagnosticsEngine &Diags, DependencyConsumer &Consumer)
static std::unique_ptr< DiagnosticOptions > createDiagOptions(const std::vector< std::string > &CommandLine)
static bool forEachDriverJob(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, llvm::function_ref< bool(const driver::Command &Cmd)> Callback)
Abstract interface for callback invocations by the ASTReader.
virtual bool needsInputFileVisitation()
Returns true if this ASTReaderListener wants to receive the input files of the AST file via visitInpu...
virtual bool visitInputFile(StringRef Filename, bool isSystem, bool isOverridden, bool isExplicitModule)
if needsInputFileVisitation returns true, this is called for each non-system input file of the AST Fi...
virtual bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts, bool Complain)
Receives the header search paths.
virtual bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts, StringRef ModuleFilename, StringRef SpecificModuleCachePath, bool Complain)
Receives the header search options.
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,...
virtual bool needsSystemInputFileVisitation()
Returns true if this ASTReaderListener wants to receive the system input files of the AST file via vi...
@ 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 ModuleCache &ModCache, 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 ...
FileManager & getFileManager() const
Return the current file manager to the caller.
ModuleCache & 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()
void setDependencyDirectivesGetter(std::unique_ptr< DependencyDirectivesGetter > Getter)
Helper class for holding the data necessary to invoke the compiler.
static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef< const char * > CommandLineArgs, DiagnosticsEngine &Diags, const char *Argv0=nullptr)
Create a compiler invocation from a list of input options.
DependencyOutputOptions & getDependencyOutputOpts()
ArrayRef< std::string > getDependencies() const
Functor that returns the dependency directives for a given file.
virtual std::optional< ArrayRef< dependency_directives_scan::Directive > > operator()(FileEntryRef File)=0
Get the dependency directives for the given file.
virtual std::unique_ptr< DependencyDirectivesGetter > cloneFor(FileManager &FileMgr)=0
Clone the getter for a new FileManager instance.
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
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.
llvm::vfs::FileSystem & getVirtualFileSystem() const
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.
unsigned DisableFree
Disable memory freeing on exit.
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...
void setBuildingModule(bool BuildingModuleFlag)
Flag indicating whether this instance is building a module.
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
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)
@ Success
Annotation was successful.
IntrusiveRefCntPtr< llvm::vfs::FileSystem > createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags)
int __ovld __cnfn any(char)
Returns 1 if the most significant bit in any component of x is set; otherwise returns 0.