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