Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 1 | // Copyright 2019 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | //go:generate go run . -execute |
| 6 | |
| 7 | package main |
| 8 | |
| 9 | import ( |
| 10 | "flag" |
| 11 | "fmt" |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 12 | "go/format" |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 13 | "io/ioutil" |
| 14 | "os" |
| 15 | "os/exec" |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 16 | "path" |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 17 | "path/filepath" |
| 18 | "regexp" |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 19 | "sort" |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 20 | "strings" |
| 21 | |
| 22 | gengogrpc "github.com/golang/protobuf/v2/cmd/protoc-gen-go-grpc/internal_gengogrpc" |
| 23 | gengo "github.com/golang/protobuf/v2/cmd/protoc-gen-go/internal_gengo" |
Joe Tsai | ce07f39 | 2019-03-18 16:25:54 -0700 | [diff] [blame] | 24 | "github.com/golang/protobuf/v2/internal/detrand" |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 25 | "github.com/golang/protobuf/v2/protogen" |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 26 | "github.com/golang/protobuf/v2/reflect/protoreflect" |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 27 | ) |
| 28 | |
| 29 | func init() { |
Joe Tsai | ca46d8c | 2019-03-20 16:51:09 -0700 | [diff] [blame^] | 30 | // Determine repository root path. |
| 31 | out, err := exec.Command("git", "rev-parse", "--show-toplevel").CombinedOutput() |
| 32 | check(err) |
| 33 | repoRoot = strings.TrimSpace(string(out)) |
| 34 | |
| 35 | // Determine the module path. |
| 36 | cmd := exec.Command("go", "list", "-m", "-f", "{{.Path}}") |
| 37 | cmd.Dir = repoRoot |
| 38 | out, err = cmd.CombinedOutput() |
| 39 | check(err) |
| 40 | modulePath = strings.TrimSpace(string(out)) |
| 41 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 42 | // When the environment variable RUN_AS_PROTOC_PLUGIN is set, |
| 43 | // we skip running main and instead act as a protoc plugin. |
| 44 | // This allows the binary to pass itself to protoc. |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 45 | if plugins := os.Getenv("RUN_AS_PROTOC_PLUGIN"); plugins != "" { |
Joe Tsai | ce07f39 | 2019-03-18 16:25:54 -0700 | [diff] [blame] | 46 | // Disable deliberate output instability for generated files. |
| 47 | // This is reasonable since we fully control the output. |
| 48 | detrand.Disable() |
| 49 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 50 | protogen.Run(nil, func(gen *protogen.Plugin) error { |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 51 | for _, plugin := range strings.Split(plugins, ",") { |
| 52 | for _, file := range gen.Files { |
| 53 | if file.Generate { |
| 54 | switch plugin { |
| 55 | case "go": |
| 56 | gengo.GenerateFile(gen, file) |
Joe Tsai | ca46d8c | 2019-03-20 16:51:09 -0700 | [diff] [blame^] | 57 | generateFieldNumbers(gen, file) |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 58 | case "gogrpc": |
| 59 | gengogrpc.GenerateFile(gen, file) |
| 60 | } |
| 61 | } |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 62 | } |
| 63 | } |
| 64 | return nil |
| 65 | }) |
| 66 | os.Exit(0) |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | var ( |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 71 | run bool |
| 72 | protoRoot string |
| 73 | repoRoot string |
| 74 | modulePath string |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 75 | |
| 76 | generatedPreamble = []string{ |
| 77 | "// Copyright 2019 The Go Authors. All rights reserved.", |
| 78 | "// Use of this source code is governed by a BSD-style.", |
| 79 | "// license that can be found in the LICENSE file.", |
| 80 | "", |
| 81 | "// Code generated by generate-protos. DO NOT EDIT.", |
| 82 | "", |
| 83 | } |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 84 | ) |
| 85 | |
| 86 | func main() { |
| 87 | flag.BoolVar(&run, "execute", false, "Write generated files to destination.") |
| 88 | flag.StringVar(&protoRoot, "protoroot", os.Getenv("PROTOBUF_ROOT"), "The root of the protobuf source tree.") |
| 89 | flag.Parse() |
| 90 | if protoRoot == "" { |
| 91 | panic("protobuf source root is not set") |
| 92 | } |
| 93 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 94 | generateLocalProtos() |
| 95 | generateRemoteProtos() |
| 96 | } |
| 97 | |
| 98 | func generateLocalProtos() { |
| 99 | tmpDir, err := ioutil.TempDir(repoRoot, "tmp") |
| 100 | check(err) |
| 101 | defer os.RemoveAll(tmpDir) |
| 102 | |
| 103 | // Generate all local proto files (except version-locked files). |
| 104 | dirs := []struct { |
| 105 | path string |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 106 | grpcPlugin bool |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 107 | annotateFor map[string]bool |
| 108 | }{ |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 109 | {path: "cmd/protoc-gen-go/testdata", annotateFor: map[string]bool{"annotations/annotations.proto": true}}, |
| 110 | {path: "cmd/protoc-gen-go-grpc/testdata", grpcPlugin: true}, |
| 111 | {path: "internal/testprotos"}, |
| 112 | {path: "encoding/testprotos"}, |
| 113 | {path: "reflect/protoregistry/testprotos"}, |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 114 | } |
| 115 | semVerRx := regexp.MustCompile(`v[0-9]+\.[0-9]+\.[0-9]+`) |
| 116 | for _, d := range dirs { |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 117 | subDirs := map[string]bool{} |
| 118 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 119 | dstDir := filepath.Join(tmpDir, filepath.FromSlash(d.path)) |
| 120 | check(os.MkdirAll(dstDir, 0775)) |
| 121 | |
| 122 | srcDir := filepath.Join(repoRoot, filepath.FromSlash(d.path)) |
| 123 | filepath.Walk(srcDir, func(srcPath string, _ os.FileInfo, _ error) error { |
| 124 | if !strings.HasSuffix(srcPath, ".proto") || semVerRx.MatchString(srcPath) { |
| 125 | return nil |
| 126 | } |
| 127 | relPath, err := filepath.Rel(srcDir, srcPath) |
| 128 | check(err) |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 129 | subDirs[filepath.Dir(relPath)] = true |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 130 | |
| 131 | // Emit a .meta file for certain files. |
| 132 | var opts string |
| 133 | if d.annotateFor[filepath.ToSlash(relPath)] { |
| 134 | opts = ",annotate_code" |
| 135 | } |
| 136 | |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 137 | // Determine which set of plugins to use. |
| 138 | plugins := "go" |
| 139 | if d.grpcPlugin { |
| 140 | plugins += ",gogrpc" |
| 141 | } |
| 142 | |
| 143 | protoc(plugins, "-I"+filepath.Join(protoRoot, "src"), "-I"+srcDir, "--go_out=paths=source_relative"+opts+":"+dstDir, relPath) |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 144 | return nil |
| 145 | }) |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 146 | |
| 147 | // For directories in testdata, generate a test that links in all |
| 148 | // generated packages to ensure that it builds and initializes properly. |
| 149 | // This is done because "go build ./..." does not build sub-packages |
| 150 | // under testdata. |
| 151 | if filepath.Base(d.path) == "testdata" { |
| 152 | var imports []string |
| 153 | for sd := range subDirs { |
| 154 | imports = append(imports, fmt.Sprintf("_ %q", path.Join(modulePath, d.path, filepath.ToSlash(sd)))) |
| 155 | } |
| 156 | sort.Strings(imports) |
| 157 | |
| 158 | s := strings.Join(append(generatedPreamble, []string{ |
| 159 | "package main", |
| 160 | "", |
| 161 | "import (" + strings.Join(imports, "\n") + ")", |
| 162 | }...), "\n") |
| 163 | b, err := format.Source([]byte(s)) |
| 164 | check(err) |
| 165 | check(ioutil.WriteFile(filepath.Join(tmpDir, filepath.FromSlash(d.path+"/gen_test.go")), b, 0664)) |
| 166 | } |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | syncOutput(repoRoot, tmpDir) |
| 170 | } |
| 171 | |
| 172 | func generateRemoteProtos() { |
| 173 | tmpDir, err := ioutil.TempDir(repoRoot, "tmp") |
| 174 | check(err) |
| 175 | defer os.RemoveAll(tmpDir) |
| 176 | |
| 177 | // Generate all remote proto files. |
| 178 | files := []struct{ prefix, path string }{ |
| 179 | {"", "conformance/conformance.proto"}, |
| 180 | {"src", "google/protobuf/any.proto"}, |
| 181 | {"src", "google/protobuf/api.proto"}, |
| 182 | {"src", "google/protobuf/descriptor.proto"}, |
| 183 | {"src", "google/protobuf/duration.proto"}, |
| 184 | {"src", "google/protobuf/empty.proto"}, |
| 185 | {"src", "google/protobuf/field_mask.proto"}, |
| 186 | {"src", "google/protobuf/source_context.proto"}, |
| 187 | {"src", "google/protobuf/struct.proto"}, |
| 188 | {"src", "google/protobuf/timestamp.proto"}, |
| 189 | {"src", "google/protobuf/type.proto"}, |
| 190 | {"src", "google/protobuf/wrappers.proto"}, |
| 191 | {"src", "google/protobuf/compiler/plugin.proto"}, |
| 192 | } |
| 193 | for _, f := range files { |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 194 | protoc("go", "-I"+filepath.Join(protoRoot, f.prefix), "--go_out="+tmpDir, f.path) |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 195 | } |
| 196 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 197 | syncOutput(repoRoot, filepath.Join(tmpDir, modulePath)) |
| 198 | } |
| 199 | |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 200 | func protoc(plugins string, args ...string) { |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 201 | cmd := exec.Command("protoc", "--plugin=protoc-gen-go="+os.Args[0]) |
| 202 | cmd.Args = append(cmd.Args, args...) |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 203 | cmd.Env = append(os.Environ(), "RUN_AS_PROTOC_PLUGIN="+plugins) |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 204 | out, err := cmd.CombinedOutput() |
| 205 | if err != nil { |
| 206 | fmt.Printf("executing: %v\n%s\n", strings.Join(cmd.Args, " "), out) |
| 207 | } |
| 208 | check(err) |
| 209 | } |
| 210 | |
Joe Tsai | ca46d8c | 2019-03-20 16:51:09 -0700 | [diff] [blame^] | 211 | // generateFieldNumbers generates an internal package for descriptor.proto |
| 212 | // and well-known types. |
| 213 | func generateFieldNumbers(gen *protogen.Plugin, file *protogen.File) { |
| 214 | if file.Desc.Package() != "google.protobuf" { |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 215 | return |
| 216 | } |
| 217 | |
Joe Tsai | ca46d8c | 2019-03-20 16:51:09 -0700 | [diff] [blame^] | 218 | importPath := modulePath + "/internal/fieldnum" |
| 219 | base := strings.TrimSuffix(path.Base(file.Desc.Path()), ".proto") |
| 220 | g := gen.NewGeneratedFile(importPath+"/"+base+"_gen.go", protogen.GoImportPath(importPath)) |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 221 | for _, s := range generatedPreamble { |
| 222 | g.P(s) |
| 223 | } |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 224 | g.P("package ", path.Base(importPath)) |
| 225 | g.P("") |
| 226 | |
| 227 | var processMessages func([]*protogen.Message) |
| 228 | processMessages = func(messages []*protogen.Message) { |
| 229 | for _, message := range messages { |
| 230 | g.P("// Field numbers for ", message.Desc.FullName(), ".") |
| 231 | g.P("const (") |
| 232 | for _, field := range message.Fields { |
| 233 | fd := field.Desc |
| 234 | typeName := fd.Kind().String() |
| 235 | switch fd.Kind() { |
| 236 | case protoreflect.EnumKind: |
| 237 | typeName = string(fd.EnumType().FullName()) |
| 238 | case protoreflect.MessageKind, protoreflect.GroupKind: |
| 239 | typeName = string(fd.MessageType().FullName()) |
| 240 | } |
| 241 | g.P(message.GoIdent.GoName, "_", field.GoName, "=", fd.Number(), "// ", fd.Cardinality(), " ", typeName) |
| 242 | } |
| 243 | g.P(")") |
| 244 | processMessages(message.Messages) |
| 245 | } |
| 246 | } |
| 247 | processMessages(file.Messages) |
| 248 | } |
| 249 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 250 | func syncOutput(dstDir, srcDir string) { |
| 251 | filepath.Walk(srcDir, func(srcPath string, _ os.FileInfo, _ error) error { |
| 252 | if !strings.HasSuffix(srcPath, ".go") && !strings.HasSuffix(srcPath, ".meta") { |
| 253 | return nil |
| 254 | } |
| 255 | relPath, err := filepath.Rel(srcDir, srcPath) |
| 256 | check(err) |
| 257 | dstPath := filepath.Join(dstDir, relPath) |
| 258 | |
| 259 | if run { |
| 260 | fmt.Println("#", relPath) |
| 261 | b, err := ioutil.ReadFile(srcPath) |
| 262 | check(err) |
| 263 | check(os.MkdirAll(filepath.Dir(dstPath), 0775)) |
| 264 | check(ioutil.WriteFile(dstPath, b, 0664)) |
| 265 | } else { |
| 266 | cmd := exec.Command("diff", dstPath, srcPath, "-N", "-u") |
| 267 | cmd.Stdout = os.Stdout |
| 268 | cmd.Run() |
| 269 | } |
| 270 | return nil |
| 271 | }) |
| 272 | } |
| 273 | |
| 274 | func check(err error) { |
| 275 | if err != nil { |
| 276 | panic(err) |
| 277 | } |
| 278 | } |