You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
4.6 KiB
192 lines
4.6 KiB
// Package bootstrap implements the bootstrapping logic: generation of a .go file to |
|
// launch the actual generator and launching the generator itself. |
|
// |
|
// The package may be preferred to a command-line utility if generating the serializers |
|
// from golang code is required. |
|
package bootstrap |
|
|
|
import ( |
|
"fmt" |
|
"io/ioutil" |
|
"os" |
|
"os/exec" |
|
"path/filepath" |
|
"sort" |
|
) |
|
|
|
const genPackage = "github.com/mailru/easyjson/gen" |
|
const pkgWriter = "github.com/mailru/easyjson/jwriter" |
|
const pkgLexer = "github.com/mailru/easyjson/jlexer" |
|
|
|
type Generator struct { |
|
PkgPath, PkgName string |
|
Types []string |
|
|
|
NoStdMarshalers bool |
|
SnakeCase bool |
|
LowerCamelCase bool |
|
OmitEmpty bool |
|
DisallowUnknownFields bool |
|
|
|
OutName string |
|
BuildTags string |
|
|
|
StubsOnly bool |
|
LeaveTemps bool |
|
NoFormat bool |
|
} |
|
|
|
// writeStub outputs an initial stubs for marshalers/unmarshalers so that the package |
|
// using marshalers/unmarshales compiles correctly for boostrapping code. |
|
func (g *Generator) writeStub() error { |
|
f, err := os.Create(g.OutName) |
|
if err != nil { |
|
return err |
|
} |
|
defer f.Close() |
|
|
|
if g.BuildTags != "" { |
|
fmt.Fprintln(f, "// +build ", g.BuildTags) |
|
fmt.Fprintln(f) |
|
} |
|
fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: easyjson stub code to make the package") |
|
fmt.Fprintln(f, "// compilable during generation.") |
|
fmt.Fprintln(f) |
|
fmt.Fprintln(f, "package ", g.PkgName) |
|
|
|
if len(g.Types) > 0 { |
|
fmt.Fprintln(f) |
|
fmt.Fprintln(f, "import (") |
|
fmt.Fprintln(f, ` "`+pkgWriter+`"`) |
|
fmt.Fprintln(f, ` "`+pkgLexer+`"`) |
|
fmt.Fprintln(f, ")") |
|
} |
|
|
|
sort.Strings(g.Types) |
|
for _, t := range g.Types { |
|
fmt.Fprintln(f) |
|
if !g.NoStdMarshalers { |
|
fmt.Fprintln(f, "func (", t, ") MarshalJSON() ([]byte, error) { return nil, nil }") |
|
fmt.Fprintln(f, "func (*", t, ") UnmarshalJSON([]byte) error { return nil }") |
|
} |
|
|
|
fmt.Fprintln(f, "func (", t, ") MarshalEasyJSON(w *jwriter.Writer) {}") |
|
fmt.Fprintln(f, "func (*", t, ") UnmarshalEasyJSON(l *jlexer.Lexer) {}") |
|
fmt.Fprintln(f) |
|
fmt.Fprintln(f, "type EasyJSON_exporter_"+t+" *"+t) |
|
} |
|
return nil |
|
} |
|
|
|
// writeMain creates a .go file that launches the generator if 'go run'. |
|
func (g *Generator) writeMain() (path string, err error) { |
|
f, err := ioutil.TempFile(filepath.Dir(g.OutName), "easyjson-bootstrap") |
|
if err != nil { |
|
return "", err |
|
} |
|
|
|
fmt.Fprintln(f, "// +build ignore") |
|
fmt.Fprintln(f) |
|
fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: easyjson bootstapping code to launch") |
|
fmt.Fprintln(f, "// the actual generator.") |
|
fmt.Fprintln(f) |
|
fmt.Fprintln(f, "package main") |
|
fmt.Fprintln(f) |
|
fmt.Fprintln(f, "import (") |
|
fmt.Fprintln(f, ` "fmt"`) |
|
fmt.Fprintln(f, ` "os"`) |
|
fmt.Fprintln(f) |
|
fmt.Fprintf(f, " %q\n", genPackage) |
|
if len(g.Types) > 0 { |
|
fmt.Fprintln(f) |
|
fmt.Fprintf(f, " pkg %q\n", g.PkgPath) |
|
} |
|
fmt.Fprintln(f, ")") |
|
fmt.Fprintln(f) |
|
fmt.Fprintln(f, "func main() {") |
|
fmt.Fprintf(f, " g := gen.NewGenerator(%q)\n", filepath.Base(g.OutName)) |
|
fmt.Fprintf(f, " g.SetPkg(%q, %q)\n", g.PkgName, g.PkgPath) |
|
if g.BuildTags != "" { |
|
fmt.Fprintf(f, " g.SetBuildTags(%q)\n", g.BuildTags) |
|
} |
|
if g.SnakeCase { |
|
fmt.Fprintln(f, " g.UseSnakeCase()") |
|
} |
|
if g.LowerCamelCase { |
|
fmt.Fprintln(f, " g.UseLowerCamelCase()") |
|
} |
|
if g.OmitEmpty { |
|
fmt.Fprintln(f, " g.OmitEmpty()") |
|
} |
|
if g.NoStdMarshalers { |
|
fmt.Fprintln(f, " g.NoStdMarshalers()") |
|
} |
|
if g.DisallowUnknownFields { |
|
fmt.Fprintln(f, " g.DisallowUnknownFields()") |
|
} |
|
|
|
sort.Strings(g.Types) |
|
for _, v := range g.Types { |
|
fmt.Fprintln(f, " g.Add(pkg.EasyJSON_exporter_"+v+"(nil))") |
|
} |
|
|
|
fmt.Fprintln(f, " if err := g.Run(os.Stdout); err != nil {") |
|
fmt.Fprintln(f, " fmt.Fprintln(os.Stderr, err)") |
|
fmt.Fprintln(f, " os.Exit(1)") |
|
fmt.Fprintln(f, " }") |
|
fmt.Fprintln(f, "}") |
|
|
|
src := f.Name() |
|
if err := f.Close(); err != nil { |
|
return src, err |
|
} |
|
|
|
dest := src + ".go" |
|
return dest, os.Rename(src, dest) |
|
} |
|
|
|
func (g *Generator) Run() error { |
|
if err := g.writeStub(); err != nil { |
|
return err |
|
} |
|
if g.StubsOnly { |
|
return nil |
|
} |
|
|
|
path, err := g.writeMain() |
|
if err != nil { |
|
return err |
|
} |
|
if !g.LeaveTemps { |
|
defer os.Remove(path) |
|
} |
|
|
|
f, err := os.Create(g.OutName + ".tmp") |
|
if err != nil { |
|
return err |
|
} |
|
if !g.LeaveTemps { |
|
defer os.Remove(f.Name()) // will not remove after rename |
|
} |
|
|
|
cmd := exec.Command("go", "run", "-tags", g.BuildTags, path) |
|
cmd.Stdout = f |
|
cmd.Stderr = os.Stderr |
|
if err = cmd.Run(); err != nil { |
|
return err |
|
} |
|
|
|
f.Close() |
|
|
|
if !g.NoFormat { |
|
cmd = exec.Command("gofmt", "-w", f.Name()) |
|
cmd.Stderr = os.Stderr |
|
cmd.Stdout = os.Stdout |
|
|
|
if err = cmd.Run(); err != nil { |
|
return err |
|
} |
|
} |
|
|
|
return os.Rename(f.Name(), g.OutName) |
|
}
|
|
|