blob: 4ef3b1ee2e9ac28369273595a9540e804184df82 [file] [log] [blame]
Justin Bogner86d12592014-06-19 19:36:03 +00001//===--- 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
Bruno Cardoso Lopes4775fcf2016-04-07 01:12:18 +000014#include "clang/Basic/CharInfo.h"
Justin Bogner86d12592014-06-19 19:36:03 +000015#include "clang/Frontend/Utils.h"
Bruno Cardoso Lopese62cfd72016-03-30 23:54:25 +000016#include "clang/Lex/Preprocessor.h"
Justin Bogner86d12592014-06-19 19:36:03 +000017#include "clang/Serialization/ASTReader.h"
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +000018#include "llvm/ADT/StringMap.h"
Chandler Carruth0d9593d2015-01-14 11:29:14 +000019#include "llvm/ADT/iterator_range.h"
Justin Bognercbda32f2014-06-19 19:49:28 +000020#include "llvm/Support/FileSystem.h"
Justin Bogner86d12592014-06-19 19:36:03 +000021#include "llvm/Support/Path.h"
22#include "llvm/Support/raw_ostream.h"
23
24using namespace clang;
25
26namespace {
Bruno Cardoso Lopese62cfd72016-03-30 23:54:25 +000027/// Private implementations for ModuleDependencyCollector
Justin Bogner86d12592014-06-19 19:36:03 +000028class ModuleDependencyListener : public ASTReaderListener {
29 ModuleDependencyCollector &Collector;
Justin Bogner86d12592014-06-19 19:36:03 +000030public:
31 ModuleDependencyListener(ModuleDependencyCollector &Collector)
32 : Collector(Collector) {}
33 bool needsInputFileVisitation() override { return true; }
34 bool needsSystemInputFileVisitation() override { return true; }
Richard Smith216a3bd2015-08-13 17:57:10 +000035 bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden,
Bruno Cardoso Lopesb1631d92016-03-29 23:47:40 +000036 bool IsExplicitModule) override {
37 Collector.addFile(Filename);
38 return true;
39 }
Justin Bogner86d12592014-06-19 19:36:03 +000040};
Bruno Cardoso Lopese62cfd72016-03-30 23:54:25 +000041
42struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks {
43 ModuleDependencyCollector &Collector;
44 ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector)
45 : Collector(Collector) {}
46
Bruno Cardoso Lopesf0841792016-05-06 23:21:50 +000047 void moduleMapAddHeader(StringRef HeaderPath) override {
Bruno Cardoso Lopese62cfd72016-03-30 23:54:25 +000048 if (llvm::sys::path::is_absolute(HeaderPath))
49 Collector.addFile(HeaderPath);
50 }
51};
52
Alexander Kornienkoab9db512015-06-22 23:07:51 +000053}
Justin Bogner86d12592014-06-19 19:36:03 +000054
Bruno Cardoso Lopes7caebc12016-04-07 00:00:42 +000055// TODO: move this to Support/Path.h and check for HAVE_REALPATH?
56static bool real_path(StringRef SrcPath, SmallVectorImpl<char> &RealPath) {
57#ifdef LLVM_ON_UNIX
58 char CanonicalPath[PATH_MAX];
59
60 // TODO: emit a warning in case this fails...?
61 if (!realpath(SrcPath.str().c_str(), CanonicalPath))
62 return false;
63
64 SmallString<256> RPath(CanonicalPath);
65 RealPath.swap(RPath);
66 return true;
67#else
68 // FIXME: Add support for systems without realpath.
69 return false;
70#endif
71}
72
Justin Bogner86d12592014-06-19 19:36:03 +000073void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {
David Blaikie2721c322014-08-10 16:54:39 +000074 R.addListener(llvm::make_unique<ModuleDependencyListener>(*this));
Justin Bogner86d12592014-06-19 19:36:03 +000075}
76
Bruno Cardoso Lopese62cfd72016-03-30 23:54:25 +000077void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) {
78 PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks(
79 llvm::make_unique<ModuleDependencyMMCallbacks>(*this));
80}
81
Bruno Cardoso Lopes4c20bef2016-04-07 00:00:57 +000082static bool isCaseSensitivePath(StringRef Path) {
Sean Silva72af4722016-04-07 01:58:14 +000083 SmallString<256> TmpDest = Path, UpperDest, RealDest;
Bruno Cardoso Lopes4c20bef2016-04-07 00:00:57 +000084 // Remove component traversals, links, etc.
85 if (!real_path(Path, TmpDest))
86 return true; // Current default value in vfs.yaml
87 Path = TmpDest;
88
89 // Change path to all upper case and ask for its real path, if the latter
90 // exists and is equal to Path, it's not case sensitive. Default to case
91 // sensitive in the absense of realpath, since this is what the VFSWriter
92 // already expects when sensitivity isn't setup.
93 for (auto &C : Path)
Bruno Cardoso Lopes4775fcf2016-04-07 01:12:18 +000094 UpperDest.push_back(toUppercase(C));
Bruno Cardoso Lopes4c20bef2016-04-07 00:00:57 +000095 if (real_path(UpperDest, RealDest) && Path.equals(RealDest))
96 return false;
97 return true;
98}
99
Justin Bogner86d12592014-06-19 19:36:03 +0000100void ModuleDependencyCollector::writeFileMap() {
101 if (Seen.empty())
102 return;
103
Bruno Cardoso Lopes4c20bef2016-04-07 00:00:57 +0000104 StringRef VFSDir = getDest();
Justin Bogner86d12592014-06-19 19:36:03 +0000105
Bruno Cardoso Lopesd878e282016-03-20 02:08:48 +0000106 // Default to use relative overlay directories in the VFS yaml file. This
107 // allows crash reproducer scripts to work across machines.
Bruno Cardoso Lopes4c20bef2016-04-07 00:00:57 +0000108 VFSWriter.setOverlayDir(VFSDir);
109
110 // Explicitly set case sensitivity for the YAML writer. For that, find out
111 // the sensitivity at the path where the headers all collected to.
112 VFSWriter.setCaseSensitivity(isCaseSensitivePath(VFSDir));
Bruno Cardoso Lopesd878e282016-03-20 02:08:48 +0000113
Bruno Cardoso Lopesfc8644c2016-04-13 19:28:21 +0000114 // Do not rely on real path names when executing the crash reproducer scripts
115 // since we only want to actually use the files we have on the VFS cache.
116 VFSWriter.setUseExternalNames(false);
117
Rafael Espindoladae941a2014-08-25 18:17:04 +0000118 std::error_code EC;
Bruno Cardoso Lopes4c20bef2016-04-07 00:00:57 +0000119 SmallString<256> YAMLPath = VFSDir;
120 llvm::sys::path::append(YAMLPath, "vfs.yaml");
121 llvm::raw_fd_ostream OS(YAMLPath, EC, llvm::sys::fs::F_Text);
Rafael Espindoladae941a2014-08-25 18:17:04 +0000122 if (EC) {
Bruno Cardoso Lopesb1631d92016-03-29 23:47:40 +0000123 HasErrors = true;
Justin Bogner86d12592014-06-19 19:36:03 +0000124 return;
125 }
126 VFSWriter.write(OS);
127}
128
Bruno Cardoso Lopesb1631d92016-03-29 23:47:40 +0000129bool ModuleDependencyCollector::getRealPath(StringRef SrcPath,
130 SmallVectorImpl<char> &Result) {
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000131 using namespace llvm::sys;
132 SmallString<256> RealPath;
133 StringRef FileName = path::filename(SrcPath);
134 std::string Dir = path::parent_path(SrcPath).str();
135 auto DirWithSymLink = SymLinkMap.find(Dir);
136
137 // Use real_path to fix any symbolic link component present in a path.
138 // Computing the real path is expensive, cache the search through the
139 // parent path directory.
140 if (DirWithSymLink == SymLinkMap.end()) {
141 if (!real_path(Dir, RealPath))
142 return false;
143 SymLinkMap[Dir] = RealPath.str();
144 } else {
145 RealPath = DirWithSymLink->second;
146 }
147
148 path::append(RealPath, FileName);
149 Result.swap(RealPath);
150 return true;
151}
152
Bruno Cardoso Lopesb1631d92016-03-29 23:47:40 +0000153std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src) {
Justin Bogner86d12592014-06-19 19:36:03 +0000154 using namespace llvm::sys;
155
156 // We need an absolute path to append to the root.
157 SmallString<256> AbsoluteSrc = Src;
158 fs::make_absolute(AbsoluteSrc);
Justin Bogner93e3cfc2014-12-12 23:12:27 +0000159 // Canonicalize to a native path to avoid mixed separator styles.
160 path::native(AbsoluteSrc);
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000161 // Remove redundant leading "./" pieces and consecutive separators.
162 AbsoluteSrc = path::remove_leading_dotslash(AbsoluteSrc);
163
164 // Canonicalize path by removing "..", "." components.
165 SmallString<256> CanonicalPath = AbsoluteSrc;
166 path::remove_dots(CanonicalPath, /*remove_dot_dot=*/true);
167
168 // If a ".." component is present after a symlink component, remove_dots may
169 // lead to the wrong real destination path. Let the source be canonicalized
170 // like that but make sure the destination uses the real path.
171 bool HasDotDotInPath =
172 std::count(path::begin(AbsoluteSrc), path::end(AbsoluteSrc), "..") > 0;
173 SmallString<256> RealPath;
174 bool HasRemovedSymlinkComponent = HasDotDotInPath &&
175 getRealPath(AbsoluteSrc, RealPath) &&
176 !StringRef(CanonicalPath).equals(RealPath);
Justin Bognere1552f62014-06-20 03:28:46 +0000177
Justin Bogner86d12592014-06-19 19:36:03 +0000178 // Build the destination path.
Bruno Cardoso Lopesb1631d92016-03-29 23:47:40 +0000179 SmallString<256> Dest = getDest();
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000180 path::append(Dest, path::relative_path(HasRemovedSymlinkComponent ? RealPath
181 : CanonicalPath));
Justin Bogner86d12592014-06-19 19:36:03 +0000182
183 // Copy the file into place.
184 if (std::error_code EC = fs::create_directories(path::parent_path(Dest),
185 /*IgnoreExisting=*/true))
186 return EC;
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000187 if (std::error_code EC = fs::copy_file(
188 HasRemovedSymlinkComponent ? RealPath : CanonicalPath, Dest))
Justin Bogner86d12592014-06-19 19:36:03 +0000189 return EC;
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000190
191 // Use the canonical path under the root for the file mapping. Also create
192 // an additional entry for the real path.
Bruno Cardoso Lopesb1631d92016-03-29 23:47:40 +0000193 addFileMapping(CanonicalPath, Dest);
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000194 if (HasRemovedSymlinkComponent)
Bruno Cardoso Lopesb1631d92016-03-29 23:47:40 +0000195 addFileMapping(RealPath, Dest);
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000196
Justin Bogner86d12592014-06-19 19:36:03 +0000197 return std::error_code();
198}
199
Bruno Cardoso Lopesb1631d92016-03-29 23:47:40 +0000200void ModuleDependencyCollector::addFile(StringRef Filename) {
201 if (insertSeen(Filename))
Justin Bogner86d12592014-06-19 19:36:03 +0000202 if (copyToRoot(Filename))
Bruno Cardoso Lopesb1631d92016-03-29 23:47:40 +0000203 HasErrors = true;
Justin Bogner86d12592014-06-19 19:36:03 +0000204}