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