//===- BuildSystem.cpp - Utilities for use by build systems ---------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements various utilities for use by build systems. // //===----------------------------------------------------------------------===// #include "clang-c/BuildSystem.h" #include "CXString.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/TimeValue.h" using namespace clang; using namespace llvm::sys; unsigned long long clang_getBuildSessionTimestamp(void) { return llvm::sys::TimeValue::now().toEpochTime(); } struct CXVirtualFileOverlayImpl { std::vector > Mappings; }; CXVirtualFileOverlay clang_VirtualFileOverlay_create(unsigned) { return new CXVirtualFileOverlayImpl(); } enum CXErrorCode clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay VFO, const char *virtualPath, const char *realPath) { if (!VFO || !virtualPath || !realPath) return CXError_InvalidArguments; if (!path::is_absolute(virtualPath)) return CXError_InvalidArguments; if (!path::is_absolute(realPath)) return CXError_InvalidArguments; for (path::const_iterator PI = path::begin(virtualPath), PE = path::end(virtualPath); PI != PE; ++PI) { StringRef Comp = *PI; if (Comp == "." || Comp == "..") return CXError_InvalidArguments; } VFO->Mappings.push_back(std::make_pair(virtualPath, realPath)); return CXError_Success; } namespace { struct EntryTy { std::string VPath; std::string RPath; friend bool operator < (const EntryTy &LHS, const EntryTy &RHS) { return LHS.VPath < RHS.VPath; } }; class JSONVFSPrinter { llvm::raw_ostream &OS; public: JSONVFSPrinter(llvm::raw_ostream &OS) : OS(OS) {} /// Entries must be sorted. void print(ArrayRef Entries) { OS << "{\n" " 'version': 0,\n" " 'roots': [\n"; printDirNodes(Entries, "", 4); OS << " ]\n" "}\n"; } private: ArrayRef printDirNodes(ArrayRef Entries, StringRef ParentPath, unsigned Indent) { while (!Entries.empty()) { const EntryTy &Entry = Entries.front(); OS.indent(Indent) << "{\n"; Indent += 2; OS.indent(Indent) << "'type': 'directory',\n"; OS.indent(Indent) << "'name': \""; StringRef DirName = containedPart(ParentPath, path::parent_path(Entry.VPath)); OS.write_escaped(DirName) << "\",\n"; OS.indent(Indent) << "'contents': [\n"; Entries = printContents(Entries, Indent + 2); OS.indent(Indent) << "]\n"; Indent -= 2; OS.indent(Indent) << '}'; if (Entries.empty()) { OS << '\n'; break; } StringRef NextVPath = Entries.front().VPath; if (!containedIn(ParentPath, NextVPath)) { OS << '\n'; break; } OS << ",\n"; } return Entries; } ArrayRef printContents(ArrayRef Entries, unsigned Indent) { while (!Entries.empty()) { const EntryTy &Entry = Entries.front(); Entries = Entries.slice(1); StringRef ParentPath = path::parent_path(Entry.VPath); StringRef VName = path::filename(Entry.VPath); OS.indent(Indent) << "{\n"; Indent += 2; OS.indent(Indent) << "'type': 'file',\n"; OS.indent(Indent) << "'name': \""; OS.write_escaped(VName) << "\",\n"; OS.indent(Indent) << "'external-contents': \""; OS.write_escaped(Entry.RPath) << "\"\n"; Indent -= 2; OS.indent(Indent) << '}'; if (Entries.empty()) { OS << '\n'; break; } StringRef NextVPath = Entries.front().VPath; if (!containedIn(ParentPath, NextVPath)) { OS << '\n'; break; } OS << ",\n"; if (path::parent_path(NextVPath) != ParentPath) { Entries = printDirNodes(Entries, ParentPath, Indent); } } return Entries; } bool containedIn(StringRef Parent, StringRef Path) { return Path.startswith(Parent); } StringRef containedPart(StringRef Parent, StringRef Path) { assert(containedIn(Parent, Path)); if (Parent.empty()) return Path; return Path.slice(Parent.size()+1, StringRef::npos); } }; } enum CXErrorCode clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay VFO, unsigned, CXString *out_buffer) { if (!VFO || !out_buffer) return CXError_InvalidArguments; llvm::SmallVector Entries; for (unsigned i = 0, e = VFO->Mappings.size(); i != e; ++i) { EntryTy Entry; Entry.VPath = VFO->Mappings[i].first; Entry.RPath = VFO->Mappings[i].second; Entries.push_back(Entry); } // FIXME: We should add options to determine if the paths are case sensitive // or not. The following assumes that if paths are case-insensitive the caller // did not mix cases in the virtual paths it provided. std::sort(Entries.begin(), Entries.end()); llvm::SmallString<256> Buf; llvm::raw_svector_ostream OS(Buf); JSONVFSPrinter Printer(OS); Printer.print(Entries); *out_buffer = cxstring::createDup(OS.str()); return CXError_Success; } void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay VFO) { delete VFO; }