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/iterator_range.h"
17#include "llvm/ADT/StringSet.h"
18#include "llvm/Support/FileSystem.h"
19#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;
28
29  std::error_code copyToRoot(StringRef Src);
30public:
31  ModuleDependencyListener(ModuleDependencyCollector &Collector)
32      : Collector(Collector) {}
33  bool needsInputFileVisitation() override { return true; }
34  bool needsSystemInputFileVisitation() override { return true; }
35  bool visitInputFile(StringRef Filename, bool IsSystem,
36                      bool IsOverridden) override;
37};
38}
39
40void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {
41  R.addListener(new ModuleDependencyListener(*this));
42}
43
44void ModuleDependencyCollector::writeFileMap() {
45  if (Seen.empty())
46    return;
47
48  SmallString<256> Dest = getDest();
49  llvm::sys::path::append(Dest, "vfs.yaml");
50
51  std::string ErrorInfo;
52  llvm::raw_fd_ostream OS(Dest.c_str(), ErrorInfo, llvm::sys::fs::F_Text);
53  if (!ErrorInfo.empty()) {
54    setHasErrors();
55    return;
56  }
57  VFSWriter.write(OS);
58}
59
60/// Remove traversal (ie, . or ..) from the given absolute path.
61static void removePathTraversal(SmallVectorImpl<char> &Path) {
62  using namespace llvm::sys;
63  SmallVector<StringRef, 16> ComponentStack;
64  StringRef P(Path.data(), Path.size());
65
66  // Skip the root path, then look for traversal in the components.
67  StringRef Rel = path::relative_path(P);
68  for (StringRef C : llvm::make_range(path::begin(Rel), path::end(Rel))) {
69    if (C == ".")
70      continue;
71    if (C == "..") {
72      assert(ComponentStack.size() && "Path traverses out of parent");
73      ComponentStack.pop_back();
74    } else
75      ComponentStack.push_back(C);
76  }
77
78  // The stack is now the path without any directory traversal.
79  SmallString<256> Buffer = path::root_path(P);
80  for (StringRef C : ComponentStack)
81    path::append(Buffer, C);
82
83  // Put the result in Path.
84  Path.swap(Buffer);
85}
86
87std::error_code ModuleDependencyListener::copyToRoot(StringRef Src) {
88  using namespace llvm::sys;
89
90  // We need an absolute path to append to the root.
91  SmallString<256> AbsoluteSrc = Src;
92  fs::make_absolute(AbsoluteSrc);
93  removePathTraversal(AbsoluteSrc);
94
95  // Build the destination path.
96  SmallString<256> Dest = Collector.getDest();
97  path::append(Dest, path::relative_path(AbsoluteSrc));
98
99  // Copy the file into place.
100  if (std::error_code EC = fs::create_directories(path::parent_path(Dest),
101                                                   /*IgnoreExisting=*/true))
102    return EC;
103  if (std::error_code EC = fs::copy_file(AbsoluteSrc.str(), Dest.str()))
104    return EC;
105  // Use the absolute path under the root for the file mapping.
106  Collector.addFileMapping(AbsoluteSrc.str(), Dest.str());
107  return std::error_code();
108}
109
110bool ModuleDependencyListener::visitInputFile(StringRef Filename, bool IsSystem,
111                                              bool IsOverridden) {
112  if (Collector.insertSeen(Filename))
113    if (copyToRoot(Filename))
114      Collector.setHasErrors();
115  return true;
116}
117