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; 29bdd1243dSDimitry Andric FileManager &FileMgr; 300b57cec5SDimitry Andric public: 31bdd1243dSDimitry Andric ModuleDependencyListener(ModuleDependencyCollector &Collector, 32bdd1243dSDimitry Andric FileManager &FileMgr) 33bdd1243dSDimitry 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 { 38bdd1243dSDimitry Andric // Run this through the FileManager in order to respect 'use-external-name' 39bdd1243dSDimitry Andric // in case we have a VFS overlay. 40bdd1243dSDimitry Andric if (auto FE = FileMgr.getOptionalFileRef(Filename)) 41bdd1243dSDimitry 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, 57bdd1243dSDimitry Andric OptionalFileEntryRef File, StringRef SearchPath, 58*0fca6ea1SDimitry Andric StringRef RelativePath, const Module *SuggestedModule, 59*0fca6ea1SDimitry Andric bool ModuleImported, 600b57cec5SDimitry Andric SrcMgr::CharacteristicKind FileType) override { 610b57cec5SDimitry Andric if (!File) 620b57cec5SDimitry Andric return; 630b57cec5SDimitry Andric Collector.addFile(File->getName()); 640b57cec5SDimitry Andric } 650b57cec5SDimitry Andric }; 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks { 680b57cec5SDimitry Andric ModuleDependencyCollector &Collector; 690b57cec5SDimitry Andric ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector) 700b57cec5SDimitry Andric : Collector(Collector) {} 710b57cec5SDimitry Andric 720b57cec5SDimitry Andric void moduleMapAddHeader(StringRef HeaderPath) override { 730b57cec5SDimitry Andric if (llvm::sys::path::is_absolute(HeaderPath)) 740b57cec5SDimitry Andric Collector.addFile(HeaderPath); 750b57cec5SDimitry Andric } 7606c3fb27SDimitry Andric void moduleMapAddUmbrellaHeader(FileEntryRef Header) override { 7706c3fb27SDimitry Andric moduleMapAddHeader(Header.getNameAsRequested()); 780b57cec5SDimitry Andric } 790b57cec5SDimitry Andric }; 800b57cec5SDimitry Andric 8106c3fb27SDimitry Andric } // namespace 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { 84bdd1243dSDimitry Andric R.addListener( 85bdd1243dSDimitry Andric std::make_unique<ModuleDependencyListener>(*this, R.getFileManager())); 860b57cec5SDimitry Andric } 870b57cec5SDimitry Andric 880b57cec5SDimitry Andric void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) { 89a7dea167SDimitry Andric PP.addPPCallbacks(std::make_unique<ModuleDependencyPPCallbacks>( 900b57cec5SDimitry Andric *this, PP.getSourceManager())); 910b57cec5SDimitry Andric PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( 92a7dea167SDimitry Andric std::make_unique<ModuleDependencyMMCallbacks>(*this)); 930b57cec5SDimitry Andric } 940b57cec5SDimitry Andric 950b57cec5SDimitry Andric static bool isCaseSensitivePath(StringRef Path) { 960b57cec5SDimitry Andric SmallString<256> TmpDest = Path, UpperDest, RealDest; 970b57cec5SDimitry Andric // Remove component traversals, links, etc. 980b57cec5SDimitry Andric if (llvm::sys::fs::real_path(Path, TmpDest)) 990b57cec5SDimitry Andric return true; // Current default value in vfs.yaml 1000b57cec5SDimitry Andric Path = TmpDest; 1010b57cec5SDimitry Andric 1020b57cec5SDimitry Andric // Change path to all upper case and ask for its real path, if the latter 1030b57cec5SDimitry Andric // exists and is equal to Path, it's not case sensitive. Default to case 1040b57cec5SDimitry Andric // sensitive in the absence of realpath, since this is what the VFSWriter 1050b57cec5SDimitry Andric // already expects when sensitivity isn't setup. 1060b57cec5SDimitry Andric for (auto &C : Path) 1070b57cec5SDimitry Andric UpperDest.push_back(toUppercase(C)); 108*0fca6ea1SDimitry Andric if (!llvm::sys::fs::real_path(UpperDest, RealDest) && Path == RealDest) 1090b57cec5SDimitry Andric return false; 1100b57cec5SDimitry Andric return true; 1110b57cec5SDimitry Andric } 1120b57cec5SDimitry Andric 1130b57cec5SDimitry Andric void ModuleDependencyCollector::writeFileMap() { 1140b57cec5SDimitry Andric if (Seen.empty()) 1150b57cec5SDimitry Andric return; 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric StringRef VFSDir = getDest(); 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric // Default to use relative overlay directories in the VFS yaml file. This 1200b57cec5SDimitry Andric // allows crash reproducer scripts to work across machines. 1210b57cec5SDimitry Andric VFSWriter.setOverlayDir(VFSDir); 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric // Explicitly set case sensitivity for the YAML writer. For that, find out 1240b57cec5SDimitry Andric // the sensitivity at the path where the headers all collected to. 1250b57cec5SDimitry Andric VFSWriter.setCaseSensitivity(isCaseSensitivePath(VFSDir)); 1260b57cec5SDimitry Andric 1270b57cec5SDimitry Andric // Do not rely on real path names when executing the crash reproducer scripts 1280b57cec5SDimitry Andric // since we only want to actually use the files we have on the VFS cache. 1290b57cec5SDimitry Andric VFSWriter.setUseExternalNames(false); 1300b57cec5SDimitry Andric 1310b57cec5SDimitry Andric std::error_code EC; 1320b57cec5SDimitry Andric SmallString<256> YAMLPath = VFSDir; 1330b57cec5SDimitry Andric llvm::sys::path::append(YAMLPath, "vfs.yaml"); 134fe6060f1SDimitry Andric llvm::raw_fd_ostream OS(YAMLPath, EC, llvm::sys::fs::OF_TextWithCRLF); 1350b57cec5SDimitry Andric if (EC) { 1360b57cec5SDimitry Andric HasErrors = true; 1370b57cec5SDimitry Andric return; 1380b57cec5SDimitry Andric } 1390b57cec5SDimitry Andric VFSWriter.write(OS); 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src, 1430b57cec5SDimitry Andric StringRef Dst) { 1440b57cec5SDimitry Andric using namespace llvm::sys; 145e8d8bef9SDimitry Andric llvm::FileCollector::PathCanonicalizer::PathStorage Paths = 146e8d8bef9SDimitry Andric Canonicalizer.canonicalize(Src); 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric SmallString<256> CacheDst = getDest(); 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric if (Dst.empty()) { 1510b57cec5SDimitry Andric // The common case is to map the virtual path to the same path inside the 1520b57cec5SDimitry Andric // cache. 153e8d8bef9SDimitry Andric path::append(CacheDst, path::relative_path(Paths.CopyFrom)); 1540b57cec5SDimitry Andric } else { 1550b57cec5SDimitry Andric // When collecting entries from input vfsoverlays, copy the external 1560b57cec5SDimitry Andric // contents into the cache but still map from the source. 1570b57cec5SDimitry Andric if (!fs::exists(Dst)) 1580b57cec5SDimitry Andric return std::error_code(); 1590b57cec5SDimitry Andric path::append(CacheDst, Dst); 160e8d8bef9SDimitry Andric Paths.CopyFrom = Dst; 1610b57cec5SDimitry Andric } 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric // Copy the file into place. 1640b57cec5SDimitry Andric if (std::error_code EC = fs::create_directories(path::parent_path(CacheDst), 1650b57cec5SDimitry Andric /*IgnoreExisting=*/true)) 1660b57cec5SDimitry Andric return EC; 167e8d8bef9SDimitry Andric if (std::error_code EC = fs::copy_file(Paths.CopyFrom, CacheDst)) 1680b57cec5SDimitry Andric return EC; 1690b57cec5SDimitry Andric 1700b57cec5SDimitry Andric // Always map a canonical src path to its real path into the YAML, by doing 1710b57cec5SDimitry Andric // this we map different virtual src paths to the same entry in the VFS 1720b57cec5SDimitry Andric // overlay, which is a way to emulate symlink inside the VFS; this is also 1730b57cec5SDimitry Andric // needed for correctness, not doing that can lead to module redefinition 1740b57cec5SDimitry Andric // errors. 175e8d8bef9SDimitry Andric addFileMapping(Paths.VirtualPath, CacheDst); 1760b57cec5SDimitry Andric return std::error_code(); 1770b57cec5SDimitry Andric } 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric void ModuleDependencyCollector::addFile(StringRef Filename, StringRef FileDst) { 1800b57cec5SDimitry Andric if (insertSeen(Filename)) 1810b57cec5SDimitry Andric if (copyToRoot(Filename, FileDst)) 1820b57cec5SDimitry Andric HasErrors = true; 1830b57cec5SDimitry Andric } 184