184 lines · cpp
1//===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Collect the dependencies of a set of modules.10//11//===----------------------------------------------------------------------===//12 13#include "clang/Basic/CharInfo.h"14#include "clang/Frontend/Utils.h"15#include "clang/Lex/Preprocessor.h"16#include "clang/Serialization/ASTReader.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"21 22using namespace clang;23 24namespace {25/// Private implementations for ModuleDependencyCollector26class ModuleDependencyListener : public ASTReaderListener {27 ModuleDependencyCollector &Collector;28 FileManager &FileMgr;29public:30 ModuleDependencyListener(ModuleDependencyCollector &Collector,31 FileManager &FileMgr)32 : Collector(Collector), FileMgr(FileMgr) {}33 bool needsInputFileVisitation() override { return true; }34 bool needsSystemInputFileVisitation() override { return true; }35 bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden,36 bool IsExplicitModule) override {37 // Run this through the FileManager in order to respect 'use-external-name'38 // in case we have a VFS overlay.39 if (auto FE = FileMgr.getOptionalFileRef(Filename))40 Filename = FE->getName();41 Collector.addFile(Filename);42 return true;43 }44};45 46struct ModuleDependencyPPCallbacks : public PPCallbacks {47 ModuleDependencyCollector &Collector;48 SourceManager &SM;49 ModuleDependencyPPCallbacks(ModuleDependencyCollector &Collector,50 SourceManager &SM)51 : Collector(Collector), SM(SM) {}52 53 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,54 StringRef FileName, bool IsAngled,55 CharSourceRange FilenameRange,56 OptionalFileEntryRef File, StringRef SearchPath,57 StringRef RelativePath, const Module *SuggestedModule,58 bool ModuleImported,59 SrcMgr::CharacteristicKind FileType) override {60 if (!File)61 return;62 Collector.addFile(File->getName());63 }64};65 66struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks {67 ModuleDependencyCollector &Collector;68 ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector)69 : Collector(Collector) {}70 71 void moduleMapAddHeader(StringRef HeaderPath) override {72 if (llvm::sys::path::is_absolute(HeaderPath))73 Collector.addFile(HeaderPath);74 }75 void moduleMapAddUmbrellaHeader(FileEntryRef Header) override {76 moduleMapAddHeader(Header.getNameAsRequested());77 }78};79 80} // namespace81 82void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {83 R.addListener(84 std::make_unique<ModuleDependencyListener>(*this, R.getFileManager()));85}86 87void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) {88 PP.addPPCallbacks(std::make_unique<ModuleDependencyPPCallbacks>(89 *this, PP.getSourceManager()));90 PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks(91 std::make_unique<ModuleDependencyMMCallbacks>(*this));92}93 94static bool isCaseSensitivePath(llvm::vfs::FileSystem &VFS, StringRef Path) {95 SmallString<256> TmpDest = Path, UpperDest, RealDest;96 // Remove component traversals, links, etc.97 if (VFS.getRealPath(Path, TmpDest))98 return true; // Current default value in vfs.yaml99 Path = TmpDest;100 101 // 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.105 for (auto &C : Path)106 UpperDest.push_back(toUppercase(C));107 if (!VFS.getRealPath(UpperDest, RealDest) && Path == RealDest)108 return false;109 return true;110}111 112void ModuleDependencyCollector::writeFileMap() {113 if (Seen.empty())114 return;115 116 StringRef VFSDir = getDest();117 118 // Default to use relative overlay directories in the VFS yaml file. This119 // allows crash reproducer scripts to work across machines.120 VFSWriter.setOverlayDir(VFSDir);121 122 // Explicitly set case sensitivity for the YAML writer. For that, find out123 // the sensitivity at the path where the headers all collected to.124 VFSWriter.setCaseSensitivity(125 isCaseSensitivePath(Canonicalizer.getFileSystem(), VFSDir));126 127 // Do not rely on real path names when executing the crash reproducer scripts128 // since we only want to actually use the files we have on the VFS cache.129 VFSWriter.setUseExternalNames(false);130 131 std::error_code EC;132 SmallString<256> YAMLPath = VFSDir;133 llvm::sys::path::append(YAMLPath, "vfs.yaml");134 llvm::raw_fd_ostream OS(YAMLPath, EC, llvm::sys::fs::OF_TextWithCRLF);135 if (EC) {136 HasErrors = true;137 return;138 }139 VFSWriter.write(OS);140}141 142std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src,143 StringRef Dst) {144 using namespace llvm::sys;145 llvm::FileCollector::PathCanonicalizer::PathStorage Paths =146 Canonicalizer.canonicalize(Src);147 148 SmallString<256> CacheDst = getDest();149 150 if (Dst.empty()) {151 // The common case is to map the virtual path to the same path inside the152 // cache.153 path::append(CacheDst, path::relative_path(Paths.CopyFrom));154 } else {155 // When collecting entries from input vfsoverlays, copy the external156 // contents into the cache but still map from the source.157 if (!Canonicalizer.getFileSystem().exists(Dst))158 return std::error_code();159 path::append(CacheDst, Dst);160 Paths.CopyFrom = Dst;161 }162 163 // Copy the file into place.164 if (std::error_code EC = fs::create_directories(path::parent_path(CacheDst),165 /*IgnoreExisting=*/true))166 return EC;167 if (std::error_code EC = fs::copy_file(Paths.CopyFrom, CacheDst))168 return EC;169 170 // Always map a canonical src path to its real path into the YAML, by doing171 // this we map different virtual src paths to the same entry in the VFS172 // overlay, which is a way to emulate symlink inside the VFS; this is also173 // needed for correctness, not doing that can lead to module redefinition174 // errors.175 addFileMapping(Paths.VirtualPath, CacheDst);176 return std::error_code();177}178 179void ModuleDependencyCollector::addFile(StringRef Filename, StringRef FileDst) {180 if (insertSeen(Filename))181 if (copyToRoot(Filename, FileDst))182 HasErrors = true;183}184