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 | |
Damien Neil | e89e624 | 2019-05-13 23:55:40 -0700 | [diff] [blame] | 22 | gengogrpc "google.golang.org/protobuf/cmd/protoc-gen-go-grpc/internal_gengogrpc" |
| 23 | gengo "google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo" |
Damien Neil | 5c5b531 | 2019-05-14 12:44:37 -0700 | [diff] [blame] | 24 | "google.golang.org/protobuf/compiler/protogen" |
Damien Neil | e89e624 | 2019-05-13 23:55:40 -0700 | [diff] [blame] | 25 | "google.golang.org/protobuf/internal/detrand" |
Damien Neil | e89e624 | 2019-05-13 23:55:40 -0700 | [diff] [blame] | 26 | "google.golang.org/protobuf/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": |
Joe Tsai | bab3d40 | 2019-07-10 00:15:35 -0700 | [diff] [blame^] | 56 | gengo.GenerateVersionMarkers = false |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 57 | gengo.GenerateFile(gen, file) |
Joe Tsai | ca46d8c | 2019-03-20 16:51:09 -0700 | [diff] [blame] | 58 | generateFieldNumbers(gen, file) |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 59 | case "gogrpc": |
| 60 | gengogrpc.GenerateFile(gen, file) |
| 61 | } |
| 62 | } |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 63 | } |
| 64 | } |
| 65 | return nil |
| 66 | }) |
| 67 | os.Exit(0) |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | var ( |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 72 | run bool |
| 73 | protoRoot string |
| 74 | repoRoot string |
| 75 | modulePath string |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 76 | |
| 77 | generatedPreamble = []string{ |
| 78 | "// Copyright 2019 The Go Authors. All rights reserved.", |
| 79 | "// Use of this source code is governed by a BSD-style.", |
| 80 | "// license that can be found in the LICENSE file.", |
| 81 | "", |
| 82 | "// Code generated by generate-protos. DO NOT EDIT.", |
| 83 | "", |
| 84 | } |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 85 | ) |
| 86 | |
| 87 | func main() { |
| 88 | flag.BoolVar(&run, "execute", false, "Write generated files to destination.") |
| 89 | flag.StringVar(&protoRoot, "protoroot", os.Getenv("PROTOBUF_ROOT"), "The root of the protobuf source tree.") |
| 90 | flag.Parse() |
| 91 | if protoRoot == "" { |
| 92 | panic("protobuf source root is not set") |
| 93 | } |
| 94 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 95 | generateLocalProtos() |
| 96 | generateRemoteProtos() |
| 97 | } |
| 98 | |
| 99 | func generateLocalProtos() { |
| 100 | tmpDir, err := ioutil.TempDir(repoRoot, "tmp") |
| 101 | check(err) |
| 102 | defer os.RemoveAll(tmpDir) |
| 103 | |
| 104 | // Generate all local proto files (except version-locked files). |
| 105 | dirs := []struct { |
| 106 | path string |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 107 | grpcPlugin bool |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 108 | annotateFor map[string]bool |
Damien Neil | 5b6d047 | 2019-06-14 11:54:07 -0700 | [diff] [blame] | 109 | exclude map[string]bool |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 110 | }{ |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 111 | {path: "cmd/protoc-gen-go/testdata", annotateFor: map[string]bool{"annotations/annotations.proto": true}}, |
| 112 | {path: "cmd/protoc-gen-go-grpc/testdata", grpcPlugin: true}, |
Damien Neil | 5b6d047 | 2019-06-14 11:54:07 -0700 | [diff] [blame] | 113 | {path: "internal/testprotos", exclude: map[string]bool{"irregular/irregular.proto": true}}, |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 114 | {path: "encoding/testprotos"}, |
| 115 | {path: "reflect/protoregistry/testprotos"}, |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 116 | } |
| 117 | semVerRx := regexp.MustCompile(`v[0-9]+\.[0-9]+\.[0-9]+`) |
| 118 | for _, d := range dirs { |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 119 | subDirs := map[string]bool{} |
| 120 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 121 | dstDir := filepath.Join(tmpDir, filepath.FromSlash(d.path)) |
| 122 | check(os.MkdirAll(dstDir, 0775)) |
| 123 | |
| 124 | srcDir := filepath.Join(repoRoot, filepath.FromSlash(d.path)) |
| 125 | filepath.Walk(srcDir, func(srcPath string, _ os.FileInfo, _ error) error { |
| 126 | if !strings.HasSuffix(srcPath, ".proto") || semVerRx.MatchString(srcPath) { |
| 127 | return nil |
| 128 | } |
| 129 | relPath, err := filepath.Rel(srcDir, srcPath) |
| 130 | check(err) |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 131 | subDirs[filepath.Dir(relPath)] = true |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 132 | |
Damien Neil | 5b6d047 | 2019-06-14 11:54:07 -0700 | [diff] [blame] | 133 | if d.exclude[filepath.ToSlash(relPath)] { |
| 134 | return nil |
| 135 | } |
| 136 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 137 | // Emit a .meta file for certain files. |
| 138 | var opts string |
| 139 | if d.annotateFor[filepath.ToSlash(relPath)] { |
| 140 | opts = ",annotate_code" |
| 141 | } |
| 142 | |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 143 | // Determine which set of plugins to use. |
| 144 | plugins := "go" |
| 145 | if d.grpcPlugin { |
| 146 | plugins += ",gogrpc" |
| 147 | } |
| 148 | |
| 149 | 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] | 150 | return nil |
| 151 | }) |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 152 | |
| 153 | // For directories in testdata, generate a test that links in all |
| 154 | // generated packages to ensure that it builds and initializes properly. |
| 155 | // This is done because "go build ./..." does not build sub-packages |
| 156 | // under testdata. |
| 157 | if filepath.Base(d.path) == "testdata" { |
| 158 | var imports []string |
| 159 | for sd := range subDirs { |
| 160 | imports = append(imports, fmt.Sprintf("_ %q", path.Join(modulePath, d.path, filepath.ToSlash(sd)))) |
| 161 | } |
| 162 | sort.Strings(imports) |
| 163 | |
| 164 | s := strings.Join(append(generatedPreamble, []string{ |
| 165 | "package main", |
| 166 | "", |
| 167 | "import (" + strings.Join(imports, "\n") + ")", |
| 168 | }...), "\n") |
| 169 | b, err := format.Source([]byte(s)) |
| 170 | check(err) |
| 171 | check(ioutil.WriteFile(filepath.Join(tmpDir, filepath.FromSlash(d.path+"/gen_test.go")), b, 0664)) |
| 172 | } |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | syncOutput(repoRoot, tmpDir) |
| 176 | } |
| 177 | |
| 178 | func generateRemoteProtos() { |
| 179 | tmpDir, err := ioutil.TempDir(repoRoot, "tmp") |
| 180 | check(err) |
| 181 | defer os.RemoveAll(tmpDir) |
| 182 | |
| 183 | // Generate all remote proto files. |
| 184 | files := []struct{ prefix, path string }{ |
| 185 | {"", "conformance/conformance.proto"}, |
Damien Neil | a80229e | 2019-06-20 12:53:48 -0700 | [diff] [blame] | 186 | {"benchmarks", "benchmarks.proto"}, |
| 187 | {"benchmarks", "datasets/google_message1/proto2/benchmark_message1_proto2.proto"}, |
| 188 | {"benchmarks", "datasets/google_message1/proto3/benchmark_message1_proto3.proto"}, |
| 189 | {"benchmarks", "datasets/google_message2/benchmark_message2.proto"}, |
| 190 | {"benchmarks", "datasets/google_message3/benchmark_message3.proto"}, |
| 191 | {"benchmarks", "datasets/google_message3/benchmark_message3_1.proto"}, |
| 192 | {"benchmarks", "datasets/google_message3/benchmark_message3_2.proto"}, |
| 193 | {"benchmarks", "datasets/google_message3/benchmark_message3_3.proto"}, |
| 194 | {"benchmarks", "datasets/google_message3/benchmark_message3_4.proto"}, |
| 195 | {"benchmarks", "datasets/google_message3/benchmark_message3_5.proto"}, |
| 196 | {"benchmarks", "datasets/google_message3/benchmark_message3_6.proto"}, |
| 197 | {"benchmarks", "datasets/google_message3/benchmark_message3_7.proto"}, |
| 198 | {"benchmarks", "datasets/google_message3/benchmark_message3_8.proto"}, |
| 199 | {"benchmarks", "datasets/google_message4/benchmark_message4.proto"}, |
| 200 | {"benchmarks", "datasets/google_message4/benchmark_message4_1.proto"}, |
| 201 | {"benchmarks", "datasets/google_message4/benchmark_message4_2.proto"}, |
| 202 | {"benchmarks", "datasets/google_message4/benchmark_message4_3.proto"}, |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 203 | {"src", "google/protobuf/any.proto"}, |
| 204 | {"src", "google/protobuf/api.proto"}, |
Damien Neil | a80229e | 2019-06-20 12:53:48 -0700 | [diff] [blame] | 205 | {"src", "google/protobuf/compiler/plugin.proto"}, |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 206 | {"src", "google/protobuf/descriptor.proto"}, |
| 207 | {"src", "google/protobuf/duration.proto"}, |
| 208 | {"src", "google/protobuf/empty.proto"}, |
| 209 | {"src", "google/protobuf/field_mask.proto"}, |
| 210 | {"src", "google/protobuf/source_context.proto"}, |
| 211 | {"src", "google/protobuf/struct.proto"}, |
Herbie Ong | d64dceb | 2019-04-25 01:19:57 -0700 | [diff] [blame] | 212 | {"src", "google/protobuf/test_messages_proto2.proto"}, |
| 213 | {"src", "google/protobuf/test_messages_proto3.proto"}, |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 214 | {"src", "google/protobuf/timestamp.proto"}, |
| 215 | {"src", "google/protobuf/type.proto"}, |
| 216 | {"src", "google/protobuf/wrappers.proto"}, |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 217 | } |
| 218 | for _, f := range files { |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 219 | 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] | 220 | } |
| 221 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 222 | syncOutput(repoRoot, filepath.Join(tmpDir, modulePath)) |
| 223 | } |
| 224 | |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 225 | func protoc(plugins string, args ...string) { |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 226 | cmd := exec.Command("protoc", "--plugin=protoc-gen-go="+os.Args[0]) |
| 227 | cmd.Args = append(cmd.Args, args...) |
Joe Tsai | d56458e | 2019-03-01 18:11:42 -0800 | [diff] [blame] | 228 | cmd.Env = append(os.Environ(), "RUN_AS_PROTOC_PLUGIN="+plugins) |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 229 | out, err := cmd.CombinedOutput() |
| 230 | if err != nil { |
| 231 | fmt.Printf("executing: %v\n%s\n", strings.Join(cmd.Args, " "), out) |
| 232 | } |
| 233 | check(err) |
| 234 | } |
| 235 | |
Joe Tsai | ca46d8c | 2019-03-20 16:51:09 -0700 | [diff] [blame] | 236 | // generateFieldNumbers generates an internal package for descriptor.proto |
| 237 | // and well-known types. |
| 238 | func generateFieldNumbers(gen *protogen.Plugin, file *protogen.File) { |
| 239 | if file.Desc.Package() != "google.protobuf" { |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 240 | return |
| 241 | } |
| 242 | |
Joe Tsai | ca46d8c | 2019-03-20 16:51:09 -0700 | [diff] [blame] | 243 | importPath := modulePath + "/internal/fieldnum" |
| 244 | base := strings.TrimSuffix(path.Base(file.Desc.Path()), ".proto") |
| 245 | g := gen.NewGeneratedFile(importPath+"/"+base+"_gen.go", protogen.GoImportPath(importPath)) |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 246 | for _, s := range generatedPreamble { |
| 247 | g.P(s) |
| 248 | } |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 249 | g.P("package ", path.Base(importPath)) |
| 250 | g.P("") |
| 251 | |
| 252 | var processMessages func([]*protogen.Message) |
| 253 | processMessages = func(messages []*protogen.Message) { |
| 254 | for _, message := range messages { |
| 255 | g.P("// Field numbers for ", message.Desc.FullName(), ".") |
| 256 | g.P("const (") |
| 257 | for _, field := range message.Fields { |
| 258 | fd := field.Desc |
| 259 | typeName := fd.Kind().String() |
| 260 | switch fd.Kind() { |
| 261 | case protoreflect.EnumKind: |
Joe Tsai | d24bc72 | 2019-04-15 23:39:09 -0700 | [diff] [blame] | 262 | typeName = string(fd.Enum().FullName()) |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 263 | case protoreflect.MessageKind, protoreflect.GroupKind: |
Joe Tsai | d24bc72 | 2019-04-15 23:39:09 -0700 | [diff] [blame] | 264 | typeName = string(fd.Message().FullName()) |
Joe Tsai | 1af1de0 | 2019-03-01 16:12:32 -0800 | [diff] [blame] | 265 | } |
| 266 | g.P(message.GoIdent.GoName, "_", field.GoName, "=", fd.Number(), "// ", fd.Cardinality(), " ", typeName) |
| 267 | } |
| 268 | g.P(")") |
| 269 | processMessages(message.Messages) |
| 270 | } |
| 271 | } |
| 272 | processMessages(file.Messages) |
| 273 | } |
| 274 | |
Joe Tsai | 1905843 | 2019-02-27 21:46:29 -0800 | [diff] [blame] | 275 | func syncOutput(dstDir, srcDir string) { |
| 276 | filepath.Walk(srcDir, func(srcPath string, _ os.FileInfo, _ error) error { |
| 277 | if !strings.HasSuffix(srcPath, ".go") && !strings.HasSuffix(srcPath, ".meta") { |
| 278 | return nil |
| 279 | } |
| 280 | relPath, err := filepath.Rel(srcDir, srcPath) |
| 281 | check(err) |
| 282 | dstPath := filepath.Join(dstDir, relPath) |
| 283 | |
| 284 | if run { |
| 285 | fmt.Println("#", relPath) |
| 286 | b, err := ioutil.ReadFile(srcPath) |
| 287 | check(err) |
| 288 | check(os.MkdirAll(filepath.Dir(dstPath), 0775)) |
| 289 | check(ioutil.WriteFile(dstPath, b, 0664)) |
| 290 | } else { |
| 291 | cmd := exec.Command("diff", dstPath, srcPath, "-N", "-u") |
| 292 | cmd.Stdout = os.Stdout |
| 293 | cmd.Run() |
| 294 | } |
| 295 | return nil |
| 296 | }) |
| 297 | } |
| 298 | |
| 299 | func check(err error) { |
| 300 | if err != nil { |
| 301 | panic(err) |
| 302 | } |
| 303 | } |