1 //===- BuildSystem.cpp - Utilities for use by build systems ---------------===//
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 // This file implements various utilities for use by build systems.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang-c/BuildSystem.h"
15 #include "CXString.h"
16 #include "llvm/ADT/ArrayRef.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/raw_ostream.h"
20 #include "llvm/Support/TimeValue.h"
21 
22 using namespace clang;
23 using namespace llvm::sys;
24 
25 unsigned long long clang_getBuildSessionTimestamp(void) {
26   return llvm::sys::TimeValue::now().toEpochTime();
27 }
28 
29 struct CXVirtualFileOverlayImpl {
30   std::vector<std::pair<std::string, std::string> > Mappings;
31 };
32 
33 CXVirtualFileOverlay clang_VirtualFileOverlay_create(unsigned) {
34   return new CXVirtualFileOverlayImpl();
35 }
36 
37 enum CXErrorCode
38 clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay VFO,
39                                         const char *virtualPath,
40                                         const char *realPath) {
41   if (!VFO || !virtualPath || !realPath)
42     return CXError_InvalidArguments;
43   if (!path::is_absolute(virtualPath))
44     return CXError_InvalidArguments;
45   if (!path::is_absolute(realPath))
46     return CXError_InvalidArguments;
47 
48   for (path::const_iterator
49          PI = path::begin(virtualPath),
50          PE = path::end(virtualPath); PI != PE; ++PI) {
51     StringRef Comp = *PI;
52     if (Comp == "." || Comp == "..")
53       return CXError_InvalidArguments;
54   }
55 
56   VFO->Mappings.push_back(std::make_pair(virtualPath, realPath));
57   return CXError_Success;
58 }
59 
60 namespace {
61 struct EntryTy {
62   std::string VPath;
63   std::string RPath;
64 
65   friend bool operator < (const EntryTy &LHS, const EntryTy &RHS) {
66     return LHS.VPath < RHS.VPath;
67   }
68 };
69 
70 class JSONVFSPrinter {
71   llvm::raw_ostream &OS;
72 
73 public:
74   JSONVFSPrinter(llvm::raw_ostream &OS) : OS(OS) {}
75 
76   /// Entries must be sorted.
77   void print(ArrayRef<EntryTy> Entries) {
78     OS << "{\n"
79           "  'version': 0,\n"
80           "  'roots': [\n";
81     printDirNodes(Entries, "", 4);
82     OS << "  ]\n"
83           "}\n";
84   }
85 
86 private:
87   ArrayRef<EntryTy> printDirNodes(ArrayRef<EntryTy> Entries,
88                                   StringRef ParentPath,
89                                   unsigned Indent) {
90     while (!Entries.empty()) {
91       const EntryTy &Entry = Entries.front();
92       OS.indent(Indent) << "{\n";
93       Indent += 2;
94       OS.indent(Indent) << "'type': 'directory',\n";
95       OS.indent(Indent) << "'name': \"";
96       StringRef DirName = containedPart(ParentPath,
97                                         path::parent_path(Entry.VPath));
98       OS.write_escaped(DirName) << "\",\n";
99       OS.indent(Indent) << "'contents': [\n";
100       Entries = printContents(Entries, Indent + 2);
101       OS.indent(Indent) << "]\n";
102       Indent -= 2;
103       OS.indent(Indent) << '}';
104       if (Entries.empty()) {
105         OS << '\n';
106         break;
107       }
108       StringRef NextVPath = Entries.front().VPath;
109       if (!containedIn(ParentPath, NextVPath)) {
110         OS << '\n';
111         break;
112       }
113       OS << ",\n";
114     }
115     return Entries;
116   }
117 
118   ArrayRef<EntryTy> printContents(ArrayRef<EntryTy> Entries,
119                                   unsigned Indent) {
120     while (!Entries.empty()) {
121       const EntryTy &Entry = Entries.front();
122       Entries = Entries.slice(1);
123       StringRef ParentPath = path::parent_path(Entry.VPath);
124       StringRef VName = path::filename(Entry.VPath);
125       OS.indent(Indent) << "{\n";
126       Indent += 2;
127       OS.indent(Indent) << "'type': 'file',\n";
128       OS.indent(Indent) << "'name': \"";
129       OS.write_escaped(VName) << "\",\n";
130       OS.indent(Indent) << "'external-contents': \"";
131       OS.write_escaped(Entry.RPath) << "\"\n";
132       Indent -= 2;
133       OS.indent(Indent) << '}';
134       if (Entries.empty()) {
135         OS << '\n';
136         break;
137       }
138       StringRef NextVPath = Entries.front().VPath;
139       if (!containedIn(ParentPath, NextVPath)) {
140         OS << '\n';
141         break;
142       }
143       OS << ",\n";
144       if (path::parent_path(NextVPath) != ParentPath) {
145         Entries = printDirNodes(Entries, ParentPath, Indent);
146       }
147     }
148     return Entries;
149   }
150 
151   bool containedIn(StringRef Parent, StringRef Path) {
152     return Path.startswith(Parent);
153   }
154 
155   StringRef containedPart(StringRef Parent, StringRef Path) {
156     assert(containedIn(Parent, Path));
157     if (Parent.empty())
158       return Path;
159     return Path.slice(Parent.size()+1, StringRef::npos);
160   }
161 };
162 }
163 
164 enum CXErrorCode
165 clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay VFO,
166                                        unsigned, CXString *out_buffer) {
167   if (!VFO || !out_buffer)
168     return CXError_InvalidArguments;
169 
170   llvm::SmallVector<EntryTy, 16> Entries;
171   for (unsigned i = 0, e = VFO->Mappings.size(); i != e; ++i) {
172     EntryTy Entry;
173     Entry.VPath = VFO->Mappings[i].first;
174     Entry.RPath = VFO->Mappings[i].second;
175     Entries.push_back(Entry);
176   }
177 
178   // FIXME: We should add options to determine if the paths are case sensitive
179   // or not. The following assumes that if paths are case-insensitive the caller
180   // did not mix cases in the virtual paths it provided.
181 
182   std::sort(Entries.begin(), Entries.end());
183 
184   llvm::SmallString<256> Buf;
185   llvm::raw_svector_ostream OS(Buf);
186   JSONVFSPrinter Printer(OS);
187   Printer.print(Entries);
188 
189   *out_buffer = cxstring::createDup(OS.str());
190   return CXError_Success;
191 }
192 
193 void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay VFO) {
194   delete VFO;
195 }
196