Clang Project

clang_source_code/lib/Frontend/ModuleDependencyCollector.cpp
1//===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// Collect the dependencies of a set of modules.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/Basic/CharInfo.h"
14#include "clang/Frontend/Utils.h"
15#include "clang/Lex/Preprocessor.h"
16#include "clang/Serialization/ASTReader.h"
17#include "llvm/ADT/iterator_range.h"
18#include "llvm/Config/llvm-config.h"
19#include "llvm/Support/FileSystem.h"
20#include "llvm/Support/Path.h"
21#include "llvm/Support/raw_ostream.h"
22
23using namespace clang;
24
25namespace {
26/// Private implementations for ModuleDependencyCollector
27class ModuleDependencyListener : public ASTReaderListener {
28  ModuleDependencyCollector &Collector;
29public:
30  ModuleDependencyListener(ModuleDependencyCollector &Collector)
31      : Collector(Collector) {}
32  bool needsInputFileVisitation() override { return true; }
33  bool needsSystemInputFileVisitation() override { return true; }
34  bool visitInputFile(StringRef Filenamebool IsSystembool IsOverridden,
35                      bool IsExplicitModule) override {
36    Collector.addFile(Filename);
37    return true;
38  }
39};
40
41struct 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 HashLocconst Token &IncludeTok,
49                          StringRef FileNamebool IsAngled,
50                          CharSourceRange FilenameRangeconst FileEntry *File,
51                          StringRef SearchPathStringRef RelativePath,
52                          const Module *Imported,
53                          SrcMgr::CharacteristicKind FileType) override {
54    if (!File)
55      return;
56    Collector.addFile(File->getName());
57  }
58};
59
60struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks {
61  ModuleDependencyCollector &Collector;
62  ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector)
63      : Collector(Collector) {}
64
65  void moduleMapAddHeader(StringRef HeaderPath) override {
66    if (llvm::sys::path::is_absolute(HeaderPath))
67      Collector.addFile(HeaderPath);
68  }
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<128AltHeaderFilename;
91      llvm::sys::path::append(AltHeaderFilename, UmbrellaDir,
92                              llvm::sys::path::filename(HeaderFilename));
93      if (FileMgr->getFile(AltHeaderFilename))
94        moduleMapAddHeader(AltHeaderFilename);
95    }
96  }
97};
98
99}
100
101void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {
102  R.addListener(llvm::make_unique<ModuleDependencyListener>(*this));
103}
104
105void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) {
106  PP.addPPCallbacks(llvm::make_unique<ModuleDependencyPPCallbacks>(
107      *this, PP.getSourceManager()));
108  PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks(
109      llvm::make_unique<ModuleDependencyMMCallbacks>(*this));
110}
111
112static bool isCaseSensitivePath(StringRef Path) {
113  SmallString<256TmpDest = Path, UpperDestRealDest;
114  // Remove component traversals, links, etc.
115  if (llvm::sys::fs::real_path(Path, TmpDest))
116    return true// Current default value in vfs.yaml
117  Path = TmpDest;
118
119  // Change path to all upper case and ask for its real path, if the latter
120  // exists and is equal to Path, it's not case sensitive. Default to case
121  // sensitive in the absence of realpath, since this is what the VFSWriter
122  // already expects when sensitivity isn't setup.
123  for (auto &C : Path)
124    UpperDest.push_back(toUppercase(C));
125  if (!llvm::sys::fs::real_path(UpperDest, RealDest) && Path.equals(RealDest))
126    return false;
127  return true;
128}
129
130void ModuleDependencyCollector::writeFileMap() {
131  if (Seen.empty())
132    return;
133
134  StringRef VFSDir = getDest();
135
136  // Default to use relative overlay directories in the VFS yaml file. This
137  // allows crash reproducer scripts to work across machines.
138  VFSWriter.setOverlayDir(VFSDir);
139
140  // Explicitly set case sensitivity for the YAML writer. For that, find out
141  // the sensitivity at the path where the headers all collected to.
142  VFSWriter.setCaseSensitivity(isCaseSensitivePath(VFSDir));
143
144  // Do not rely on real path names when executing the crash reproducer scripts
145  // since we only want to actually use the files we have on the VFS cache.
146  VFSWriter.setUseExternalNames(false);
147
148  std::error_code EC;
149  SmallString<256YAMLPath = VFSDir;
150  llvm::sys::path::append(YAMLPath, "vfs.yaml");
151  llvm::raw_fd_ostream OS(YAMLPath, EC, llvm::sys::fs::F_Text);
152  if (EC) {
153    HasErrors = true;
154    return;
155  }
156  VFSWriter.write(OS);
157}
158
159bool ModuleDependencyCollector::getRealPath(StringRef SrcPath,
160                                            SmallVectorImpl<char> &Result) {
161  using namespace llvm::sys;
162  SmallString<256RealPath;
163  StringRef FileName = path::filename(SrcPath);
164  std::string Dir = path::parent_path(SrcPath).str();
165  auto DirWithSymLink = SymLinkMap.find(Dir);
166
167  // Use real_path to fix any symbolic link component present in a path.
168  // Computing the real path is expensive, cache the search through the
169  // parent path directory.
170  if (DirWithSymLink == SymLinkMap.end()) {
171    if (llvm::sys::fs::real_path(Dir, RealPath))
172      return false;
173    SymLinkMap[Dir] = RealPath.str();
174  } else {
175    RealPath = DirWithSymLink->second;
176  }
177
178  path::append(RealPath, FileName);
179  Result.swap(RealPath);
180  return true;
181}
182
183std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src,
184                                                      StringRef Dst) {
185  using namespace llvm::sys;
186
187  // We need an absolute src path to append to the root.
188  SmallString<256AbsoluteSrc = Src;
189  fs::make_absolute(AbsoluteSrc);
190  // Canonicalize src to a native path to avoid mixed separator styles.
191  path::native(AbsoluteSrc);
192  // Remove redundant leading "./" pieces and consecutive separators.
193  AbsoluteSrc = path::remove_leading_dotslash(AbsoluteSrc);
194
195  // Canonicalize the source path by removing "..", "." components.
196  SmallString<256VirtualPath = AbsoluteSrc;
197  path::remove_dots(VirtualPath, /*remove_dot_dot=*/true);
198
199  // If a ".." component is present after a symlink component, remove_dots may
200  // lead to the wrong real destination path. Let the source be canonicalized
201  // like that but make sure we always use the real path for the destination.
202  SmallString<256CopyFrom;
203  if (!getRealPath(AbsoluteSrc, CopyFrom))
204    CopyFrom = VirtualPath;
205  SmallString<256CacheDst = getDest();
206
207  if (Dst.empty()) {
208    // The common case is to map the virtual path to the same path inside the
209    // cache.
210    path::append(CacheDst, path::relative_path(CopyFrom));
211  } else {
212    // When collecting entries from input vfsoverlays, copy the external
213    // contents into the cache but still map from the source.
214    if (!fs::exists(Dst))
215      return std::error_code();
216    path::append(CacheDst, Dst);
217    CopyFrom = Dst;
218  }
219
220  // Copy the file into place.
221  if (std::error_code EC = fs::create_directories(path::parent_path(CacheDst),
222                                                  /*IgnoreExisting=*/true))
223    return EC;
224  if (std::error_code EC = fs::copy_file(CopyFrom, CacheDst))
225    return EC;
226
227  // Always map a canonical src path to its real path into the YAML, by doing
228  // this we map different virtual src paths to the same entry in the VFS
229  // overlay, which is a way to emulate symlink inside the VFS; this is also
230  // needed for correctness, not doing that can lead to module redefinition
231  // errors.
232  addFileMapping(VirtualPath, CacheDst);
233  return std::error_code();
234}
235
236void ModuleDependencyCollector::addFile(StringRef FilenameStringRef FileDst) {
237  if (insertSeen(Filename))
238    if (copyToRoot(Filename, FileDst))
239      HasErrors = true;
240}
241
clang::ModuleDependencyCollector::attachToASTReader
clang::ModuleDependencyCollector::attachToPreprocessor
clang::ModuleDependencyCollector::writeFileMap
clang::ModuleDependencyCollector::getRealPath
clang::ModuleDependencyCollector::copyToRoot
clang::ModuleDependencyCollector::addFile