blob: 1f187b12604e77ecd4405c67cb17d06d62dfce56 [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
14#include "clang/Frontend/Utils.h"
15#include "clang/Serialization/ASTReader.h"
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +000016#include "llvm/ADT/StringMap.h"
Chandler Carruth0d9593d2015-01-14 11:29:14 +000017#include "llvm/ADT/iterator_range.h"
Justin Bognercbda32f2014-06-19 19:49:28 +000018#include "llvm/Support/FileSystem.h"
Justin Bogner86d12592014-06-19 19:36:03 +000019#include "llvm/Support/Path.h"
20#include "llvm/Support/raw_ostream.h"
21
22using namespace clang;
23
24namespace {
25/// Private implementation for ModuleDependencyCollector
26class ModuleDependencyListener : public ASTReaderListener {
27 ModuleDependencyCollector &Collector;
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +000028 llvm::StringMap<std::string> SymLinkMap;
Justin Bogner86d12592014-06-19 19:36:03 +000029
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +000030 bool getRealPath(StringRef SrcPath, SmallVectorImpl<char> &Result);
Justin Bogner86d12592014-06-19 19:36:03 +000031 std::error_code copyToRoot(StringRef Src);
32public:
33 ModuleDependencyListener(ModuleDependencyCollector &Collector)
34 : Collector(Collector) {}
35 bool needsInputFileVisitation() override { return true; }
36 bool needsSystemInputFileVisitation() override { return true; }
Richard Smith216a3bd2015-08-13 17:57:10 +000037 bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden,
38 bool IsExplicitModule) override;
Justin Bogner86d12592014-06-19 19:36:03 +000039};
Alexander Kornienkoab9db512015-06-22 23:07:51 +000040}
Justin Bogner86d12592014-06-19 19:36:03 +000041
42void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {
David Blaikie2721c322014-08-10 16:54:39 +000043 R.addListener(llvm::make_unique<ModuleDependencyListener>(*this));
Justin Bogner86d12592014-06-19 19:36:03 +000044}
45
46void ModuleDependencyCollector::writeFileMap() {
47 if (Seen.empty())
48 return;
49
50 SmallString<256> Dest = getDest();
51 llvm::sys::path::append(Dest, "vfs.yaml");
52
Bruno Cardoso Lopesd878e282016-03-20 02:08:48 +000053 // 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
Rafael Espindoladae941a2014-08-25 18:17:04 +000057 std::error_code EC;
58 llvm::raw_fd_ostream OS(Dest, EC, llvm::sys::fs::F_Text);
59 if (EC) {
Justin Bogner86d12592014-06-19 19:36:03 +000060 setHasErrors();
61 return;
62 }
63 VFSWriter.write(OS);
64}
65
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +000066// TODO: move this to Support/Path.h and check for HAVE_REALPATH?
67static 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
84bool 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
Justin Bogner86d12592014-06-19 19:36:03 +0000108std::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);
Justin Bogner93e3cfc2014-12-12 23:12:27 +0000114 // Canonicalize to a native path to avoid mixed separator styles.
115 path::native(AbsoluteSrc);
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000116 // 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);
Justin Bognere1552f62014-06-20 03:28:46 +0000132
Justin Bogner86d12592014-06-19 19:36:03 +0000133 // Build the destination path.
134 SmallString<256> Dest = Collector.getDest();
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000135 path::append(Dest, path::relative_path(HasRemovedSymlinkComponent ? RealPath
136 : CanonicalPath));
Justin Bogner86d12592014-06-19 19:36:03 +0000137
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;
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000142 if (std::error_code EC = fs::copy_file(
143 HasRemovedSymlinkComponent ? RealPath : CanonicalPath, Dest))
Justin Bogner86d12592014-06-19 19:36:03 +0000144 return EC;
Bruno Cardoso Lopesb76c0272016-03-17 02:20:43 +0000145
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
Justin Bogner86d12592014-06-19 19:36:03 +0000152 return std::error_code();
153}
154
155bool ModuleDependencyListener::visitInputFile(StringRef Filename, bool IsSystem,
Richard Smith216a3bd2015-08-13 17:57:10 +0000156 bool IsOverridden,
157 bool IsExplicitModule) {
Justin Bogner86d12592014-06-19 19:36:03 +0000158 if (Collector.insertSeen(Filename))
159 if (copyToRoot(Filename))
160 Collector.setHasErrors();
161 return true;
162}