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