Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spawn gengo/v2 (KEP 4402) #259

Merged
merged 60 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
6a789c9
Fix bad 'find' args in verify
thockin Feb 24, 2024
130553f
Copy all code to v2, no changes
thockin Jan 25, 2024
63cd203
v2: Change all imports to reference v2
thockin Jan 25, 2024
70d4955
v2: Bump golang.org/x/tools and mod tidy
thockin Jan 25, 2024
4c6a0f3
v2: Run gofmt on all Go files
thockin Jan 25, 2024
ce1e343
v2: Run and fix golangci-lint
thockin Jan 25, 2024
b843a12
v2: Don't auto-set the Go header file
thockin Jan 25, 2024
f59fc19
v2: Remove set-gen: it's almost never used in k/k
thockin Jan 25, 2024
e06da46
v2: Add trivial gengo/v2 "tracer" example
thockin Jan 25, 2024
8e61ce4
v2: Add trivial same-package example (kilroy)
thockin Jan 25, 2024
92bda7a
v2: Add trivial other-pkg example (pointuh)
thockin Jan 25, 2024
7bd92cd
v2: Use Go's packages.Load()
thockin Jan 26, 2024
6064973
v2: Fix deepcopy-gen for v2
thockin Jan 26, 2024
d4d7c3c
v2: Fix defaulter-gen for v2
thockin Jan 26, 2024
ec9c476
v2: Fix import-boss for v2
thockin Jan 26, 2024
8cca9bb
v2: Fix tracer for v2
thockin Jan 26, 2024
dceb5f1
v2: Fix kilroy for v2
thockin Jan 26, 2024
9b09b53
v2: Fix pointuh for v2
thockin Jan 26, 2024
a118b3d
v2: Remove defunct references to "vendor"
thockin Jan 26, 2024
b291295
v2: Add a gengo/v2 README
thockin Jan 26, 2024
bb502ed
v2: Deepcopy: Better logging
thockin Jan 26, 2024
f06a6c1
v2: Remove unused functions
thockin Jan 26, 2024
3d886b1
v2: Remove defunct trim-path-prefix
thockin Jan 26, 2024
c682af1
v2: Make comment handling easier to follow
thockin Jan 26, 2024
5458972
v2: Use a type-switch in findTypesIn
thockin Jan 26, 2024
67e6f6a
v2: Don't mkdir in verify mode
thockin Jan 26, 2024
20e69f2
v2: Improve V(5) logging
thockin Jan 26, 2024
97504d1
v2: Tighten the API to parser: args >> fields
thockin Jan 26, 2024
2a2caee
v2: Rename DefaultPackage to SimplePackage
thockin Jan 27, 2024
08e97a3
v2: Drop `generator.Packages` type
thockin Jan 27, 2024
0f7c7f4
v2: Rename generator.Package -> Target
thockin Jan 27, 2024
1d55203
v2: Rename Target.SourcePath() -> Dir()
thockin Jan 27, 2024
be91502
v2: Rename DefaultGen -> GolangGenerator
thockin Jan 27, 2024
28180da
v2: Type-assert CustomArgs blindly
thockin Jan 27, 2024
3d41cf4
v2: Make the OutputBase flag each tool's problem
thockin Jan 27, 2024
b02b6ab
v2: Rename output-package -> output-pkg
thockin Jan 27, 2024
292d08b
v2: Move OutputFileBase flag to each tool
thockin Jan 27, 2024
e58ef8d
v2: Remove verify-only - unused and untested
thockin Jan 27, 2024
c80e16a
v2: Move go-header-file flag to each tool
thockin Jan 27, 2024
f90bbd9
v2: Get rid of WithoutDefaultFlagParsing
thockin Jan 27, 2024
eb27a87
v2: Get rid of import-boss (moved to k/k)
thockin Jan 27, 2024
ea6ce31
v2: Drop test-files support
thockin Jan 27, 2024
13c3e12
v2: Get rid of InputDirs flag - just use args
thockin Jan 27, 2024
8efa95a
v2: Get rid of deepcopy-gen (moved to k/k)
thockin Jan 28, 2024
b08ffa4
v2: Get rid of defaulter-gen (moved to k/k)
thockin Jan 28, 2024
ccc4c0d
v2: Get rid of CustomArgs entirely
thockin Jan 28, 2024
3446e13
v2: Get rid of the 'args' pkg
thockin Jan 28, 2024
8679c27
v2: Remove now-unused FlattenMembers
thockin Jan 28, 2024
d5e4a35
v2: Move utils from gengo/v2/types -> gengo/v2
thockin Jan 28, 2024
bb5d06d
v2: Rename 'tc' import to 'gotypes'
thockin Jan 28, 2024
e08acb0
v2: Drop importPathString type
thockin Jan 28, 2024
4d1b815
v2: Rename Builder -> Parser
thockin Jan 28, 2024
e246f57
v2: Rename SourcePath -> Dir
thockin Jan 28, 2024
d43ccb5
v2: No need to check nil iterating Inputs
thockin Jan 28, 2024
780d761
v2: Only log it the first time a package is added
thockin Jan 28, 2024
008dc98
v2: Adapt PR 244 for v2
thockin Jan 28, 2024
f79cce5
Add a top-level 'make test' for v0 and v2
thockin Feb 24, 2024
9d8f49d
v2: verify script chdir to v2 root
thockin Feb 24, 2024
483edb0
v2: s/Golang/Go/
thockin Feb 24, 2024
e7d5fc5
v2: Pass BuildTags in an Options struct
thockin Feb 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
all:
go build ./...
go -C v2 build ./...

