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