add prune and remove unused packages
This commit is contained in:
8
vendor/k8s.io/code-generator/cmd/client-gen/OWNERS
generated
vendored
8
vendor/k8s.io/code-generator/cmd/client-gen/OWNERS
generated
vendored
@@ -1,8 +0,0 @@
|
||||
approvers:
|
||||
- lavalamp
|
||||
- wojtek-t
|
||||
- caesarxuchao
|
||||
reviewers:
|
||||
- lavalamp
|
||||
- wojtek-t
|
||||
- caesarxuchao
|
4
vendor/k8s.io/code-generator/cmd/client-gen/README.md
generated
vendored
4
vendor/k8s.io/code-generator/cmd/client-gen/README.md
generated
vendored
@@ -1,4 +0,0 @@
|
||||
See [generating-clientset.md](https://git.k8s.io/community/contributors/devel/generating-clientset.md)
|
||||
|
||||
|
||||
[]()
|
116
vendor/k8s.io/code-generator/cmd/client-gen/args/gvpackages_test.go
generated
vendored
116
vendor/k8s.io/code-generator/cmd/client-gen/args/gvpackages_test.go
generated
vendored
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
package args
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/code-generator/cmd/client-gen/types"
|
||||
)
|
||||
|
||||
func TestGVPackageFlag(t *testing.T) {
|
||||
tests := []struct {
|
||||
args []string
|
||||
def []string
|
||||
importBasePath string
|
||||
expected map[types.GroupVersion]string
|
||||
expectedGroups []types.GroupVersions
|
||||
parseError string
|
||||
}{
|
||||
{
|
||||
args: []string{},
|
||||
expected: map[types.GroupVersion]string{},
|
||||
expectedGroups: []types.GroupVersions{},
|
||||
},
|
||||
{
|
||||
args: []string{"foo/bar/v1", "foo/bar/v2", "foo/bar/", "foo/v1"},
|
||||
expectedGroups: []types.GroupVersions{
|
||||
{PackageName: "bar", Group: types.Group("bar"), Versions: []types.PackageVersion{
|
||||
{Version: "v1", Package: "foo/bar/v1"},
|
||||
{Version: "v2", Package: "foo/bar/v2"},
|
||||
{Version: "", Package: "foo/bar"},
|
||||
}},
|
||||
{PackageName: "foo", Group: types.Group("foo"), Versions: []types.PackageVersion{
|
||||
{Version: "v1", Package: "foo/v1"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
args: []string{"foo/bar/v1", "foo/bar/v2", "foo/bar/", "foo/v1"},
|
||||
def: []string{"foo/bar/v1alpha1", "foo/v1"},
|
||||
expectedGroups: []types.GroupVersions{
|
||||
{PackageName: "bar", Group: types.Group("bar"), Versions: []types.PackageVersion{
|
||||
{Version: "v1", Package: "foo/bar/v1"},
|
||||
{Version: "v2", Package: "foo/bar/v2"},
|
||||
{Version: "", Package: "foo/bar"},
|
||||
}},
|
||||
{PackageName: "foo", Group: types.Group("foo"), Versions: []types.PackageVersion{
|
||||
{Version: "v1", Package: "foo/v1"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
args: []string{"api/v1", "api"},
|
||||
expectedGroups: []types.GroupVersions{
|
||||
{PackageName: "core", Group: types.Group("api"), Versions: []types.PackageVersion{
|
||||
{Version: "v1", Package: "core/v1"},
|
||||
{Version: "", Package: "core"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
args: []string{"foo/v1"},
|
||||
importBasePath: "k8s.io/api",
|
||||
expectedGroups: []types.GroupVersions{
|
||||
{PackageName: "foo", Group: types.Group("foo"), Versions: []types.PackageVersion{
|
||||
{Version: "v1", Package: "k8s.io/api/foo/v1"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
fs := pflag.NewFlagSet("testGVPackage", pflag.ContinueOnError)
|
||||
groups := []types.GroupVersions{}
|
||||
builder := NewGroupVersionsBuilder(&groups)
|
||||
fs.Var(NewGVPackagesValue(builder, test.def), "input", "usage")
|
||||
fs.Var(NewInputBasePathValue(builder, test.importBasePath), "input-base-path", "usage")
|
||||
|
||||
args := []string{}
|
||||
for _, a := range test.args {
|
||||
args = append(args, fmt.Sprintf("--input=%s", a))
|
||||
}
|
||||
|
||||
err := fs.Parse(args)
|
||||
if test.parseError != "" {
|
||||
if err == nil {
|
||||
t.Errorf("%d: expected error %q, got nil", i, test.parseError)
|
||||
} else if !strings.Contains(err.Error(), test.parseError) {
|
||||
t.Errorf("%d: expected error %q, got %q", i, test.parseError, err)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Errorf("%d: expected nil error, got %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, test.expectedGroups) {
|
||||
t.Errorf("%d: expected groups %+v, got groups %+v", i, test.expectedGroups, groups)
|
||||
}
|
||||
}
|
||||
}
|
148
vendor/k8s.io/code-generator/cmd/client-gen/generators/util/tags_test.go
generated
vendored
148
vendor/k8s.io/code-generator/cmd/client-gen/generators/util/tags_test.go
generated
vendored
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseTags(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
lines []string
|
||||
expectTags Tags
|
||||
expectError bool
|
||||
}{
|
||||
"genclient": {
|
||||
lines: []string{`+genclient`},
|
||||
expectTags: Tags{GenerateClient: true},
|
||||
},
|
||||
"genclient=true": {
|
||||
lines: []string{`+genclient=true`},
|
||||
expectError: true,
|
||||
},
|
||||
"nonNamespaced=true": {
|
||||
lines: []string{`+genclient=true`, `+nonNamespaced=true`},
|
||||
expectError: true,
|
||||
},
|
||||
"readonly=true": {
|
||||
lines: []string{`+genclient=true`, `+readonly=true`},
|
||||
expectError: true,
|
||||
},
|
||||
"genclient:nonNamespaced": {
|
||||
lines: []string{`+genclient`, `+genclient:nonNamespaced`},
|
||||
expectTags: Tags{GenerateClient: true, NonNamespaced: true},
|
||||
},
|
||||
"genclient:noVerbs": {
|
||||
lines: []string{`+genclient`, `+genclient:noVerbs`},
|
||||
expectTags: Tags{GenerateClient: true, NoVerbs: true},
|
||||
},
|
||||
"genclient:noStatus": {
|
||||
lines: []string{`+genclient`, `+genclient:noStatus`},
|
||||
expectTags: Tags{GenerateClient: true, NoStatus: true},
|
||||
},
|
||||
"genclient:onlyVerbs": {
|
||||
lines: []string{`+genclient`, `+genclient:onlyVerbs=create,delete`},
|
||||
expectTags: Tags{GenerateClient: true, SkipVerbs: []string{"update", "updateStatus", "deleteCollection", "get", "list", "watch", "patch"}},
|
||||
},
|
||||
"genclient:readonly": {
|
||||
lines: []string{`+genclient`, `+genclient:readonly`},
|
||||
expectTags: Tags{GenerateClient: true, SkipVerbs: []string{"create", "update", "updateStatus", "delete", "deleteCollection", "patch"}},
|
||||
},
|
||||
"genclient:conflict": {
|
||||
lines: []string{`+genclient`, `+genclient:onlyVerbs=create`, `+genclient:skipVerbs=create`},
|
||||
expectError: true,
|
||||
},
|
||||
"genclient:invalid": {
|
||||
lines: []string{`+genclient`, `+genclient:invalid`},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for key, c := range testCases {
|
||||
result, err := ParseClientGenTags(c.lines)
|
||||
if err != nil && !c.expectError {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !c.expectError && !reflect.DeepEqual(result, c.expectTags) {
|
||||
t.Errorf("[%s] expected %#v to be %#v", key, result, c.expectTags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTagsExtension(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
lines []string
|
||||
expectedExtensions []extension
|
||||
expectError bool
|
||||
}{
|
||||
"simplest extension": {
|
||||
lines: []string{`+genclient:method=Foo,verb=create`},
|
||||
expectedExtensions: []extension{{VerbName: "Foo", VerbType: "create"}},
|
||||
},
|
||||
"multiple extensions": {
|
||||
lines: []string{`+genclient:method=Foo,verb=create`, `+genclient:method=Bar,verb=get`},
|
||||
expectedExtensions: []extension{{VerbName: "Foo", VerbType: "create"}, {VerbName: "Bar", VerbType: "get"}},
|
||||
},
|
||||
"extension without verb": {
|
||||
lines: []string{`+genclient:method`},
|
||||
expectError: true,
|
||||
},
|
||||
"extension without verb type": {
|
||||
lines: []string{`+genclient:method=Foo`},
|
||||
expectError: true,
|
||||
},
|
||||
"sub-resource extension": {
|
||||
lines: []string{`+genclient:method=Foo,verb=create,subresource=bar`},
|
||||
expectedExtensions: []extension{{VerbName: "Foo", VerbType: "create", SubResourcePath: "bar"}},
|
||||
},
|
||||
"output type extension": {
|
||||
lines: []string{`+genclient:method=Foos,verb=list,result=Bars`},
|
||||
expectedExtensions: []extension{{VerbName: "Foos", VerbType: "list", ResultTypeOverride: "Bars"}},
|
||||
},
|
||||
"input type extension": {
|
||||
lines: []string{`+genclient:method=Foo,verb=update,input=Bar`},
|
||||
expectedExtensions: []extension{{VerbName: "Foo", VerbType: "update", InputTypeOverride: "Bar"}},
|
||||
},
|
||||
"unknown verb type extension": {
|
||||
lines: []string{`+genclient:method=Foo,verb=explode`},
|
||||
expectedExtensions: nil,
|
||||
expectError: true,
|
||||
},
|
||||
"invalid verb extension": {
|
||||
lines: []string{`+genclient:method=Foo,unknown=bar`},
|
||||
expectedExtensions: nil,
|
||||
expectError: true,
|
||||
},
|
||||
"empty verb extension subresource": {
|
||||
lines: []string{`+genclient:method=Foo,verb=get,subresource=`},
|
||||
expectedExtensions: nil,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for key, c := range testCases {
|
||||
result, err := ParseClientGenTags(c.lines)
|
||||
if err != nil && !c.expectError {
|
||||
t.Fatalf("[%s] unexpected error: %v", key, err)
|
||||
}
|
||||
if err != nil && c.expectError {
|
||||
t.Logf("[%s] got expected error: %+v", key, err)
|
||||
}
|
||||
if !c.expectError && !reflect.DeepEqual(result.Extensions, c.expectedExtensions) {
|
||||
t.Errorf("[%s] expected %#+v to be %#+v", key, result.Extensions, c.expectedExtensions)
|
||||
}
|
||||
}
|
||||
}
|
32
vendor/k8s.io/code-generator/cmd/client-gen/types/helpers_test.go
generated
vendored
32
vendor/k8s.io/code-generator/cmd/client-gen/types/helpers_test.go
generated
vendored
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVersionSort(t *testing.T) {
|
||||
unsortedVersions := []string{"v4beta1", "v2beta1", "v2alpha1", "v3", "v1"}
|
||||
expected := []string{"v2alpha1", "v2beta1", "v4beta1", "v1", "v3"}
|
||||
sort.Sort(sortableSliceOfVersions(unsortedVersions))
|
||||
if !reflect.DeepEqual(unsortedVersions, expected) {
|
||||
t.Errorf("expected %#v\ngot %#v", expected, unsortedVersions)
|
||||
}
|
||||
}
|
83
vendor/k8s.io/code-generator/cmd/conversion-gen/args/args.go
generated
vendored
83
vendor/k8s.io/code-generator/cmd/conversion-gen/args/args.go
generated
vendored
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
package args
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/gengo/args"
|
||||
)
|
||||
|
||||
// DefaultBasePeerDirs are the peer-dirs nearly everybody will use, i.e. those coming from
|
||||
// apimachinery.
|
||||
var DefaultBasePeerDirs = []string{
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||
"k8s.io/apimachinery/pkg/conversion",
|
||||
"k8s.io/apimachinery/pkg/runtime",
|
||||
}
|
||||
|
||||
// CustomArgs is used by the gengo framework to pass args specific to this generator.
|
||||
type CustomArgs struct {
|
||||
// Base peer dirs which nearly everybody will use, i.e. outside of Kubernetes core. Peer dirs
|
||||
// are declared to make the generator pick up manually written conversion funcs from external
|
||||
// packages.
|
||||
BasePeerDirs []string
|
||||
|
||||
// Custom peer dirs which are application specific. Peer dirs are declared to make the
|
||||
// generator pick up manually written conversion funcs from external packages.
|
||||
ExtraPeerDirs []string
|
||||
|
||||
// SkipUnsafe indicates whether to generate unsafe conversions to improve the efficiency
|
||||
// of these operations. The unsafe operation is a direct pointer assignment via unsafe
|
||||
// (within the allowed uses of unsafe) and is equivalent to a proposed Golang change to
|
||||
// allow structs that are identical to be assigned to each other.
|
||||
SkipUnsafe bool
|
||||
}
|
||||
|
||||
// NewDefaults returns default arguments for the generator.
|
||||
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
|
||||
genericArgs := args.Default().WithoutDefaultFlagParsing()
|
||||
customArgs := &CustomArgs{
|
||||
BasePeerDirs: DefaultBasePeerDirs,
|
||||
SkipUnsafe: false,
|
||||
}
|
||||
genericArgs.CustomArgs = customArgs
|
||||
genericArgs.OutputFileBaseName = "conversion_generated"
|
||||
return genericArgs, customArgs
|
||||
}
|
||||
|
||||
// AddFlags add the generator flags to the flag set.
|
||||
func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) {
|
||||
pflag.CommandLine.StringSliceVar(&ca.BasePeerDirs, "base-peer-dirs", ca.BasePeerDirs,
|
||||
"Comma-separated list of apimachinery import paths which are considered, after tag-specified peers, for conversions. Only change these if you have very good reasons.")
|
||||
pflag.CommandLine.StringSliceVar(&ca.ExtraPeerDirs, "extra-peer-dirs", ca.ExtraPeerDirs,
|
||||
"Application specific comma-separated list of import paths which are considered, after tag-specified peers and base-peer-dirs, for conversions.")
|
||||
pflag.CommandLine.BoolVar(&ca.SkipUnsafe, "skip-unsafe", ca.SkipUnsafe,
|
||||
"If true, will not generate code using unsafe pointer conversions; resulting code may be slower.")
|
||||
}
|
||||
|
||||
// Validate checks the given arguments.
|
||||
func Validate(genericArgs *args.GeneratorArgs) error {
|
||||
_ = genericArgs.CustomArgs.(*CustomArgs)
|
||||
|
||||
if len(genericArgs.OutputFileBaseName) == 0 {
|
||||
return fmt.Errorf("output file base name cannot be empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
984
vendor/k8s.io/code-generator/cmd/conversion-gen/generators/conversion.go
generated
vendored
984
vendor/k8s.io/code-generator/cmd/conversion-gen/generators/conversion.go
generated
vendored
@@ -1,984 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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.
|
||||
*/
|
||||
|
||||
package generators
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
conversionargs "k8s.io/code-generator/cmd/conversion-gen/args"
|
||||
)
|
||||
|
||||
// These are the comment tags that carry parameters for conversion generation.
|
||||
const (
|
||||
// e.g., "+k8s:conversion-gen=<peer-pkg>" in doc.go, where <peer-pkg> is the
|
||||
// import path of the package the peer types are defined in.
|
||||
// e.g., "+k8s:conversion-gen=false" in a type's comment will let
|
||||
// conversion-gen skip that type.
|
||||
tagName = "k8s:conversion-gen"
|
||||
// e.g., "+k8s:conversion-gen-external-types=<type-pkg>" in doc.go, where
|
||||
// <type-pkg> is the relative path to the package the types are defined in.
|
||||
externalTypesTagName = "k8s:conversion-gen-external-types"
|
||||
)
|
||||
|
||||
func extractTag(comments []string) []string {
|
||||
return types.ExtractCommentTags("+", comments)[tagName]
|
||||
}
|
||||
|
||||
func extractExternalTypesTag(comments []string) []string {
|
||||
return types.ExtractCommentTags("+", comments)[externalTypesTagName]
|
||||
}
|
||||
|
||||
func isCopyOnly(comments []string) bool {
|
||||
values := types.ExtractCommentTags("+", comments)["k8s:conversion-fn"]
|
||||
return len(values) == 1 && values[0] == "copy-only"
|
||||
}
|
||||
|
||||
func isDrop(comments []string) bool {
|
||||
values := types.ExtractCommentTags("+", comments)["k8s:conversion-fn"]
|
||||
return len(values) == 1 && values[0] == "drop"
|
||||
}
|
||||
|
||||
// TODO: This is created only to reduce number of changes in a single PR.
|
||||
// Remove it and use PublicNamer instead.
|
||||
func conversionNamer() *namer.NameStrategy {
|
||||
return &namer.NameStrategy{
|
||||
Join: func(pre string, in []string, post string) string {
|
||||
return strings.Join(in, "_")
|
||||
},
|
||||
PrependPackageNames: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultFnNamer() *namer.NameStrategy {
|
||||
return &namer.NameStrategy{
|
||||
Prefix: "SetDefaults_",
|
||||
Join: func(pre string, in []string, post string) string {
|
||||
return pre + strings.Join(in, "_") + post
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NameSystems returns the name system used by the generators in this package.
|
||||
func NameSystems() namer.NameSystems {
|
||||
return namer.NameSystems{
|
||||
"public": conversionNamer(),
|
||||
"raw": namer.NewRawNamer("", nil),
|
||||
"defaultfn": defaultFnNamer(),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultNameSystem returns the default name system for ordering the types to be
|
||||
// processed by the generators in this package.
|
||||
func DefaultNameSystem() string {
|
||||
return "public"
|
||||
}
|
||||
|
||||
func getPeerTypeFor(context *generator.Context, t *types.Type, potenialPeerPkgs []string) *types.Type {
|
||||
for _, ppp := range potenialPeerPkgs {
|
||||
p := context.Universe.Package(ppp)
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
if p.Has(t.Name.Name) {
|
||||
return p.Type(t.Name.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type conversionPair struct {
|
||||
inType *types.Type
|
||||
outType *types.Type
|
||||
}
|
||||
|
||||
// All of the types in conversions map are of type "DeclarationOf" with
|
||||
// the underlying type being "Func".
|
||||
type conversionFuncMap map[conversionPair]*types.Type
|
||||
|
||||
// Returns all manually-defined conversion functions in the package.
|
||||
func getManualConversionFunctions(context *generator.Context, pkg *types.Package, manualMap conversionFuncMap) {
|
||||
if pkg == nil {
|
||||
klog.Warningf("Skipping nil package passed to getManualConversionFunctions")
|
||||
return
|
||||
}
|
||||
klog.V(5).Infof("Scanning for conversion functions in %v", pkg.Name)
|
||||
|
||||
scopeName := types.Ref(conversionPackagePath, "Scope").Name
|
||||
errorName := types.Ref("", "error").Name
|
||||
buffer := &bytes.Buffer{}
|
||||
sw := generator.NewSnippetWriter(buffer, context, "$", "$")
|
||||
|
||||
for _, f := range pkg.Functions {
|
||||
if f.Underlying == nil || f.Underlying.Kind != types.Func {
|
||||
klog.Errorf("Malformed function: %#v", f)
|
||||
continue
|
||||
}
|
||||
if f.Underlying.Signature == nil {
|
||||
klog.Errorf("Function without signature: %#v", f)
|
||||
continue
|
||||
}
|
||||
klog.V(8).Infof("Considering function %s", f.Name)
|
||||
signature := f.Underlying.Signature
|
||||
// Check whether the function is conversion function.
|
||||
// Note that all of them have signature:
|
||||
// func Convert_inType_To_outType(inType, outType, conversion.Scope) error
|
||||
if signature.Receiver != nil {
|
||||
klog.V(8).Infof("%s has a receiver", f.Name)
|
||||
continue
|
||||
}
|
||||
if len(signature.Parameters) != 3 || signature.Parameters[2].Name != scopeName {
|
||||
klog.V(8).Infof("%s has wrong parameters", f.Name)
|
||||
continue
|
||||
}
|
||||
if len(signature.Results) != 1 || signature.Results[0].Name != errorName {
|
||||
klog.V(8).Infof("%s has wrong results", f.Name)
|
||||
continue
|
||||
}
|
||||
inType := signature.Parameters[0]
|
||||
outType := signature.Parameters[1]
|
||||
if inType.Kind != types.Pointer || outType.Kind != types.Pointer {
|
||||
klog.V(8).Infof("%s has wrong parameter types", f.Name)
|
||||
continue
|
||||
}
|
||||
// Now check if the name satisfies the convention.
|
||||
// TODO: This should call the Namer directly.
|
||||
args := argsFromType(inType.Elem, outType.Elem)
|
||||
sw.Do("Convert_$.inType|public$_To_$.outType|public$", args)
|
||||
if f.Name.Name == buffer.String() {
|
||||
klog.V(4).Infof("Found conversion function %s", f.Name)
|
||||
key := conversionPair{inType.Elem, outType.Elem}
|
||||
// We might scan the same package twice, and that's OK.
|
||||
if v, ok := manualMap[key]; ok && v != nil && v.Name.Package != pkg.Path {
|
||||
panic(fmt.Sprintf("duplicate static conversion defined: %s -> %s from:\n%s.%s\n%s.%s", key.inType, key.outType, v.Name.Package, v.Name.Name, f.Name.Package, f.Name.Name))
|
||||
}
|
||||
manualMap[key] = f
|
||||
} else {
|
||||
// prevent user error when they don't get the correct conversion signature
|
||||
if strings.HasPrefix(f.Name.Name, "Convert_") {
|
||||
klog.Errorf("Rename function %s %s -> %s to match expected conversion signature", f.Name.Package, f.Name.Name, buffer.String())
|
||||
}
|
||||
klog.V(8).Infof("%s has wrong name", f.Name)
|
||||
}
|
||||
buffer.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
|
||||
boilerplate, err := arguments.LoadGoBoilerplate()
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed loading boilerplate: %v", err)
|
||||
}
|
||||
|
||||
packages := generator.Packages{}
|
||||
header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
|
||||
|
||||
// Accumulate pre-existing conversion functions.
|
||||
// TODO: This is too ad-hoc. We need a better way.
|
||||
manualConversions := conversionFuncMap{}
|
||||
|
||||
// Record types that are memory equivalent. A type is memory equivalent
|
||||
// if it has the same memory layout and no nested manual conversion is
|
||||
// defined.
|
||||
// TODO: in the future, relax the nested manual conversion requirement
|
||||
// if we can show that a large enough types are memory identical but
|
||||
// have non-trivial conversion
|
||||
memoryEquivalentTypes := equalMemoryTypes{}
|
||||
|
||||
// We are generating conversions only for packages that are explicitly
|
||||
// passed as InputDir.
|
||||
processed := map[string]bool{}
|
||||
for _, i := range context.Inputs {
|
||||
// skip duplicates
|
||||
if processed[i] {
|
||||
continue
|
||||
}
|
||||
processed[i] = true
|
||||
|
||||
klog.V(5).Infof("considering pkg %q", i)
|
||||
pkg := context.Universe[i]
|
||||
// typesPkg is where the versioned types are defined. Sometimes it is
|
||||
// different from pkg. For example, kubernetes core/v1 types are defined
|
||||
// in vendor/k8s.io/api/core/v1, while pkg is at pkg/api/v1.
|
||||
typesPkg := pkg
|
||||
if pkg == nil {
|
||||
// If the input had no Go files, for example.
|
||||
continue
|
||||
}
|
||||
|
||||
// Add conversion and defaulting functions.
|
||||
getManualConversionFunctions(context, pkg, manualConversions)
|
||||
|
||||
// Only generate conversions for packages which explicitly request it
|
||||
// by specifying one or more "+k8s:conversion-gen=<peer-pkg>"
|
||||
// in their doc.go file.
|
||||
peerPkgs := extractTag(pkg.Comments)
|
||||
if peerPkgs != nil {
|
||||
klog.V(5).Infof(" tags: %q", peerPkgs)
|
||||
} else {
|
||||
klog.V(5).Infof(" no tag")
|
||||
continue
|
||||
}
|
||||
skipUnsafe := false
|
||||
if customArgs, ok := arguments.CustomArgs.(*conversionargs.CustomArgs); ok {
|
||||
peerPkgs = append(peerPkgs, customArgs.BasePeerDirs...)
|
||||
peerPkgs = append(peerPkgs, customArgs.ExtraPeerDirs...)
|
||||
skipUnsafe = customArgs.SkipUnsafe
|
||||
}
|
||||
|
||||
// if the external types are not in the same package where the conversion functions to be generated
|
||||
externalTypesValues := extractExternalTypesTag(pkg.Comments)
|
||||
if externalTypesValues != nil {
|
||||
if len(externalTypesValues) != 1 {
|
||||
klog.Fatalf(" expect only one value for %q tag, got: %q", externalTypesTagName, externalTypesValues)
|
||||
}
|
||||
externalTypes := externalTypesValues[0]
|
||||
klog.V(5).Infof(" external types tags: %q", externalTypes)
|
||||
var err error
|
||||
typesPkg, err = context.AddDirectory(externalTypes)
|
||||
if err != nil {
|
||||
klog.Fatalf("cannot import package %s", externalTypes)
|
||||
}
|
||||
// update context.Order to the latest context.Universe
|
||||
orderer := namer.Orderer{Namer: namer.NewPublicNamer(1)}
|
||||
context.Order = orderer.OrderUniverse(context.Universe)
|
||||
}
|
||||
|
||||
// if the source path is within a /vendor/ directory (for example,
|
||||
// k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow
|
||||
// generation to output to the proper relative path (under vendor).
|
||||
// Otherwise, the generator will create the file in the wrong location
|
||||
// in the output directory.
|
||||
// TODO: build a more fundamental concept in gengo for dealing with modifications
|
||||
// to vendored packages.
|
||||
vendorless := func(pkg string) string {
|
||||
if pos := strings.LastIndex(pkg, "/vendor/"); pos != -1 {
|
||||
return pkg[pos+len("/vendor/"):]
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
for i := range peerPkgs {
|
||||
peerPkgs[i] = vendorless(peerPkgs[i])
|
||||
}
|
||||
|
||||
// Make sure our peer-packages are added and fully parsed.
|
||||
for _, pp := range peerPkgs {
|
||||
context.AddDir(pp)
|
||||
p := context.Universe[pp]
|
||||
if nil == p {
|
||||
klog.Fatalf("failed to find pkg: %s", pp)
|
||||
}
|
||||
getManualConversionFunctions(context, p, manualConversions)
|
||||
}
|
||||
|
||||
unsafeEquality := TypesEqual(memoryEquivalentTypes)
|
||||
if skipUnsafe {
|
||||
unsafeEquality = noEquality{}
|
||||
}
|
||||
|
||||
path := pkg.Path
|
||||
// if the source path is within a /vendor/ directory (for example,
|
||||
// k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow
|
||||
// generation to output to the proper relative path (under vendor).
|
||||
// Otherwise, the generator will create the file in the wrong location
|
||||
// in the output directory.
|
||||
// TODO: build a more fundamental concept in gengo for dealing with modifications
|
||||
// to vendored packages.
|
||||
if strings.HasPrefix(pkg.SourcePath, arguments.OutputBase) {
|
||||
expandedPath := strings.TrimPrefix(pkg.SourcePath, arguments.OutputBase)
|
||||
if strings.Contains(expandedPath, "/vendor/") {
|
||||
path = expandedPath
|
||||
}
|
||||
}
|
||||
packages = append(packages,
|
||||
&generator.DefaultPackage{
|
||||
PackageName: filepath.Base(pkg.Path),
|
||||
PackagePath: path,
|
||||
HeaderText: header,
|
||||
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
|
||||
return []generator.Generator{
|
||||
NewGenConversion(arguments.OutputFileBaseName, typesPkg.Path, pkg.Path, manualConversions, peerPkgs, unsafeEquality),
|
||||
}
|
||||
},
|
||||
FilterFunc: func(c *generator.Context, t *types.Type) bool {
|
||||
return t.Name.Package == typesPkg.Path
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// If there is a manual conversion defined between two types, exclude it
|
||||
// from being a candidate for unsafe conversion
|
||||
for k, v := range manualConversions {
|
||||
if isCopyOnly(v.CommentLines) {
|
||||
klog.V(5).Infof("Conversion function %s will not block memory copy because it is copy-only", v.Name)
|
||||
continue
|
||||
}
|
||||
// this type should be excluded from all equivalence, because the converter must be called.
|
||||
memoryEquivalentTypes.Skip(k.inType, k.outType)
|
||||
}
|
||||
|
||||
return packages
|
||||
}
|
||||
|
||||
type equalMemoryTypes map[conversionPair]bool
|
||||
|
||||
func (e equalMemoryTypes) Skip(a, b *types.Type) {
|
||||
e[conversionPair{a, b}] = false
|
||||
e[conversionPair{b, a}] = false
|
||||
}
|
||||
|
||||
func (e equalMemoryTypes) Equal(a, b *types.Type) bool {
|
||||
// alreadyVisitedTypes holds all the types that have already been checked in the structural type recursion.
|
||||
alreadyVisitedTypes := make(map[*types.Type]bool)
|
||||
return e.cachingEqual(a, b, alreadyVisitedTypes)
|
||||
}
|
||||
|
||||
func (e equalMemoryTypes) cachingEqual(a, b *types.Type, alreadyVisitedTypes map[*types.Type]bool) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
if equal, ok := e[conversionPair{a, b}]; ok {
|
||||
return equal
|
||||
}
|
||||
if equal, ok := e[conversionPair{b, a}]; ok {
|
||||
return equal
|
||||
}
|
||||
result := e.equal(a, b, alreadyVisitedTypes)
|
||||
e[conversionPair{a, b}] = result
|
||||
e[conversionPair{b, a}] = result
|
||||
return result
|
||||
}
|
||||
|
||||
func (e equalMemoryTypes) equal(a, b *types.Type, alreadyVisitedTypes map[*types.Type]bool) bool {
|
||||
in, out := unwrapAlias(a), unwrapAlias(b)
|
||||
switch {
|
||||
case in == out:
|
||||
return true
|
||||
case in.Kind == out.Kind:
|
||||
// if the type exists already, return early to avoid recursion
|
||||
if alreadyVisitedTypes[in] {
|
||||
return true
|
||||
}
|
||||
alreadyVisitedTypes[in] = true
|
||||
|
||||
switch in.Kind {
|
||||
case types.Struct:
|
||||
if len(in.Members) != len(out.Members) {
|
||||
return false
|
||||
}
|
||||
for i, inMember := range in.Members {
|
||||
outMember := out.Members[i]
|
||||
if !e.cachingEqual(inMember.Type, outMember.Type, alreadyVisitedTypes) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case types.Pointer:
|
||||
return e.cachingEqual(in.Elem, out.Elem, alreadyVisitedTypes)
|
||||
case types.Map:
|
||||
return e.cachingEqual(in.Key, out.Key, alreadyVisitedTypes) && e.cachingEqual(in.Elem, out.Elem, alreadyVisitedTypes)
|
||||
case types.Slice:
|
||||
return e.cachingEqual(in.Elem, out.Elem, alreadyVisitedTypes)
|
||||
case types.Interface:
|
||||
// TODO: determine whether the interfaces are actually equivalent - for now, they must have the
|
||||
// same type.
|
||||
return false
|
||||
case types.Builtin:
|
||||
return in.Name.Name == out.Name.Name
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func findMember(t *types.Type, name string) (types.Member, bool) {
|
||||
if t.Kind != types.Struct {
|
||||
return types.Member{}, false
|
||||
}
|
||||
for _, member := range t.Members {
|
||||
if member.Name == name {
|
||||
return member, true
|
||||
}
|
||||
}
|
||||
return types.Member{}, false
|
||||
}
|
||||
|
||||
// unwrapAlias recurses down aliased types to find the bedrock type.
|
||||
func unwrapAlias(in *types.Type) *types.Type {
|
||||
for in.Kind == types.Alias {
|
||||
in = in.Underlying
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
const (
|
||||
runtimePackagePath = "k8s.io/apimachinery/pkg/runtime"
|
||||
conversionPackagePath = "k8s.io/apimachinery/pkg/conversion"
|
||||
)
|
||||
|
||||
type noEquality struct{}
|
||||
|
||||
func (noEquality) Equal(_, _ *types.Type) bool { return false }
|
||||
|
||||
type TypesEqual interface {
|
||||
Equal(a, b *types.Type) bool
|
||||
}
|
||||
|
||||
// genConversion produces a file with a autogenerated conversions.
|
||||
type genConversion struct {
|
||||
generator.DefaultGen
|
||||
// the package that contains the types that conversion func are going to be
|
||||
// generated for
|
||||
typesPackage string
|
||||
// the package that the conversion funcs are going to be output to
|
||||
outputPackage string
|
||||
// packages that contain the peer of types in typesPacakge
|
||||
peerPackages []string
|
||||
manualConversions conversionFuncMap
|
||||
imports namer.ImportTracker
|
||||
types []*types.Type
|
||||
skippedFields map[*types.Type][]string
|
||||
useUnsafe TypesEqual
|
||||
}
|
||||
|
||||
func NewGenConversion(sanitizedName, typesPackage, outputPackage string, manualConversions conversionFuncMap, peerPkgs []string, useUnsafe TypesEqual) generator.Generator {
|
||||
return &genConversion{
|
||||
DefaultGen: generator.DefaultGen{
|
||||
OptionalName: sanitizedName,
|
||||
},
|
||||
typesPackage: typesPackage,
|
||||
outputPackage: outputPackage,
|
||||
peerPackages: peerPkgs,
|
||||
manualConversions: manualConversions,
|
||||
imports: generator.NewImportTracker(),
|
||||
types: []*types.Type{},
|
||||
skippedFields: map[*types.Type][]string{},
|
||||
useUnsafe: useUnsafe,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *genConversion) Namers(c *generator.Context) namer.NameSystems {
|
||||
// Have the raw namer for this file track what it imports.
|
||||
return namer.NameSystems{
|
||||
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
|
||||
"publicIT": &namerPlusImportTracking{
|
||||
delegate: conversionNamer(),
|
||||
tracker: g.imports,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type namerPlusImportTracking struct {
|
||||
delegate namer.Namer
|
||||
tracker namer.ImportTracker
|
||||
}
|
||||
|
||||
func (n *namerPlusImportTracking) Name(t *types.Type) string {
|
||||
n.tracker.AddType(t)
|
||||
return n.delegate.Name(t)
|
||||
}
|
||||
|
||||
func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type) bool {
|
||||
var t *types.Type
|
||||
var other *types.Type
|
||||
if inType.Name.Package == g.typesPackage {
|
||||
t, other = inType, outType
|
||||
} else {
|
||||
t, other = outType, inType
|
||||
}
|
||||
|
||||
if t.Name.Package != g.typesPackage {
|
||||
return false
|
||||
}
|
||||
// If the type has opted out, skip it.
|
||||
tagvals := extractTag(t.CommentLines)
|
||||
if tagvals != nil {
|
||||
if tagvals[0] != "false" {
|
||||
klog.Fatalf("Type %v: unsupported %s value: %q", t, tagName, tagvals[0])
|
||||
}
|
||||
klog.V(5).Infof("type %v requests no conversion generation, skipping", t)
|
||||
return false
|
||||
}
|
||||
// TODO: Consider generating functions for other kinds too.
|
||||
if t.Kind != types.Struct {
|
||||
return false
|
||||
}
|
||||
// Also, filter out private types.
|
||||
if namer.IsPrivateGoName(other.Name.Name) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *genConversion) Filter(c *generator.Context, t *types.Type) bool {
|
||||
peerType := getPeerTypeFor(c, t, g.peerPackages)
|
||||
if peerType == nil {
|
||||
return false
|
||||
}
|
||||
if !g.convertibleOnlyWithinPackage(t, peerType) {
|
||||
return false
|
||||
}
|
||||
|
||||
g.types = append(g.types, t)
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *genConversion) isOtherPackage(pkg string) bool {
|
||||
if pkg == g.outputPackage {
|
||||
return false
|
||||
}
|
||||
if strings.HasSuffix(pkg, `"`+g.outputPackage+`"`) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *genConversion) Imports(c *generator.Context) (imports []string) {
|
||||
var importLines []string
|
||||
for _, singleImport := range g.imports.ImportLines() {
|
||||
if g.isOtherPackage(singleImport) {
|
||||
importLines = append(importLines, singleImport)
|
||||
}
|
||||
}
|
||||
return importLines
|
||||
}
|
||||
|
||||
func argsFromType(inType, outType *types.Type) generator.Args {
|
||||
return generator.Args{
|
||||
"inType": inType,
|
||||
"outType": outType,
|
||||
}
|
||||
}
|
||||
|
||||
const nameTmpl = "Convert_$.inType|publicIT$_To_$.outType|publicIT$"
|
||||
|
||||
func (g *genConversion) preexists(inType, outType *types.Type) (*types.Type, bool) {
|
||||
function, ok := g.manualConversions[conversionPair{inType, outType}]
|
||||
return function, ok
|
||||
}
|
||||
|
||||
func (g *genConversion) Init(c *generator.Context, w io.Writer) error {
|
||||
if klog.V(5) {
|
||||
if m, ok := g.useUnsafe.(equalMemoryTypes); ok {
|
||||
var result []string
|
||||
klog.Infof("All objects without identical memory layout:")
|
||||
for k, v := range m {
|
||||
if v {
|
||||
continue
|
||||
}
|
||||
result = append(result, fmt.Sprintf(" %s -> %s = %t", k.inType, k.outType, v))
|
||||
}
|
||||
sort.Strings(result)
|
||||
for _, s := range result {
|
||||
klog.Infof(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||
sw.Do("func init() {\n", nil)
|
||||
sw.Do("localSchemeBuilder.Register(RegisterConversions)\n", nil)
|
||||
sw.Do("}\n", nil)
|
||||
|
||||
scheme := c.Universe.Type(types.Name{Package: runtimePackagePath, Name: "Scheme"})
|
||||
schemePtr := &types.Type{
|
||||
Kind: types.Pointer,
|
||||
Elem: scheme,
|
||||
}
|
||||
sw.Do("// RegisterConversions adds conversion functions to the given scheme.\n", nil)
|
||||
sw.Do("// Public to allow building arbitrary schemes.\n", nil)
|
||||
sw.Do("func RegisterConversions(s $.|raw$) error {\n", schemePtr)
|
||||
for _, t := range g.types {
|
||||
peerType := getPeerTypeFor(c, t, g.peerPackages)
|
||||
args := argsFromType(t, peerType).With("Scope", types.Ref(conversionPackagePath, "Scope"))
|
||||
sw.Do("if err := s.AddGeneratedConversionFunc((*$.inType|raw$)(nil), (*$.outType|raw$)(nil), func(a, b interface{}, scope $.Scope|raw$) error { return "+nameTmpl+"(a.(*$.inType|raw$), b.(*$.outType|raw$), scope) }); err != nil { return err }\n", args)
|
||||
args = argsFromType(peerType, t).With("Scope", types.Ref(conversionPackagePath, "Scope"))
|
||||
sw.Do("if err := s.AddGeneratedConversionFunc((*$.inType|raw$)(nil), (*$.outType|raw$)(nil), func(a, b interface{}, scope $.Scope|raw$) error { return "+nameTmpl+"(a.(*$.inType|raw$), b.(*$.outType|raw$), scope) }); err != nil { return err }\n", args)
|
||||
}
|
||||
var pairs []conversionPair
|
||||
for pair, t := range g.manualConversions {
|
||||
if t.Name.Package != g.outputPackage {
|
||||
continue
|
||||
}
|
||||
pairs = append(pairs, pair)
|
||||
}
|
||||
// sort by name of the conversion function
|
||||
sort.Slice(pairs, func(i, j int) bool {
|
||||
if g.manualConversions[pairs[i]].Name.Name < g.manualConversions[pairs[j]].Name.Name {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
for _, pair := range pairs {
|
||||
args := argsFromType(pair.inType, pair.outType).With("Scope", types.Ref(conversionPackagePath, "Scope")).With("fn", g.manualConversions[pair])
|
||||
sw.Do("if err := s.AddConversionFunc((*$.inType|raw$)(nil), (*$.outType|raw$)(nil), func(a, b interface{}, scope $.Scope|raw$) error { return $.fn|raw$(a.(*$.inType|raw$), b.(*$.outType|raw$), scope) }); err != nil { return err }\n", args)
|
||||
}
|
||||
|
||||
sw.Do("return nil\n", nil)
|
||||
sw.Do("}\n\n", nil)
|
||||
return sw.Error()
|
||||
}
|
||||
|
||||
func (g *genConversion) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
|
||||
klog.V(5).Infof("generating for type %v", t)
|
||||
peerType := getPeerTypeFor(c, t, g.peerPackages)
|
||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||
g.generateConversion(t, peerType, sw)
|
||||
g.generateConversion(peerType, t, sw)
|
||||
return sw.Error()
|
||||
}
|
||||
|
||||
func (g *genConversion) generateConversion(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||
args := argsFromType(inType, outType).
|
||||
With("Scope", types.Ref(conversionPackagePath, "Scope"))
|
||||
|
||||
sw.Do("func auto"+nameTmpl+"(in *$.inType|raw$, out *$.outType|raw$, s $.Scope|raw$) error {\n", args)
|
||||
g.generateFor(inType, outType, sw)
|
||||
sw.Do("return nil\n", nil)
|
||||
sw.Do("}\n\n", nil)
|
||||
|
||||
if _, found := g.preexists(inType, outType); found {
|
||||
// There is a public manual Conversion method: use it.
|
||||
} else if skipped := g.skippedFields[inType]; len(skipped) != 0 {
|
||||
// The inType had some fields we could not generate.
|
||||
klog.Errorf("Warning: could not find nor generate a final Conversion function for %v -> %v", inType, outType)
|
||||
klog.Errorf(" the following fields need manual conversion:")
|
||||
for _, f := range skipped {
|
||||
klog.Errorf(" - %v", f)
|
||||
}
|
||||
} else {
|
||||
// Emit a public conversion function.
|
||||
sw.Do("// "+nameTmpl+" is an autogenerated conversion function.\n", args)
|
||||
sw.Do("func "+nameTmpl+"(in *$.inType|raw$, out *$.outType|raw$, s $.Scope|raw$) error {\n", args)
|
||||
sw.Do("return auto"+nameTmpl+"(in, out, s)\n", args)
|
||||
sw.Do("}\n\n", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// we use the system of shadowing 'in' and 'out' so that the same code is valid
|
||||
// at any nesting level. This makes the autogenerator easy to understand, and
|
||||
// the compiler shouldn't care.
|
||||
func (g *genConversion) generateFor(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||
klog.V(5).Infof("generating %v -> %v", inType, outType)
|
||||
var f func(*types.Type, *types.Type, *generator.SnippetWriter)
|
||||
|
||||
switch inType.Kind {
|
||||
case types.Builtin:
|
||||
f = g.doBuiltin
|
||||
case types.Map:
|
||||
f = g.doMap
|
||||
case types.Slice:
|
||||
f = g.doSlice
|
||||
case types.Struct:
|
||||
f = g.doStruct
|
||||
case types.Pointer:
|
||||
f = g.doPointer
|
||||
case types.Alias:
|
||||
f = g.doAlias
|
||||
default:
|
||||
f = g.doUnknown
|
||||
}
|
||||
|
||||
f(inType, outType, sw)
|
||||
}
|
||||
|
||||
func (g *genConversion) doBuiltin(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||
if inType == outType {
|
||||
sw.Do("*out = *in\n", nil)
|
||||
} else {
|
||||
sw.Do("*out = $.|raw$(*in)\n", outType)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *genConversion) doMap(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||
sw.Do("*out = make($.|raw$, len(*in))\n", outType)
|
||||
if isDirectlyAssignable(inType.Key, outType.Key) {
|
||||
sw.Do("for key, val := range *in {\n", nil)
|
||||
if isDirectlyAssignable(inType.Elem, outType.Elem) {
|
||||
if inType.Key == outType.Key {
|
||||
sw.Do("(*out)[key] = ", nil)
|
||||
} else {
|
||||
sw.Do("(*out)[$.|raw$(key)] = ", outType.Key)
|
||||
}
|
||||
if inType.Elem == outType.Elem {
|
||||
sw.Do("val\n", nil)
|
||||
} else {
|
||||
sw.Do("$.|raw$(val)\n", outType.Elem)
|
||||
}
|
||||
} else {
|
||||
sw.Do("newVal := new($.|raw$)\n", outType.Elem)
|
||||
if function, ok := g.preexists(inType.Elem, outType.Elem); ok {
|
||||
sw.Do("if err := $.|raw$(&val, newVal, s); err != nil {\n", function)
|
||||
} else if g.convertibleOnlyWithinPackage(inType.Elem, outType.Elem) {
|
||||
sw.Do("if err := "+nameTmpl+"(&val, newVal, s); err != nil {\n", argsFromType(inType.Elem, outType.Elem))
|
||||
} else {
|
||||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil)
|
||||
sw.Do("if err := s.Convert(&val, newVal, 0); err != nil {\n", nil)
|
||||
}
|
||||
sw.Do("return err\n", nil)
|
||||
sw.Do("}\n", nil)
|
||||
if inType.Key == outType.Key {
|
||||
sw.Do("(*out)[key] = *newVal\n", nil)
|
||||
} else {
|
||||
sw.Do("(*out)[$.|raw$(key)] = *newVal\n", outType.Key)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: Implement it when necessary.
|
||||
sw.Do("for range *in {\n", nil)
|
||||
sw.Do("// FIXME: Converting unassignable keys unsupported $.|raw$\n", inType.Key)
|
||||
}
|
||||
sw.Do("}\n", nil)
|
||||
}
|
||||
|
||||
func (g *genConversion) doSlice(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||
sw.Do("*out = make($.|raw$, len(*in))\n", outType)
|
||||
if inType.Elem == outType.Elem && inType.Elem.Kind == types.Builtin {
|
||||
sw.Do("copy(*out, *in)\n", nil)
|
||||
} else {
|
||||
sw.Do("for i := range *in {\n", nil)
|
||||
if isDirectlyAssignable(inType.Elem, outType.Elem) {
|
||||
if inType.Elem == outType.Elem {
|
||||
sw.Do("(*out)[i] = (*in)[i]\n", nil)
|
||||
} else {
|
||||
sw.Do("(*out)[i] = $.|raw$((*in)[i])\n", outType.Elem)
|
||||
}
|
||||
} else {
|
||||
if function, ok := g.preexists(inType.Elem, outType.Elem); ok {
|
||||
sw.Do("if err := $.|raw$(&(*in)[i], &(*out)[i], s); err != nil {\n", function)
|
||||
} else if g.convertibleOnlyWithinPackage(inType.Elem, outType.Elem) {
|
||||
sw.Do("if err := "+nameTmpl+"(&(*in)[i], &(*out)[i], s); err != nil {\n", argsFromType(inType.Elem, outType.Elem))
|
||||
} else {
|
||||
// TODO: This triggers on metav1.ObjectMeta <-> metav1.ObjectMeta and
|
||||
// similar because neither package is the target package, and
|
||||
// we really don't know which package will have the conversion
|
||||
// function defined. This fires on basically every object
|
||||
// conversion outside of pkg/api/v1.
|
||||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil)
|
||||
sw.Do("if err := s.Convert(&(*in)[i], &(*out)[i], 0); err != nil {\n", nil)
|
||||
}
|
||||
sw.Do("return err\n", nil)
|
||||
sw.Do("}\n", nil)
|
||||
}
|
||||
sw.Do("}\n", nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *genConversion) doStruct(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||
for _, inMember := range inType.Members {
|
||||
if tagvals := extractTag(inMember.CommentLines); tagvals != nil && tagvals[0] == "false" {
|
||||
// This field is excluded from conversion.
|
||||
sw.Do("// INFO: in."+inMember.Name+" opted out of conversion generation\n", nil)
|
||||
continue
|
||||
}
|
||||
outMember, found := findMember(outType, inMember.Name)
|
||||
if !found {
|
||||
// This field doesn't exist in the peer.
|
||||
sw.Do("// WARNING: in."+inMember.Name+" requires manual conversion: does not exist in peer-type\n", nil)
|
||||
g.skippedFields[inType] = append(g.skippedFields[inType], inMember.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
inMemberType, outMemberType := inMember.Type, outMember.Type
|
||||
// create a copy of both underlying types but give them the top level alias name (since aliases
|
||||
// are assignable)
|
||||
if underlying := unwrapAlias(inMemberType); underlying != inMemberType {
|
||||
copied := *underlying
|
||||
copied.Name = inMemberType.Name
|
||||
inMemberType = &copied
|
||||
}
|
||||
if underlying := unwrapAlias(outMemberType); underlying != outMemberType {
|
||||
copied := *underlying
|
||||
copied.Name = outMemberType.Name
|
||||
outMemberType = &copied
|
||||
}
|
||||
|
||||
args := argsFromType(inMemberType, outMemberType).With("name", inMember.Name)
|
||||
|
||||
// try a direct memory copy for any type that has exactly equivalent values
|
||||
if g.useUnsafe.Equal(inMemberType, outMemberType) {
|
||||
args = args.
|
||||
With("Pointer", types.Ref("unsafe", "Pointer")).
|
||||
With("SliceHeader", types.Ref("reflect", "SliceHeader"))
|
||||
switch inMemberType.Kind {
|
||||
case types.Pointer:
|
||||
sw.Do("out.$.name$ = ($.outType|raw$)($.Pointer|raw$(in.$.name$))\n", args)
|
||||
continue
|
||||
case types.Map:
|
||||
sw.Do("out.$.name$ = *(*$.outType|raw$)($.Pointer|raw$(&in.$.name$))\n", args)
|
||||
continue
|
||||
case types.Slice:
|
||||
sw.Do("out.$.name$ = *(*$.outType|raw$)($.Pointer|raw$(&in.$.name$))\n", args)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// check based on the top level name, not the underlying names
|
||||
if function, ok := g.preexists(inMember.Type, outMember.Type); ok {
|
||||
if isDrop(function.CommentLines) {
|
||||
continue
|
||||
}
|
||||
// copy-only functions that are directly assignable can be inlined instead of invoked.
|
||||
// As an example, conversion functions exist that allow types with private fields to be
|
||||
// correctly copied between types. These functions are equivalent to a memory assignment,
|
||||
// and are necessary for the reflection path, but should not block memory conversion.
|
||||
// Convert_unversioned_Time_to_unversioned_Time is an example of this logic.
|
||||
if !isCopyOnly(function.CommentLines) || !g.isFastConversion(inMemberType, outMemberType) {
|
||||
args["function"] = function
|
||||
sw.Do("if err := $.function|raw$(&in.$.name$, &out.$.name$, s); err != nil {\n", args)
|
||||
sw.Do("return err\n", nil)
|
||||
sw.Do("}\n", nil)
|
||||
continue
|
||||
}
|
||||
klog.V(5).Infof("Skipped function %s because it is copy-only and we can use direct assignment", function.Name)
|
||||
}
|
||||
|
||||
// If we can't auto-convert, punt before we emit any code.
|
||||
if inMemberType.Kind != outMemberType.Kind {
|
||||
sw.Do("// WARNING: in."+inMember.Name+" requires manual conversion: inconvertible types ("+
|
||||
inMemberType.String()+" vs "+outMemberType.String()+")\n", nil)
|
||||
g.skippedFields[inType] = append(g.skippedFields[inType], inMember.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
switch inMemberType.Kind {
|
||||
case types.Builtin:
|
||||
if inMemberType == outMemberType {
|
||||
sw.Do("out.$.name$ = in.$.name$\n", args)
|
||||
} else {
|
||||
sw.Do("out.$.name$ = $.outType|raw$(in.$.name$)\n", args)
|
||||
}
|
||||
case types.Map, types.Slice, types.Pointer:
|
||||
if g.isDirectlyAssignable(inMemberType, outMemberType) {
|
||||
sw.Do("out.$.name$ = in.$.name$\n", args)
|
||||
continue
|
||||
}
|
||||
|
||||
sw.Do("if in.$.name$ != nil {\n", args)
|
||||
sw.Do("in, out := &in.$.name$, &out.$.name$\n", args)
|
||||
g.generateFor(inMemberType, outMemberType, sw)
|
||||
sw.Do("} else {\n", nil)
|
||||
sw.Do("out.$.name$ = nil\n", args)
|
||||
sw.Do("}\n", nil)
|
||||
case types.Struct:
|
||||
if g.isDirectlyAssignable(inMemberType, outMemberType) {
|
||||
sw.Do("out.$.name$ = in.$.name$\n", args)
|
||||
continue
|
||||
}
|
||||
if g.convertibleOnlyWithinPackage(inMemberType, outMemberType) {
|
||||
sw.Do("if err := "+nameTmpl+"(&in.$.name$, &out.$.name$, s); err != nil {\n", args)
|
||||
} else {
|
||||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil)
|
||||
sw.Do("if err := s.Convert(&in.$.name$, &out.$.name$, 0); err != nil {\n", args)
|
||||
}
|
||||
sw.Do("return err\n", nil)
|
||||
sw.Do("}\n", nil)
|
||||
case types.Alias:
|
||||
if isDirectlyAssignable(inMemberType, outMemberType) {
|
||||
if inMemberType == outMemberType {
|
||||
sw.Do("out.$.name$ = in.$.name$\n", args)
|
||||
} else {
|
||||
sw.Do("out.$.name$ = $.outType|raw$(in.$.name$)\n", args)
|
||||
}
|
||||
} else {
|
||||
if g.convertibleOnlyWithinPackage(inMemberType, outMemberType) {
|
||||
sw.Do("if err := "+nameTmpl+"(&in.$.name$, &out.$.name$, s); err != nil {\n", args)
|
||||
} else {
|
||||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil)
|
||||
sw.Do("if err := s.Convert(&in.$.name$, &out.$.name$, 0); err != nil {\n", args)
|
||||
}
|
||||
sw.Do("return err\n", nil)
|
||||
sw.Do("}\n", nil)
|
||||
}
|
||||
default:
|
||||
if g.convertibleOnlyWithinPackage(inMemberType, outMemberType) {
|
||||
sw.Do("if err := "+nameTmpl+"(&in.$.name$, &out.$.name$, s); err != nil {\n", args)
|
||||
} else {
|
||||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil)
|
||||
sw.Do("if err := s.Convert(&in.$.name$, &out.$.name$, 0); err != nil {\n", args)
|
||||
}
|
||||
sw.Do("return err\n", nil)
|
||||
sw.Do("}\n", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *genConversion) isFastConversion(inType, outType *types.Type) bool {
|
||||
switch inType.Kind {
|
||||
case types.Builtin:
|
||||
return true
|
||||
case types.Map, types.Slice, types.Pointer, types.Struct, types.Alias:
|
||||
return g.isDirectlyAssignable(inType, outType)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (g *genConversion) isDirectlyAssignable(inType, outType *types.Type) bool {
|
||||
return unwrapAlias(inType) == unwrapAlias(outType)
|
||||
}
|
||||
|
||||
func (g *genConversion) doPointer(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||
sw.Do("*out = new($.Elem|raw$)\n", outType)
|
||||
if isDirectlyAssignable(inType.Elem, outType.Elem) {
|
||||
if inType.Elem == outType.Elem {
|
||||
sw.Do("**out = **in\n", nil)
|
||||
} else {
|
||||
sw.Do("**out = $.|raw$(**in)\n", outType.Elem)
|
||||
}
|
||||
} else {
|
||||
if function, ok := g.preexists(inType.Elem, outType.Elem); ok {
|
||||
sw.Do("if err := $.|raw$(*in, *out, s); err != nil {\n", function)
|
||||
} else if g.convertibleOnlyWithinPackage(inType.Elem, outType.Elem) {
|
||||
sw.Do("if err := "+nameTmpl+"(*in, *out, s); err != nil {\n", argsFromType(inType.Elem, outType.Elem))
|
||||
} else {
|
||||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil)
|
||||
sw.Do("if err := s.Convert(*in, *out, 0); err != nil {\n", nil)
|
||||
}
|
||||
sw.Do("return err\n", nil)
|
||||
sw.Do("}\n", nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *genConversion) doAlias(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||
// TODO: Add support for aliases.
|
||||
g.doUnknown(inType, outType, sw)
|
||||
}
|
||||
|
||||
func (g *genConversion) doUnknown(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||
sw.Do("// FIXME: Type $.|raw$ is unsupported.\n", inType)
|
||||
}
|
||||
|
||||
func isDirectlyAssignable(inType, outType *types.Type) bool {
|
||||
// TODO: This should maybe check for actual assignability between the two
|
||||
// types, rather than superficial traits that happen to indicate it is
|
||||
// assignable in the ways we currently use this code.
|
||||
return inType.IsAssignable() && (inType.IsPrimitive() || isSamePackage(inType, outType))
|
||||
}
|
||||
|
||||
func isSamePackage(inType, outType *types.Type) bool {
|
||||
return inType.Name.Package == outType.Name.Package
|
||||
}
|
77
vendor/k8s.io/code-generator/cmd/conversion-gen/main.go
generated
vendored
77
vendor/k8s.io/code-generator/cmd/conversion-gen/main.go
generated
vendored
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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.
|
||||
*/
|
||||
|
||||
// conversion-gen is a tool for auto-generating Conversion functions.
|
||||
//
|
||||
// Given a list of input directories, it will scan for "peer" packages and
|
||||
// generate functions that efficiently convert between same-name types in each
|
||||
// package. For any pair of types that has a
|
||||
// `Convert_<pkg1>_<type>_To_<pkg2>_<Type()`
|
||||
// function (and its reciprocal), it will simply call that. use standard value
|
||||
// assignment whenever possible. The resulting file will be stored in the same
|
||||
// directory as the processed source package.
|
||||
//
|
||||
// Generation is governed by comment tags in the source. Any package may
|
||||
// request Conversion generation by including a comment in the file-comments of
|
||||
// one file, of the form:
|
||||
// // +k8s:conversion-gen=<import-path-of-peer-package>
|
||||
//
|
||||
// When generating for a package, individual types or fields of structs may opt
|
||||
// out of Conversion generation by specifying a comment on the of the form:
|
||||
// // +k8s:conversion-gen=false
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/klog"
|
||||
|
||||
generatorargs "k8s.io/code-generator/cmd/conversion-gen/args"
|
||||
"k8s.io/code-generator/cmd/conversion-gen/generators"
|
||||
"k8s.io/code-generator/pkg/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
klog.InitFlags(nil)
|
||||
genericArgs, customArgs := generatorargs.NewDefaults()
|
||||
|
||||
// Override defaults.
|
||||
// TODO: move this out of conversion-gen
|
||||
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath())
|
||||
|
||||
genericArgs.AddFlags(pflag.CommandLine)
|
||||
customArgs.AddFlags(pflag.CommandLine)
|
||||
flag.Set("logtostderr", "true")
|
||||
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||
pflag.Parse()
|
||||
|
||||
if err := generatorargs.Validate(genericArgs); err != nil {
|
||||
klog.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
||||
// Run it.
|
||||
if err := genericArgs.Execute(
|
||||
generators.NameSystems(),
|
||||
generators.DefaultNameSystem(),
|
||||
generators.Packages,
|
||||
); err != nil {
|
||||
klog.Fatalf("Error: %v", err)
|
||||
}
|
||||
klog.V(2).Info("Completed successfully.")
|
||||
}
|
1
vendor/k8s.io/code-generator/cmd/go-to-protobuf/.gitignore
generated
vendored
1
vendor/k8s.io/code-generator/cmd/go-to-protobuf/.gitignore
generated
vendored
@@ -1 +0,0 @@
|
||||
go-to-protobuf
|
4
vendor/k8s.io/code-generator/cmd/go-to-protobuf/OWNERS
generated
vendored
4
vendor/k8s.io/code-generator/cmd/go-to-protobuf/OWNERS
generated
vendored
@@ -1,4 +0,0 @@
|
||||
approvers:
|
||||
- smarterclayton
|
||||
reviewers:
|
||||
- smarterclayton
|
39
vendor/k8s.io/code-generator/cmd/go-to-protobuf/main.go
generated
vendored
39
vendor/k8s.io/code-generator/cmd/go-to-protobuf/main.go
generated
vendored
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
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 main
|
||||
|
||||
import (
|
||||
goflag "flag"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
"k8s.io/code-generator/cmd/go-to-protobuf/protobuf"
|
||||
)
|
||||
|
||||
var g = protobuf.New()
|
||||
|
||||
func init() {
|
||||
g.BindFlags(flag.CommandLine)
|
||||
goflag.Set("logtostderr", "true")
|
||||
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
protobuf.Run(g)
|
||||
}
|
349
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/cmd.go
generated
vendored
349
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/cmd.go
generated
vendored
@@ -1,349 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/code-generator/pkg/util"
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/parser"
|
||||
"k8s.io/gengo/types"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type Generator struct {
|
||||
Common args.GeneratorArgs
|
||||
APIMachineryPackages string
|
||||
Packages string
|
||||
OutputBase string
|
||||
VendorOutputBase 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, util.BoilerplatePath()),
|
||||
}
|
||||
defaultProtoImport := filepath.Join(sourceTree, "k8s.io", "kubernetes", "vendor", "github.com", "gogo", "protobuf", "protobuf")
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot get current directory.")
|
||||
}
|
||||
return &Generator{
|
||||
Common: common,
|
||||
OutputBase: sourceTree,
|
||||
VendorOutputBase: filepath.Join(cwd, "vendor"),
|
||||
ProtoImport: []string{defaultProtoImport},
|
||||
APIMachineryPackages: strings.Join([]string{
|
||||
`+k8s.io/apimachinery/pkg/util/intstr`,
|
||||
`+k8s.io/apimachinery/pkg/api/resource`,
|
||||
`+k8s.io/apimachinery/pkg/runtime/schema`,
|
||||
`+k8s.io/apimachinery/pkg/runtime`,
|
||||
`k8s.io/apimachinery/pkg/apis/meta/v1`,
|
||||
`k8s.io/apimachinery/pkg/apis/meta/v1beta1`,
|
||||
`k8s.io/apimachinery/pkg/apis/testapigroup/v1`,
|
||||
}, ","),
|
||||
Packages: "",
|
||||
DropEmbeddedFields: "k8s.io/apimachinery/pkg/apis/meta/v1.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.StringVar(&g.APIMachineryPackages, "apimachinery-packages", g.APIMachineryPackages, "comma-separated list of directories to get apimachinery input types from which are needed by any API. 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.StringVar(&g.VendorOutputBase, "vendor-output-base", g.VendorOutputBase, "The vendor/ directory to look for packages in; defaults to $PWD/vendor/.")
|
||||
flag.StringSliceVar(&g.ProtoImport, "proto-import", g.ProtoImport, "The search path for the core protobuf .protos, required; defaults $GOPATH/src/k8s.io/kubernetes/vendor/github.com/gogo/protobuf/protobuf.")
|
||||
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 (consider using the go-header-file flag): %v", err)
|
||||
}
|
||||
|
||||
protobufNames := NewProtobufNamer()
|
||||
outputPackages := generator.Packages{}
|
||||
nonOutputPackages := map[string]struct{}{}
|
||||
|
||||
var packages []string
|
||||
if len(g.APIMachineryPackages) != 0 {
|
||||
packages = append(packages, strings.Split(g.APIMachineryPackages, ",")...)
|
||||
}
|
||||
if len(g.Packages) != 0 {
|
||||
packages = append(packages, strings.Split(g.Packages, ",")...)
|
||||
}
|
||||
if len(packages) == 0 {
|
||||
log.Fatalf("Both apimachinery-packages and packages are empty. At least one package must be specified.")
|
||||
}
|
||||
|
||||
for _, d := range packages {
|
||||
generateAllTypes, outputPackage := true, true
|
||||
switch {
|
||||
case strings.HasPrefix(d, "+"):
|
||||
d = d[1:]
|
||||
generateAllTypes = false
|
||||
case strings.HasPrefix(d, "-"):
|
||||
d = d[1:]
|
||||
outputPackage = false
|
||||
}
|
||||
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)
|
||||
} else {
|
||||
nonOutputPackages[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
var vendoredOutputPackages, localOutputPackages generator.Packages
|
||||
for _, p := range protobufNames.packages {
|
||||
if _, ok := nonOutputPackages[p.Name()]; ok {
|
||||
// if we're not outputting the package, don't include it in either package list
|
||||
continue
|
||||
}
|
||||
p.Vendored = strings.Contains(c.Universe[p.PackagePath].SourcePath, "/vendor/")
|
||||
if p.Vendored {
|
||||
vendoredOutputPackages = append(vendoredOutputPackages, p)
|
||||
} else {
|
||||
localOutputPackages = append(localOutputPackages, p)
|
||||
}
|
||||
}
|
||||
|
||||
if err := protobufNames.AssignTypesToPackages(c); err != nil {
|
||||
log.Fatalf("Failed to identify Common types: %v", err)
|
||||
}
|
||||
|
||||
if err := c.ExecutePackages(g.VendorOutputBase, vendoredOutputPackages); err != nil {
|
||||
log.Fatalf("Failed executing vendor generator: %v", err)
|
||||
}
|
||||
if err := c.ExecutePackages(g.OutputBase, localOutputPackages); err != nil {
|
||||
log.Fatalf("Failed executing local 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())
|
||||
if p.Vendored {
|
||||
path = filepath.Join(g.VendorOutputBase, p.ImportPath())
|
||||
outputPath = filepath.Join(g.VendorOutputBase, 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.VendorOutputBase, vendoredOutputPackages); err != nil {
|
||||
log.Fatalf("Failed executing vendor generator: %v", err)
|
||||
}
|
||||
if err := c.ExecutePackages(g.OutputBase, localOutputPackages); err != nil {
|
||||
log.Fatalf("Failed executing local generator: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, outputPackage := range outputPackages {
|
||||
p := outputPackage.(*protobufPackage)
|
||||
|
||||
if len(p.StructTags) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
pattern := filepath.Join(g.OutputBase, p.PackagePath, "*.go")
|
||||
if p.Vendored {
|
||||
pattern = filepath.Join(g.VendorOutputBase, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
773
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/generator.go
generated
vendored
773
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/generator.go
generated
vendored
@@ -1,773 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package protobuf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
// genProtoIDL produces a .proto IDL.
|
||||
type genProtoIDL struct {
|
||||
generator.DefaultGen
|
||||
localPackage types.Name
|
||||
localGoPackage types.Name
|
||||
imports namer.ImportTracker
|
||||
|
||||
generateAll bool
|
||||
omitGogo bool
|
||||
omitFieldTypes map[types.Name]struct{}
|
||||
}
|
||||
|
||||
func (g *genProtoIDL) PackageVars(c *generator.Context) []string {
|
||||
if g.omitGogo {
|
||||
return []string{
|
||||
fmt.Sprintf("option go_package = %q;", g.localGoPackage.Name),
|
||||
}
|
||||
}
|
||||
return []string{
|
||||
"option (gogoproto.marshaler_all) = true;",
|
||||
"option (gogoproto.stable_marshaler_all) = true;",
|
||||
"option (gogoproto.sizer_all) = true;",
|
||||
"option (gogoproto.goproto_stringer_all) = false;",
|
||||
"option (gogoproto.stringer_all) = true;",
|
||||
"option (gogoproto.unmarshaler_all) = true;",
|
||||
"option (gogoproto.goproto_unrecognized_all) = false;",
|
||||
"option (gogoproto.goproto_enum_prefix_all) = false;",
|
||||
"option (gogoproto.goproto_getters_all) = false;",
|
||||
fmt.Sprintf("option go_package = %q;", g.localGoPackage.Name),
|
||||
}
|
||||
}
|
||||
func (g *genProtoIDL) Filename() string { return g.OptionalName + ".proto" }
|
||||
func (g *genProtoIDL) FileType() string { return "protoidl" }
|
||||
func (g *genProtoIDL) Namers(c *generator.Context) namer.NameSystems {
|
||||
return namer.NameSystems{
|
||||
// The local namer returns the correct protobuf name for a proto type
|
||||
// in the context of a package
|
||||
"local": localNamer{g.localPackage},
|
||||
}
|
||||
}
|
||||
|
||||
// Filter ignores types that are identified as not exportable.
|
||||
func (g *genProtoIDL) Filter(c *generator.Context, t *types.Type) bool {
|
||||
tagVals := types.ExtractCommentTags("+", t.CommentLines)["protobuf"]
|
||||
if tagVals != nil {
|
||||
if tagVals[0] == "false" {
|
||||
// Type specified "false".
|
||||
return false
|
||||
}
|
||||
if tagVals[0] == "true" {
|
||||
// Type specified "true".
|
||||
return true
|
||||
}
|
||||
klog.Fatalf(`Comment tag "protobuf" must be true or false, found: %q`, tagVals[0])
|
||||
}
|
||||
if !g.generateAll {
|
||||
// We're not generating everything.
|
||||
return false
|
||||
}
|
||||
seen := map[*types.Type]bool{}
|
||||
ok := isProtoable(seen, t)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isProtoable(seen map[*types.Type]bool, t *types.Type) bool {
|
||||
if seen[t] {
|
||||
// be optimistic in the case of type cycles.
|
||||
return true
|
||||
}
|
||||
seen[t] = true
|
||||
switch t.Kind {
|
||||
case types.Builtin:
|
||||
return true
|
||||
case types.Alias:
|
||||
return isProtoable(seen, t.Underlying)
|
||||
case types.Slice, types.Pointer:
|
||||
return isProtoable(seen, t.Elem)
|
||||
case types.Map:
|
||||
return isProtoable(seen, t.Key) && isProtoable(seen, t.Elem)
|
||||
case types.Struct:
|
||||
if len(t.Members) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, m := range t.Members {
|
||||
if isProtoable(seen, m.Type) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case types.Func, types.Chan:
|
||||
return false
|
||||
case types.DeclarationOf, types.Unknown, types.Unsupported:
|
||||
return false
|
||||
case types.Interface:
|
||||
return false
|
||||
default:
|
||||
log.Printf("WARNING: type %q is not portable: %s", t.Kind, t.Name)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// isOptionalAlias should return true if the specified type has an underlying type
|
||||
// (is an alias) of a map or slice and has the comment tag protobuf.nullable=true,
|
||||
// indicating that the type should be nullable in protobuf.
|
||||
func isOptionalAlias(t *types.Type) bool {
|
||||
if t.Underlying == nil || (t.Underlying.Kind != types.Map && t.Underlying.Kind != types.Slice) {
|
||||
return false
|
||||
}
|
||||
if extractBoolTagOrDie("protobuf.nullable", t.CommentLines) == false {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *genProtoIDL) Imports(c *generator.Context) (imports []string) {
|
||||
lines := []string{}
|
||||
// TODO: this could be expressed more cleanly
|
||||
for _, line := range g.imports.ImportLines() {
|
||||
if g.omitGogo && line == "github.com/gogo/protobuf/gogoproto/gogo.proto" {
|
||||
continue
|
||||
}
|
||||
lines = append(lines, line)
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
// GenerateType makes the body of a file implementing a set for type t.
|
||||
func (g *genProtoIDL) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
|
||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||
b := bodyGen{
|
||||
locator: &protobufLocator{
|
||||
namer: c.Namers["proto"].(ProtobufFromGoNamer),
|
||||
tracker: g.imports,
|
||||
universe: c.Universe,
|
||||
|
||||
localGoPackage: g.localGoPackage.Package,
|
||||
},
|
||||
localPackage: g.localPackage,
|
||||
|
||||
omitGogo: g.omitGogo,
|
||||
omitFieldTypes: g.omitFieldTypes,
|
||||
|
||||
t: t,
|
||||
}
|
||||
switch t.Kind {
|
||||
case types.Alias:
|
||||
return b.doAlias(sw)
|
||||
case types.Struct:
|
||||
return b.doStruct(sw)
|
||||
default:
|
||||
return b.unknown(sw)
|
||||
}
|
||||
}
|
||||
|
||||
// ProtobufFromGoNamer finds the protobuf name of a type (and its package, and
|
||||
// the package path) from its Go name.
|
||||
type ProtobufFromGoNamer interface {
|
||||
GoNameToProtoName(name types.Name) types.Name
|
||||
}
|
||||
|
||||
type ProtobufLocator interface {
|
||||
ProtoTypeFor(t *types.Type) (*types.Type, error)
|
||||
GoTypeForName(name types.Name) *types.Type
|
||||
CastTypeName(name types.Name) string
|
||||
}
|
||||
|
||||
type protobufLocator struct {
|
||||
namer ProtobufFromGoNamer
|
||||
tracker namer.ImportTracker
|
||||
universe types.Universe
|
||||
|
||||
localGoPackage string
|
||||
}
|
||||
|
||||
// CastTypeName returns the cast type name of a Go type
|
||||
// TODO: delegate to a new localgo namer?
|
||||
func (p protobufLocator) CastTypeName(name types.Name) string {
|
||||
if name.Package == p.localGoPackage {
|
||||
return name.Name
|
||||
}
|
||||
return name.String()
|
||||
}
|
||||
|
||||
func (p protobufLocator) GoTypeForName(name types.Name) *types.Type {
|
||||
if len(name.Package) == 0 {
|
||||
name.Package = p.localGoPackage
|
||||
}
|
||||
return p.universe.Type(name)
|
||||
}
|
||||
|
||||
// ProtoTypeFor locates a Protobuf type for the provided Go type (if possible).
|
||||
func (p protobufLocator) ProtoTypeFor(t *types.Type) (*types.Type, error) {
|
||||
switch {
|
||||
// we've already converted the type, or it's a map
|
||||
case t.Kind == types.Protobuf || t.Kind == types.Map:
|
||||
p.tracker.AddType(t)
|
||||
return t, nil
|
||||
}
|
||||
// it's a fundamental type
|
||||
if t, ok := isFundamentalProtoType(t); ok {
|
||||
p.tracker.AddType(t)
|
||||
return t, nil
|
||||
}
|
||||
// it's a message
|
||||
if t.Kind == types.Struct || isOptionalAlias(t) {
|
||||
t := &types.Type{
|
||||
Name: p.namer.GoNameToProtoName(t.Name),
|
||||
Kind: types.Protobuf,
|
||||
|
||||
CommentLines: t.CommentLines,
|
||||
}
|
||||
p.tracker.AddType(t)
|
||||
return t, nil
|
||||
}
|
||||
return nil, errUnrecognizedType
|
||||
}
|
||||
|
||||
type bodyGen struct {
|
||||
locator ProtobufLocator
|
||||
localPackage types.Name
|
||||
omitGogo bool
|
||||
omitFieldTypes map[types.Name]struct{}
|
||||
|
||||
t *types.Type
|
||||
}
|
||||
|
||||
func (b bodyGen) unknown(sw *generator.SnippetWriter) error {
|
||||
return fmt.Errorf("not sure how to generate: %#v", b.t)
|
||||
}
|
||||
|
||||
func (b bodyGen) doAlias(sw *generator.SnippetWriter) error {
|
||||
if !isOptionalAlias(b.t) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var kind string
|
||||
switch b.t.Underlying.Kind {
|
||||
case types.Map:
|
||||
kind = "map"
|
||||
default:
|
||||
kind = "slice"
|
||||
}
|
||||
optional := &types.Type{
|
||||
Name: b.t.Name,
|
||||
Kind: types.Struct,
|
||||
|
||||
CommentLines: b.t.CommentLines,
|
||||
SecondClosestCommentLines: b.t.SecondClosestCommentLines,
|
||||
Members: []types.Member{
|
||||
{
|
||||
Name: "Items",
|
||||
CommentLines: []string{fmt.Sprintf("items, if empty, will result in an empty %s\n", kind)},
|
||||
Type: b.t.Underlying,
|
||||
},
|
||||
},
|
||||
}
|
||||
nested := b
|
||||
nested.t = optional
|
||||
return nested.doStruct(sw)
|
||||
}
|
||||
|
||||
func (b bodyGen) doStruct(sw *generator.SnippetWriter) error {
|
||||
if len(b.t.Name.Name) == 0 {
|
||||
return nil
|
||||
}
|
||||
if namer.IsPrivateGoName(b.t.Name.Name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var alias *types.Type
|
||||
var fields []protoField
|
||||
options := []string{}
|
||||
allOptions := types.ExtractCommentTags("+", b.t.CommentLines)
|
||||
for k, v := range allOptions {
|
||||
switch {
|
||||
case strings.HasPrefix(k, "protobuf.options."):
|
||||
key := strings.TrimPrefix(k, "protobuf.options.")
|
||||
switch key {
|
||||
case "marshal":
|
||||
if v[0] == "false" {
|
||||
if !b.omitGogo {
|
||||
options = append(options,
|
||||
"(gogoproto.marshaler) = false",
|
||||
"(gogoproto.unmarshaler) = false",
|
||||
"(gogoproto.sizer) = false",
|
||||
)
|
||||
}
|
||||
}
|
||||
default:
|
||||
if !b.omitGogo || !strings.HasPrefix(key, "(gogoproto.") {
|
||||
if key == "(gogoproto.goproto_stringer)" && v[0] == "false" {
|
||||
options = append(options, "(gogoproto.stringer) = false")
|
||||
}
|
||||
options = append(options, fmt.Sprintf("%s = %s", key, v[0]))
|
||||
}
|
||||
}
|
||||
// protobuf.as allows a type to have the same message contents as another Go type
|
||||
case k == "protobuf.as":
|
||||
fields = nil
|
||||
if alias = b.locator.GoTypeForName(types.Name{Name: v[0]}); alias == nil {
|
||||
return fmt.Errorf("type %v references alias %q which does not exist", b.t, v[0])
|
||||
}
|
||||
// protobuf.embed instructs the generator to use the named type in this package
|
||||
// as an embedded message.
|
||||
case k == "protobuf.embed":
|
||||
fields = []protoField{
|
||||
{
|
||||
Tag: 1,
|
||||
Name: v[0],
|
||||
Type: &types.Type{
|
||||
Name: types.Name{
|
||||
Name: v[0],
|
||||
Package: b.localPackage.Package,
|
||||
Path: b.localPackage.Path,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
if alias == nil {
|
||||
alias = b.t
|
||||
}
|
||||
|
||||
// If we don't explicitly embed anything, generate fields by traversing fields.
|
||||
if fields == nil {
|
||||
memberFields, err := membersToFields(b.locator, alias, b.localPackage, b.omitFieldTypes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("type %v cannot be converted to protobuf: %v", b.t, err)
|
||||
}
|
||||
fields = memberFields
|
||||
}
|
||||
|
||||
out := sw.Out()
|
||||
genComment(out, b.t.CommentLines, "")
|
||||
sw.Do(`message $.Name.Name$ {
|
||||
`, b.t)
|
||||
|
||||
if len(options) > 0 {
|
||||
sort.Sort(sort.StringSlice(options))
|
||||
for _, s := range options {
|
||||
fmt.Fprintf(out, " option %s;\n", s)
|
||||
}
|
||||
fmt.Fprintln(out)
|
||||
}
|
||||
|
||||
for i, field := range fields {
|
||||
genComment(out, field.CommentLines, " ")
|
||||
fmt.Fprintf(out, " ")
|
||||
switch {
|
||||
case field.Map:
|
||||
case field.Repeated:
|
||||
fmt.Fprintf(out, "repeated ")
|
||||
case field.Required:
|
||||
fmt.Fprintf(out, "required ")
|
||||
default:
|
||||
fmt.Fprintf(out, "optional ")
|
||||
}
|
||||
sw.Do(`$.Type|local$ $.Name$ = $.Tag$`, field)
|
||||
if len(field.Extras) > 0 {
|
||||
extras := []string{}
|
||||
for k, v := range field.Extras {
|
||||
if b.omitGogo && strings.HasPrefix(k, "(gogoproto.") {
|
||||
continue
|
||||
}
|
||||
extras = append(extras, fmt.Sprintf("%s = %s", k, v))
|
||||
}
|
||||
sort.Sort(sort.StringSlice(extras))
|
||||
if len(extras) > 0 {
|
||||
fmt.Fprintf(out, " [")
|
||||
fmt.Fprint(out, strings.Join(extras, ", "))
|
||||
fmt.Fprintf(out, "]")
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(out, ";\n")
|
||||
if i != len(fields)-1 {
|
||||
fmt.Fprintf(out, "\n")
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(out, "}\n\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type protoField struct {
|
||||
LocalPackage types.Name
|
||||
|
||||
Tag int
|
||||
Name string
|
||||
Type *types.Type
|
||||
Map bool
|
||||
Repeated bool
|
||||
Optional bool
|
||||
Required bool
|
||||
Nullable bool
|
||||
Extras map[string]string
|
||||
|
||||
CommentLines []string
|
||||
}
|
||||
|
||||
var (
|
||||
errUnrecognizedType = fmt.Errorf("did not recognize the provided type")
|
||||
)
|
||||
|
||||
func isFundamentalProtoType(t *types.Type) (*types.Type, bool) {
|
||||
// TODO: when we enable proto3, also include other fundamental types in the google.protobuf package
|
||||
// switch {
|
||||
// case t.Kind == types.Struct && t.Name == types.Name{Package: "time", Name: "Time"}:
|
||||
// return &types.Type{
|
||||
// Kind: types.Protobuf,
|
||||
// Name: types.Name{Path: "google/protobuf/timestamp.proto", Package: "google.protobuf", Name: "Timestamp"},
|
||||
// }, true
|
||||
// }
|
||||
switch t.Kind {
|
||||
case types.Slice:
|
||||
if t.Elem.Name.Name == "byte" && len(t.Elem.Name.Package) == 0 {
|
||||
return &types.Type{Name: types.Name{Name: "bytes"}, Kind: types.Protobuf}, true
|
||||
}
|
||||
case types.Builtin:
|
||||
switch t.Name.Name {
|
||||
case "string", "uint32", "int32", "uint64", "int64", "bool":
|
||||
return &types.Type{Name: types.Name{Name: t.Name.Name}, Kind: types.Protobuf}, true
|
||||
case "int":
|
||||
return &types.Type{Name: types.Name{Name: "int64"}, Kind: types.Protobuf}, true
|
||||
case "uint":
|
||||
return &types.Type{Name: types.Name{Name: "uint64"}, Kind: types.Protobuf}, true
|
||||
case "float64", "float":
|
||||
return &types.Type{Name: types.Name{Name: "double"}, Kind: types.Protobuf}, true
|
||||
case "float32":
|
||||
return &types.Type{Name: types.Name{Name: "float"}, Kind: types.Protobuf}, true
|
||||
case "uintptr":
|
||||
return &types.Type{Name: types.Name{Name: "uint64"}, Kind: types.Protobuf}, true
|
||||
}
|
||||
// TODO: complex?
|
||||
}
|
||||
return t, false
|
||||
}
|
||||
|
||||
func memberTypeToProtobufField(locator ProtobufLocator, field *protoField, t *types.Type) error {
|
||||
var err error
|
||||
switch t.Kind {
|
||||
case types.Protobuf:
|
||||
field.Type, err = locator.ProtoTypeFor(t)
|
||||
case types.Builtin:
|
||||
field.Type, err = locator.ProtoTypeFor(t)
|
||||
case types.Map:
|
||||
valueField := &protoField{}
|
||||
if err := memberTypeToProtobufField(locator, valueField, t.Elem); err != nil {
|
||||
return err
|
||||
}
|
||||
keyField := &protoField{}
|
||||
if err := memberTypeToProtobufField(locator, keyField, t.Key); err != nil {
|
||||
return err
|
||||
}
|
||||
// All other protobuf types have kind types.Protobuf, so setting types.Map
|
||||
// here would be very misleading.
|
||||
field.Type = &types.Type{
|
||||
Kind: types.Protobuf,
|
||||
Key: keyField.Type,
|
||||
Elem: valueField.Type,
|
||||
}
|
||||
if !strings.HasPrefix(t.Name.Name, "map[") {
|
||||
field.Extras["(gogoproto.casttype)"] = strconv.Quote(locator.CastTypeName(t.Name))
|
||||
}
|
||||
if k, ok := keyField.Extras["(gogoproto.casttype)"]; ok {
|
||||
field.Extras["(gogoproto.castkey)"] = k
|
||||
}
|
||||
if v, ok := valueField.Extras["(gogoproto.casttype)"]; ok {
|
||||
field.Extras["(gogoproto.castvalue)"] = v
|
||||
}
|
||||
field.Map = true
|
||||
case types.Pointer:
|
||||
if err := memberTypeToProtobufField(locator, field, t.Elem); err != nil {
|
||||
return err
|
||||
}
|
||||
field.Nullable = true
|
||||
case types.Alias:
|
||||
if isOptionalAlias(t) {
|
||||
field.Type, err = locator.ProtoTypeFor(t)
|
||||
field.Nullable = true
|
||||
} else {
|
||||
if err := memberTypeToProtobufField(locator, field, t.Underlying); err != nil {
|
||||
log.Printf("failed to alias: %s %s: err %v", t.Name, t.Underlying.Name, err)
|
||||
return err
|
||||
}
|
||||
// If this is not an alias to a slice, cast to the alias
|
||||
if !field.Repeated {
|
||||
if field.Extras == nil {
|
||||
field.Extras = make(map[string]string)
|
||||
}
|
||||
field.Extras["(gogoproto.casttype)"] = strconv.Quote(locator.CastTypeName(t.Name))
|
||||
}
|
||||
}
|
||||
case types.Slice:
|
||||
if t.Elem.Name.Name == "byte" && len(t.Elem.Name.Package) == 0 {
|
||||
field.Type = &types.Type{Name: types.Name{Name: "bytes"}, Kind: types.Protobuf}
|
||||
return nil
|
||||
}
|
||||
if err := memberTypeToProtobufField(locator, field, t.Elem); err != nil {
|
||||
return err
|
||||
}
|
||||
field.Repeated = true
|
||||
case types.Struct:
|
||||
if len(t.Name.Name) == 0 {
|
||||
return errUnrecognizedType
|
||||
}
|
||||
field.Type, err = locator.ProtoTypeFor(t)
|
||||
field.Nullable = false
|
||||
default:
|
||||
return errUnrecognizedType
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// protobufTagToField extracts information from an existing protobuf tag
|
||||
func protobufTagToField(tag string, field *protoField, m types.Member, t *types.Type, localPackage types.Name) error {
|
||||
if len(tag) == 0 || tag == "-" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// protobuf:"bytes,3,opt,name=Id,customtype=github.com/gogo/protobuf/test.Uuid"
|
||||
parts := strings.Split(tag, ",")
|
||||
if len(parts) < 3 {
|
||||
return fmt.Errorf("member %q of %q malformed 'protobuf' tag, not enough segments\n", m.Name, t.Name)
|
||||
}
|
||||
protoTag, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("member %q of %q malformed 'protobuf' tag, field ID is %q which is not an integer: %v\n", m.Name, t.Name, parts[1], err)
|
||||
}
|
||||
field.Tag = protoTag
|
||||
|
||||
// In general there is doesn't make sense to parse the protobuf tags to get the type,
|
||||
// as all auto-generated once will have wire type "bytes", "varint" or "fixed64".
|
||||
// However, sometimes we explicitly set them to have a custom serialization, e.g.:
|
||||
// type Time struct {
|
||||
// time.Time `protobuf:"Timestamp,1,req,name=time"`
|
||||
// }
|
||||
// to force the generator to use a given type (that we manually wrote serialization &
|
||||
// deserialization methods for).
|
||||
switch parts[0] {
|
||||
case "varint", "fixed32", "fixed64", "bytes", "group":
|
||||
default:
|
||||
name := types.Name{}
|
||||
if last := strings.LastIndex(parts[0], "."); last != -1 {
|
||||
prefix := parts[0][:last]
|
||||
name = types.Name{
|
||||
Name: parts[0][last+1:],
|
||||
Package: prefix,
|
||||
Path: strings.Replace(prefix, ".", "/", -1),
|
||||
}
|
||||
} else {
|
||||
name = types.Name{
|
||||
Name: parts[0],
|
||||
Package: localPackage.Package,
|
||||
Path: localPackage.Path,
|
||||
}
|
||||
}
|
||||
field.Type = &types.Type{
|
||||
Name: name,
|
||||
Kind: types.Protobuf,
|
||||
}
|
||||
}
|
||||
|
||||
protoExtra := make(map[string]string)
|
||||
for i, extra := range parts[3:] {
|
||||
parts := strings.SplitN(extra, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("member %q of %q malformed 'protobuf' tag, tag %d should be key=value, got %q\n", m.Name, t.Name, i+4, extra)
|
||||
}
|
||||
switch parts[0] {
|
||||
case "name":
|
||||
protoExtra[parts[0]] = parts[1]
|
||||
case "casttype", "castkey", "castvalue":
|
||||
parts[0] = fmt.Sprintf("(gogoproto.%s)", parts[0])
|
||||
protoExtra[parts[0]] = strconv.Quote(parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
field.Extras = protoExtra
|
||||
if name, ok := protoExtra["name"]; ok {
|
||||
field.Name = name
|
||||
delete(protoExtra, "name")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func membersToFields(locator ProtobufLocator, t *types.Type, localPackage types.Name, omitFieldTypes map[types.Name]struct{}) ([]protoField, error) {
|
||||
fields := []protoField{}
|
||||
|
||||
for _, m := range t.Members {
|
||||
if namer.IsPrivateGoName(m.Name) {
|
||||
// skip private fields
|
||||
continue
|
||||
}
|
||||
if _, ok := omitFieldTypes[types.Name{Name: m.Type.Name.Name, Package: m.Type.Name.Package}]; ok {
|
||||
continue
|
||||
}
|
||||
tags := reflect.StructTag(m.Tags)
|
||||
field := protoField{
|
||||
LocalPackage: localPackage,
|
||||
|
||||
Tag: -1,
|
||||
Extras: make(map[string]string),
|
||||
}
|
||||
|
||||
protobufTag := tags.Get("protobuf")
|
||||
if protobufTag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := protobufTagToField(protobufTag, &field, m, t, localPackage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// extract information from JSON field tag
|
||||
if tag := tags.Get("json"); len(tag) > 0 {
|
||||
parts := strings.Split(tag, ",")
|
||||
if len(field.Name) == 0 && len(parts[0]) != 0 {
|
||||
field.Name = parts[0]
|
||||
}
|
||||
if field.Tag == -1 && field.Name == "-" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if field.Type == nil {
|
||||
if err := memberTypeToProtobufField(locator, &field, m.Type); err != nil {
|
||||
return nil, fmt.Errorf("unable to embed type %q as field %q in %q: %v", m.Type, field.Name, t.Name, err)
|
||||
}
|
||||
}
|
||||
if len(field.Name) == 0 {
|
||||
field.Name = namer.IL(m.Name)
|
||||
}
|
||||
|
||||
if field.Map && field.Repeated {
|
||||
// maps cannot be repeated
|
||||
field.Repeated = false
|
||||
field.Nullable = true
|
||||
}
|
||||
|
||||
if !field.Nullable {
|
||||
field.Extras["(gogoproto.nullable)"] = "false"
|
||||
}
|
||||
if (field.Type.Name.Name == "bytes" && field.Type.Name.Package == "") || (field.Repeated && field.Type.Name.Package == "" && namer.IsPrivateGoName(field.Type.Name.Name)) {
|
||||
delete(field.Extras, "(gogoproto.nullable)")
|
||||
}
|
||||
if field.Name != m.Name {
|
||||
field.Extras["(gogoproto.customname)"] = strconv.Quote(m.Name)
|
||||
}
|
||||
field.CommentLines = m.CommentLines
|
||||
fields = append(fields, field)
|
||||
}
|
||||
|
||||
// assign tags
|
||||
highest := 0
|
||||
byTag := make(map[int]*protoField)
|
||||
// fields are in Go struct order, which we preserve
|
||||
for i := range fields {
|
||||
field := &fields[i]
|
||||
tag := field.Tag
|
||||
if tag != -1 {
|
||||
if existing, ok := byTag[tag]; ok {
|
||||
return nil, fmt.Errorf("field %q and %q both have tag %d", field.Name, existing.Name, tag)
|
||||
}
|
||||
byTag[tag] = field
|
||||
}
|
||||
if tag > highest {
|
||||
highest = tag
|
||||
}
|
||||
}
|
||||
// starting from the highest observed tag, assign new field tags
|
||||
for i := range fields {
|
||||
field := &fields[i]
|
||||
if field.Tag != -1 {
|
||||
continue
|
||||
}
|
||||
highest++
|
||||
field.Tag = highest
|
||||
byTag[field.Tag] = field
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
func genComment(out io.Writer, lines []string, indent string) {
|
||||
for {
|
||||
l := len(lines)
|
||||
if l == 0 || len(lines[l-1]) != 0 {
|
||||
break
|
||||
}
|
||||
lines = lines[:l-1]
|
||||
}
|
||||
for _, c := range lines {
|
||||
if len(c) == 0 {
|
||||
fmt.Fprintf(out, "%s//\n", indent) // avoid trailing whitespace
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(out, "%s// %s\n", indent, c)
|
||||
}
|
||||
}
|
||||
|
||||
func formatProtoFile(source []byte) ([]byte, error) {
|
||||
// TODO; Is there any protobuf formatter?
|
||||
return source, nil
|
||||
}
|
||||
|
||||
func assembleProtoFile(w io.Writer, f *generator.File) {
|
||||
w.Write(f.Header)
|
||||
|
||||
fmt.Fprint(w, "syntax = 'proto2';\n\n")
|
||||
|
||||
if len(f.PackageName) > 0 {
|
||||
fmt.Fprintf(w, "package %s;\n\n", f.PackageName)
|
||||
}
|
||||
|
||||
if len(f.Imports) > 0 {
|
||||
imports := []string{}
|
||||
for i := range f.Imports {
|
||||
imports = append(imports, i)
|
||||
}
|
||||
sort.Strings(imports)
|
||||
for _, s := range imports {
|
||||
fmt.Fprintf(w, "import %q;\n", s)
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
}
|
||||
|
||||
if f.Vars.Len() > 0 {
|
||||
fmt.Fprintf(w, "%s\n", f.Vars.String())
|
||||
}
|
||||
|
||||
w.Write(f.Body.Bytes())
|
||||
}
|
||||
|
||||
func NewProtoFile() *generator.DefaultFileType {
|
||||
return &generator.DefaultFileType{
|
||||
Format: formatProtoFile,
|
||||
Assemble: assembleProtoFile,
|
||||
}
|
||||
}
|
50
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/import_tracker.go
generated
vendored
50
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/import_tracker.go
generated
vendored
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package protobuf
|
||||
|
||||
import (
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
type ImportTracker struct {
|
||||
namer.DefaultImportTracker
|
||||
}
|
||||
|
||||
func NewImportTracker(local types.Name, typesToAdd ...*types.Type) *ImportTracker {
|
||||
tracker := namer.NewDefaultImportTracker(local)
|
||||
tracker.IsInvalidType = func(t *types.Type) bool { return t.Kind != types.Protobuf }
|
||||
tracker.LocalName = func(name types.Name) string { return name.Package }
|
||||
tracker.PrintImport = func(path, name string) string { return path }
|
||||
|
||||
tracker.AddTypes(typesToAdd...)
|
||||
return &ImportTracker{
|
||||
DefaultImportTracker: tracker,
|
||||
}
|
||||
}
|
||||
|
||||
// AddNullable ensures that support for the nullable Gogo-protobuf extension is added.
|
||||
func (tracker *ImportTracker) AddNullable() {
|
||||
tracker.AddType(&types.Type{
|
||||
Kind: types.Protobuf,
|
||||
Name: types.Name{
|
||||
Name: "nullable",
|
||||
Package: "gogoproto",
|
||||
Path: "github.com/gogo/protobuf/gogoproto/gogo.proto",
|
||||
},
|
||||
})
|
||||
}
|
208
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/namer.go
generated
vendored
208
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/namer.go
generated
vendored
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package protobuf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
type localNamer struct {
|
||||
localPackage types.Name
|
||||
}
|
||||
|
||||
func (n localNamer) Name(t *types.Type) string {
|
||||
if t.Key != nil && t.Elem != nil {
|
||||
return fmt.Sprintf("map<%s, %s>", n.Name(t.Key), n.Name(t.Elem))
|
||||
}
|
||||
if len(n.localPackage.Package) != 0 && n.localPackage.Package == t.Name.Package {
|
||||
return t.Name.Name
|
||||
}
|
||||
return t.Name.String()
|
||||
}
|
||||
|
||||
type protobufNamer struct {
|
||||
packages []*protobufPackage
|
||||
packagesByPath map[string]*protobufPackage
|
||||
}
|
||||
|
||||
func NewProtobufNamer() *protobufNamer {
|
||||
return &protobufNamer{
|
||||
packagesByPath: make(map[string]*protobufPackage),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *protobufNamer) Name(t *types.Type) string {
|
||||
if t.Kind == types.Map {
|
||||
return fmt.Sprintf("map<%s, %s>", n.Name(t.Key), n.Name(t.Elem))
|
||||
}
|
||||
return t.Name.String()
|
||||
}
|
||||
|
||||
func (n *protobufNamer) List() []generator.Package {
|
||||
packages := make([]generator.Package, 0, len(n.packages))
|
||||
for i := range n.packages {
|
||||
packages = append(packages, n.packages[i])
|
||||
}
|
||||
return packages
|
||||
}
|
||||
|
||||
func (n *protobufNamer) Add(p *protobufPackage) {
|
||||
if _, ok := n.packagesByPath[p.PackagePath]; !ok {
|
||||
n.packagesByPath[p.PackagePath] = p
|
||||
n.packages = append(n.packages, p)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *protobufNamer) GoNameToProtoName(name types.Name) types.Name {
|
||||
if p, ok := n.packagesByPath[name.Package]; ok {
|
||||
return types.Name{
|
||||
Name: name.Name,
|
||||
Package: p.PackageName,
|
||||
Path: p.ImportPath(),
|
||||
}
|
||||
}
|
||||
for _, p := range n.packages {
|
||||
if _, ok := p.FilterTypes[name]; ok {
|
||||
return types.Name{
|
||||
Name: name.Name,
|
||||
Package: p.PackageName,
|
||||
Path: p.ImportPath(),
|
||||
}
|
||||
}
|
||||
}
|
||||
return types.Name{Name: name.Name}
|
||||
}
|
||||
|
||||
func protoSafePackage(name string) string {
|
||||
pkg := strings.Replace(name, "/", ".", -1)
|
||||
return strings.Replace(pkg, "-", "_", -1)
|
||||
}
|
||||
|
||||
type typeNameSet map[types.Name]*protobufPackage
|
||||
|
||||
// assignGoTypeToProtoPackage looks for Go and Protobuf types that are referenced by a type in
|
||||
// a package. It will not recurse into protobuf types.
|
||||
func assignGoTypeToProtoPackage(p *protobufPackage, t *types.Type, local, global typeNameSet, optional map[types.Name]struct{}) {
|
||||
newT, isProto := isFundamentalProtoType(t)
|
||||
if isProto {
|
||||
t = newT
|
||||
}
|
||||
if otherP, ok := global[t.Name]; ok {
|
||||
if _, ok := local[t.Name]; !ok {
|
||||
p.Imports.AddType(&types.Type{
|
||||
Kind: types.Protobuf,
|
||||
Name: otherP.ProtoTypeName(),
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
if t.Name.Package == p.PackagePath {
|
||||
// Associate types only to their own package
|
||||
global[t.Name] = p
|
||||
}
|
||||
if _, ok := local[t.Name]; ok {
|
||||
return
|
||||
}
|
||||
// don't recurse into existing proto types
|
||||
if isProto {
|
||||
p.Imports.AddType(t)
|
||||
return
|
||||
}
|
||||
|
||||
local[t.Name] = p
|
||||
for _, m := range t.Members {
|
||||
if namer.IsPrivateGoName(m.Name) {
|
||||
continue
|
||||
}
|
||||
field := &protoField{}
|
||||
tag := reflect.StructTag(m.Tags).Get("protobuf")
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
if err := protobufTagToField(tag, field, m, t, p.ProtoTypeName()); err == nil && field.Type != nil {
|
||||
assignGoTypeToProtoPackage(p, field.Type, local, global, optional)
|
||||
continue
|
||||
}
|
||||
assignGoTypeToProtoPackage(p, m.Type, local, global, optional)
|
||||
}
|
||||
// TODO: should methods be walked?
|
||||
if t.Elem != nil {
|
||||
assignGoTypeToProtoPackage(p, t.Elem, local, global, optional)
|
||||
}
|
||||
if t.Key != nil {
|
||||
assignGoTypeToProtoPackage(p, t.Key, local, global, optional)
|
||||
}
|
||||
if t.Underlying != nil {
|
||||
if t.Kind == types.Alias && isOptionalAlias(t) {
|
||||
optional[t.Name] = struct{}{}
|
||||
}
|
||||
assignGoTypeToProtoPackage(p, t.Underlying, local, global, optional)
|
||||
}
|
||||
}
|
||||
|
||||
// isTypeApplicableToProtobuf checks to see if a type is relevant for protobuf processing.
|
||||
// Currently, it filters out functions and private types.
|
||||
func isTypeApplicableToProtobuf(t *types.Type) bool {
|
||||
// skip functions -- we don't care about them for protobuf
|
||||
if t.Kind == types.Func || (t.Kind == types.DeclarationOf && t.Underlying.Kind == types.Func) {
|
||||
return false
|
||||
}
|
||||
// skip private types
|
||||
if namer.IsPrivateGoName(t.Name.Name) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (n *protobufNamer) AssignTypesToPackages(c *generator.Context) error {
|
||||
global := make(typeNameSet)
|
||||
for _, p := range n.packages {
|
||||
local := make(typeNameSet)
|
||||
optional := make(map[types.Name]struct{})
|
||||
p.Imports = NewImportTracker(p.ProtoTypeName())
|
||||
for _, t := range c.Order {
|
||||
if t.Name.Package != p.PackagePath {
|
||||
continue
|
||||
}
|
||||
if !isTypeApplicableToProtobuf(t) {
|
||||
// skip types that we don't care about, like functions
|
||||
continue
|
||||
}
|
||||
assignGoTypeToProtoPackage(p, t, local, global, optional)
|
||||
}
|
||||
p.FilterTypes = make(map[types.Name]struct{})
|
||||
p.LocalNames = make(map[string]struct{})
|
||||
p.OptionalTypeNames = make(map[string]struct{})
|
||||
for k, v := range local {
|
||||
if v == p {
|
||||
p.FilterTypes[k] = struct{}{}
|
||||
p.LocalNames[k.Name] = struct{}{}
|
||||
if _, ok := optional[k]; ok {
|
||||
p.OptionalTypeNames[k.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
50
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/namer_test.go
generated
vendored
50
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/namer_test.go
generated
vendored
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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.
|
||||
*/
|
||||
|
||||
package protobuf
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestProtoSafePackage(t *testing.T) {
|
||||
tests := []struct {
|
||||
pkg string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
pkg: "foo",
|
||||
expected: "foo",
|
||||
},
|
||||
{
|
||||
pkg: "foo/bar",
|
||||
expected: "foo.bar",
|
||||
},
|
||||
{
|
||||
pkg: "foo/bar/baz",
|
||||
expected: "foo.bar.baz",
|
||||
},
|
||||
{
|
||||
pkg: "foo/bar-baz/x/y-z/q",
|
||||
expected: "foo.bar_baz.x.y_z.q",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
actual := protoSafePackage(test.pkg)
|
||||
if e, a := test.expected, actual; e != a {
|
||||
t.Errorf("%s: expected %s, got %s", test.pkg, e, a)
|
||||
}
|
||||
}
|
||||
}
|
215
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/package.go
generated
vendored
215
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/package.go
generated
vendored
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package protobuf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
func newProtobufPackage(packagePath, packageName string, generateAll bool, omitFieldTypes map[types.Name]struct{}) *protobufPackage {
|
||||
pkg := &protobufPackage{
|
||||
DefaultPackage: generator.DefaultPackage{
|
||||
// The protobuf package name (foo.bar.baz)
|
||||
PackageName: packageName,
|
||||
// A path segment relative to the GOPATH root (foo/bar/baz)
|
||||
PackagePath: packagePath,
|
||||
HeaderText: []byte(
|
||||
`
|
||||
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
|
||||
|
||||
`),
|
||||
PackageDocumentation: []byte(fmt.Sprintf(
|
||||
`// Package %s is an autogenerated protobuf IDL.
|
||||
`, packageName)),
|
||||
},
|
||||
GenerateAll: generateAll,
|
||||
OmitFieldTypes: omitFieldTypes,
|
||||
}
|
||||
pkg.FilterFunc = pkg.filterFunc
|
||||
pkg.GeneratorFunc = pkg.generatorFunc
|
||||
return pkg
|
||||
}
|
||||
|
||||
// protobufPackage contains the protobuf implementation of Package.
|
||||
type protobufPackage struct {
|
||||
generator.DefaultPackage
|
||||
|
||||
// If true, this package has been vendored into our source tree and thus can
|
||||
// only be generated by changing the vendor tree.
|
||||
Vendored bool
|
||||
|
||||
// If true, generate protobuf serializations for all public types.
|
||||
// If false, only generate protobuf serializations for structs that
|
||||
// request serialization.
|
||||
GenerateAll bool
|
||||
|
||||
// A list of types to filter to; if not specified all types will be included.
|
||||
FilterTypes map[types.Name]struct{}
|
||||
|
||||
// If true, omit any gogoprotobuf extensions not defined as types.
|
||||
OmitGogo bool
|
||||
|
||||
// A list of field types that will be excluded from the output struct
|
||||
OmitFieldTypes map[types.Name]struct{}
|
||||
|
||||
// A list of names that this package exports
|
||||
LocalNames map[string]struct{}
|
||||
|
||||
// A list of type names in this package that will need marshaller rewriting
|
||||
// to remove synthetic protobuf fields.
|
||||
OptionalTypeNames map[string]struct{}
|
||||
|
||||
// A list of struct tags to generate onto named struct fields
|
||||
StructTags map[string]map[string]string
|
||||
|
||||
// An import tracker for this package
|
||||
Imports *ImportTracker
|
||||
}
|
||||
|
||||
func (p *protobufPackage) Clean(outputBase string) error {
|
||||
for _, s := range []string{p.ImportPath(), p.OutputPath()} {
|
||||
if err := os.Remove(filepath.Join(outputBase, s)); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *protobufPackage) ProtoTypeName() types.Name {
|
||||
return types.Name{
|
||||
Name: p.Path(), // the go path "foo/bar/baz"
|
||||
Package: p.Name(), // the protobuf package "foo.bar.baz"
|
||||
Path: p.ImportPath(), // the path of the import to get the proto
|
||||
}
|
||||
}
|
||||
|
||||
func (p *protobufPackage) filterFunc(c *generator.Context, t *types.Type) bool {
|
||||
switch t.Kind {
|
||||
case types.Func, types.Chan:
|
||||
return false
|
||||
case types.Struct:
|
||||
if t.Name.Name == "struct{}" {
|
||||
return false
|
||||
}
|
||||
case types.Builtin:
|
||||
return false
|
||||
case types.Alias:
|
||||
if !isOptionalAlias(t) {
|
||||
return false
|
||||
}
|
||||
case types.Slice, types.Array, types.Map:
|
||||
return false
|
||||
case types.Pointer:
|
||||
return false
|
||||
}
|
||||
if _, ok := isFundamentalProtoType(t); ok {
|
||||
return false
|
||||
}
|
||||
_, ok := p.FilterTypes[t.Name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *protobufPackage) HasGoType(name string) bool {
|
||||
_, ok := p.LocalNames[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *protobufPackage) OptionalTypeName(name string) bool {
|
||||
_, ok := p.OptionalTypeNames[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *protobufPackage) ExtractGeneratedType(t *ast.TypeSpec) bool {
|
||||
if !p.HasGoType(t.Name.Name) {
|
||||
return false
|
||||
}
|
||||
|
||||
switch s := t.Type.(type) {
|
||||
case *ast.StructType:
|
||||
for i, f := range s.Fields.List {
|
||||
if len(f.Tag.Value) == 0 {
|
||||
continue
|
||||
}
|
||||
tag := strings.Trim(f.Tag.Value, "`")
|
||||
protobufTag := reflect.StructTag(tag).Get("protobuf")
|
||||
if len(protobufTag) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(f.Names) > 1 {
|
||||
log.Printf("WARNING: struct %s field %d %s: defined multiple names but single protobuf tag", t.Name.Name, i, f.Names[0].Name)
|
||||
// TODO hard error?
|
||||
}
|
||||
if p.StructTags == nil {
|
||||
p.StructTags = make(map[string]map[string]string)
|
||||
}
|
||||
m := p.StructTags[t.Name.Name]
|
||||
if m == nil {
|
||||
m = make(map[string]string)
|
||||
p.StructTags[t.Name.Name] = m
|
||||
}
|
||||
m[f.Names[0].Name] = tag
|
||||
}
|
||||
default:
|
||||
log.Printf("WARNING: unexpected Go AST type definition: %#v", t)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *protobufPackage) generatorFunc(c *generator.Context) []generator.Generator {
|
||||
generators := []generator.Generator{}
|
||||
|
||||
p.Imports.AddNullable()
|
||||
|
||||
generators = append(generators, &genProtoIDL{
|
||||
DefaultGen: generator.DefaultGen{
|
||||
OptionalName: "generated",
|
||||
},
|
||||
localPackage: types.Name{Package: p.PackageName, Path: p.PackagePath},
|
||||
localGoPackage: types.Name{Package: p.PackagePath, Name: p.GoPackageName()},
|
||||
imports: p.Imports,
|
||||
generateAll: p.GenerateAll,
|
||||
omitGogo: p.OmitGogo,
|
||||
omitFieldTypes: p.OmitFieldTypes,
|
||||
})
|
||||
return generators
|
||||
}
|
||||
|
||||
func (p *protobufPackage) GoPackageName() string {
|
||||
return filepath.Base(p.PackagePath)
|
||||
}
|
||||
|
||||
func (p *protobufPackage) ImportPath() string {
|
||||
return filepath.Join(p.PackagePath, "generated.proto")
|
||||
}
|
||||
|
||||
func (p *protobufPackage) OutputPath() string {
|
||||
return filepath.Join(p.PackagePath, "generated.pb.go")
|
||||
}
|
||||
|
||||
var (
|
||||
_ = generator.Package(&protobufPackage{})
|
||||
)
|
452
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/parser.go
generated
vendored
452
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/parser.go
generated
vendored
@@ -1,452 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package protobuf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
customreflect "k8s.io/code-generator/third_party/forked/golang/reflect"
|
||||
)
|
||||
|
||||
func rewriteFile(name string, header []byte, rewriteFn func(*token.FileSet, *ast.File) error) error {
|
||||
fset := token.NewFileSet()
|
||||
src, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := parser.ParseFile(fset, name, src, parser.DeclarationErrors|parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := rewriteFn(fset, file); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
b.Write(header)
|
||||
if err := printer.Fprint(b, fset, file); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := f.Write(body); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
// ExtractFunc extracts information from the provided TypeSpec and returns true if the type should be
|
||||
// removed from the destination file.
|
||||
type ExtractFunc func(*ast.TypeSpec) bool
|
||||
|
||||
// OptionalFunc returns true if the provided local name is a type that has protobuf.nullable=true
|
||||
// and should have its marshal functions adjusted to remove the 'Items' accessor.
|
||||
type OptionalFunc func(name string) bool
|
||||
|
||||
func RewriteGeneratedGogoProtobufFile(name string, extractFn ExtractFunc, optionalFn OptionalFunc, header []byte) error {
|
||||
return rewriteFile(name, header, func(fset *token.FileSet, file *ast.File) error {
|
||||
cmap := ast.NewCommentMap(fset, file, file.Comments)
|
||||
|
||||
// transform methods that point to optional maps or slices
|
||||
for _, d := range file.Decls {
|
||||
rewriteOptionalMethods(d, optionalFn)
|
||||
}
|
||||
|
||||
// remove types that are already declared
|
||||
decls := []ast.Decl{}
|
||||
for _, d := range file.Decls {
|
||||
if dropExistingTypeDeclarations(d, extractFn) {
|
||||
continue
|
||||
}
|
||||
if dropEmptyImportDeclarations(d) {
|
||||
continue
|
||||
}
|
||||
decls = append(decls, d)
|
||||
}
|
||||
file.Decls = decls
|
||||
|
||||
// remove unmapped comments
|
||||
file.Comments = cmap.Filter(file).Comments()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// rewriteOptionalMethods makes specific mutations to marshaller methods that belong to types identified
|
||||
// as being "optional" (they may be nil on the wire). This allows protobuf to serialize a map or slice and
|
||||
// properly discriminate between empty and nil (which is not possible in protobuf).
|
||||
// TODO: move into upstream gogo-protobuf once https://github.com/gogo/protobuf/issues/181
|
||||
// has agreement
|
||||
func rewriteOptionalMethods(decl ast.Decl, isOptional OptionalFunc) {
|
||||
switch t := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
ident, ptr, ok := receiver(t)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// correct initialization of the form `m.Field = &OptionalType{}` to
|
||||
// `m.Field = OptionalType{}`
|
||||
if t.Name.Name == "Unmarshal" {
|
||||
ast.Walk(optionalAssignmentVisitor{fn: isOptional}, t.Body)
|
||||
}
|
||||
|
||||
if !isOptional(ident.Name) {
|
||||
return
|
||||
}
|
||||
|
||||
switch t.Name.Name {
|
||||
case "Unmarshal":
|
||||
ast.Walk(&optionalItemsVisitor{}, t.Body)
|
||||
case "MarshalTo", "Size", "String":
|
||||
ast.Walk(&optionalItemsVisitor{}, t.Body)
|
||||
fallthrough
|
||||
case "Marshal":
|
||||
// if the method has a pointer receiver, set it back to a normal receiver
|
||||
if ptr {
|
||||
t.Recv.List[0].Type = ident
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type optionalAssignmentVisitor struct {
|
||||
fn OptionalFunc
|
||||
}
|
||||
|
||||
// Visit walks the provided node, transforming field initializations of the form
|
||||
// m.Field = &OptionalType{} -> m.Field = OptionalType{}
|
||||
func (v optionalAssignmentVisitor) Visit(n ast.Node) ast.Visitor {
|
||||
switch t := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
if len(t.Lhs) == 1 && len(t.Rhs) == 1 {
|
||||
if !isFieldSelector(t.Lhs[0], "m", "") {
|
||||
return nil
|
||||
}
|
||||
unary, ok := t.Rhs[0].(*ast.UnaryExpr)
|
||||
if !ok || unary.Op != token.AND {
|
||||
return nil
|
||||
}
|
||||
composite, ok := unary.X.(*ast.CompositeLit)
|
||||
if !ok || composite.Type == nil || len(composite.Elts) != 0 {
|
||||
return nil
|
||||
}
|
||||
if ident, ok := composite.Type.(*ast.Ident); ok && v.fn(ident.Name) {
|
||||
t.Rhs[0] = composite
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
type optionalItemsVisitor struct{}
|
||||
|
||||
// Visit walks the provided node, looking for specific patterns to transform that match
|
||||
// the effective outcome of turning struct{ map[x]y || []x } into map[x]y or []x.
|
||||
func (v *optionalItemsVisitor) Visit(n ast.Node) ast.Visitor {
|
||||
switch t := n.(type) {
|
||||
case *ast.RangeStmt:
|
||||
if isFieldSelector(t.X, "m", "Items") {
|
||||
t.X = &ast.Ident{Name: "m"}
|
||||
}
|
||||
case *ast.AssignStmt:
|
||||
if len(t.Lhs) == 1 && len(t.Rhs) == 1 {
|
||||
switch lhs := t.Lhs[0].(type) {
|
||||
case *ast.IndexExpr:
|
||||
if isFieldSelector(lhs.X, "m", "Items") {
|
||||
lhs.X = &ast.StarExpr{X: &ast.Ident{Name: "m"}}
|
||||
}
|
||||
default:
|
||||
if isFieldSelector(t.Lhs[0], "m", "Items") {
|
||||
t.Lhs[0] = &ast.StarExpr{X: &ast.Ident{Name: "m"}}
|
||||
}
|
||||
}
|
||||
switch rhs := t.Rhs[0].(type) {
|
||||
case *ast.CallExpr:
|
||||
if ident, ok := rhs.Fun.(*ast.Ident); ok && ident.Name == "append" {
|
||||
ast.Walk(v, rhs)
|
||||
if len(rhs.Args) > 0 {
|
||||
switch arg := rhs.Args[0].(type) {
|
||||
case *ast.Ident:
|
||||
if arg.Name == "m" {
|
||||
rhs.Args[0] = &ast.StarExpr{X: &ast.Ident{Name: "m"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.IfStmt:
|
||||
switch cond := t.Cond.(type) {
|
||||
case *ast.BinaryExpr:
|
||||
if cond.Op == token.EQL {
|
||||
if isFieldSelector(cond.X, "m", "Items") && isIdent(cond.Y, "nil") {
|
||||
cond.X = &ast.StarExpr{X: &ast.Ident{Name: "m"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
if t.Init != nil {
|
||||
// Find form:
|
||||
// if err := m[len(m.Items)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
|
||||
// return err
|
||||
// }
|
||||
switch s := t.Init.(type) {
|
||||
case *ast.AssignStmt:
|
||||
if call, ok := s.Rhs[0].(*ast.CallExpr); ok {
|
||||
if sel, ok := call.Fun.(*ast.SelectorExpr); ok {
|
||||
if x, ok := sel.X.(*ast.IndexExpr); ok {
|
||||
// m[] -> (*m)[]
|
||||
if sel2, ok := x.X.(*ast.SelectorExpr); ok {
|
||||
if ident, ok := sel2.X.(*ast.Ident); ok && ident.Name == "m" {
|
||||
x.X = &ast.StarExpr{X: &ast.Ident{Name: "m"}}
|
||||
}
|
||||
}
|
||||
// len(m.Items) -> len(*m)
|
||||
if bin, ok := x.Index.(*ast.BinaryExpr); ok {
|
||||
if call2, ok := bin.X.(*ast.CallExpr); ok && len(call2.Args) == 1 {
|
||||
if isFieldSelector(call2.Args[0], "m", "Items") {
|
||||
call2.Args[0] = &ast.StarExpr{X: &ast.Ident{Name: "m"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.IndexExpr:
|
||||
if isFieldSelector(t.X, "m", "Items") {
|
||||
t.X = &ast.Ident{Name: "m"}
|
||||
return nil
|
||||
}
|
||||
case *ast.CallExpr:
|
||||
changed := false
|
||||
for i := range t.Args {
|
||||
if isFieldSelector(t.Args[i], "m", "Items") {
|
||||
t.Args[i] = &ast.Ident{Name: "m"}
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func isFieldSelector(n ast.Expr, name, field string) bool {
|
||||
s, ok := n.(*ast.SelectorExpr)
|
||||
if !ok || s.Sel == nil || (field != "" && s.Sel.Name != field) {
|
||||
return false
|
||||
}
|
||||
return isIdent(s.X, name)
|
||||
}
|
||||
|
||||
func isIdent(n ast.Expr, value string) bool {
|
||||
ident, ok := n.(*ast.Ident)
|
||||
return ok && ident.Name == value
|
||||
}
|
||||
|
||||
func receiver(f *ast.FuncDecl) (ident *ast.Ident, pointer bool, ok bool) {
|
||||
if f.Recv == nil || len(f.Recv.List) != 1 {
|
||||
return nil, false, false
|
||||
}
|
||||
switch t := f.Recv.List[0].Type.(type) {
|
||||
case *ast.StarExpr:
|
||||
identity, ok := t.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return nil, false, false
|
||||
}
|
||||
return identity, true, true
|
||||
case *ast.Ident:
|
||||
return t, false, true
|
||||
}
|
||||
return nil, false, false
|
||||
}
|
||||
|
||||
// dropExistingTypeDeclarations removes any type declaration for which extractFn returns true. The function
|
||||
// returns true if the entire declaration should be dropped.
|
||||
func dropExistingTypeDeclarations(decl ast.Decl, extractFn ExtractFunc) bool {
|
||||
switch t := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
if t.Tok != token.TYPE {
|
||||
return false
|
||||
}
|
||||
specs := []ast.Spec{}
|
||||
for _, s := range t.Specs {
|
||||
switch spec := s.(type) {
|
||||
case *ast.TypeSpec:
|
||||
if extractFn(spec) {
|
||||
continue
|
||||
}
|
||||
specs = append(specs, spec)
|
||||
}
|
||||
}
|
||||
if len(specs) == 0 {
|
||||
return true
|
||||
}
|
||||
t.Specs = specs
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// dropEmptyImportDeclarations strips any generated but no-op imports from the generated code
|
||||
// to prevent generation from being able to define side-effects. The function returns true
|
||||
// if the entire declaration should be dropped.
|
||||
func dropEmptyImportDeclarations(decl ast.Decl) bool {
|
||||
switch t := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
if t.Tok != token.IMPORT {
|
||||
return false
|
||||
}
|
||||
specs := []ast.Spec{}
|
||||
for _, s := range t.Specs {
|
||||
switch spec := s.(type) {
|
||||
case *ast.ImportSpec:
|
||||
if spec.Name != nil && spec.Name.Name == "_" {
|
||||
continue
|
||||
}
|
||||
specs = append(specs, spec)
|
||||
}
|
||||
}
|
||||
if len(specs) == 0 {
|
||||
return true
|
||||
}
|
||||
t.Specs = specs
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func RewriteTypesWithProtobufStructTags(name string, structTags map[string]map[string]string) error {
|
||||
return rewriteFile(name, []byte{}, func(fset *token.FileSet, file *ast.File) error {
|
||||
allErrs := []error{}
|
||||
|
||||
// set any new struct tags
|
||||
for _, d := range file.Decls {
|
||||
if errs := updateStructTags(d, structTags, []string{"protobuf"}); len(errs) > 0 {
|
||||
allErrs = append(allErrs, errs...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(allErrs) > 0 {
|
||||
var s string
|
||||
for _, err := range allErrs {
|
||||
s += err.Error() + "\n"
|
||||
}
|
||||
return errors.New(s)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func updateStructTags(decl ast.Decl, structTags map[string]map[string]string, toCopy []string) []error {
|
||||
var errs []error
|
||||
t, ok := decl.(*ast.GenDecl)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if t.Tok != token.TYPE {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, s := range t.Specs {
|
||||
spec, ok := s.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
typeName := spec.Name.Name
|
||||
fieldTags, ok := structTags[typeName]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
st, ok := spec.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := range st.Fields.List {
|
||||
f := st.Fields.List[i]
|
||||
var name string
|
||||
if len(f.Names) == 0 {
|
||||
switch t := f.Type.(type) {
|
||||
case *ast.Ident:
|
||||
name = t.Name
|
||||
case *ast.SelectorExpr:
|
||||
name = t.Sel.Name
|
||||
default:
|
||||
errs = append(errs, fmt.Errorf("unable to get name for tag from struct %q, field %#v", spec.Name.Name, t))
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
name = f.Names[0].Name
|
||||
}
|
||||
value, ok := fieldTags[name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var tags customreflect.StructTags
|
||||
if f.Tag != nil {
|
||||
oldTags, err := customreflect.ParseStructTags(strings.Trim(f.Tag.Value, "`"))
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("unable to read struct tag from struct %q, field %q: %v", spec.Name.Name, name, err))
|
||||
continue
|
||||
}
|
||||
tags = oldTags
|
||||
}
|
||||
for _, name := range toCopy {
|
||||
// don't overwrite existing tags
|
||||
if tags.Has(name) {
|
||||
continue
|
||||
}
|
||||
// append new tags
|
||||
if v := reflect.StructTag(value).Get(name); len(v) > 0 {
|
||||
tags = append(tags, customreflect.StructTag{Name: name, Value: v})
|
||||
}
|
||||
}
|
||||
if len(tags) == 0 {
|
||||
continue
|
||||
}
|
||||
if f.Tag == nil {
|
||||
f.Tag = &ast.BasicLit{}
|
||||
}
|
||||
f.Tag.Value = tags.String()
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
33
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/tags.go
generated
vendored
33
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protobuf/tags.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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.
|
||||
*/
|
||||
|
||||
package protobuf
|
||||
|
||||
import (
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if
|
||||
// it exists, the value is boolean. If the tag did not exist, it returns
|
||||
// false.
|
||||
func extractBoolTagOrDie(key string, lines []string) bool {
|
||||
val, err := types.ExtractSingleBoolCommentTag("+", key, false, lines)
|
||||
if err != nil {
|
||||
klog.Fatal(err)
|
||||
}
|
||||
return val
|
||||
}
|
32
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo/main.go
generated
vendored
32
vendor/k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo/main.go
generated
vendored
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Package main defines the protoc-gen-gogo binary we use to generate our proto go files,
|
||||
// as well as takes dependencies on the correct gogo/protobuf packages for godeps.
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogo/protobuf/vanity/command"
|
||||
|
||||
// dependencies that are required for our packages
|
||||
_ "github.com/gogo/protobuf/gogoproto"
|
||||
_ "github.com/gogo/protobuf/proto"
|
||||
_ "github.com/gogo/protobuf/sortkeys"
|
||||
)
|
||||
|
||||
func main() {
|
||||
command.Write(command.Generate(command.Read()))
|
||||
}
|
1
vendor/k8s.io/code-generator/cmd/import-boss/.gitignore
generated
vendored
1
vendor/k8s.io/code-generator/cmd/import-boss/.gitignore
generated
vendored
@@ -1 +0,0 @@
|
||||
import-boss
|
90
vendor/k8s.io/code-generator/cmd/import-boss/main.go
generated
vendored
90
vendor/k8s.io/code-generator/cmd/import-boss/main.go
generated
vendored
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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.
|
||||
*/
|
||||
|
||||
// import-boss enforces import restrictions in a given repository.
|
||||
//
|
||||
// When a directory is verified, import-boss looks for a file called
|
||||
// ".import-restrictions". If this file is not found, parent directories will be
|
||||
// recursively searched.
|
||||
//
|
||||
// If an ".import-restrictions" file is found, then all imports of the package
|
||||
// are checked against each "rule" in the file. A rule consists of three parts:
|
||||
// * A SelectorRegexp, to select the import paths that the rule applies to.
|
||||
// * A list of AllowedPrefixes
|
||||
// * A list of ForbiddenPrefixes
|
||||
// An import is allowed if it matches at least one allowed prefix and does not
|
||||
// match any forbidden prefix. An example file looks like this:
|
||||
//
|
||||
// {
|
||||
// "Rules": [
|
||||
// {
|
||||
// "SelectorRegexp": "k8s[.]io",
|
||||
// "AllowedPrefixes": [
|
||||
// "k8s.io/gengo/examples",
|
||||
// "k8s.io/kubernetes/third_party"
|
||||
// ],
|
||||
// "ForbiddenPrefixes": [
|
||||
// "k8s.io/kubernetes/pkg/third_party/deprecated"
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// "SelectorRegexp": "^unsafe$",
|
||||
// "AllowedPrefixes": [
|
||||
// ],
|
||||
// "ForbiddenPrefixes": [
|
||||
// ""
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// Note the second block explicitly matches the unsafe package, and forbids it
|
||||
// ("" is a prefix of everything).
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/code-generator/pkg/util"
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/gengo/examples/import-boss/generators"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
klog.InitFlags(nil)
|
||||
arguments := args.Default()
|
||||
|
||||
// Override defaults.
|
||||
arguments.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath())
|
||||
arguments.InputDirs = []string{
|
||||
"k8s.io/kubernetes/pkg/...",
|
||||
"k8s.io/kubernetes/cmd/...",
|
||||
"k8s.io/kubernetes/plugin/...",
|
||||
}
|
||||
|
||||
if err := arguments.Execute(
|
||||
generators.NameSystems(),
|
||||
generators.DefaultNameSystem(),
|
||||
generators.Packages,
|
||||
); err != nil {
|
||||
klog.Errorf("Error: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
klog.V(2).Info("Completed successfully.")
|
||||
}
|
1
vendor/k8s.io/code-generator/cmd/lister-gen/.import-restrictions
generated
vendored
1
vendor/k8s.io/code-generator/cmd/lister-gen/.import-restrictions
generated
vendored
@@ -1 +0,0 @@
|
||||
{}
|
39
vendor/k8s.io/code-generator/cmd/register-gen/args/args.go
generated
vendored
39
vendor/k8s.io/code-generator/cmd/register-gen/args/args.go
generated
vendored
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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.
|
||||
*/
|
||||
|
||||
package args
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/gengo/args"
|
||||
)
|
||||
|
||||
// NewDefaults returns default arguments for the generator.
|
||||
func NewDefaults() *args.GeneratorArgs {
|
||||
genericArgs := args.Default().WithoutDefaultFlagParsing()
|
||||
genericArgs.OutputFileBaseName = "zz_generated.register"
|
||||
return genericArgs
|
||||
}
|
||||
|
||||
// Validate checks the given arguments.
|
||||
func Validate(genericArgs *args.GeneratorArgs) error {
|
||||
if len(genericArgs.OutputFileBaseName) == 0 {
|
||||
return fmt.Errorf("output file base name cannot be empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
137
vendor/k8s.io/code-generator/cmd/register-gen/generators/packages.go
generated
vendored
137
vendor/k8s.io/code-generator/cmd/register-gen/generators/packages.go
generated
vendored
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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.
|
||||
*/
|
||||
|
||||
package generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
// NameSystems returns the name system used by the generators in this package.
|
||||
func NameSystems() namer.NameSystems {
|
||||
return namer.NameSystems{}
|
||||
}
|
||||
|
||||
// DefaultNameSystem returns the default name system for ordering the types to be
|
||||
// processed by the generators in this package.
|
||||
func DefaultNameSystem() string {
|
||||
return "public"
|
||||
}
|
||||
|
||||
// Packages makes packages to generate.
|
||||
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
|
||||
boilerplate, err := arguments.LoadGoBoilerplate()
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed loading boilerplate: %v", err)
|
||||
}
|
||||
|
||||
packages := generator.Packages{}
|
||||
for _, inputDir := range arguments.InputDirs {
|
||||
pkg := context.Universe.Package(inputDir)
|
||||
internal, err := isInternal(pkg)
|
||||
if err != nil {
|
||||
klog.V(5).Infof("skipping the generation of %s file, due to err %v", arguments.OutputFileBaseName, err)
|
||||
continue
|
||||
}
|
||||
if internal {
|
||||
klog.V(5).Infof("skipping the generation of %s file because %s package contains internal types, note that internal types don't have \"json\" tags", arguments.OutputFileBaseName, pkg.Name)
|
||||
continue
|
||||
}
|
||||
registerFileName := "register.go"
|
||||
searchPath := path.Join(args.DefaultSourceTree(), inputDir, registerFileName)
|
||||
if _, err := os.Stat(path.Join(searchPath)); err == nil {
|
||||
klog.V(5).Infof("skipping the generation of %s file because %s already exists in the path %s", arguments.OutputFileBaseName, registerFileName, searchPath)
|
||||
continue
|
||||
} else if err != nil && !os.IsNotExist(err) {
|
||||
klog.Fatalf("an error %v has occurred while checking if %s exists", err, registerFileName)
|
||||
}
|
||||
|
||||
gv := clientgentypes.GroupVersion{}
|
||||
{
|
||||
pathParts := strings.Split(pkg.Path, "/")
|
||||
if len(pathParts) < 2 {
|
||||
klog.Errorf("the path of the package must contain the group name and the version, path = %s", pkg.Path)
|
||||
continue
|
||||
}
|
||||
gv.Group = clientgentypes.Group(pathParts[len(pathParts)-2])
|
||||
gv.Version = clientgentypes.Version(pathParts[len(pathParts)-1])
|
||||
|
||||
// if there is a comment of the form "// +groupName=somegroup" or "// +groupName=somegroup.foo.bar.io",
|
||||
// extract the fully qualified API group name from it and overwrite the group inferred from the package path
|
||||
if override := types.ExtractCommentTags("+", pkg.DocComments)["groupName"]; override != nil {
|
||||
groupName := override[0]
|
||||
klog.V(5).Infof("overriding the group name with = %s", groupName)
|
||||
gv.Group = clientgentypes.Group(groupName)
|
||||
}
|
||||
}
|
||||
|
||||
typesToRegister := []*types.Type{}
|
||||
for _, t := range pkg.Types {
|
||||
klog.V(5).Infof("considering type = %s", t.Name.String())
|
||||
for _, typeMember := range t.Members {
|
||||
if typeMember.Name == "TypeMeta" && typeMember.Embedded == true {
|
||||
typesToRegister = append(typesToRegister, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packages = append(packages,
|
||||
&generator.DefaultPackage{
|
||||
PackageName: pkg.Name,
|
||||
PackagePath: pkg.Path,
|
||||
HeaderText: boilerplate,
|
||||
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
|
||||
return []generator.Generator{
|
||||
®isterExternalGenerator{
|
||||
DefaultGen: generator.DefaultGen{
|
||||
OptionalName: arguments.OutputFileBaseName,
|
||||
},
|
||||
gv: gv,
|
||||
typesToGenerate: typesToRegister,
|
||||
outputPackage: pkg.Path,
|
||||
imports: generator.NewImportTracker(),
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return packages
|
||||
}
|
||||
|
||||
// isInternal determines whether the given package
|
||||
// contains the internal types or not
|
||||
func isInternal(p *types.Package) (bool, error) {
|
||||
for _, t := range p.Types {
|
||||
for _, member := range t.Members {
|
||||
if member.Name == "TypeMeta" {
|
||||
return !strings.Contains(member.Tags, "json"), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, fmt.Errorf("unable to find TypeMeta for any types in package %s", p.Path)
|
||||
}
|
117
vendor/k8s.io/code-generator/cmd/register-gen/generators/register_external.go
generated
vendored
117
vendor/k8s.io/code-generator/cmd/register-gen/generators/register_external.go
generated
vendored
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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.
|
||||
*/
|
||||
|
||||
package generators
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
type registerExternalGenerator struct {
|
||||
generator.DefaultGen
|
||||
outputPackage string
|
||||
gv clientgentypes.GroupVersion
|
||||
typesToGenerate []*types.Type
|
||||
imports namer.ImportTracker
|
||||
}
|
||||
|
||||
var _ generator.Generator = ®isterExternalGenerator{}
|
||||
|
||||
func (g *registerExternalGenerator) Filter(_ *generator.Context, _ *types.Type) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *registerExternalGenerator) Imports(c *generator.Context) (imports []string) {
|
||||
return g.imports.ImportLines()
|
||||
}
|
||||
|
||||
func (g *registerExternalGenerator) Namers(_ *generator.Context) namer.NameSystems {
|
||||
return namer.NameSystems{
|
||||
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *registerExternalGenerator) Finalize(context *generator.Context, w io.Writer) error {
|
||||
typesToGenerateOnlyNames := make([]string, len(g.typesToGenerate))
|
||||
for index, typeToGenerate := range g.typesToGenerate {
|
||||
typesToGenerateOnlyNames[index] = typeToGenerate.Name.Name
|
||||
}
|
||||
|
||||
// sort the list of types to register, so that the generator produces stable output
|
||||
sort.Strings(typesToGenerateOnlyNames)
|
||||
|
||||
sw := generator.NewSnippetWriter(w, context, "$", "$")
|
||||
m := map[string]interface{}{
|
||||
"groupName": g.gv.Group,
|
||||
"version": g.gv.Version,
|
||||
"types": typesToGenerateOnlyNames,
|
||||
"addToGroupVersion": context.Universe.Function(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "AddToGroupVersion"}),
|
||||
"groupVersion": context.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "GroupVersion"}),
|
||||
}
|
||||
sw.Do(registerExternalTypesTemplate, m)
|
||||
return sw.Error()
|
||||
}
|
||||
|
||||
var registerExternalTypesTemplate = `
|
||||
// GroupName specifies the group name used to register the objects.
|
||||
const GroupName = "$.groupName$"
|
||||
|
||||
// GroupVersion specifies the group and the version used to register the objects.
|
||||
var GroupVersion = $.groupVersion|raw${Group: GroupName, Version: "$.version$"}
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
// Deprecated: use GroupVersion instead.
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "$.version$"}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
var (
|
||||
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
// Depreciated: use Install instead
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
Install = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addKnownTypes)
|
||||
}
|
||||
|
||||
// Adds the list of known types to Scheme.
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
$range .types -$
|
||||
&$.${},
|
||||
$end$
|
||||
)
|
||||
// AddToGroupVersion allows the serialization of client types like ListOptions.
|
||||
$.addToGroupVersion|raw$(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
}
|
||||
`
|
53
vendor/k8s.io/code-generator/cmd/register-gen/main.go
generated
vendored
53
vendor/k8s.io/code-generator/cmd/register-gen/main.go
generated
vendored
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/klog"
|
||||
|
||||
generatorargs "k8s.io/code-generator/cmd/register-gen/args"
|
||||
"k8s.io/code-generator/cmd/register-gen/generators"
|
||||
"k8s.io/code-generator/pkg/util"
|
||||
"k8s.io/gengo/args"
|
||||
)
|
||||
|
||||
func main() {
|
||||
klog.InitFlags(nil)
|
||||
genericArgs := generatorargs.NewDefaults()
|
||||
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath())
|
||||
genericArgs.AddFlags(pflag.CommandLine)
|
||||
flag.Set("logtostderr", "true")
|
||||
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||
|
||||
pflag.Parse()
|
||||
if err := generatorargs.Validate(genericArgs); err != nil {
|
||||
klog.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
||||
if err := genericArgs.Execute(
|
||||
generators.NameSystems(),
|
||||
generators.DefaultNameSystem(),
|
||||
generators.Packages,
|
||||
); err != nil {
|
||||
klog.Fatalf("Error: %v", err)
|
||||
}
|
||||
klog.V(2).Info("Completed successfully.")
|
||||
}
|
1
vendor/k8s.io/code-generator/cmd/set-gen/.gitignore
generated
vendored
1
vendor/k8s.io/code-generator/cmd/set-gen/.gitignore
generated
vendored
@@ -1 +0,0 @@
|
||||
set-gen
|
56
vendor/k8s.io/code-generator/cmd/set-gen/main.go
generated
vendored
56
vendor/k8s.io/code-generator/cmd/set-gen/main.go
generated
vendored
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// set-gen is an example usage of gengo.
|
||||
//
|
||||
// Structs in the input directories with the below line in their comments will
|
||||
// have sets generated for them.
|
||||
// // +genset
|
||||
//
|
||||
// Any builtin type referenced anywhere in the input directories will have a
|
||||
// set generated for it.
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/code-generator/pkg/util"
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/gengo/examples/set-gen/generators"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
klog.InitFlags(nil)
|
||||
arguments := args.Default()
|
||||
|
||||
// Override defaults.
|
||||
arguments.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath())
|
||||
arguments.InputDirs = []string{"k8s.io/kubernetes/pkg/util/sets/types"}
|
||||
arguments.OutputPackagePath = "k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
if err := arguments.Execute(
|
||||
generators.NameSystems(),
|
||||
generators.DefaultNameSystem(),
|
||||
generators.Packages,
|
||||
); err != nil {
|
||||
klog.Errorf("Error: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
klog.V(2).Info("Completed successfully.")
|
||||
}
|
Reference in New Issue
Block a user