logo

class

compiler::WritePod

sys::Obj
  compiler::CompilerSupport
    compiler::CompilerStep
      compiler::WritePod
   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  }