blob: 00fe0d47e4e941c557c42c7341fcc03bbae6cb32 [file] [log] [blame]
Joe Tsai19058432019-02-27 21:46:29 -08001// 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
7package main
8
9import (
10 "flag"
11 "fmt"
Joe Tsaid56458e2019-03-01 18:11:42 -080012 "go/format"
Joe Tsai19058432019-02-27 21:46:29 -080013 "io/ioutil"
14 "os"
15 "os/exec"
Joe Tsai1af1de02019-03-01 16:12:32 -080016 "path"
Joe Tsai19058432019-02-27 21:46:29 -080017 "path/filepath"
18 "regexp"
Joe Tsaid56458e2019-03-01 18:11:42 -080019 "sort"
Joe Tsai19058432019-02-27 21:46:29 -080020 "strings"
21
Damien Neile89e6242019-05-13 23:55:40 -070022 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 Neil5c5b5312019-05-14 12:44:37 -070024 "google.golang.org/protobuf/compiler/protogen"
Damien Neile89e6242019-05-13 23:55:40 -070025 "google.golang.org/protobuf/internal/detrand"
Damien Neile89e6242019-05-13 23:55:40 -070026 "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsai19058432019-02-27 21:46:29 -080027)
28
29func init() {
Joe Tsaica46d8c2019-03-20 16:51:09 -070030 // 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 Tsai19058432019-02-27 21:46:29 -080042 // 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 Tsaid56458e2019-03-01 18:11:42 -080045 if plugins := os.Getenv("RUN_AS_PROTOC_PLUGIN"); plugins != "" {
Joe Tsaice07f392019-03-18 16:25:54 -070046 // Disable deliberate output instability for generated files.
47 // This is reasonable since we fully control the output.
48 detrand.Disable()
49
Joe Tsai19058432019-02-27 21:46:29 -080050 protogen.Run(nil, func(gen *protogen.Plugin) error {
Joe Tsaid56458e2019-03-01 18:11:42 -080051 for _, plugin := range strings.Split(plugins, ",") {
52 for _, file := range gen.Files {
53 if file.Generate {
54 switch plugin {
55 case "go":
Joe Tsaibab3d402019-07-10 00:15:35 -070056 gengo.GenerateVersionMarkers = false
Joe Tsaid56458e2019-03-01 18:11:42 -080057 gengo.GenerateFile(gen, file)
Joe Tsaica46d8c2019-03-20 16:51:09 -070058 generateFieldNumbers(gen, file)
Joe Tsaid56458e2019-03-01 18:11:42 -080059 case "gogrpc":
60 gengogrpc.GenerateFile(gen, file)
61 }
62 }
Joe Tsai19058432019-02-27 21:46:29 -080063 }
64 }
65 return nil
66 })
67 os.Exit(0)
68 }
69}
70
71var (
Joe Tsaid56458e2019-03-01 18:11:42 -080072 run bool
73 protoRoot string
74 repoRoot string
75 modulePath string
Joe Tsai1af1de02019-03-01 16:12:32 -080076
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 Tsai19058432019-02-27 21:46:29 -080085)
86
87func 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 Tsai19058432019-02-27 21:46:29 -080095 generateLocalProtos()
96 generateRemoteProtos()
97}
98
99func 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 Tsaid56458e2019-03-01 18:11:42 -0800107 grpcPlugin bool
Joe Tsai19058432019-02-27 21:46:29 -0800108 annotateFor map[string]bool
Damien Neil5b6d0472019-06-14 11:54:07 -0700109 exclude map[string]bool
Joe Tsai19058432019-02-27 21:46:29 -0800110 }{
Damien Neil3c5fb5f2020-02-04 15:03:30 -0800111 {path: "cmd/protoc-gen-go/testdata", annotateFor: map[string]bool{
112 "cmd/protoc-gen-go/testdata/annotations/annotations.proto": true},
113 },
Joe Tsaid56458e2019-03-01 18:11:42 -0800114 {path: "cmd/protoc-gen-go-grpc/testdata", grpcPlugin: true},
Damien Neil3c5fb5f2020-02-04 15:03:30 -0800115 {path: "internal/testprotos", exclude: map[string]bool{
116 "internal/testprotos/irregular/irregular.proto": true,
117 }},
Joe Tsai19058432019-02-27 21:46:29 -0800118 }
Joe Tsai55f18252020-01-11 00:25:01 -0800119 excludeRx := regexp.MustCompile(`legacy/proto[23]_[0-9]{8}_[0-9a-f]{8}/`)
Joe Tsai19058432019-02-27 21:46:29 -0800120 for _, d := range dirs {
Joe Tsaid56458e2019-03-01 18:11:42 -0800121 subDirs := map[string]bool{}
122
Damien Neil3c5fb5f2020-02-04 15:03:30 -0800123 dstDir := tmpDir
Joe Tsai19058432019-02-27 21:46:29 -0800124 check(os.MkdirAll(dstDir, 0775))
125
126 srcDir := filepath.Join(repoRoot, filepath.FromSlash(d.path))
127 filepath.Walk(srcDir, func(srcPath string, _ os.FileInfo, _ error) error {
Joe Tsai55f18252020-01-11 00:25:01 -0800128 if !strings.HasSuffix(srcPath, ".proto") || excludeRx.MatchString(srcPath) {
Joe Tsai19058432019-02-27 21:46:29 -0800129 return nil
130 }
Damien Neil3c5fb5f2020-02-04 15:03:30 -0800131 relPath, err := filepath.Rel(repoRoot, srcPath)
Joe Tsai19058432019-02-27 21:46:29 -0800132 check(err)
Damien Neil3c5fb5f2020-02-04 15:03:30 -0800133
134 srcRelPath, err := filepath.Rel(srcDir, srcPath)
135 check(err)
136 subDirs[filepath.Dir(srcRelPath)] = true
Joe Tsai19058432019-02-27 21:46:29 -0800137
Damien Neil5b6d0472019-06-14 11:54:07 -0700138 if d.exclude[filepath.ToSlash(relPath)] {
139 return nil
140 }
141
Joe Tsai19058432019-02-27 21:46:29 -0800142 // Emit a .meta file for certain files.
143 var opts string
144 if d.annotateFor[filepath.ToSlash(relPath)] {
145 opts = ",annotate_code"
146 }
147
Joe Tsaid56458e2019-03-01 18:11:42 -0800148 // Determine which set of plugins to use.
149 plugins := "go"
150 if d.grpcPlugin {
151 plugins += ",gogrpc"
152 }
153
Damien Neil3c5fb5f2020-02-04 15:03:30 -0800154 protoc(plugins, "-I"+filepath.Join(protoRoot, "src"), "-I"+repoRoot, "--go_out=paths=source_relative"+opts+":"+dstDir, relPath)
Joe Tsai19058432019-02-27 21:46:29 -0800155 return nil
156 })
Joe Tsaid56458e2019-03-01 18:11:42 -0800157
158 // For directories in testdata, generate a test that links in all
159 // generated packages to ensure that it builds and initializes properly.
160 // This is done because "go build ./..." does not build sub-packages
161 // under testdata.
162 if filepath.Base(d.path) == "testdata" {
163 var imports []string
164 for sd := range subDirs {
165 imports = append(imports, fmt.Sprintf("_ %q", path.Join(modulePath, d.path, filepath.ToSlash(sd))))
166 }
167 sort.Strings(imports)
168
169 s := strings.Join(append(generatedPreamble, []string{
170 "package main",
171 "",
172 "import (" + strings.Join(imports, "\n") + ")",
173 }...), "\n")
174 b, err := format.Source([]byte(s))
175 check(err)
176 check(ioutil.WriteFile(filepath.Join(tmpDir, filepath.FromSlash(d.path+"/gen_test.go")), b, 0664))
177 }
Joe Tsai19058432019-02-27 21:46:29 -0800178 }
179
180 syncOutput(repoRoot, tmpDir)
181}
182
183func generateRemoteProtos() {
184 tmpDir, err := ioutil.TempDir(repoRoot, "tmp")
185 check(err)
186 defer os.RemoveAll(tmpDir)
187
188 // Generate all remote proto files.
189 files := []struct{ prefix, path string }{
190 {"", "conformance/conformance.proto"},
Damien Neila80229e2019-06-20 12:53:48 -0700191 {"benchmarks", "benchmarks.proto"},
192 {"benchmarks", "datasets/google_message1/proto2/benchmark_message1_proto2.proto"},
193 {"benchmarks", "datasets/google_message1/proto3/benchmark_message1_proto3.proto"},
194 {"benchmarks", "datasets/google_message2/benchmark_message2.proto"},
195 {"benchmarks", "datasets/google_message3/benchmark_message3.proto"},
196 {"benchmarks", "datasets/google_message3/benchmark_message3_1.proto"},
197 {"benchmarks", "datasets/google_message3/benchmark_message3_2.proto"},
198 {"benchmarks", "datasets/google_message3/benchmark_message3_3.proto"},
199 {"benchmarks", "datasets/google_message3/benchmark_message3_4.proto"},
200 {"benchmarks", "datasets/google_message3/benchmark_message3_5.proto"},
201 {"benchmarks", "datasets/google_message3/benchmark_message3_6.proto"},
202 {"benchmarks", "datasets/google_message3/benchmark_message3_7.proto"},
203 {"benchmarks", "datasets/google_message3/benchmark_message3_8.proto"},
204 {"benchmarks", "datasets/google_message4/benchmark_message4.proto"},
205 {"benchmarks", "datasets/google_message4/benchmark_message4_1.proto"},
206 {"benchmarks", "datasets/google_message4/benchmark_message4_2.proto"},
207 {"benchmarks", "datasets/google_message4/benchmark_message4_3.proto"},
Joe Tsai19058432019-02-27 21:46:29 -0800208 {"src", "google/protobuf/any.proto"},
209 {"src", "google/protobuf/api.proto"},
Damien Neila80229e2019-06-20 12:53:48 -0700210 {"src", "google/protobuf/compiler/plugin.proto"},
Joe Tsai19058432019-02-27 21:46:29 -0800211 {"src", "google/protobuf/descriptor.proto"},
212 {"src", "google/protobuf/duration.proto"},
213 {"src", "google/protobuf/empty.proto"},
214 {"src", "google/protobuf/field_mask.proto"},
215 {"src", "google/protobuf/source_context.proto"},
216 {"src", "google/protobuf/struct.proto"},
Herbie Ongd64dceb2019-04-25 01:19:57 -0700217 {"src", "google/protobuf/test_messages_proto2.proto"},
218 {"src", "google/protobuf/test_messages_proto3.proto"},
Joe Tsai19058432019-02-27 21:46:29 -0800219 {"src", "google/protobuf/timestamp.proto"},
220 {"src", "google/protobuf/type.proto"},
221 {"src", "google/protobuf/wrappers.proto"},
Joe Tsai19058432019-02-27 21:46:29 -0800222 }
223 for _, f := range files {
Joe Tsaid56458e2019-03-01 18:11:42 -0800224 protoc("go", "-I"+filepath.Join(protoRoot, f.prefix), "--go_out="+tmpDir, f.path)
Joe Tsai19058432019-02-27 21:46:29 -0800225 }
226
Joe Tsai19058432019-02-27 21:46:29 -0800227 syncOutput(repoRoot, filepath.Join(tmpDir, modulePath))
228}
229
Joe Tsaid56458e2019-03-01 18:11:42 -0800230func protoc(plugins string, args ...string) {
Joe Tsai19058432019-02-27 21:46:29 -0800231 cmd := exec.Command("protoc", "--plugin=protoc-gen-go="+os.Args[0])
232 cmd.Args = append(cmd.Args, args...)
Joe Tsaid56458e2019-03-01 18:11:42 -0800233 cmd.Env = append(os.Environ(), "RUN_AS_PROTOC_PLUGIN="+plugins)
Joe Tsai19058432019-02-27 21:46:29 -0800234 out, err := cmd.CombinedOutput()
235 if err != nil {
236 fmt.Printf("executing: %v\n%s\n", strings.Join(cmd.Args, " "), out)
237 }
238 check(err)
239}
240
Joe Tsaica46d8c2019-03-20 16:51:09 -0700241// generateFieldNumbers generates an internal package for descriptor.proto
242// and well-known types.
243func generateFieldNumbers(gen *protogen.Plugin, file *protogen.File) {
244 if file.Desc.Package() != "google.protobuf" {
Joe Tsai1af1de02019-03-01 16:12:32 -0800245 return
246 }
247
Joe Tsaica46d8c2019-03-20 16:51:09 -0700248 importPath := modulePath + "/internal/fieldnum"
249 base := strings.TrimSuffix(path.Base(file.Desc.Path()), ".proto")
250 g := gen.NewGeneratedFile(importPath+"/"+base+"_gen.go", protogen.GoImportPath(importPath))
Joe Tsai1af1de02019-03-01 16:12:32 -0800251 for _, s := range generatedPreamble {
252 g.P(s)
253 }
Joe Tsai1af1de02019-03-01 16:12:32 -0800254 g.P("package ", path.Base(importPath))
255 g.P("")
256
257 var processMessages func([]*protogen.Message)
258 processMessages = func(messages []*protogen.Message) {
259 for _, message := range messages {
260 g.P("// Field numbers for ", message.Desc.FullName(), ".")
261 g.P("const (")
262 for _, field := range message.Fields {
263 fd := field.Desc
264 typeName := fd.Kind().String()
265 switch fd.Kind() {
266 case protoreflect.EnumKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700267 typeName = string(fd.Enum().FullName())
Joe Tsai1af1de02019-03-01 16:12:32 -0800268 case protoreflect.MessageKind, protoreflect.GroupKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700269 typeName = string(fd.Message().FullName())
Joe Tsai1af1de02019-03-01 16:12:32 -0800270 }
271 g.P(message.GoIdent.GoName, "_", field.GoName, "=", fd.Number(), "// ", fd.Cardinality(), " ", typeName)
272 }
273 g.P(")")
274 processMessages(message.Messages)
275 }
276 }
277 processMessages(file.Messages)
278}
279
Joe Tsai19058432019-02-27 21:46:29 -0800280func syncOutput(dstDir, srcDir string) {
281 filepath.Walk(srcDir, func(srcPath string, _ os.FileInfo, _ error) error {
282 if !strings.HasSuffix(srcPath, ".go") && !strings.HasSuffix(srcPath, ".meta") {
283 return nil
284 }
285 relPath, err := filepath.Rel(srcDir, srcPath)
286 check(err)
287 dstPath := filepath.Join(dstDir, relPath)
288
289 if run {
290 fmt.Println("#", relPath)
291 b, err := ioutil.ReadFile(srcPath)
292 check(err)
293 check(os.MkdirAll(filepath.Dir(dstPath), 0775))
294 check(ioutil.WriteFile(dstPath, b, 0664))
295 } else {
296 cmd := exec.Command("diff", dstPath, srcPath, "-N", "-u")
297 cmd.Stdout = os.Stdout
298 cmd.Run()
299 }
300 return nil
301 })
302}
303
304func check(err error) {
305 if err != nil {
306 panic(err)
307 }
308}