regen.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // Copyright 2016 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // +build ignore
  15. // Regen.go regenerates the genproto repository.
  16. //
  17. // Regen.go recursively walks through each directory named by given arguments,
  18. // looking for all .proto files. (Symlinks are not followed.)
  19. // If the pkg_prefix flag is not an empty string,
  20. // any proto file without `go_package` option
  21. // or whose option does not begin with the prefix is ignored.
  22. // Protoc is executed on remaining files,
  23. // one invocation per set of files declaring the same Go package.
  24. package main
  25. import (
  26. "flag"
  27. "fmt"
  28. "io/ioutil"
  29. "log"
  30. "os"
  31. "os/exec"
  32. "path/filepath"
  33. "regexp"
  34. "strconv"
  35. "strings"
  36. )
  37. var goPkgOptRe = regexp.MustCompile(`(?m)^option go_package = (.*);`)
  38. func usage() {
  39. fmt.Fprintln(os.Stderr, `usage: go run regen.go -go_out=path/to/output [-pkg_prefix=pkg/prefix] roots...
  40. Most users will not need to run this file directly.
  41. To regenerate this repository, run regen.sh instead.`)
  42. flag.PrintDefaults()
  43. }
  44. func main() {
  45. goOutDir := flag.String("go_out", "", "go_out argument to pass to protoc-gen-go")
  46. pkgPrefix := flag.String("pkg_prefix", "", "only include proto files with go_package starting with this prefix")
  47. flag.Usage = usage
  48. flag.Parse()
  49. if *goOutDir == "" {
  50. log.Fatal("need go_out flag")
  51. }
  52. pkgFiles := make(map[string][]string)
  53. walkFn := func(path string, info os.FileInfo, err error) error {
  54. if err != nil {
  55. return err
  56. }
  57. if !info.Mode().IsRegular() || !strings.HasSuffix(path, ".proto") {
  58. return nil
  59. }
  60. pkg, err := goPkg(path)
  61. if err != nil {
  62. return err
  63. }
  64. pkgFiles[pkg] = append(pkgFiles[pkg], path)
  65. return nil
  66. }
  67. for _, root := range flag.Args() {
  68. if err := filepath.Walk(root, walkFn); err != nil {
  69. log.Fatal(err)
  70. }
  71. }
  72. for pkg, fnames := range pkgFiles {
  73. if !strings.HasPrefix(pkg, *pkgPrefix) {
  74. continue
  75. }
  76. if out, err := protoc(*goOutDir, flag.Args(), fnames); err != nil {
  77. log.Fatalf("error executing protoc: %s\n%s", err, out)
  78. }
  79. }
  80. }
  81. // goPkg reports the import path declared in the given file's
  82. // `go_package` option. If the option is missing, goPkg returns empty string.
  83. func goPkg(fname string) (string, error) {
  84. content, err := ioutil.ReadFile(fname)
  85. if err != nil {
  86. return "", err
  87. }
  88. var pkgName string
  89. if match := goPkgOptRe.FindSubmatch(content); len(match) > 0 {
  90. pn, err := strconv.Unquote(string(match[1]))
  91. if err != nil {
  92. return "", err
  93. }
  94. pkgName = pn
  95. }
  96. if p := strings.IndexRune(pkgName, ';'); p > 0 {
  97. pkgName = pkgName[:p]
  98. }
  99. return pkgName, nil
  100. }
  101. // protoc executes the "protoc" command on files named in fnames,
  102. // passing go_out and include flags specified in goOut and includes respectively.
  103. // protoc returns combined output from stdout and stderr.
  104. func protoc(goOut string, includes, fnames []string) ([]byte, error) {
  105. args := []string{"--go_out=plugins=grpc:" + goOut}
  106. for _, inc := range includes {
  107. args = append(args, "-I", inc)
  108. }
  109. args = append(args, fnames...)
  110. return exec.Command("protoc", args...).CombinedOutput()
  111. }