bootstrap.go 4.6 KB

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