1fdd4f30fSDmitri Gribenko //===- BuildSystem.cpp - Utilities for use by build systems ---------------===//
2fdd4f30fSDmitri Gribenko //
3fdd4f30fSDmitri Gribenko //                     The LLVM Compiler Infrastructure
4fdd4f30fSDmitri Gribenko //
5fdd4f30fSDmitri Gribenko // This file is distributed under the University of Illinois Open Source
6fdd4f30fSDmitri Gribenko // License. See LICENSE.TXT for details.
7fdd4f30fSDmitri Gribenko //
8fdd4f30fSDmitri Gribenko //===----------------------------------------------------------------------===//
9fdd4f30fSDmitri Gribenko //
10fdd4f30fSDmitri Gribenko // This file implements various utilities for use by build systems.
11fdd4f30fSDmitri Gribenko //
12fdd4f30fSDmitri Gribenko //===----------------------------------------------------------------------===//
13fdd4f30fSDmitri Gribenko 
14fdd4f30fSDmitri Gribenko #include "clang-c/BuildSystem.h"
15*0b9682efSArgyrios Kyrtzidis #include "CXString.h"
16*0b9682efSArgyrios Kyrtzidis #include "llvm/ADT/ArrayRef.h"
17*0b9682efSArgyrios Kyrtzidis #include "llvm/ADT/SmallString.h"
18*0b9682efSArgyrios Kyrtzidis #include "llvm/Support/Path.h"
19*0b9682efSArgyrios Kyrtzidis #include "llvm/Support/raw_ostream.h"
20fdd4f30fSDmitri Gribenko #include "llvm/Support/TimeValue.h"
21fdd4f30fSDmitri Gribenko 
22*0b9682efSArgyrios Kyrtzidis using namespace clang;
23*0b9682efSArgyrios Kyrtzidis using namespace llvm::sys;
24*0b9682efSArgyrios Kyrtzidis 
25fdd4f30fSDmitri Gribenko unsigned long long clang_getBuildSessionTimestamp(void) {
26fdd4f30fSDmitri Gribenko   return llvm::sys::TimeValue::now().toEpochTime();
27fdd4f30fSDmitri Gribenko }
28fdd4f30fSDmitri Gribenko 
29*0b9682efSArgyrios Kyrtzidis struct CXVirtualFileOverlayImpl {
30*0b9682efSArgyrios Kyrtzidis   std::vector<std::pair<std::string, std::string> > Mappings;
31*0b9682efSArgyrios Kyrtzidis };
32*0b9682efSArgyrios Kyrtzidis 
33*0b9682efSArgyrios Kyrtzidis CXVirtualFileOverlay clang_VirtualFileOverlay_create(unsigned) {
34*0b9682efSArgyrios Kyrtzidis   return new CXVirtualFileOverlayImpl();
35*0b9682efSArgyrios Kyrtzidis }
36*0b9682efSArgyrios Kyrtzidis 
37*0b9682efSArgyrios Kyrtzidis enum CXErrorCode
38*0b9682efSArgyrios Kyrtzidis clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay VFO,
39*0b9682efSArgyrios Kyrtzidis                                         const char *virtualPath,
40*0b9682efSArgyrios Kyrtzidis                                         const char *realPath) {
41*0b9682efSArgyrios Kyrtzidis   if (!VFO || !virtualPath || !realPath)
42*0b9682efSArgyrios Kyrtzidis     return CXError_InvalidArguments;
43*0b9682efSArgyrios Kyrtzidis   if (!path::is_absolute(virtualPath))
44*0b9682efSArgyrios Kyrtzidis     return CXError_InvalidArguments;
45*0b9682efSArgyrios Kyrtzidis   if (!path::is_absolute(realPath))
46*0b9682efSArgyrios Kyrtzidis     return CXError_InvalidArguments;
47*0b9682efSArgyrios Kyrtzidis 
48*0b9682efSArgyrios Kyrtzidis   for (path::const_iterator
49*0b9682efSArgyrios Kyrtzidis          PI = path::begin(virtualPath),
50*0b9682efSArgyrios Kyrtzidis          PE = path::end(virtualPath); PI != PE; ++PI) {
51*0b9682efSArgyrios Kyrtzidis     StringRef Comp = *PI;
52*0b9682efSArgyrios Kyrtzidis     if (Comp == "." || Comp == "..")
53*0b9682efSArgyrios Kyrtzidis       return CXError_InvalidArguments;
54*0b9682efSArgyrios Kyrtzidis   }
55*0b9682efSArgyrios Kyrtzidis 
56*0b9682efSArgyrios Kyrtzidis   VFO->Mappings.push_back(std::make_pair(virtualPath, realPath));
57*0b9682efSArgyrios Kyrtzidis   return CXError_Success;
58*0b9682efSArgyrios Kyrtzidis }
59*0b9682efSArgyrios Kyrtzidis 
60*0b9682efSArgyrios Kyrtzidis namespace {
61*0b9682efSArgyrios Kyrtzidis struct EntryTy {
62*0b9682efSArgyrios Kyrtzidis   std::string VPath;
63*0b9682efSArgyrios Kyrtzidis   std::string RPath;
64*0b9682efSArgyrios Kyrtzidis 
65*0b9682efSArgyrios Kyrtzidis   friend bool operator < (const EntryTy &LHS, const EntryTy &RHS) {
66*0b9682efSArgyrios Kyrtzidis     return LHS.VPath < RHS.VPath;
67*0b9682efSArgyrios Kyrtzidis   }
68*0b9682efSArgyrios Kyrtzidis };
69*0b9682efSArgyrios Kyrtzidis 
70*0b9682efSArgyrios Kyrtzidis class JSONVFSPrinter {
71*0b9682efSArgyrios Kyrtzidis   llvm::raw_ostream &OS;
72*0b9682efSArgyrios Kyrtzidis 
73*0b9682efSArgyrios Kyrtzidis public:
74*0b9682efSArgyrios Kyrtzidis   JSONVFSPrinter(llvm::raw_ostream &OS) : OS(OS) {}
75*0b9682efSArgyrios Kyrtzidis 
76*0b9682efSArgyrios Kyrtzidis   /// Entries must be sorted.
77*0b9682efSArgyrios Kyrtzidis   void print(ArrayRef<EntryTy> Entries) {
78*0b9682efSArgyrios Kyrtzidis     OS << "{\n"
79*0b9682efSArgyrios Kyrtzidis           "  'version': 0,\n"
80*0b9682efSArgyrios Kyrtzidis           "  'roots': [\n";
81*0b9682efSArgyrios Kyrtzidis     printDirNodes(Entries, "", 4);
82*0b9682efSArgyrios Kyrtzidis     OS << "  ]\n"
83*0b9682efSArgyrios Kyrtzidis           "}\n";
84*0b9682efSArgyrios Kyrtzidis   }
85*0b9682efSArgyrios Kyrtzidis 
86*0b9682efSArgyrios Kyrtzidis private:
87*0b9682efSArgyrios Kyrtzidis   ArrayRef<EntryTy> printDirNodes(ArrayRef<EntryTy> Entries,
88*0b9682efSArgyrios Kyrtzidis                                   StringRef ParentPath,
89*0b9682efSArgyrios Kyrtzidis                                   unsigned Indent) {
90*0b9682efSArgyrios Kyrtzidis     while (!Entries.empty()) {
91*0b9682efSArgyrios Kyrtzidis       const EntryTy &Entry = Entries.front();
92*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << "{\n";
93*0b9682efSArgyrios Kyrtzidis       Indent += 2;
94*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << "'type': 'directory',\n";
95*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << "'name': \"";
96*0b9682efSArgyrios Kyrtzidis       StringRef DirName = containedPart(ParentPath,
97*0b9682efSArgyrios Kyrtzidis                                         path::parent_path(Entry.VPath));
98*0b9682efSArgyrios Kyrtzidis       OS.write_escaped(DirName) << "\",\n";
99*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << "'contents': [\n";
100*0b9682efSArgyrios Kyrtzidis       Entries = printContents(Entries, Indent + 2);
101*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << "]\n";
102*0b9682efSArgyrios Kyrtzidis       Indent -= 2;
103*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << '}';
104*0b9682efSArgyrios Kyrtzidis       if (Entries.empty()) {
105*0b9682efSArgyrios Kyrtzidis         OS << '\n';
106*0b9682efSArgyrios Kyrtzidis         break;
107*0b9682efSArgyrios Kyrtzidis       }
108*0b9682efSArgyrios Kyrtzidis       StringRef NextVPath = Entries.front().VPath;
109*0b9682efSArgyrios Kyrtzidis       if (!containedIn(ParentPath, NextVPath)) {
110*0b9682efSArgyrios Kyrtzidis         OS << '\n';
111*0b9682efSArgyrios Kyrtzidis         break;
112*0b9682efSArgyrios Kyrtzidis       }
113*0b9682efSArgyrios Kyrtzidis       OS << ",\n";
114*0b9682efSArgyrios Kyrtzidis     }
115*0b9682efSArgyrios Kyrtzidis     return Entries;
116*0b9682efSArgyrios Kyrtzidis   }
117*0b9682efSArgyrios Kyrtzidis 
118*0b9682efSArgyrios Kyrtzidis   ArrayRef<EntryTy> printContents(ArrayRef<EntryTy> Entries,
119*0b9682efSArgyrios Kyrtzidis                                   unsigned Indent) {
120*0b9682efSArgyrios Kyrtzidis     while (!Entries.empty()) {
121*0b9682efSArgyrios Kyrtzidis       const EntryTy &Entry = Entries.front();
122*0b9682efSArgyrios Kyrtzidis       Entries = Entries.slice(1);
123*0b9682efSArgyrios Kyrtzidis       StringRef ParentPath = path::parent_path(Entry.VPath);
124*0b9682efSArgyrios Kyrtzidis       StringRef VName = path::filename(Entry.VPath);
125*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << "{\n";
126*0b9682efSArgyrios Kyrtzidis       Indent += 2;
127*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << "'type': 'file',\n";
128*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << "'name': \"";
129*0b9682efSArgyrios Kyrtzidis       OS.write_escaped(VName) << "\",\n";
130*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << "'external-contents': \"";
131*0b9682efSArgyrios Kyrtzidis       OS.write_escaped(Entry.RPath) << "\"\n";
132*0b9682efSArgyrios Kyrtzidis       Indent -= 2;
133*0b9682efSArgyrios Kyrtzidis       OS.indent(Indent) << '}';
134*0b9682efSArgyrios Kyrtzidis       if (Entries.empty()) {
135*0b9682efSArgyrios Kyrtzidis         OS << '\n';
136*0b9682efSArgyrios Kyrtzidis         break;
137*0b9682efSArgyrios Kyrtzidis       }
138*0b9682efSArgyrios Kyrtzidis       StringRef NextVPath = Entries.front().VPath;
139*0b9682efSArgyrios Kyrtzidis       if (!containedIn(ParentPath, NextVPath)) {
140*0b9682efSArgyrios Kyrtzidis         OS << '\n';
141*0b9682efSArgyrios Kyrtzidis         break;
142*0b9682efSArgyrios Kyrtzidis       }
143*0b9682efSArgyrios Kyrtzidis       OS << ",\n";
144*0b9682efSArgyrios Kyrtzidis       if (path::parent_path(NextVPath) != ParentPath) {
145*0b9682efSArgyrios Kyrtzidis         Entries = printDirNodes(Entries, ParentPath, Indent);
146*0b9682efSArgyrios Kyrtzidis       }
147*0b9682efSArgyrios Kyrtzidis     }
148*0b9682efSArgyrios Kyrtzidis     return Entries;
149*0b9682efSArgyrios Kyrtzidis   }
150*0b9682efSArgyrios Kyrtzidis 
151*0b9682efSArgyrios Kyrtzidis   bool containedIn(StringRef Parent, StringRef Path) {
152*0b9682efSArgyrios Kyrtzidis     return Path.startswith(Parent);
153*0b9682efSArgyrios Kyrtzidis   }
154*0b9682efSArgyrios Kyrtzidis 
155*0b9682efSArgyrios Kyrtzidis   StringRef containedPart(StringRef Parent, StringRef Path) {
156*0b9682efSArgyrios Kyrtzidis     assert(containedIn(Parent, Path));
157*0b9682efSArgyrios Kyrtzidis     if (Parent.empty())
158*0b9682efSArgyrios Kyrtzidis       return Path;
159*0b9682efSArgyrios Kyrtzidis     return Path.slice(Parent.size()+1, StringRef::npos);
160*0b9682efSArgyrios Kyrtzidis   }
161*0b9682efSArgyrios Kyrtzidis };
162*0b9682efSArgyrios Kyrtzidis }
163*0b9682efSArgyrios Kyrtzidis 
164*0b9682efSArgyrios Kyrtzidis enum CXErrorCode
165*0b9682efSArgyrios Kyrtzidis clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay VFO,
166*0b9682efSArgyrios Kyrtzidis                                        unsigned, CXString *out_buffer) {
167*0b9682efSArgyrios Kyrtzidis   if (!VFO || !out_buffer)
168*0b9682efSArgyrios Kyrtzidis     return CXError_InvalidArguments;
169*0b9682efSArgyrios Kyrtzidis 
170*0b9682efSArgyrios Kyrtzidis   llvm::SmallVector<EntryTy, 16> Entries;
171*0b9682efSArgyrios Kyrtzidis   for (unsigned i = 0, e = VFO->Mappings.size(); i != e; ++i) {
172*0b9682efSArgyrios Kyrtzidis     EntryTy Entry;
173*0b9682efSArgyrios Kyrtzidis     Entry.VPath = VFO->Mappings[i].first;
174*0b9682efSArgyrios Kyrtzidis     Entry.RPath = VFO->Mappings[i].second;
175*0b9682efSArgyrios Kyrtzidis     Entries.push_back(Entry);
176*0b9682efSArgyrios Kyrtzidis   }
177*0b9682efSArgyrios Kyrtzidis 
178*0b9682efSArgyrios Kyrtzidis   // FIXME: We should add options to determine if the paths are case sensitive
179*0b9682efSArgyrios Kyrtzidis   // or not. The following assumes that if paths are case-insensitive the caller
180*0b9682efSArgyrios Kyrtzidis   // did not mix cases in the virtual paths it provided.
181*0b9682efSArgyrios Kyrtzidis 
182*0b9682efSArgyrios Kyrtzidis   std::sort(Entries.begin(), Entries.end());
183*0b9682efSArgyrios Kyrtzidis 
184*0b9682efSArgyrios Kyrtzidis   llvm::SmallString<256> Buf;
185*0b9682efSArgyrios Kyrtzidis   llvm::raw_svector_ostream OS(Buf);
186*0b9682efSArgyrios Kyrtzidis   JSONVFSPrinter Printer(OS);
187*0b9682efSArgyrios Kyrtzidis   Printer.print(Entries);
188*0b9682efSArgyrios Kyrtzidis 
189*0b9682efSArgyrios Kyrtzidis   *out_buffer = cxstring::createDup(OS.str());
190*0b9682efSArgyrios Kyrtzidis   return CXError_Success;
191*0b9682efSArgyrios Kyrtzidis }
192*0b9682efSArgyrios Kyrtzidis 
193*0b9682efSArgyrios Kyrtzidis void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay VFO) {
194*0b9682efSArgyrios Kyrtzidis   delete VFO;
195*0b9682efSArgyrios Kyrtzidis }
196