bootstrap.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Package bootstrap implements the bootstrapping logic: generation of a .go file to
  2. // launch the actual generator and launching the generator itself.
  3. //
  4. // The package may be preferred to a command-line utility if generating the serializers
  5. // from golang code is required.
  6. package bootstrap
  7. import (
  8. "fmt"
  9. "go/format"
  10. "io/ioutil"
  11. "os"
  12. "os/exec"
  13. "path/filepath"
  14. "sort"
  15. )
  16. const genPackage = "github.com/mailru/easyjson/gen"
  17. const pkgWriter = "github.com/mailru/easyjson/jwriter"
  18. const pkgLexer = "github.com/mailru/easyjson/jlexer"
  19. type Generator struct {
  20. PkgPath, PkgName string
  21. Types []string
  22. NoStdMarshalers bool
  23. SnakeCase bool
  24. LowerCamelCase bool
  25. OmitEmpty bool
  26. DisallowUnknownFields bool
  27. OutName string
  28. BuildTags string
  29. StubsOnly bool
  30. LeaveTemps bool
  31. NoFormat bool
  32. }
  33. // writeStub outputs an initial stub for marshalers/unmarshalers so that the package
  34. // using marshalers/unmarshales compiles correctly for boostrapping code.
  35. func (g *Generator) writeStub() error {
  36. f, err := os.Create(g.OutName)
  37. if err != nil {
  38. return err
  39. }
  40. defer f.Close()
  41. if g.BuildTags != "" {
  42. fmt.Fprintln(f, "// +build ", g.BuildTags)
  43. fmt.Fprintln(f)
  44. }
  45. fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: easyjson stub code to make the package")
  46. fmt.Fprintln(f, "// compilable during generation.")
  47. fmt.Fprintln(f)
  48. fmt.Fprintln(f, "package ", g.PkgName)
  49. if len(g.Types) > 0 {
  50. fmt.Fprintln(f)
  51. fmt.Fprintln(f, "import (")
  52. fmt.Fprintln(f, ` "`+pkgWriter+`"`)
  53. fmt.Fprintln(f, ` "`+pkgLexer+`"`)
  54. fmt.Fprintln(f, ")")
  55. }
  56. sort.Strings(g.Types)
  57. for _, t := range g.Types {
  58. fmt.Fprintln(f)
  59. if !g.NoStdMarshalers {
  60. fmt.Fprintln(f, "func (", t, ") MarshalJSON() ([]byte, error) { return nil, nil }")
  61. fmt.Fprintln(f, "func (*", t, ") UnmarshalJSON([]byte) error { return nil }")
  62. }
  63. fmt.Fprintln(f, "func (", t, ") MarshalEasyJSON(w *jwriter.Writer) {}")
  64. fmt.Fprintln(f, "func (*", t, ") UnmarshalEasyJSON(l *jlexer.Lexer) {}")
  65. fmt.Fprintln(f)
  66. fmt.Fprintln(f, "type EasyJSON_exporter_"+t+" *"+t)
  67. }
  68. return nil
  69. }
  70. // writeMain creates a .go file that launches the generator if 'go run'.
  71. func (g *Generator) writeMain() (path string, err error) {
  72. f, err := ioutil.TempFile(filepath.Dir(g.OutName), "easyjson-bootstrap")
  73. if err != nil {
  74. return "", err
  75. }
  76. fmt.Fprintln(f, "// +build ignore")
  77. fmt.Fprintln(f)
  78. fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: easyjson bootstapping code to launch")
  79. fmt.Fprintln(f, "// the actual generator.")
  80. fmt.Fprintln(f)
  81. fmt.Fprintln(f, "package main")
  82. fmt.Fprintln(f)
  83. fmt.Fprintln(f, "import (")
  84. fmt.Fprintln(f, ` "fmt"`)
  85. fmt.Fprintln(f, ` "os"`)
  86. fmt.Fprintln(f)
  87. fmt.Fprintf(f, " %q\n", genPackage)
  88. if len(g.Types) > 0 {
  89. fmt.Fprintln(f)
  90. fmt.Fprintf(f, " pkg %q\n", g.PkgPath)
  91. }
  92. fmt.Fprintln(f, ")")
  93. fmt.Fprintln(f)
  94. fmt.Fprintln(f, "func main() {")
  95. fmt.Fprintf(f, " g := gen.NewGenerator(%q)\n", filepath.Base(g.OutName))
  96. fmt.Fprintf(f, " g.SetPkg(%q, %q)\n", g.PkgName, g.PkgPath)
  97. if g.BuildTags != "" {
  98. fmt.Fprintf(f, " g.SetBuildTags(%q)\n", g.BuildTags)
  99. }
  100. if g.SnakeCase {
  101. fmt.Fprintln(f, " g.UseSnakeCase()")
  102. }
  103. if g.LowerCamelCase {
  104. fmt.Fprintln(f, " g.UseLowerCamelCase()")
  105. }
  106. if g.OmitEmpty {
  107. fmt.Fprintln(f, " g.OmitEmpty()")
  108. }
  109. if g.NoStdMarshalers {
  110. fmt.Fprintln(f, " g.NoStdMarshalers()")
  111. }
  112. if g.DisallowUnknownFields {
  113. fmt.Fprintln(f, " g.DisallowUnknownFields()")
  114. }
  115. sort.Strings(g.Types)
  116. for _, v := range g.Types {
  117. fmt.Fprintln(f, " g.Add(pkg.EasyJSON_exporter_"+v+"(nil))")
  118. }
  119. fmt.Fprintln(f, " if err := g.Run(os.Stdout); err != nil {")
  120. fmt.Fprintln(f, " fmt.Fprintln(os.Stderr, err)")
  121. fmt.Fprintln(f, " os.Exit(1)")
  122. fmt.Fprintln(f, " }")
  123. fmt.Fprintln(f, "}")
  124. src := f.Name()
  125. if err := f.Close(); err != nil {
  126. return src, err
  127. }
  128. dest := src + ".go"
  129. return dest, os.Rename(src, dest)
  130. }
  131. func (g *Generator) Run() error {
  132. if err := g.writeStub(); err != nil {
  133. return err
  134. }
  135. if g.StubsOnly {
  136. return nil
  137. }
  138. path, err := g.writeMain()
  139. if err != nil {
  140. return err
  141. }
  142. if !g.LeaveTemps {
  143. defer os.Remove(path)
  144. }
  145. f, err := os.Create(g.OutName + ".tmp")
  146. if err != nil {
  147. return err
  148. }
  149. if !g.LeaveTemps {
  150. defer os.Remove(f.Name()) // will not remove after rename
  151. }
  152. cmd := exec.Command("go", "run", "-tags", g.BuildTags, filepath.Base(path))
  153. cmd.Stdout = f
  154. cmd.Stderr = os.Stderr
  155. cmd.Dir = filepath.Dir(path)
  156. if err = cmd.Run(); err != nil {
  157. return err
  158. }
  159. f.Close()
  160. // move unformatted file to out path
  161. if g.NoFormat {
  162. return os.Rename(f.Name(), g.OutName)
  163. }
  164. // format file and write to out path
  165. in, err := ioutil.ReadFile(f.Name())
  166. if err != nil {
  167. return err
  168. }
  169. out, err := format.Source(in)
  170. if err != nil {
  171. return err
  172. }
  173. return ioutil.WriteFile(g.OutName, out, 0644)
  174. }