Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 1 | //===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame^] | 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Collect the dependencies of a set of modules. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
Bruno Cardoso Lopes | 4775fcf | 2016-04-07 01:12:18 +0000 | [diff] [blame] | 13 | #include "clang/Basic/CharInfo.h" |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 14 | #include "clang/Frontend/Utils.h" |
Bruno Cardoso Lopes | e62cfd7 | 2016-03-30 23:54:25 +0000 | [diff] [blame] | 15 | #include "clang/Lex/Preprocessor.h" |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 16 | #include "clang/Serialization/ASTReader.h" |
Chandler Carruth | 0d9593d | 2015-01-14 11:29:14 +0000 | [diff] [blame] | 17 | #include "llvm/ADT/iterator_range.h" |
Nico Weber | d637c05 | 2018-04-30 13:52:15 +0000 | [diff] [blame] | 18 | #include "llvm/Config/llvm-config.h" |
Justin Bogner | cbda32f | 2014-06-19 19:49:28 +0000 | [diff] [blame] | 19 | #include "llvm/Support/FileSystem.h" |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 20 | #include "llvm/Support/Path.h" |
| 21 | #include "llvm/Support/raw_ostream.h" |
| 22 | |
| 23 | using namespace clang; |
| 24 | |
| 25 | namespace { |
Bruno Cardoso Lopes | e62cfd7 | 2016-03-30 23:54:25 +0000 | [diff] [blame] | 26 | /// Private implementations for ModuleDependencyCollector |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 27 | class ModuleDependencyListener : public ASTReaderListener { |
| 28 | ModuleDependencyCollector &Collector; |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 29 | public: |
| 30 | ModuleDependencyListener(ModuleDependencyCollector &Collector) |
| 31 | : Collector(Collector) {} |
| 32 | bool needsInputFileVisitation() override { return true; } |
| 33 | bool needsSystemInputFileVisitation() override { return true; } |
Richard Smith | 216a3bd | 2015-08-13 17:57:10 +0000 | [diff] [blame] | 34 | bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden, |
Bruno Cardoso Lopes | b1631d9 | 2016-03-29 23:47:40 +0000 | [diff] [blame] | 35 | bool IsExplicitModule) override { |
| 36 | Collector.addFile(Filename); |
| 37 | return true; |
| 38 | } |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 39 | }; |
Bruno Cardoso Lopes | e62cfd7 | 2016-03-30 23:54:25 +0000 | [diff] [blame] | 40 | |
Bruno Cardoso Lopes | 66e9627 | 2016-12-11 04:27:31 +0000 | [diff] [blame] | 41 | struct ModuleDependencyPPCallbacks : public PPCallbacks { |
| 42 | ModuleDependencyCollector &Collector; |
| 43 | SourceManager &SM; |
| 44 | ModuleDependencyPPCallbacks(ModuleDependencyCollector &Collector, |
| 45 | SourceManager &SM) |
| 46 | : Collector(Collector), SM(SM) {} |
| 47 | |
| 48 | void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, |
| 49 | StringRef FileName, bool IsAngled, |
| 50 | CharSourceRange FilenameRange, const FileEntry *File, |
| 51 | StringRef SearchPath, StringRef RelativePath, |
Julie Hockett | 96fbe58 | 2018-05-10 19:05:36 +0000 | [diff] [blame] | 52 | const Module *Imported, |
| 53 | SrcMgr::CharacteristicKind FileType) override { |
Bruno Cardoso Lopes | 66e9627 | 2016-12-11 04:27:31 +0000 | [diff] [blame] | 54 | if (!File) |
| 55 | return; |
| 56 | Collector.addFile(File->getName()); |
| 57 | } |
| 58 | }; |
| 59 | |
Bruno Cardoso Lopes | e62cfd7 | 2016-03-30 23:54:25 +0000 | [diff] [blame] | 60 | struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks { |
| 61 | ModuleDependencyCollector &Collector; |
| 62 | ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector) |
| 63 | : Collector(Collector) {} |
| 64 | |
Bruno Cardoso Lopes | f084179 | 2016-05-06 23:21:50 +0000 | [diff] [blame] | 65 | void moduleMapAddHeader(StringRef HeaderPath) override { |
Bruno Cardoso Lopes | e62cfd7 | 2016-03-30 23:54:25 +0000 | [diff] [blame] | 66 | if (llvm::sys::path::is_absolute(HeaderPath)) |
| 67 | Collector.addFile(HeaderPath); |
| 68 | } |
Bruno Cardoso Lopes | b3a0fa4 | 2016-05-13 22:21:51 +0000 | [diff] [blame] | 69 | void moduleMapAddUmbrellaHeader(FileManager *FileMgr, |
| 70 | const FileEntry *Header) override { |
| 71 | StringRef HeaderFilename = Header->getName(); |
| 72 | moduleMapAddHeader(HeaderFilename); |
| 73 | // The FileManager can find and cache the symbolic link for a framework |
| 74 | // header before its real path, this means a module can have some of its |
| 75 | // headers to use other paths. Although this is usually not a problem, it's |
| 76 | // inconsistent, and not collecting the original path header leads to |
| 77 | // umbrella clashes while rebuilding modules in the crash reproducer. For |
| 78 | // example: |
| 79 | // ApplicationServices.framework/Frameworks/ImageIO.framework/ImageIO.h |
| 80 | // instead of: |
| 81 | // ImageIO.framework/ImageIO.h |
| 82 | // |
| 83 | // FIXME: this shouldn't be necessary once we have FileName instances |
| 84 | // around instead of FileEntry ones. For now, make sure we collect all |
| 85 | // that we need for the reproducer to work correctly. |
| 86 | StringRef UmbreallDirFromHeader = |
| 87 | llvm::sys::path::parent_path(HeaderFilename); |
| 88 | StringRef UmbrellaDir = Header->getDir()->getName(); |
| 89 | if (!UmbrellaDir.equals(UmbreallDirFromHeader)) { |
| 90 | SmallString<128> AltHeaderFilename; |
| 91 | llvm::sys::path::append(AltHeaderFilename, UmbrellaDir, |
| 92 | llvm::sys::path::filename(HeaderFilename)); |
| 93 | if (FileMgr->getFile(AltHeaderFilename)) |
| 94 | moduleMapAddHeader(AltHeaderFilename); |
| 95 | } |
| 96 | } |
Bruno Cardoso Lopes | e62cfd7 | 2016-03-30 23:54:25 +0000 | [diff] [blame] | 97 | }; |
| 98 | |
Alexander Kornienko | ab9db51 | 2015-06-22 23:07:51 +0000 | [diff] [blame] | 99 | } |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 100 | |
Nico Weber | 6bc635e | 2018-04-27 20:29:57 +0000 | [diff] [blame] | 101 | // TODO: move this to Support/Path.h and check for HAVE_REALPATH? |
| 102 | static bool real_path(StringRef SrcPath, SmallVectorImpl<char> &RealPath) { |
| 103 | #ifdef LLVM_ON_UNIX |
| 104 | char CanonicalPath[PATH_MAX]; |
| 105 | |
| 106 | // TODO: emit a warning in case this fails...? |
| 107 | if (!realpath(SrcPath.str().c_str(), CanonicalPath)) |
| 108 | return false; |
| 109 | |
| 110 | SmallString<256> RPath(CanonicalPath); |
| 111 | RealPath.swap(RPath); |
| 112 | return true; |
| 113 | #else |
| 114 | // FIXME: Add support for systems without realpath. |
| 115 | return false; |
| 116 | #endif |
| 117 | } |
| 118 | |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 119 | void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { |
David Blaikie | 2721c32 | 2014-08-10 16:54:39 +0000 | [diff] [blame] | 120 | R.addListener(llvm::make_unique<ModuleDependencyListener>(*this)); |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 121 | } |
| 122 | |
Bruno Cardoso Lopes | e62cfd7 | 2016-03-30 23:54:25 +0000 | [diff] [blame] | 123 | void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) { |
Bruno Cardoso Lopes | 66e9627 | 2016-12-11 04:27:31 +0000 | [diff] [blame] | 124 | PP.addPPCallbacks(llvm::make_unique<ModuleDependencyPPCallbacks>( |
| 125 | *this, PP.getSourceManager())); |
Bruno Cardoso Lopes | e62cfd7 | 2016-03-30 23:54:25 +0000 | [diff] [blame] | 126 | PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( |
| 127 | llvm::make_unique<ModuleDependencyMMCallbacks>(*this)); |
| 128 | } |
| 129 | |
Bruno Cardoso Lopes | 4c20bef | 2016-04-07 00:00:57 +0000 | [diff] [blame] | 130 | static bool isCaseSensitivePath(StringRef Path) { |
Sean Silva | 72af472 | 2016-04-07 01:58:14 +0000 | [diff] [blame] | 131 | SmallString<256> TmpDest = Path, UpperDest, RealDest; |
Bruno Cardoso Lopes | 4c20bef | 2016-04-07 00:00:57 +0000 | [diff] [blame] | 132 | // Remove component traversals, links, etc. |
Nico Weber | 6bc635e | 2018-04-27 20:29:57 +0000 | [diff] [blame] | 133 | if (!real_path(Path, TmpDest)) |
Bruno Cardoso Lopes | 4c20bef | 2016-04-07 00:00:57 +0000 | [diff] [blame] | 134 | return true; // Current default value in vfs.yaml |
| 135 | Path = TmpDest; |
| 136 | |
| 137 | // Change path to all upper case and ask for its real path, if the latter |
| 138 | // exists and is equal to Path, it's not case sensitive. Default to case |
Alexander Kornienko | 2a8c18d | 2018-04-06 15:14:32 +0000 | [diff] [blame] | 139 | // sensitive in the absence of realpath, since this is what the VFSWriter |
Bruno Cardoso Lopes | 4c20bef | 2016-04-07 00:00:57 +0000 | [diff] [blame] | 140 | // already expects when sensitivity isn't setup. |
| 141 | for (auto &C : Path) |
Bruno Cardoso Lopes | 4775fcf | 2016-04-07 01:12:18 +0000 | [diff] [blame] | 142 | UpperDest.push_back(toUppercase(C)); |
Nico Weber | 6bc635e | 2018-04-27 20:29:57 +0000 | [diff] [blame] | 143 | if (real_path(UpperDest, RealDest) && Path.equals(RealDest)) |
Bruno Cardoso Lopes | 4c20bef | 2016-04-07 00:00:57 +0000 | [diff] [blame] | 144 | return false; |
| 145 | return true; |
| 146 | } |
| 147 | |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 148 | void ModuleDependencyCollector::writeFileMap() { |
| 149 | if (Seen.empty()) |
| 150 | return; |
| 151 | |
Bruno Cardoso Lopes | 4c20bef | 2016-04-07 00:00:57 +0000 | [diff] [blame] | 152 | StringRef VFSDir = getDest(); |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 153 | |
Bruno Cardoso Lopes | d878e28 | 2016-03-20 02:08:48 +0000 | [diff] [blame] | 154 | // Default to use relative overlay directories in the VFS yaml file. This |
| 155 | // allows crash reproducer scripts to work across machines. |
Bruno Cardoso Lopes | 4c20bef | 2016-04-07 00:00:57 +0000 | [diff] [blame] | 156 | VFSWriter.setOverlayDir(VFSDir); |
| 157 | |
| 158 | // Explicitly set case sensitivity for the YAML writer. For that, find out |
| 159 | // the sensitivity at the path where the headers all collected to. |
| 160 | VFSWriter.setCaseSensitivity(isCaseSensitivePath(VFSDir)); |
Bruno Cardoso Lopes | d878e28 | 2016-03-20 02:08:48 +0000 | [diff] [blame] | 161 | |
Bruno Cardoso Lopes | fc8644c | 2016-04-13 19:28:21 +0000 | [diff] [blame] | 162 | // Do not rely on real path names when executing the crash reproducer scripts |
| 163 | // since we only want to actually use the files we have on the VFS cache. |
| 164 | VFSWriter.setUseExternalNames(false); |
| 165 | |
Rafael Espindola | dae941a | 2014-08-25 18:17:04 +0000 | [diff] [blame] | 166 | std::error_code EC; |
Bruno Cardoso Lopes | 4c20bef | 2016-04-07 00:00:57 +0000 | [diff] [blame] | 167 | SmallString<256> YAMLPath = VFSDir; |
| 168 | llvm::sys::path::append(YAMLPath, "vfs.yaml"); |
| 169 | llvm::raw_fd_ostream OS(YAMLPath, EC, llvm::sys::fs::F_Text); |
Rafael Espindola | dae941a | 2014-08-25 18:17:04 +0000 | [diff] [blame] | 170 | if (EC) { |
Bruno Cardoso Lopes | b1631d9 | 2016-03-29 23:47:40 +0000 | [diff] [blame] | 171 | HasErrors = true; |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 172 | return; |
| 173 | } |
| 174 | VFSWriter.write(OS); |
| 175 | } |
| 176 | |
Bruno Cardoso Lopes | b1631d9 | 2016-03-29 23:47:40 +0000 | [diff] [blame] | 177 | bool ModuleDependencyCollector::getRealPath(StringRef SrcPath, |
| 178 | SmallVectorImpl<char> &Result) { |
Bruno Cardoso Lopes | b76c027 | 2016-03-17 02:20:43 +0000 | [diff] [blame] | 179 | using namespace llvm::sys; |
| 180 | SmallString<256> RealPath; |
| 181 | StringRef FileName = path::filename(SrcPath); |
| 182 | std::string Dir = path::parent_path(SrcPath).str(); |
| 183 | auto DirWithSymLink = SymLinkMap.find(Dir); |
| 184 | |
| 185 | // Use real_path to fix any symbolic link component present in a path. |
| 186 | // Computing the real path is expensive, cache the search through the |
| 187 | // parent path directory. |
| 188 | if (DirWithSymLink == SymLinkMap.end()) { |
Nico Weber | 6bc635e | 2018-04-27 20:29:57 +0000 | [diff] [blame] | 189 | if (!real_path(Dir, RealPath)) |
Bruno Cardoso Lopes | b76c027 | 2016-03-17 02:20:43 +0000 | [diff] [blame] | 190 | return false; |
| 191 | SymLinkMap[Dir] = RealPath.str(); |
| 192 | } else { |
| 193 | RealPath = DirWithSymLink->second; |
| 194 | } |
| 195 | |
| 196 | path::append(RealPath, FileName); |
| 197 | Result.swap(RealPath); |
| 198 | return true; |
| 199 | } |
| 200 | |
Bruno Cardoso Lopes | 82ec4fde | 2016-12-22 07:06:03 +0000 | [diff] [blame] | 201 | std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src, |
| 202 | StringRef Dst) { |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 203 | using namespace llvm::sys; |
| 204 | |
Bruno Cardoso Lopes | 0df3e04 | 2016-05-06 23:58:58 +0000 | [diff] [blame] | 205 | // We need an absolute src path to append to the root. |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 206 | SmallString<256> AbsoluteSrc = Src; |
| 207 | fs::make_absolute(AbsoluteSrc); |
Bruno Cardoso Lopes | 0df3e04 | 2016-05-06 23:58:58 +0000 | [diff] [blame] | 208 | // Canonicalize src to a native path to avoid mixed separator styles. |
Justin Bogner | 93e3cfc | 2014-12-12 23:12:27 +0000 | [diff] [blame] | 209 | path::native(AbsoluteSrc); |
Bruno Cardoso Lopes | b76c027 | 2016-03-17 02:20:43 +0000 | [diff] [blame] | 210 | // Remove redundant leading "./" pieces and consecutive separators. |
| 211 | AbsoluteSrc = path::remove_leading_dotslash(AbsoluteSrc); |
| 212 | |
Bruno Cardoso Lopes | 0df3e04 | 2016-05-06 23:58:58 +0000 | [diff] [blame] | 213 | // Canonicalize the source path by removing "..", "." components. |
Bruno Cardoso Lopes | 82ec4fde | 2016-12-22 07:06:03 +0000 | [diff] [blame] | 214 | SmallString<256> VirtualPath = AbsoluteSrc; |
| 215 | path::remove_dots(VirtualPath, /*remove_dot_dot=*/true); |
Bruno Cardoso Lopes | b76c027 | 2016-03-17 02:20:43 +0000 | [diff] [blame] | 216 | |
| 217 | // If a ".." component is present after a symlink component, remove_dots may |
| 218 | // lead to the wrong real destination path. Let the source be canonicalized |
Bruno Cardoso Lopes | 0df3e04 | 2016-05-06 23:58:58 +0000 | [diff] [blame] | 219 | // like that but make sure we always use the real path for the destination. |
Bruno Cardoso Lopes | 82ec4fde | 2016-12-22 07:06:03 +0000 | [diff] [blame] | 220 | SmallString<256> CopyFrom; |
| 221 | if (!getRealPath(AbsoluteSrc, CopyFrom)) |
| 222 | CopyFrom = VirtualPath; |
| 223 | SmallString<256> CacheDst = getDest(); |
| 224 | |
| 225 | if (Dst.empty()) { |
| 226 | // The common case is to map the virtual path to the same path inside the |
| 227 | // cache. |
| 228 | path::append(CacheDst, path::relative_path(CopyFrom)); |
| 229 | } else { |
| 230 | // When collecting entries from input vfsoverlays, copy the external |
| 231 | // contents into the cache but still map from the source. |
| 232 | if (!fs::exists(Dst)) |
| 233 | return std::error_code(); |
| 234 | path::append(CacheDst, Dst); |
| 235 | CopyFrom = Dst; |
| 236 | } |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 237 | |
| 238 | // Copy the file into place. |
Bruno Cardoso Lopes | 82ec4fde | 2016-12-22 07:06:03 +0000 | [diff] [blame] | 239 | if (std::error_code EC = fs::create_directories(path::parent_path(CacheDst), |
| 240 | /*IgnoreExisting=*/true)) |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 241 | return EC; |
Bruno Cardoso Lopes | 82ec4fde | 2016-12-22 07:06:03 +0000 | [diff] [blame] | 242 | if (std::error_code EC = fs::copy_file(CopyFrom, CacheDst)) |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 243 | return EC; |
Bruno Cardoso Lopes | b76c027 | 2016-03-17 02:20:43 +0000 | [diff] [blame] | 244 | |
Bruno Cardoso Lopes | 0df3e04 | 2016-05-06 23:58:58 +0000 | [diff] [blame] | 245 | // Always map a canonical src path to its real path into the YAML, by doing |
| 246 | // this we map different virtual src paths to the same entry in the VFS |
| 247 | // overlay, which is a way to emulate symlink inside the VFS; this is also |
Hiroshi Inoue | 3170de0 | 2017-07-01 08:46:43 +0000 | [diff] [blame] | 248 | // needed for correctness, not doing that can lead to module redefinition |
Bruno Cardoso Lopes | 0df3e04 | 2016-05-06 23:58:58 +0000 | [diff] [blame] | 249 | // errors. |
Bruno Cardoso Lopes | 82ec4fde | 2016-12-22 07:06:03 +0000 | [diff] [blame] | 250 | addFileMapping(VirtualPath, CacheDst); |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 251 | return std::error_code(); |
| 252 | } |
| 253 | |
Bruno Cardoso Lopes | 82ec4fde | 2016-12-22 07:06:03 +0000 | [diff] [blame] | 254 | void ModuleDependencyCollector::addFile(StringRef Filename, StringRef FileDst) { |
Bruno Cardoso Lopes | b1631d9 | 2016-03-29 23:47:40 +0000 | [diff] [blame] | 255 | if (insertSeen(Filename)) |
Bruno Cardoso Lopes | 82ec4fde | 2016-12-22 07:06:03 +0000 | [diff] [blame] | 256 | if (copyToRoot(Filename, FileDst)) |
Bruno Cardoso Lopes | b1631d9 | 2016-03-29 23:47:40 +0000 | [diff] [blame] | 257 | HasErrors = true; |
Justin Bogner | 86d1259 | 2014-06-19 19:36:03 +0000 | [diff] [blame] | 258 | } |