1
2
3
4
5
6 package bootstrap
7
8 import (
9 "fmt"
10 "go/format"
11 "io/ioutil"
12 "os"
13 "os/exec"
14 "path/filepath"
15 "regexp"
16 "sort"
17 )
18
19 const genPackage = "github.com/mailru/easyjson/gen"
20 const pkgWriter = "github.com/mailru/easyjson/jwriter"
21 const pkgLexer = "github.com/mailru/easyjson/jlexer"
22
23 var buildFlagsRegexp = regexp.MustCompile("'.+'|\".+\"|\\S+")
24
25 type Generator struct {
26 PkgPath, PkgName string
27 Types []string
28
29 NoStdMarshalers bool
30 SnakeCase bool
31 LowerCamelCase bool
32 OmitEmpty bool
33 DisallowUnknownFields bool
34 SkipMemberNameUnescaping bool
35
36 OutName string
37 BuildTags string
38 GenBuildFlags string
39
40 StubsOnly bool
41 LeaveTemps bool
42 NoFormat bool
43 SimpleBytes bool
44 }
45
46
47
48 func (g *Generator) writeStub() error {
49 f, err := os.Create(g.OutName)
50 if err != nil {
51 return err
52 }
53 defer f.Close()
54
55 if g.BuildTags != "" {
56 fmt.Fprintln(f, "// +build ", g.BuildTags)
57 fmt.Fprintln(f)
58 }
59 fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: easyjson stub code to make the package")
60 fmt.Fprintln(f, "// compilable during generation.")
61 fmt.Fprintln(f)
62 fmt.Fprintln(f, "package ", g.PkgName)
63
64 if len(g.Types) > 0 {
65 fmt.Fprintln(f)
66 fmt.Fprintln(f, "import (")
67 fmt.Fprintln(f, ` "`+pkgWriter+`"`)
68 fmt.Fprintln(f, ` "`+pkgLexer+`"`)
69 fmt.Fprintln(f, ")")
70 }
71
72 sort.Strings(g.Types)
73 for _, t := range g.Types {
74 fmt.Fprintln(f)
75 if !g.NoStdMarshalers {
76 fmt.Fprintln(f, "func (", t, ") MarshalJSON() ([]byte, error) { return nil, nil }")
77 fmt.Fprintln(f, "func (*", t, ") UnmarshalJSON([]byte) error { return nil }")
78 }
79
80 fmt.Fprintln(f, "func (", t, ") MarshalEasyJSON(w *jwriter.Writer) {}")
81 fmt.Fprintln(f, "func (*", t, ") UnmarshalEasyJSON(l *jlexer.Lexer) {}")
82 fmt.Fprintln(f)
83 fmt.Fprintln(f, "type EasyJSON_exporter_"+t+" *"+t)
84 }
85 return nil
86 }
87
88
89 func (g *Generator) writeMain() (path string, err error) {
90 f, err := ioutil.TempFile(filepath.Dir(g.OutName), "easyjson-bootstrap")
91 if err != nil {
92 return "", err
93 }
94
95 fmt.Fprintln(f, "// +build ignore")
96 fmt.Fprintln(f)
97 fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: easyjson bootstapping code to launch")
98 fmt.Fprintln(f, "// the actual generator.")
99 fmt.Fprintln(f)
100 fmt.Fprintln(f, "package main")
101 fmt.Fprintln(f)
102 fmt.Fprintln(f, "import (")
103 fmt.Fprintln(f, ` "fmt"`)
104 fmt.Fprintln(f, ` "os"`)
105 fmt.Fprintln(f)
106 fmt.Fprintf(f, " %q\n", genPackage)
107 if len(g.Types) > 0 {
108 fmt.Fprintln(f)
109 fmt.Fprintf(f, " pkg %q\n", g.PkgPath)
110 }
111 fmt.Fprintln(f, ")")
112 fmt.Fprintln(f)
113 fmt.Fprintln(f, "func main() {")
114 fmt.Fprintf(f, " g := gen.NewGenerator(%q)\n", filepath.Base(g.OutName))
115 fmt.Fprintf(f, " g.SetPkg(%q, %q)\n", g.PkgName, g.PkgPath)
116 if g.BuildTags != "" {
117 fmt.Fprintf(f, " g.SetBuildTags(%q)\n", g.BuildTags)
118 }
119 if g.SnakeCase {
120 fmt.Fprintln(f, " g.UseSnakeCase()")
121 }
122 if g.LowerCamelCase {
123 fmt.Fprintln(f, " g.UseLowerCamelCase()")
124 }
125 if g.OmitEmpty {
126 fmt.Fprintln(f, " g.OmitEmpty()")
127 }
128 if g.NoStdMarshalers {
129 fmt.Fprintln(f, " g.NoStdMarshalers()")
130 }
131 if g.DisallowUnknownFields {
132 fmt.Fprintln(f, " g.DisallowUnknownFields()")
133 }
134 if g.SimpleBytes {
135 fmt.Fprintln(f, " g.SimpleBytes()")
136 }
137 if g.SkipMemberNameUnescaping {
138 fmt.Fprintln(f, " g.SkipMemberNameUnescaping()")
139 }
140
141 sort.Strings(g.Types)
142 for _, v := range g.Types {
143 fmt.Fprintln(f, " g.Add(pkg.EasyJSON_exporter_"+v+"(nil))")
144 }
145
146 fmt.Fprintln(f, " if err := g.Run(os.Stdout); err != nil {")
147 fmt.Fprintln(f, " fmt.Fprintln(os.Stderr, err)")
148 fmt.Fprintln(f, " os.Exit(1)")
149 fmt.Fprintln(f, " }")
150 fmt.Fprintln(f, "}")
151
152 src := f.Name()
153 if err := f.Close(); err != nil {
154 return src, err
155 }
156
157 dest := src + ".go"
158 return dest, os.Rename(src, dest)
159 }
160
161 func (g *Generator) Run() error {
162 if err := g.writeStub(); err != nil {
163 return err
164 }
165 if g.StubsOnly {
166 return nil
167 }
168
169 path, err := g.writeMain()
170 if err != nil {
171 return err
172 }
173 if !g.LeaveTemps {
174 defer os.Remove(path)
175 }
176
177 f, err := os.Create(g.OutName + ".tmp")
178 if err != nil {
179 return err
180 }
181 if !g.LeaveTemps {
182 defer os.Remove(f.Name())
183 }
184
185 execArgs := []string{"run"}
186 if g.GenBuildFlags != "" {
187 buildFlags := buildFlagsRegexp.FindAllString(g.GenBuildFlags, -1)
188 execArgs = append(execArgs, buildFlags...)
189 }
190 execArgs = append(execArgs, "-tags", g.BuildTags, filepath.Base(path))
191 cmd := exec.Command("go", execArgs...)
192
193 cmd.Stdout = f
194 cmd.Stderr = os.Stderr
195 cmd.Dir = filepath.Dir(path)
196 if err = cmd.Run(); err != nil {
197 return err
198 }
199 f.Close()
200
201
202 if g.NoFormat {
203 return os.Rename(f.Name(), g.OutName)
204 }
205
206
207 in, err := ioutil.ReadFile(f.Name())
208 if err != nil {
209 return err
210 }
211 out, err := format.Source(in)
212 if err != nil {
213 return err
214 }
215 return ioutil.WriteFile(g.OutName, out, 0644)
216 }
217
View as plain text