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