1 //===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Collect the dependencies of a set of modules. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Frontend/Utils.h" 15 #include "clang/Serialization/ASTReader.h" 16 #include "llvm/ADT/StringMap.h" 17 #include "llvm/ADT/iterator_range.h" 18 #include "llvm/Support/FileSystem.h" 19 #include "llvm/Support/Path.h" 20 #include "llvm/Support/raw_ostream.h" 21 22 using namespace clang; 23 24 namespace { 25 /// Private implementation for ModuleDependencyCollector 26 class ModuleDependencyListener : public ASTReaderListener { 27 ModuleDependencyCollector &Collector; 28 public: 29 ModuleDependencyListener(ModuleDependencyCollector &Collector) 30 : Collector(Collector) {} 31 bool needsInputFileVisitation() override { return true; } 32 bool needsSystemInputFileVisitation() override { return true; } 33 bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden, 34 bool IsExplicitModule) override { 35 Collector.addFile(Filename); 36 return true; 37 } 38 }; 39 } 40 41 void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { 42 R.addListener(llvm::make_unique<ModuleDependencyListener>(*this)); 43 } 44 45 void ModuleDependencyCollector::writeFileMap() { 46 if (Seen.empty()) 47 return; 48 49 SmallString<256> Dest = getDest(); 50 llvm::sys::path::append(Dest, "vfs.yaml"); 51 52 // Default to use relative overlay directories in the VFS yaml file. This 53 // allows crash reproducer scripts to work across machines. 54 VFSWriter.setOverlayDir(getDest()); 55 56 std::error_code EC; 57 llvm::raw_fd_ostream OS(Dest, EC, llvm::sys::fs::F_Text); 58 if (EC) { 59 HasErrors = true; 60 return; 61 } 62 VFSWriter.write(OS); 63 } 64 65 // TODO: move this to Support/Path.h and check for HAVE_REALPATH? 66 static bool real_path(StringRef SrcPath, SmallVectorImpl<char> &RealPath) { 67 #ifdef LLVM_ON_UNIX 68 char CanonicalPath[PATH_MAX]; 69 70 // TODO: emit a warning in case this fails...? 71 if (!realpath(SrcPath.str().c_str(), CanonicalPath)) 72 return false; 73 74 SmallString<256> RPath(CanonicalPath); 75 RealPath.swap(RPath); 76 return true; 77 #else 78 // FIXME: Add support for systems without realpath. 79 return false; 80 #endif 81 } 82 83 bool ModuleDependencyCollector::getRealPath(StringRef SrcPath, 84 SmallVectorImpl<char> &Result) { 85 using namespace llvm::sys; 86 SmallString<256> RealPath; 87 StringRef FileName = path::filename(SrcPath); 88 std::string Dir = path::parent_path(SrcPath).str(); 89 auto DirWithSymLink = SymLinkMap.find(Dir); 90 91 // Use real_path to fix any symbolic link component present in a path. 92 // Computing the real path is expensive, cache the search through the 93 // parent path directory. 94 if (DirWithSymLink == SymLinkMap.end()) { 95 if (!real_path(Dir, RealPath)) 96 return false; 97 SymLinkMap[Dir] = RealPath.str(); 98 } else { 99 RealPath = DirWithSymLink->second; 100 } 101 102 path::append(RealPath, FileName); 103 Result.swap(RealPath); 104 return true; 105 } 106 107 std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src) { 108 using namespace llvm::sys; 109 110 // We need an absolute path to append to the root. 111 SmallString<256> AbsoluteSrc = Src; 112 fs::make_absolute(AbsoluteSrc); 113 // Canonicalize to a native path to avoid mixed separator styles. 114 path::native(AbsoluteSrc); 115 // Remove redundant leading "./" pieces and consecutive separators. 116 AbsoluteSrc = path::remove_leading_dotslash(AbsoluteSrc); 117 118 // Canonicalize path by removing "..", "." components. 119 SmallString<256> CanonicalPath = AbsoluteSrc; 120 path::remove_dots(CanonicalPath, /*remove_dot_dot=*/true); 121 122 // If a ".." component is present after a symlink component, remove_dots may 123 // lead to the wrong real destination path. Let the source be canonicalized 124 // like that but make sure the destination uses the real path. 125 bool HasDotDotInPath = 126 std::count(path::begin(AbsoluteSrc), path::end(AbsoluteSrc), "..") > 0; 127 SmallString<256> RealPath; 128 bool HasRemovedSymlinkComponent = HasDotDotInPath && 129 getRealPath(AbsoluteSrc, RealPath) && 130 !StringRef(CanonicalPath).equals(RealPath); 131 132 // Build the destination path. 133 SmallString<256> Dest = getDest(); 134 path::append(Dest, path::relative_path(HasRemovedSymlinkComponent ? RealPath 135 : CanonicalPath)); 136 137 // Copy the file into place. 138 if (std::error_code EC = fs::create_directories(path::parent_path(Dest), 139 /*IgnoreExisting=*/true)) 140 return EC; 141 if (std::error_code EC = fs::copy_file( 142 HasRemovedSymlinkComponent ? RealPath : CanonicalPath, Dest)) 143 return EC; 144 145 // Use the canonical path under the root for the file mapping. Also create 146 // an additional entry for the real path. 147 addFileMapping(CanonicalPath, Dest); 148 if (HasRemovedSymlinkComponent) 149 addFileMapping(RealPath, Dest); 150 151 return std::error_code(); 152 } 153 154 void ModuleDependencyCollector::addFile(StringRef Filename) { 155 if (insertSeen(Filename)) 156 if (copyToRoot(Filename)) 157 HasErrors = true; 158 } 159