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 #include "llvm/Support/YAMLParser.h"
23 
24 using namespace clang;
25 using namespace llvm::sys;
26 
27 unsigned long long clang_getBuildSessionTimestamp(void) {
28   return llvm::sys::TimeValue::now().toEpochTime();
29 }
30 
31 struct CXVirtualFileOverlayImpl {
32   std::vector<std::pair<std::string, std::string> > Mappings;
33   Optional<bool> IsCaseSensitive;
34 };
35 
36 CXVirtualFileOverlay clang_VirtualFileOverlay_create(unsigned) {
37   return new CXVirtualFileOverlayImpl();
38 }
39 
40 enum CXErrorCode
41 clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay VFO,
42                                         const char *virtualPath,
43                                         const char *realPath) {
44   if (!VFO || !virtualPath || !realPath)
45     return CXError_InvalidArguments;
46   if (!path::is_absolute(virtualPath))
47     return CXError_InvalidArguments;
48   if (!path::is_absolute(realPath))
49     return CXError_InvalidArguments;
50 
51   for (path::const_iterator
52          PI = path::begin(virtualPath),
53          PE = path::end(virtualPath); PI != PE; ++PI) {
54     StringRef Comp = *PI;
55     if (Comp == "." || Comp == "..")
56       return CXError_InvalidArguments;
57   }
58 
59   VFO->Mappings.push_back(std::make_pair(virtualPath, realPath));
60   return CXError_Success;
61 }
62 
63 enum CXErrorCode
64 clang_VirtualFileOverlay_setCaseSensitivity(CXVirtualFileOverlay VFO,
65                                             int caseSensitive) {
66   if (!VFO)
67     return CXError_InvalidArguments;
68 
69   VFO->IsCaseSensitive = caseSensitive;
70   return CXError_Success;
71 }
72 
73 namespace {
74 struct EntryTy {
75   std::string VPath;
76   std::string RPath;
77 
78   friend bool operator < (const EntryTy &LHS, const EntryTy &RHS) {
79     return LHS.VPath < RHS.VPath;
80   }
81 };
82 
83 class JSONVFSPrinter {
84   llvm::raw_ostream &OS;
85   CXVirtualFileOverlay VFO;
86 
87 public:
88   JSONVFSPrinter(llvm::raw_ostream &OS, CXVirtualFileOverlay VFO)
89     : OS(OS), VFO(VFO) {}
90 
91   /// Entries must be sorted.
92   void print(ArrayRef<EntryTy> Entries) {
93     OS << "{\n"
94           "  'version': 0,\n";
95     if (VFO->IsCaseSensitive.hasValue()) {
96       OS << "  'case-sensitive': '";
97       if (VFO->IsCaseSensitive.getValue())
98         OS << "true";
99       else
100         OS << "false";
101       OS << "',\n";
102     }
103     OS << "  'roots': [\n";
104     printDirNodes(Entries, "", 4);
105     OS << "  ]\n"
106           "}\n";
107   }
108 
109 private:
110   ArrayRef<EntryTy> printDirNodes(ArrayRef<EntryTy> Entries,
111                                   StringRef ParentPath,
112                                   unsigned Indent) {
113     while (!Entries.empty()) {
114       const EntryTy &Entry = Entries.front();
115       OS.indent(Indent) << "{\n";
116       Indent += 2;
117       OS.indent(Indent) << "'type': 'directory',\n";
118       StringRef DirName = containedPart(ParentPath,
119                                         path::parent_path(Entry.VPath));
120       OS.indent(Indent)
121           << "'name': \"" << llvm::yaml::escape(DirName) << "\",\n";
122       OS.indent(Indent) << "'contents': [\n";
123       Entries = printContents(Entries, Indent + 2);
124       OS.indent(Indent) << "]\n";
125       Indent -= 2;
126       OS.indent(Indent) << '}';
127       if (Entries.empty()) {
128         OS << '\n';
129         break;
130       }
131       StringRef NextVPath = Entries.front().VPath;
132       if (!containedIn(ParentPath, NextVPath)) {
133         OS << '\n';
134         break;
135       }
136       OS << ",\n";
137     }
138     return Entries;
139   }
140 
141   ArrayRef<EntryTy> printContents(ArrayRef<EntryTy> Entries,
142                                   unsigned Indent) {
143     while (!Entries.empty()) {
144       const EntryTy &Entry = Entries.front();
145       Entries = Entries.slice(1);
146       StringRef ParentPath = path::parent_path(Entry.VPath);
147       StringRef VName = path::filename(Entry.VPath);
148       OS.indent(Indent) << "{\n";
149       Indent += 2;
150       OS.indent(Indent) << "'type': 'file',\n";
151       OS.indent(Indent) << "'name': \"" << llvm::yaml::escape(VName) << "\",\n";
152       OS.indent(Indent) << "'external-contents': \""
153                         << llvm::yaml::escape(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