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