10b57cec5SDimitry Andric //===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // Collect the dependencies of a set of modules. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #include "clang/Basic/CharInfo.h" 140b57cec5SDimitry Andric #include "clang/Frontend/Utils.h" 150b57cec5SDimitry Andric #include "clang/Lex/Preprocessor.h" 160b57cec5SDimitry Andric #include "clang/Serialization/ASTReader.h" 170b57cec5SDimitry Andric #include "llvm/ADT/iterator_range.h" 180b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h" 190b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 200b57cec5SDimitry Andric #include "llvm/Support/Path.h" 210b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric using namespace clang; 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric namespace { 260b57cec5SDimitry Andric /// Private implementations for ModuleDependencyCollector 270b57cec5SDimitry Andric class ModuleDependencyListener : public ASTReaderListener { 280b57cec5SDimitry Andric ModuleDependencyCollector &Collector; 29*bdd1243dSDimitry Andric FileManager &FileMgr; 300b57cec5SDimitry Andric public: 31*bdd1243dSDimitry Andric ModuleDependencyListener(ModuleDependencyCollector &Collector, 32*bdd1243dSDimitry Andric FileManager &FileMgr) 33*bdd1243dSDimitry Andric : Collector(Collector), FileMgr(FileMgr) {} 340b57cec5SDimitry Andric bool needsInputFileVisitation() override { return true; } 350b57cec5SDimitry Andric bool needsSystemInputFileVisitation() override { return true; } 360b57cec5SDimitry Andric bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden, 370b57cec5SDimitry Andric bool IsExplicitModule) override { 38*bdd1243dSDimitry Andric // Run this through the FileManager in order to respect 'use-external-name' 39*bdd1243dSDimitry Andric // in case we have a VFS overlay. 40*bdd1243dSDimitry Andric if (auto FE = FileMgr.getOptionalFileRef(Filename)) 41*bdd1243dSDimitry Andric Filename = FE->getName(); 420b57cec5SDimitry Andric Collector.addFile(Filename); 430b57cec5SDimitry Andric return true; 440b57cec5SDimitry Andric } 450b57cec5SDimitry Andric }; 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric struct ModuleDependencyPPCallbacks : public PPCallbacks { 480b57cec5SDimitry Andric ModuleDependencyCollector &Collector; 490b57cec5SDimitry Andric SourceManager &SM; 500b57cec5SDimitry Andric ModuleDependencyPPCallbacks(ModuleDependencyCollector &Collector, 510b57cec5SDimitry Andric SourceManager &SM) 520b57cec5SDimitry Andric : Collector(Collector), SM(SM) {} 530b57cec5SDimitry Andric 540b57cec5SDimitry Andric void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, 550b57cec5SDimitry Andric StringRef FileName, bool IsAngled, 5681ad6265SDimitry Andric CharSourceRange FilenameRange, 57*bdd1243dSDimitry Andric OptionalFileEntryRef File, StringRef SearchPath, 5881ad6265SDimitry Andric StringRef RelativePath, const Module *Imported, 590b57cec5SDimitry Andric SrcMgr::CharacteristicKind FileType) override { 600b57cec5SDimitry Andric if (!File) 610b57cec5SDimitry Andric return; 620b57cec5SDimitry Andric Collector.addFile(File->getName()); 630b57cec5SDimitry Andric } 640b57cec5SDimitry Andric }; 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks { 670b57cec5SDimitry Andric ModuleDependencyCollector &Collector; 680b57cec5SDimitry Andric ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector) 690b57cec5SDimitry Andric : Collector(Collector) {} 700b57cec5SDimitry Andric 710b57cec5SDimitry Andric void moduleMapAddHeader(StringRef HeaderPath) override { 720b57cec5SDimitry Andric if (llvm::sys::path::is_absolute(HeaderPath)) 730b57cec5SDimitry Andric Collector.addFile(HeaderPath); 740b57cec5SDimitry Andric } 750b57cec5SDimitry Andric void moduleMapAddUmbrellaHeader(FileManager *FileMgr, 760b57cec5SDimitry Andric const FileEntry *Header) override { 770b57cec5SDimitry Andric StringRef HeaderFilename = Header->getName(); 780b57cec5SDimitry Andric moduleMapAddHeader(HeaderFilename); 790b57cec5SDimitry Andric // The FileManager can find and cache the symbolic link for a framework 800b57cec5SDimitry Andric // header before its real path, this means a module can have some of its 810b57cec5SDimitry Andric // headers to use other paths. Although this is usually not a problem, it's 820b57cec5SDimitry Andric // inconsistent, and not collecting the original path header leads to 830b57cec5SDimitry Andric // umbrella clashes while rebuilding modules in the crash reproducer. For 840b57cec5SDimitry Andric // example: 850b57cec5SDimitry Andric // ApplicationServices.framework/Frameworks/ImageIO.framework/ImageIO.h 860b57cec5SDimitry Andric // instead of: 870b57cec5SDimitry Andric // ImageIO.framework/ImageIO.h 880b57cec5SDimitry Andric // 890b57cec5SDimitry Andric // FIXME: this shouldn't be necessary once we have FileName instances 900b57cec5SDimitry Andric // around instead of FileEntry ones. For now, make sure we collect all 910b57cec5SDimitry Andric // that we need for the reproducer to work correctly. 920b57cec5SDimitry Andric StringRef UmbreallDirFromHeader = 930b57cec5SDimitry Andric llvm::sys::path::parent_path(HeaderFilename); 940b57cec5SDimitry Andric StringRef UmbrellaDir = Header->getDir()->getName(); 950b57cec5SDimitry Andric if (!UmbrellaDir.equals(UmbreallDirFromHeader)) { 960b57cec5SDimitry Andric SmallString<128> AltHeaderFilename; 970b57cec5SDimitry Andric llvm::sys::path::append(AltHeaderFilename, UmbrellaDir, 980b57cec5SDimitry Andric llvm::sys::path::filename(HeaderFilename)); 990b57cec5SDimitry Andric if (FileMgr->getFile(AltHeaderFilename)) 1000b57cec5SDimitry Andric moduleMapAddHeader(AltHeaderFilename); 1010b57cec5SDimitry Andric } 1020b57cec5SDimitry Andric } 1030b57cec5SDimitry Andric }; 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric } 1060b57cec5SDimitry Andric 1070b57cec5SDimitry Andric void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { 108*bdd1243dSDimitry Andric R.addListener( 109*bdd1243dSDimitry Andric std::make_unique<ModuleDependencyListener>(*this, R.getFileManager())); 1100b57cec5SDimitry Andric } 1110b57cec5SDimitry Andric 1120b57cec5SDimitry Andric void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) { 113a7dea167SDimitry Andric PP.addPPCallbacks(std::make_unique<ModuleDependencyPPCallbacks>( 1140b57cec5SDimitry Andric *this, PP.getSourceManager())); 1150b57cec5SDimitry Andric PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( 116a7dea167SDimitry Andric std::make_unique<ModuleDependencyMMCallbacks>(*this)); 1170b57cec5SDimitry Andric } 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric static bool isCaseSensitivePath(StringRef Path) { 1200b57cec5SDimitry Andric SmallString<256> TmpDest = Path, UpperDest, RealDest; 1210b57cec5SDimitry Andric // Remove component traversals, links, etc. 1220b57cec5SDimitry Andric if (llvm::sys::fs::real_path(Path, TmpDest)) 1230b57cec5SDimitry Andric return true; // Current default value in vfs.yaml 1240b57cec5SDimitry Andric Path = TmpDest; 1250b57cec5SDimitry Andric 1260b57cec5SDimitry Andric // Change path to all upper case and ask for its real path, if the latter 1270b57cec5SDimitry Andric // exists and is equal to Path, it's not case sensitive. Default to case 1280b57cec5SDimitry Andric // sensitive in the absence of realpath, since this is what the VFSWriter 1290b57cec5SDimitry Andric // already expects when sensitivity isn't setup. 1300b57cec5SDimitry Andric for (auto &C : Path) 1310b57cec5SDimitry Andric UpperDest.push_back(toUppercase(C)); 1320b57cec5SDimitry Andric if (!llvm::sys::fs::real_path(UpperDest, RealDest) && Path.equals(RealDest)) 1330b57cec5SDimitry Andric return false; 1340b57cec5SDimitry Andric return true; 1350b57cec5SDimitry Andric } 1360b57cec5SDimitry Andric 1370b57cec5SDimitry Andric void ModuleDependencyCollector::writeFileMap() { 1380b57cec5SDimitry Andric if (Seen.empty()) 1390b57cec5SDimitry Andric return; 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric StringRef VFSDir = getDest(); 1420b57cec5SDimitry Andric 1430b57cec5SDimitry Andric // Default to use relative overlay directories in the VFS yaml file. This 1440b57cec5SDimitry Andric // allows crash reproducer scripts to work across machines. 1450b57cec5SDimitry Andric VFSWriter.setOverlayDir(VFSDir); 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric // Explicitly set case sensitivity for the YAML writer. For that, find out 1480b57cec5SDimitry Andric // the sensitivity at the path where the headers all collected to. 1490b57cec5SDimitry Andric VFSWriter.setCaseSensitivity(isCaseSensitivePath(VFSDir)); 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric // Do not rely on real path names when executing the crash reproducer scripts 1520b57cec5SDimitry Andric // since we only want to actually use the files we have on the VFS cache. 1530b57cec5SDimitry Andric VFSWriter.setUseExternalNames(false); 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric std::error_code EC; 1560b57cec5SDimitry Andric SmallString<256> YAMLPath = VFSDir; 1570b57cec5SDimitry Andric llvm::sys::path::append(YAMLPath, "vfs.yaml"); 158fe6060f1SDimitry Andric llvm::raw_fd_ostream OS(YAMLPath, EC, llvm::sys::fs::OF_TextWithCRLF); 1590b57cec5SDimitry Andric if (EC) { 1600b57cec5SDimitry Andric HasErrors = true; 1610b57cec5SDimitry Andric return; 1620b57cec5SDimitry Andric } 1630b57cec5SDimitry Andric VFSWriter.write(OS); 1640b57cec5SDimitry Andric } 1650b57cec5SDimitry Andric 1660b57cec5SDimitry Andric std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src, 1670b57cec5SDimitry Andric StringRef Dst) { 1680b57cec5SDimitry Andric using namespace llvm::sys; 169e8d8bef9SDimitry Andric llvm::FileCollector::PathCanonicalizer::PathStorage Paths = 170e8d8bef9SDimitry Andric Canonicalizer.canonicalize(Src); 1710b57cec5SDimitry Andric 1720b57cec5SDimitry Andric SmallString<256> CacheDst = getDest(); 1730b57cec5SDimitry Andric 1740b57cec5SDimitry Andric if (Dst.empty()) { 1750b57cec5SDimitry Andric // The common case is to map the virtual path to the same path inside the 1760b57cec5SDimitry Andric // cache. 177e8d8bef9SDimitry Andric path::append(CacheDst, path::relative_path(Paths.CopyFrom)); 1780b57cec5SDimitry Andric } else { 1790b57cec5SDimitry Andric // When collecting entries from input vfsoverlays, copy the external 1800b57cec5SDimitry Andric // contents into the cache but still map from the source. 1810b57cec5SDimitry Andric if (!fs::exists(Dst)) 1820b57cec5SDimitry Andric return std::error_code(); 1830b57cec5SDimitry Andric path::append(CacheDst, Dst); 184e8d8bef9SDimitry Andric Paths.CopyFrom = Dst; 1850b57cec5SDimitry Andric } 1860b57cec5SDimitry Andric 1870b57cec5SDimitry Andric // Copy the file into place. 1880b57cec5SDimitry Andric if (std::error_code EC = fs::create_directories(path::parent_path(CacheDst), 1890b57cec5SDimitry Andric /*IgnoreExisting=*/true)) 1900b57cec5SDimitry Andric return EC; 191e8d8bef9SDimitry Andric if (std::error_code EC = fs::copy_file(Paths.CopyFrom, CacheDst)) 1920b57cec5SDimitry Andric return EC; 1930b57cec5SDimitry Andric 1940b57cec5SDimitry Andric // Always map a canonical src path to its real path into the YAML, by doing 1950b57cec5SDimitry Andric // this we map different virtual src paths to the same entry in the VFS 1960b57cec5SDimitry Andric // overlay, which is a way to emulate symlink inside the VFS; this is also 1970b57cec5SDimitry Andric // needed for correctness, not doing that can lead to module redefinition 1980b57cec5SDimitry Andric // errors. 199e8d8bef9SDimitry Andric addFileMapping(Paths.VirtualPath, CacheDst); 2000b57cec5SDimitry Andric return std::error_code(); 2010b57cec5SDimitry Andric } 2020b57cec5SDimitry Andric 2030b57cec5SDimitry Andric void ModuleDependencyCollector::addFile(StringRef Filename, StringRef FileDst) { 2040b57cec5SDimitry Andric if (insertSeen(Filename)) 2050b57cec5SDimitry Andric if (copyToRoot(Filename, FileDst)) 2060b57cec5SDimitry Andric HasErrors = true; 2070b57cec5SDimitry Andric } 208