Path: blob/main/contrib/llvm-project/clang/lib/Frontend/ModuleDependencyCollector.cpp
35232 views
//===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Collect the dependencies of a set of modules.9//10//===----------------------------------------------------------------------===//1112#include "clang/Basic/CharInfo.h"13#include "clang/Frontend/Utils.h"14#include "clang/Lex/Preprocessor.h"15#include "clang/Serialization/ASTReader.h"16#include "llvm/ADT/iterator_range.h"17#include "llvm/Config/llvm-config.h"18#include "llvm/Support/FileSystem.h"19#include "llvm/Support/Path.h"20#include "llvm/Support/raw_ostream.h"2122using namespace clang;2324namespace {25/// Private implementations for ModuleDependencyCollector26class ModuleDependencyListener : public ASTReaderListener {27ModuleDependencyCollector &Collector;28FileManager &FileMgr;29public:30ModuleDependencyListener(ModuleDependencyCollector &Collector,31FileManager &FileMgr)32: Collector(Collector), FileMgr(FileMgr) {}33bool needsInputFileVisitation() override { return true; }34bool needsSystemInputFileVisitation() override { return true; }35bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden,36bool IsExplicitModule) override {37// Run this through the FileManager in order to respect 'use-external-name'38// in case we have a VFS overlay.39if (auto FE = FileMgr.getOptionalFileRef(Filename))40Filename = FE->getName();41Collector.addFile(Filename);42return true;43}44};4546struct ModuleDependencyPPCallbacks : public PPCallbacks {47ModuleDependencyCollector &Collector;48SourceManager &SM;49ModuleDependencyPPCallbacks(ModuleDependencyCollector &Collector,50SourceManager &SM)51: Collector(Collector), SM(SM) {}5253void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,54StringRef FileName, bool IsAngled,55CharSourceRange FilenameRange,56OptionalFileEntryRef File, StringRef SearchPath,57StringRef RelativePath, const Module *SuggestedModule,58bool ModuleImported,59SrcMgr::CharacteristicKind FileType) override {60if (!File)61return;62Collector.addFile(File->getName());63}64};6566struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks {67ModuleDependencyCollector &Collector;68ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector)69: Collector(Collector) {}7071void moduleMapAddHeader(StringRef HeaderPath) override {72if (llvm::sys::path::is_absolute(HeaderPath))73Collector.addFile(HeaderPath);74}75void moduleMapAddUmbrellaHeader(FileEntryRef Header) override {76moduleMapAddHeader(Header.getNameAsRequested());77}78};7980} // namespace8182void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {83R.addListener(84std::make_unique<ModuleDependencyListener>(*this, R.getFileManager()));85}8687void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) {88PP.addPPCallbacks(std::make_unique<ModuleDependencyPPCallbacks>(89*this, PP.getSourceManager()));90PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks(91std::make_unique<ModuleDependencyMMCallbacks>(*this));92}9394static bool isCaseSensitivePath(StringRef Path) {95SmallString<256> TmpDest = Path, UpperDest, RealDest;96// Remove component traversals, links, etc.97if (llvm::sys::fs::real_path(Path, TmpDest))98return true; // Current default value in vfs.yaml99Path = TmpDest;100101// Change path to all upper case and ask for its real path, if the latter102// exists and is equal to Path, it's not case sensitive. Default to case103// sensitive in the absence of realpath, since this is what the VFSWriter104// already expects when sensitivity isn't setup.105for (auto &C : Path)106UpperDest.push_back(toUppercase(C));107if (!llvm::sys::fs::real_path(UpperDest, RealDest) && Path == RealDest)108return false;109return true;110}111112void ModuleDependencyCollector::writeFileMap() {113if (Seen.empty())114return;115116StringRef VFSDir = getDest();117118// Default to use relative overlay directories in the VFS yaml file. This119// allows crash reproducer scripts to work across machines.120VFSWriter.setOverlayDir(VFSDir);121122// Explicitly set case sensitivity for the YAML writer. For that, find out123// the sensitivity at the path where the headers all collected to.124VFSWriter.setCaseSensitivity(isCaseSensitivePath(VFSDir));125126// Do not rely on real path names when executing the crash reproducer scripts127// since we only want to actually use the files we have on the VFS cache.128VFSWriter.setUseExternalNames(false);129130std::error_code EC;131SmallString<256> YAMLPath = VFSDir;132llvm::sys::path::append(YAMLPath, "vfs.yaml");133llvm::raw_fd_ostream OS(YAMLPath, EC, llvm::sys::fs::OF_TextWithCRLF);134if (EC) {135HasErrors = true;136return;137}138VFSWriter.write(OS);139}140141std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src,142StringRef Dst) {143using namespace llvm::sys;144llvm::FileCollector::PathCanonicalizer::PathStorage Paths =145Canonicalizer.canonicalize(Src);146147SmallString<256> CacheDst = getDest();148149if (Dst.empty()) {150// The common case is to map the virtual path to the same path inside the151// cache.152path::append(CacheDst, path::relative_path(Paths.CopyFrom));153} else {154// When collecting entries from input vfsoverlays, copy the external155// contents into the cache but still map from the source.156if (!fs::exists(Dst))157return std::error_code();158path::append(CacheDst, Dst);159Paths.CopyFrom = Dst;160}161162// Copy the file into place.163if (std::error_code EC = fs::create_directories(path::parent_path(CacheDst),164/*IgnoreExisting=*/true))165return EC;166if (std::error_code EC = fs::copy_file(Paths.CopyFrom, CacheDst))167return EC;168169// Always map a canonical src path to its real path into the YAML, by doing170// this we map different virtual src paths to the same entry in the VFS171// overlay, which is a way to emulate symlink inside the VFS; this is also172// needed for correctness, not doing that can lead to module redefinition173// errors.174addFileMapping(Paths.VirtualPath, CacheDst);175return std::error_code();176}177178void ModuleDependencyCollector::addFile(StringRef Filename, StringRef FileDst) {179if (insertSeen(Filename))180if (copyToRoot(Filename, FileDst))181HasErrors = true;182}183184185