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