123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- /*
- Copyright 2015 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- // go-to-protobuf generates a Protobuf IDL from a Go struct, respecting any
- // existing IDL tags on the Go struct.
- package protobuf
- import (
- "bytes"
- "fmt"
- "log"
- "os/exec"
- "path/filepath"
- "strings"
- "k8s.io/kubernetes/cmd/libs/go2idl/args"
- "k8s.io/kubernetes/cmd/libs/go2idl/generator"
- "k8s.io/kubernetes/cmd/libs/go2idl/namer"
- "k8s.io/kubernetes/cmd/libs/go2idl/parser"
- "k8s.io/kubernetes/cmd/libs/go2idl/types"
- flag "github.com/spf13/pflag"
- )
- type Generator struct {
- Common args.GeneratorArgs
- Packages string
- OutputBase string
- ProtoImport []string
- Conditional string
- Clean bool
- OnlyIDL bool
- KeepGogoproto bool
- SkipGeneratedRewrite bool
- DropEmbeddedFields string
- }
- func New() *Generator {
- sourceTree := args.DefaultSourceTree()
- common := args.GeneratorArgs{
- OutputBase: sourceTree,
- GoHeaderFilePath: filepath.Join(sourceTree, "k8s.io/kubernetes/hack/boilerplate/boilerplate.go.txt"),
- }
- defaultProtoImport := filepath.Join(sourceTree, "k8s.io", "kubernetes", "vendor", "github.com", "gogo", "protobuf", "protobuf")
- return &Generator{
- Common: common,
- OutputBase: sourceTree,
- ProtoImport: []string{defaultProtoImport},
- Packages: strings.Join([]string{
- `+k8s.io/kubernetes/pkg/util/intstr`,
- `+k8s.io/kubernetes/pkg/api/resource`,
- `+k8s.io/kubernetes/pkg/runtime`,
- `+k8s.io/kubernetes/pkg/watch/versioned`,
- `k8s.io/kubernetes/pkg/api/unversioned`,
- `k8s.io/kubernetes/pkg/api/v1`,
- `k8s.io/kubernetes/pkg/apis/policy/v1alpha1`,
- `k8s.io/kubernetes/pkg/apis/extensions/v1beta1`,
- `k8s.io/kubernetes/pkg/apis/autoscaling/v1`,
- `k8s.io/kubernetes/pkg/apis/authorization/v1beta1`,
- `k8s.io/kubernetes/pkg/apis/batch/v1`,
- `k8s.io/kubernetes/pkg/apis/batch/v2alpha1`,
- `k8s.io/kubernetes/pkg/apis/apps/v1alpha1`,
- `k8s.io/kubernetes/pkg/apis/authentication/v1beta1`,
- `k8s.io/kubernetes/pkg/apis/rbac/v1alpha1`,
- `k8s.io/kubernetes/federation/apis/federation/v1beta1`,
- `k8s.io/kubernetes/pkg/apis/certificates/v1alpha1`,
- `k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1`,
- }, ","),
- DropEmbeddedFields: "k8s.io/kubernetes/pkg/api/unversioned.TypeMeta",
- }
- }
- func (g *Generator) BindFlags(flag *flag.FlagSet) {
- flag.StringVarP(&g.Common.GoHeaderFilePath, "go-header-file", "h", g.Common.GoHeaderFilePath, "File containing boilerplate header text. The string YEAR will be replaced with the current 4-digit year.")
- flag.BoolVar(&g.Common.VerifyOnly, "verify-only", g.Common.VerifyOnly, "If true, only verify existing output, do not write anything.")
- flag.StringVarP(&g.Packages, "packages", "p", g.Packages, "comma-separated list of directories to get input types from. Directories prefixed with '-' are not generated, directories prefixed with '+' only create types with explicit IDL instructions.")
- flag.StringVarP(&g.OutputBase, "output-base", "o", g.OutputBase, "Output base; defaults to $GOPATH/src/")
- flag.StringSliceVar(&g.ProtoImport, "proto-import", g.ProtoImport, "The search path for the core protobuf .protos, required, defaults to GODEPS on path.")
- flag.StringVar(&g.Conditional, "conditional", g.Conditional, "An optional Golang build tag condition to add to the generated Go code")
- flag.BoolVar(&g.Clean, "clean", g.Clean, "If true, remove all generated files for the specified Packages.")
- flag.BoolVar(&g.OnlyIDL, "only-idl", g.OnlyIDL, "If true, only generate the IDL for each package.")
- flag.BoolVar(&g.KeepGogoproto, "keep-gogoproto", g.KeepGogoproto, "If true, the generated IDL will contain gogoprotobuf extensions which are normally removed")
- flag.BoolVar(&g.SkipGeneratedRewrite, "skip-generated-rewrite", g.SkipGeneratedRewrite, "If true, skip fixing up the generated.pb.go file (debugging only).")
- flag.StringVar(&g.DropEmbeddedFields, "drop-embedded-fields", g.DropEmbeddedFields, "Comma-delimited list of embedded Go types to omit from generated protobufs")
- }
- func Run(g *Generator) {
- if g.Common.VerifyOnly {
- g.OnlyIDL = true
- g.Clean = false
- }
- b := parser.New()
- b.AddBuildTags("proto")
- omitTypes := map[types.Name]struct{}{}
- for _, t := range strings.Split(g.DropEmbeddedFields, ",") {
- name := types.Name{}
- if i := strings.LastIndex(t, "."); i != -1 {
- name.Package, name.Name = t[:i], t[i+1:]
- } else {
- name.Name = t
- }
- if len(name.Name) == 0 {
- log.Fatalf("--drop-embedded-types requires names in the form of [GOPACKAGE.]TYPENAME: %v", t)
- }
- omitTypes[name] = struct{}{}
- }
- boilerplate, err := g.Common.LoadGoBoilerplate()
- if err != nil {
- log.Fatalf("Failed loading boilerplate: %v", err)
- }
- protobufNames := NewProtobufNamer()
- outputPackages := generator.Packages{}
- for _, d := range strings.Split(g.Packages, ",") {
- generateAllTypes, outputPackage := true, true
- switch {
- case strings.HasPrefix(d, "+"):
- d = d[1:]
- generateAllTypes = false
- case strings.HasPrefix(d, "-"):
- d = d[1:]
- outputPackage = false
- }
- if strings.Contains(d, "-") {
- log.Fatalf("Package names must be valid protobuf package identifiers, which allow only [a-z0-9_]: %s", d)
- }
- name := protoSafePackage(d)
- parts := strings.SplitN(d, "=", 2)
- if len(parts) > 1 {
- d = parts[0]
- name = parts[1]
- }
- p := newProtobufPackage(d, name, generateAllTypes, omitTypes)
- header := append([]byte{}, boilerplate...)
- header = append(header, p.HeaderText...)
- p.HeaderText = header
- protobufNames.Add(p)
- if outputPackage {
- outputPackages = append(outputPackages, p)
- }
- }
- if !g.Common.VerifyOnly {
- for _, p := range outputPackages {
- if err := p.(*protobufPackage).Clean(g.OutputBase); err != nil {
- log.Fatalf("Unable to clean package %s: %v", p.Name(), err)
- }
- }
- }
- if g.Clean {
- return
- }
- for _, p := range protobufNames.List() {
- if err := b.AddDir(p.Path()); err != nil {
- log.Fatalf("Unable to add directory %q: %v", p.Path(), err)
- }
- }
- c, err := generator.NewContext(
- b,
- namer.NameSystems{
- "public": namer.NewPublicNamer(3),
- "proto": protobufNames,
- },
- "public",
- )
- if err != nil {
- log.Fatalf("Failed making a context: %v", err)
- }
- c.Verify = g.Common.VerifyOnly
- c.FileTypes["protoidl"] = NewProtoFile()
- if err := protobufNames.AssignTypesToPackages(c); err != nil {
- log.Fatalf("Failed to identify Common types: %v", err)
- }
- if err := c.ExecutePackages(g.OutputBase, outputPackages); err != nil {
- log.Fatalf("Failed executing generator: %v", err)
- }
- if g.OnlyIDL {
- return
- }
- if _, err := exec.LookPath("protoc"); err != nil {
- log.Fatalf("Unable to find 'protoc': %v", err)
- }
- searchArgs := []string{"-I", ".", "-I", g.OutputBase}
- if len(g.ProtoImport) != 0 {
- for _, s := range g.ProtoImport {
- searchArgs = append(searchArgs, "-I", s)
- }
- }
- args := append(searchArgs, fmt.Sprintf("--gogo_out=%s", g.OutputBase))
- buf := &bytes.Buffer{}
- if len(g.Conditional) > 0 {
- fmt.Fprintf(buf, "// +build %s\n\n", g.Conditional)
- }
- buf.Write(boilerplate)
- for _, outputPackage := range outputPackages {
- p := outputPackage.(*protobufPackage)
- path := filepath.Join(g.OutputBase, p.ImportPath())
- outputPath := filepath.Join(g.OutputBase, p.OutputPath())
- // generate the gogoprotobuf protoc
- cmd := exec.Command("protoc", append(args, path)...)
- out, err := cmd.CombinedOutput()
- if len(out) > 0 {
- log.Printf(string(out))
- }
- if err != nil {
- log.Println(strings.Join(cmd.Args, " "))
- log.Fatalf("Unable to generate protoc on %s: %v", p.PackageName, err)
- }
- if g.SkipGeneratedRewrite {
- continue
- }
- // alter the generated protobuf file to remove the generated types (but leave the serializers) and rewrite the
- // package statement to match the desired package name
- if err := RewriteGeneratedGogoProtobufFile(outputPath, p.ExtractGeneratedType, p.OptionalTypeName, buf.Bytes()); err != nil {
- log.Fatalf("Unable to rewrite generated %s: %v", outputPath, err)
- }
- // sort imports
- cmd = exec.Command("goimports", "-w", outputPath)
- out, err = cmd.CombinedOutput()
- if len(out) > 0 {
- log.Printf(string(out))
- }
- if err != nil {
- log.Println(strings.Join(cmd.Args, " "))
- log.Fatalf("Unable to rewrite imports for %s: %v", p.PackageName, err)
- }
- // format and simplify the generated file
- cmd = exec.Command("gofmt", "-s", "-w", outputPath)
- out, err = cmd.CombinedOutput()
- if len(out) > 0 {
- log.Printf(string(out))
- }
- if err != nil {
- log.Println(strings.Join(cmd.Args, " "))
- log.Fatalf("Unable to apply gofmt for %s: %v", p.PackageName, err)
- }
- }
- if g.SkipGeneratedRewrite {
- return
- }
- if !g.KeepGogoproto {
- // generate, but do so without gogoprotobuf extensions
- for _, outputPackage := range outputPackages {
- p := outputPackage.(*protobufPackage)
- p.OmitGogo = true
- }
- if err := c.ExecutePackages(g.OutputBase, outputPackages); err != nil {
- log.Fatalf("Failed executing generator: %v", err)
- }
- }
- for _, outputPackage := range outputPackages {
- p := outputPackage.(*protobufPackage)
- if len(p.StructTags) == 0 {
- continue
- }
- pattern := filepath.Join(g.OutputBase, p.PackagePath, "*.go")
- files, err := filepath.Glob(pattern)
- if err != nil {
- log.Fatalf("Can't glob pattern %q: %v", pattern, err)
- }
- for _, s := range files {
- if strings.HasSuffix(s, "_test.go") {
- continue
- }
- if err := RewriteTypesWithProtobufStructTags(s, p.StructTags); err != nil {
- log.Fatalf("Unable to rewrite with struct tags %s: %v", s, err)
- }
- }
- }
- }
|