1 //
2 // Copyright (c) 2006, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 3 Sep 05 Brian Frank Creation
7 // 7 Oct 06 Brian Frank Port from Java to Fan
8 //
9
10 **
11 ** WritePod writes the FPod to a zip file.
12 **
13 class WritePod : CompilerStep
14 {
15
16 //////////////////////////////////////////////////////////////////////////
17 // Constructor
18 //////////////////////////////////////////////////////////////////////////
19
20 new make(Compiler compiler)
21 : super(compiler)
22 {
23 }
24
25 //////////////////////////////////////////////////////////////////////////
26 // Run
27 //////////////////////////////////////////////////////////////////////////
28
29 **
30 ** Not used, use write instead
31 **
32 override Void run() { throw UnsupportedErr.make }
33
34 **
35 ** Run the step and return pod file written
36 **
37 File write()
38 {
39 dir := compiler.input.outDir
40 fpod := compiler.fpod
41 podFile := dir + "${fpod.name}.pod".toUri
42 location = Location.makeFile(podFile)
43
44 log.info("WritePod [${podFile.toStr}]")
45
46 // create output directory
47 dir.create
48
49 Zip zip := null
50 try
51 {
52 // open zip store
53 zip = Zip.write(podFile.out)
54
55 // write fpod data structures into zip file
56 fpod.write(zip)
57
58 // write type db indices
59 if (!compiler.input.isScript)
60 writeTypeDb(zip)
61
62 // write resource files
63 compiler.resFiles.each |File f| { writeRes(zip, f) }
64
65 // if including fandoc write it out too
66 if (compiler.input.includeDoc) writeTypeDocs(zip)
67
68 // if including source write it out too
69 if (compiler.input.includeSrc) writeSrc(zip)
70 }
71 catch (CompilerErr e)
72 {
73 throw e
74 }
75 catch (Err e)
76 {
77 throw errReport(CompilerErr.make("Cannot write", location, e))
78 }
79
80 // close file
81 if (zip != null) zip.close
82 return podFile
83 }
84
85 //////////////////////////////////////////////////////////////////////////
86 // Resource
87 //////////////////////////////////////////////////////////////////////////
88
89 private Void writeRes(Zip zip, File file, Uri path := null)
90 {
91 input := compiler.input
92 if (path == null)
93 {
94 path = file.uri
95 path = path - input.homeDir.uri
96 }
97
98 try
99 {
100 out := zip.writeNext(path, file.modified)
101 file.in.pipe(out)
102 out.close
103 }
104 catch (Err e)
105 {
106 throw errReport(CompilerErr.make("Cannot write resource file '$path'", location, e))
107 }
108 }
109
110 //////////////////////////////////////////////////////////////////////////
111 // Doc
112 //////////////////////////////////////////////////////////////////////////
113
114 private Void writeTypeDocs(Zip zip)
115 {
116 compiler.types.each |TypeDef t|
117 {
118 if (!t.isSynthetic) writeTypeDoc(zip, t)
119 }
120 }
121
122 private Void writeTypeDoc(Zip zip, TypeDef t)
123 {
124 try
125 {
126 out := zip.writeNext("doc/${t.name}.apidoc".toUri)
127 writeDoc(out, t.qname, t.doc)
128 t.slotDefs.each |SlotDef s|
129 {
130 writeDoc(out, s.qname, s.doc)
131 }
132 out.close
133 }
134 catch (Err e)
135 {
136 throw errReport(CompilerErr.make("Cannot write fandoc '$t.name'", t.location, e))
137 }
138 }
139
140 **
141 ** FDoc is used to read/write a fandoc text file. The fandoc file
142 ** format is an extremely simple plan text format with left justified
143 ** type/slot qnames, followed by the fandoc content indented two spaces.
144 **
145 private static Void writeDoc(OutStream out, Str key, Str[] doc)
146 {
147 if (doc == null) return
148 out.printLine(key)
149 doc.each |Str line| { out.print(" ").printLine(line) }
150 out.printLine
151 }
152
153 //////////////////////////////////////////////////////////////////////////
154 // Src
155 //////////////////////////////////////////////////////////////////////////
156
157 private Void writeSrc(Zip zip)
158 {
159 compiler.srcFiles.each |File f|
160 {
161 writeRes(zip, f, "src/$f.name".toUri)
162 }
163 }
164
165 //////////////////////////////////////////////////////////////////////////
166 // TypeDb
167 //////////////////////////////////////////////////////////////////////////
168
169 private Void writeTypeDb(Zip zip)
170 {
171 out := zip.writeNext(`/typedb.def`)
172
173 // pod meta-data
174 out.writeI4(FConst.TypeDbMagic)
175 out.writeI4(FConst.TypeDbVersion)
176 out.writeUtf(pod.name)
177 out.writeUtf(compiler.fpod.version.toStr)
178
179 // filter types
180 types := pod.typeDefs.findAll |TypeDef t->Bool|
181 {
182 return t.isPublic && !t.isSynthetic
183 }
184
185 // compute list of all indexed facets
186 facetNameList := Str[,]
187 facetNameMap := Str:Int[:]
188 podFacets := compiler.input.podFacets
189 podFacets.each |Obj v, Str k|
190 {
191 facetNameMap[k] = facetNameList.size
192 facetNameList.add(k)
193 }
194 types.each |TypeDef t|
195 {
196 t.indexedFacets = computeIndexedFacets(t.facets, facetNameList, facetNameMap)
197 }
198
199 // write facet names
200 out.writeI2(facetNameList.size)
201 facetNameList.each |Str n| { out.writeUtf(n) }
202
203 // write pod level facets
204 out.writeI2(podFacets.size)
205 podFacets.each |Obj v, Str k|
206 {
207 out.writeI2(facetNameMap[k])
208 out.writeUtf(Buf.make.writeObj(v).flip.readAllStr)
209 }
210
211 // write types
212 out.writeI2(types.size)
213 types.each |TypeDef t| { writeTypeDbType(out, t, facetNameMap) }
214
215 out.close
216 }
217
218 private FacetDef[] computeIndexedFacets(Str:FacetDef all, Str[] list, Str:Int map)
219 {
220 // if no facets defined, this is easy
221 if (all == null || all.size == 0)
222 return noFacets
223
224 indexed := all.values
225
226 /* filter just specific values?
227
228
229
230
231
232
233
234
235
236 // map facet names into interned list/map
237 indexed.each |FacetDef f|
238 {
239 name := f.name
240 if (map[name] == null)
241 {
242 map[name] = list.size
243 list.add(name)
244 }
245 }
246
247 return indexed
248 }
249
250 private Void writeTypeDbType(OutStream out, TypeDef t, Str:Int facetNames)
251 {
252 out.writeUtf(t.name)
253 out.writeUtf(t.base == null ? "" : t.base.qname)
254 out.writeI2(t.mixins.size)
255 t.mixins.each |CType m| { out.writeUtf(m.qname) }
256 out.writeI4(t.flags)
257 out.writeI2(t.indexedFacets.size)
258 t.indexedFacets.each |FacetDef f|
259 {
260 out.writeI2(facetNames[f.name])
261 out.writeUtf(f.value.serialize)
262 }
263 }
264
265 //////////////////////////////////////////////////////////////////////////
266 // Fields
267 //////////////////////////////////////////////////////////////////////////
268
269 private Location location
270 private FacetDef[] noFacets := FacetDef[,].ro
271 }