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/ADT/Optional.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/TimeValue.h"
21 #include "llvm/Support/raw_ostream.h"
22 
23 using namespace clang;
24 using namespace llvm::sys;
25 
26 unsigned long long clang_getBuildSessionTimestamp(void) {
27   return llvm::sys::TimeValue::now().toEpochTime();
28 }
29 
30 struct CXVirtualFileOverlayImpl {
31   std::vector<std::pair<std::string, std::string> > Mappings;
32   Optional<bool> IsCaseSensitive;
33 };
34 
35 CXVirtualFileOverlay clang_VirtualFileOverlay_create(unsigned) {
36   return new CXVirtualFileOverlayImpl();
37 }
38 
39 enum CXErrorCode
40 clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay VFO,
41                                         const char *virtualPath,
42                                         const char *realPath) {
43   if (!VFO || !virtualPath || !realPath)
44     return CXError_InvalidArguments;
45   if (!path::is_absolute(virtualPath))
46     return CXError_InvalidArguments;
47   if (!path::is_absolute(realPath))
48     return CXError_InvalidArguments;
49 
50   for (path::const_iterator
51          PI = path::begin(virtualPath),
52          PE = path::end(virtualPath); PI != PE; ++PI) {
53     StringRef Comp = *PI;
54     if (Comp == "." || Comp == "..")
55       return CXError_InvalidArguments;
56   }
57 
58   VFO->Mappings.push_back(std::make_pair(virtualPath, realPath));
59   return CXError_Success;
60 }
61 
62 enum CXErrorCode
63 clang_VirtualFileOverlay_setCaseSensitivity(CXVirtualFileOverlay VFO,
64                                             int caseSensitive) {
65   if (!VFO)
66     return CXError_InvalidArguments;
67 
68   VFO->IsCaseSensitive = caseSensitive;
69   return CXError_Success;
70 }
71 
72 namespace {
73 struct EntryTy {
74   std::string VPath;
75   std::string RPath;
76 
77   friend bool operator < (const EntryTy &LHS, const EntryTy &RHS) {
78     return LHS.VPath < RHS.VPath;
79   }
80 };
81 
82 class JSONVFSPrinter {
83   llvm::raw_ostream &OS;
84   CXVirtualFileOverlay VFO;
85 
86 public:
87   JSONVFSPrinter(llvm::raw_ostream &OS, CXVirtualFileOverlay VFO)
88     : OS(OS), VFO(VFO) {}
89 
90   /// Entries must be sorted.
91   void print(ArrayRef<EntryTy> Entries) {
92     OS << "{\n"
93           "  'version': 0,\n";
94     if (VFO->IsCaseSensitive.hasValue()) {
95       OS << "  'case-sensitive': '";
96       if (VFO->IsCaseSensitive.getValue())
97         OS << "true";
98       else
99         OS << "false";
100       OS << "',\n";
101     }
102     OS << "  'roots': [\n";
103     printDirNodes(Entries, "", 4);
104     OS << "  ]\n"
105           "}\n";
106   }
107 
108 private:
109   ArrayRef<EntryTy> printDirNodes(ArrayRef<EntryTy> Entries,
110                                   StringRef ParentPath,
111                                   unsigned Indent) {
112     while (!Entries.empty()) {
113       const EntryTy &Entry = Entries.front();
114       OS.indent(Indent) << "{\n";
115       Indent += 2;
116       OS.indent(Indent) << "'type': 'directory',\n";
117       OS.indent(Indent) << "'name': \"";
118       StringRef DirName = containedPart(ParentPath,
119                                         path::parent_path(Entry.VPath));
120       OS.write_escaped(DirName) << "\",\n";
121       OS.indent(Indent) << "'contents': [\n";
122       Entries = printContents(Entries, Indent + 2);
123       OS.indent(Indent) << "]\n";
124       Indent -= 2;
125       OS.indent(Indent) << '}';
126       if (Entries.empty()) {
127         OS << '\n';
128         break;
129       }
130       StringRef NextVPath = Entries.front().VPath;
131       if (!containedIn(ParentPath, NextVPath)) {
132         OS << '\n';
133         break;
134       }
135       OS << ",\n";
136     }
137     return Entries;
138   }
139 
140   ArrayRef<EntryTy> printContents(ArrayRef<EntryTy> Entries,
141                                   unsigned Indent) {
142     while (!Entries.empty()) {
143       const EntryTy &Entry = Entries.front();
144       Entries = Entries.slice(1);
145       StringRef ParentPath = path::parent_path(Entry.VPath);
146       StringRef VName = path::filename(Entry.VPath);
147       OS.indent(Indent) << "{\n";
148       Indent += 2;
149       OS.indent(Indent) << "'type': 'file',\n";
150       OS.indent(Indent) << "'name': \"";
151       OS.write_escaped(VName) << "\",\n";
152       OS.indent(Indent) << "'external-contents': \"";
153       OS.write_escaped(Entry.RPath) << "\"\n";
154       Indent -= 2;
155       OS.indent(Indent) << '}';
156       if (Entries.empty()) {
157         OS << '\n';
158         break;
159       }
160       StringRef NextVPath = Entries.front().VPath;
161       if (!containedIn(ParentPath, NextVPath)) {
162         OS << '\n';
163         break;
164       }
165       OS << ",\n";
166       if (path::parent_path(NextVPath) != ParentPath) {
167         Entries = printDirNodes(Entries, ParentPath, Indent);
168       }
169     }
170     return Entries;
171   }
172 
173   bool containedIn(StringRef Parent, StringRef Path) {
174     return Path.startswith(Parent);
175   }
176 
177   StringRef containedPart(StringRef Parent, StringRef Path) {
178     assert(containedIn(Parent, Path));
179     if (Parent.empty())
180       return Path;
181     return Path.slice(Parent.size()+1, StringRef::npos);
182   }
183 };
184 }
185 
186 enum CXErrorCode
187 clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay VFO, unsigned,
188                                        char **out_buffer_ptr,
189                                        unsigned *out_buffer_size) {
190   if (!VFO || !out_buffer_ptr || !out_buffer_size)
191     return CXError_InvalidArguments;
192 
193   llvm::SmallVector<EntryTy, 16> Entries;
194   for (unsigned i = 0, e = VFO->Mappings.size(); i != e; ++i) {
195     EntryTy Entry;
196     Entry.VPath = VFO->Mappings[i].first;
197     Entry.RPath = VFO->Mappings[i].second;
198     Entries.push_back(Entry);
199   }
200 
201   // FIXME: We should add options to determine if the paths are case sensitive
202   // or not. The following assumes that if paths are case-insensitive the caller
203   // did not mix cases in the virtual paths it provided.
204 
205   std::sort(Entries.begin(), Entries.end());
206 
207   llvm::SmallString<256> Buf;
208   llvm::raw_svector_ostream OS(Buf);
209   JSONVFSPrinter Printer(OS, VFO);
210   Printer.print(Entries);
211 
212   StringRef Data = OS.str();
213   *out_buffer_ptr = (char*)malloc(Data.size());
214   *out_buffer_size = Data.size();
215   memcpy(*out_buffer_ptr, Data.data(), Data.size());
216   return CXError_Success;
217 }
218 
219 void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay VFO) {
220   delete VFO;
221 }
222 
223 
224 struct CXModuleMapDescriptorImpl {
225   std::string ModuleName;
226   std::string UmbrellaHeader;
227 };
228 
229 CXModuleMapDescriptor clang_ModuleMapDescriptor_create(unsigned) {
230   return new CXModuleMapDescriptorImpl();
231 }
232 
233 enum CXErrorCode
234 clang_ModuleMapDescriptor_setFrameworkModuleName(CXModuleMapDescriptor MMD,
235                                                  const char *name) {
236   if (!MMD || !name)
237     return CXError_InvalidArguments;
238 
239   MMD->ModuleName = name;
240   return CXError_Success;
241 }
242 
243 enum CXErrorCode
244 clang_ModuleMapDescriptor_setUmbrellaHeader(CXModuleMapDescriptor MMD,
245                                             const char *name) {
246   if (!MMD || !name)
247     return CXError_InvalidArguments;
248 
249   MMD->UmbrellaHeader = name;
250   return CXError_Success;
251 }
252 
253 enum CXErrorCode
254 clang_ModuleMapDescriptor_writeToBuffer(CXModuleMapDescriptor MMD, unsigned,
255                                        char **out_buffer_ptr,
256                                        unsigned *out_buffer_size) {
257   if (!MMD || !out_buffer_ptr || !out_buffer_size)
258     return CXError_InvalidArguments;
259 
260   llvm::SmallString<256> Buf;
261   llvm::raw_svector_ostream OS(Buf);
262   OS << "framework module " << MMD->ModuleName << " {\n";
263   OS << "  umbrella header \"";
264   OS.write_escaped(MMD->UmbrellaHeader) << "\"\n";
265   OS << '\n';
266   OS << "  export *\n";
267   OS << "  module * { export * }\n";
268   OS << "}\n";
269 
270   StringRef Data = OS.str();
271   *out_buffer_ptr = (char*)malloc(Data.size());
272   *out_buffer_size = Data.size();
273   memcpy(*out_buffer_ptr, Data.data(), Data.size());
274   return CXError_Success;
275 }
276 
277 void clang_ModuleMapDescriptor_dispose(CXModuleMapDescriptor MMD) {
278   delete MMD;
279 }
280