...

Source file src/github.com/mailru/easyjson/bootstrap/bootstrap.go

Documentation: github.com/mailru/easyjson/bootstrap

     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  
     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  // writeStub outputs an initial stub for marshalers/unmarshalers so that the package
    47  // using marshalers/unmarshales compiles correctly for boostrapping code.
    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  // writeMain creates a .go file that launches the generator if 'go run'.
    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()) // will not remove after rename
   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  	// move unformatted file to out path
   202  	if g.NoFormat {
   203  		return os.Rename(f.Name(), g.OutName)
   204  	}
   205  
   206  	// format file and write to out path
   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