args.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // Package args has common command-line flags for generation programs.
  2. package args
  3. import (
  4. "bytes"
  5. "flag"
  6. "fmt"
  7. "io/ioutil"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. "time"
  14. "go-common/app/tool/gengo/generator"
  15. "go-common/app/tool/gengo/namer"
  16. "go-common/app/tool/gengo/parser"
  17. "go-common/app/tool/gengo/types"
  18. )
  19. // Default returns a defaulted GeneratorArgs. You may change the defaults
  20. // before calling AddFlags.
  21. func Default() *GeneratorArgs {
  22. return &GeneratorArgs{
  23. OutputBase: DefaultSourceTree(),
  24. GoHeaderFilePath: filepath.Join(DefaultSourceTree(), "go-common/app/tool/gengo/boilerplate/boilerplate.go.txt"),
  25. GeneratedBuildTag: "ignore_autogenerated",
  26. GeneratedByCommentTemplate: "// Code generated by GENERATOR_NAME. DO NOT EDIT.",
  27. defaultCommandLineFlags: true,
  28. }
  29. }
  30. // GeneratorArgs has arguments that are passed to generators.
  31. type GeneratorArgs struct {
  32. // Which directories to parse.
  33. InputDirs StringSliceVar
  34. // Source tree to write results to.
  35. OutputBase string
  36. // Package path within the source tree.
  37. OutputPackagePath string
  38. // Output file name.
  39. OutputFileBaseName string
  40. // Where to get copyright header text.
  41. GoHeaderFilePath string
  42. // If GeneratedByCommentTemplate is set, generate a "Code generated by" comment
  43. // below the bloilerplate, of the format defined by this string.
  44. // Any instances of "GENERATOR_NAME" will be replaced with the name of the code generator.
  45. GeneratedByCommentTemplate string
  46. // If true, only verify, don't write anything.
  47. VerifyOnly bool
  48. // GeneratedBuildTag is the tag used to identify code generated by execution
  49. // of this type. Each generator should use a different tag, and different
  50. // groups of generators (external API that depends on Kube generations) should
  51. // keep tags distinct as well.
  52. GeneratedBuildTag string
  53. // Any custom arguments go here
  54. CustomArgs interface{}
  55. // Whether to use default command line flags
  56. defaultCommandLineFlags bool
  57. }
  58. // WithoutDefaultFlagParsing disables implicit addition of command line flags and parsing.
  59. func (g *GeneratorArgs) WithoutDefaultFlagParsing() *GeneratorArgs {
  60. g.defaultCommandLineFlags = false
  61. return g
  62. }
  63. // AddFlags is
  64. func (g *GeneratorArgs) AddFlags(fs *flag.FlagSet) {
  65. fs.Var(&g.InputDirs, "input-dirs", "Comma-separated list of import paths to get input types from.")
  66. fs.StringVar(&g.OutputBase, "output-base", g.OutputBase, "Output base; defaults to $GOPATH/src/ or ./ if $GOPATH is not set.")
  67. fs.StringVar(&g.OutputPackagePath, "output-package", g.OutputPackagePath, "Base package path.")
  68. fs.StringVar(&g.OutputFileBaseName, "output-file-base", g.OutputFileBaseName, "Base name (without .go suffix) for output files.")
  69. fs.StringVar(&g.GoHeaderFilePath, "go-header-file", g.GoHeaderFilePath, "File containing boilerplate header text. The string YEAR will be replaced with the current 4-digit year.")
  70. fs.BoolVar(&g.VerifyOnly, "verify-only", g.VerifyOnly, "If true, only verify existing output, do not write anything.")
  71. fs.StringVar(&g.GeneratedBuildTag, "build-tag", g.GeneratedBuildTag, "A Go build tag to use to identify files generated by this command. Should be unique.")
  72. }
  73. // LoadGoBoilerplate loads the boilerplate file passed to --go-header-file.
  74. func (g *GeneratorArgs) LoadGoBoilerplate() ([]byte, error) {
  75. b, err := ioutil.ReadFile(g.GoHeaderFilePath)
  76. if err != nil {
  77. return nil, err
  78. }
  79. b = bytes.Replace(b, []byte("YEAR"), []byte(strconv.Itoa(time.Now().Year())), -1)
  80. if g.GeneratedByCommentTemplate != "" {
  81. if len(b) != 0 {
  82. b = append(b, byte('\n'))
  83. }
  84. generatorName := path.Base(os.Args[0])
  85. generatedByComment := strings.Replace(g.GeneratedByCommentTemplate, "GENERATOR_NAME", generatorName, -1)
  86. s := fmt.Sprintf("%s\n\n", generatedByComment)
  87. b = append(b, []byte(s)...)
  88. }
  89. return b, nil
  90. }
  91. // NewBuilder makes a new parser.Builder and populates it with the input
  92. // directories.
  93. func (g *GeneratorArgs) NewBuilder() (*parser.Builder, error) {
  94. b := parser.New()
  95. // Ignore all auto-generated files.
  96. b.AddBuildTags(g.GeneratedBuildTag)
  97. for _, d := range g.InputDirs {
  98. var err error
  99. if strings.HasSuffix(d, "/...") {
  100. err = b.AddDirRecursive(strings.TrimSuffix(d, "/..."))
  101. } else {
  102. err = b.AddDir(d)
  103. }
  104. if err != nil {
  105. return nil, fmt.Errorf("unable to add directory %q: %v", d, err)
  106. }
  107. }
  108. return b, nil
  109. }
  110. // InputIncludes returns true if the given package is a (sub) package of one of
  111. // the InputDirs.
  112. func (g *GeneratorArgs) InputIncludes(p *types.Package) bool {
  113. for _, dir := range g.InputDirs {
  114. d := dir
  115. if strings.HasSuffix(d, "...") {
  116. d = strings.TrimSuffix(d, "...")
  117. }
  118. if strings.HasPrefix(p.Path, d) {
  119. return true
  120. }
  121. }
  122. return false
  123. }
  124. // DefaultSourceTree returns the /src directory of the first entry in $GOPATH.
  125. // If $GOPATH is empty, it returns "./". Useful as a default output location.
  126. func DefaultSourceTree() string {
  127. paths := strings.Split(os.Getenv("GOPATH"), string(filepath.ListSeparator))
  128. if len(paths) > 0 && len(paths[0]) > 0 {
  129. return filepath.Join(paths[0], "src")
  130. }
  131. return "./"
  132. }
  133. // Execute implements main().
  134. // If you don't need any non-default behavior, use as:
  135. // args.Default().Execute(...)
  136. func (g *GeneratorArgs) Execute(nameSystems namer.NameSystems, defaultSystem string, pkgs func(*generator.Context, *GeneratorArgs) generator.Packages) error {
  137. if g.defaultCommandLineFlags {
  138. g.AddFlags(flag.CommandLine)
  139. // flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
  140. flag.Parse()
  141. }
  142. b, err := g.NewBuilder()
  143. if err != nil {
  144. return fmt.Errorf("Failed making a parser: %v", err)
  145. }
  146. c, err := generator.NewContext(b, nameSystems, defaultSystem)
  147. if err != nil {
  148. return fmt.Errorf("Failed making a context: %v", err)
  149. }
  150. c.Verify = g.VerifyOnly
  151. packages := pkgs(c, g)
  152. if err := c.ExecutePackages(g.OutputBase, packages); err != nil {
  153. return fmt.Errorf("Failed executing generator: %v", err)
  154. }
  155. return nil
  156. }