test:
go test -race ./...
./hack/verify-examples.sh
go -C v2 test -race ./...
(cd v2 && ./hack/verify-examples.sh)
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
[GoReport]: https://goreportcard.com/badge/github.com/kubernetes/gengo
[GoReportStatus]: https://goreportcard.com/report/github.com/kubernetes/gengo

NOTE: [`k8s.io/gengo/v2`](https://github.com/kubernetes/gengo/tree/master/v2) is the current development module.

A package for generating things based on go files. This mechanism was first used
in [Kubernetes code-generator](https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/code-generator) and is split out here for ease of reuse and maintainability.

Expand Down
2 changes: 1 addition & 1 deletion hack/verify-examples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ echo "Removing generated code"
find ./examples -name "zz_generated.go" -type f -delete

# Delete set-gen tests
find ./examples/set-gen/sets -type f -maxdepth 1 -not -name "set_test.go" -not -name "doc.go" -delete
find ./examples/set-gen/sets -maxdepth 1 -type f -not -name "set_test.go" -not -name "doc.go" -delete

# Generate set-gen first since others depend on it
echo "Generating example output..."
Expand Down
53 changes: 53 additions & 0 deletions v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[![GoDoc Widget]][GoDoc] [![GoReport]][GoReportStatus]

[GoDoc]: https://godoc.org/k8s.io/gengo
[GoDoc Widget]: https://godoc.org/k8s.io/gengo?status.svg
[GoReport]: https://goreportcard.com/badge/github.com/kubernetes/gengo
[GoReportStatus]: https://goreportcard.com/report/github.com/kubernetes/gengo

# Gengo: a framework for building simple code generators
thockin marked this conversation as resolved.
Show resolved Hide resolved
thockin marked this conversation as resolved.
Show resolved Hide resolved

This repo is used by Kubernetes to build some codegen tooling. It is not
intended to be general-purpose and makes some assumptions that may not hold
outside of Kubernetes.

In the past this repo was partially supported for external use (outside of the
Kubernetes project overall), but that is no longer true. We may change the API
in incompatible ways, without warning.

If you are not building something that is part of Kubernetes, DO NOT DEPEND ON
THIS REPO.

## New usage within Kubernetes

Gengo is a very opinionated framework. It is primarily aimed at generating Go
code derived from types defined in other Go code, but it is possible to use it
for other things (e.g. proto files). Net new tools should consider using
`golang.org/x/tools/go/packages` directly. Gengo can serve as an example of
how to do that.

If you still decide you want to use gengo, see the
[simple examples](./examples) in this repo or the more extensive tools in the
Kubernetes [code-generator](https://github.com/kubernetes/code-generator/)
repo.

## Overview

Gengo is used to build tools (generally a tool is a binary). Each tool
describes some number of `Targets`. A target is a single output package, which
may be the same as the inputs (if the tool generates code alongside the inputs)
or different. Each `Target` describes some number of `Generators`. A
thockin marked this conversation as resolved.
Show resolved Hide resolved
generator is responsible for emitting a single file into the target directory.

Gengo helps the tool to load and process input packages, e.g. extracting type
information and associating comments. Each target will be offered every known
type, and can filter that down to the set of types it cares about. Each
generator will be offered the result of the target's filtering, and can filter
the set of types further. Finally, the generator will be called to emit code
for all of the remaining types.

The `tracer` example in this repo can be used to examine all of the hooks.

## Contributing

Please see [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on how to contribute.
16 changes: 16 additions & 0 deletions v2/boilerplate/boilerplate.go.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Copyright 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.
*/

Empty file.
83 changes: 83 additions & 0 deletions v2/comments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
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 gengo

import (
"fmt"
"strings"
)

// ExtractCommentTags parses comments for lines of the form:
//
// 'marker' + "key=value".
//
// Values are optional; "" is the default. A tag can be specified more than
// one time and all values are returned. If the resulting map has an entry for
// a key, the value (a slice) is guaranteed to have at least 1 element.
//
// Example: if you pass "+" for 'marker', and the following lines are in
// the comments:
//
// +foo=value1
// +bar
// +foo=value2
// +baz="qux"
//
// Then this function will return:
//
// map[string][]string{"foo":{"value1, "value2"}, "bar": {""}, "baz": {"qux"}}
func ExtractCommentTags(marker string, lines []string) map[string][]string {
out := map[string][]string{}
for _, line := range lines {
line = strings.Trim(line, " ")
if len(line) == 0 {
continue
}
if !strings.HasPrefix(line, marker) {
continue
}
// TODO: we could support multiple values per key if we split on spaces
kv := strings.SplitN(line[len(marker):], "=", 2)
if len(kv) == 2 {
out[kv[0]] = append(out[kv[0]], kv[1])
} else if len(kv) == 1 {
out[kv[0]] = append(out[kv[0]], "")
}
}
return out
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexzielenski Just wanted you to be aware this is here. I don't know if we'll need to follow up later to somehow reconcile this with kubernetes/kube-openapi#446

Copy link
Contributor

@alexzielenski alexzielenski Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we will since a key difference between the two is the kube-openapi version does not allow implicit arrays by duplicating markers which I dont think would be backwards compatible if we merged the two (unless it was a parser optoin or something, but at that rate they could be kept separate I think)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have one convention that applied to all tools. FWIW I didn't change this logic, just copied it over. :)


// ExtractSingleBoolCommentTag parses comments for lines of the form:
//
// 'marker' + "key=value1"
//
// If the tag is not found, the default value is returned. Values are asserted
// to be boolean ("true" or "false"), and any other value will cause an error
// to be returned. If the key has multiple values, the first one will be used.
func ExtractSingleBoolCommentTag(marker string, key string, defaultVal bool, lines []string) (bool, error) {
values := ExtractCommentTags(marker, lines)[key]
if values == nil {
return defaultVal, nil
}
if values[0] == "true" {
return true, nil
}
if values[0] == "false" {
return false, nil
}
return false, fmt.Errorf("tag value for %q is not boolean: %q", key, values[0])
}
86 changes: 86 additions & 0 deletions v2/comments_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
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 gengo

import (
"reflect"
"strings"
"testing"
)

func TestExtractCommentTags(t *testing.T) {
commentLines := []string{
"Human comment that is ignored.",
"+foo=value1",
"+bar",
"+foo=value2",
"+baz=qux,zrb=true",
}

a := ExtractCommentTags("+", commentLines)
e := map[string][]string{
"foo": {"value1", "value2"},
"bar": {""},
"baz": {"qux,zrb=true"},
}
if !reflect.DeepEqual(e, a) {
t.Errorf("Wanted %q, got %q", e, a)
}
}

func TestExtractSingleBoolCommentTag(t *testing.T) {
commentLines := []string{
"Human comment that is ignored.",
"+TRUE=true",
"+FALSE=false",
"+MULTI=true",
"+MULTI=false",
"+MULTI=multi",
"+NOTBOOL=blue",
"+EMPTY",
}

testCases := []struct {
key string
def bool
exp bool
err string // if set, ignore exp.
}{
{"TRUE", false, true, ""},
{"FALSE", true, false, ""},
{"MULTI", false, true, ""},
{"NOTBOOL", false, true, "is not boolean"},
{"EMPTY", false, true, "is not boolean"},
{"ABSENT", true, true, ""},
{"ABSENT", false, false, ""},
}

for i, tc := range testCases {
v, err := ExtractSingleBoolCommentTag("+", tc.key, tc.def, commentLines)
if err != nil && tc.err == "" {
t.Errorf("[%d]: unexpected failure: %v", i, err)
} else if err == nil && tc.err != "" {
t.Errorf("[%d]: expected failure: %v", i, tc.err)
} else if err != nil {
if !strings.Contains(err.Error(), tc.err) {
t.Errorf("[%d]: unexpected error: expected %q, got %q", i, tc.err, err)
}
} else if v != tc.exp {
t.Errorf("[%d]: unexpected value: expected %t, got %t", i, tc.exp, v)
}
}
}
Loading