2019-01-12 04:58:27 +00:00
|
|
|
/*
|
|
|
|
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 generator
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
|
|
|
|
"k8s.io/gengo/namer"
|
|
|
|
"k8s.io/gengo/parser"
|
|
|
|
"k8s.io/gengo/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Package contains the contract for generating a package.
|
|
|
|
type Package interface {
|
|
|
|
// Name returns the package short name.
|
|
|
|
Name() string
|
|
|
|
// Path returns the package import path.
|
|
|
|
Path() string
|
2019-12-12 01:27:03 +00:00
|
|
|
// SourcePath returns the location of the package on disk.
|
|
|
|
SourcePath() string
|
2019-01-12 04:58:27 +00:00
|
|
|
|
|
|
|
// Filter should return true if this package cares about this type.
|
|
|
|
// Otherwise, this type will be omitted from the type ordering for
|
|
|
|
// this package.
|
|
|
|
Filter(*Context, *types.Type) bool
|
|
|
|
|
|
|
|
// Header should return a header for the file, including comment markers.
|
|
|
|
// Useful for copyright notices and doc strings. Include an
|
|
|
|
// autogeneration notice! Do not include the "package x" line.
|
|
|
|
Header(filename string) []byte
|
|
|
|
|
|
|
|
// Generators returns the list of generators for this package. It is
|
|
|
|
// allowed for more than one generator to write to the same file.
|
|
|
|
// A Context is passed in case the list of generators depends on the
|
|
|
|
// input types.
|
|
|
|
Generators(*Context) []Generator
|
|
|
|
}
|
|
|
|
|
|
|
|
type File struct {
|
2019-12-12 01:27:03 +00:00
|
|
|
Name string
|
|
|
|
FileType string
|
|
|
|
PackageName string
|
|
|
|
Header []byte
|
|
|
|
PackagePath string
|
|
|
|
PackageSourcePath string
|
|
|
|
Imports map[string]struct{}
|
|
|
|
Vars bytes.Buffer
|
|
|
|
Consts bytes.Buffer
|
|
|
|
Body bytes.Buffer
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type FileType interface {
|
|
|
|
AssembleFile(f *File, path string) error
|
|
|
|
VerifyFile(f *File, path string) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Packages is a list of packages to generate.
|
|
|
|
type Packages []Package
|
|
|
|
|
|
|
|
// Generator is the contract for anything that wants to do auto-generation.
|
|
|
|
// It's expected that the io.Writers passed to the below functions will be
|
|
|
|
// ErrorTrackers; this allows implementations to not check for io errors,
|
|
|
|
// making more readable code.
|
|
|
|
//
|
|
|
|
// The call order for the functions that take a Context is:
|
|
|
|
// 1. Filter() // Subsequent calls see only types that pass this.
|
|
|
|
// 2. Namers() // Subsequent calls see the namers provided by this.
|
|
|
|
// 3. PackageVars()
|
|
|
|
// 4. PackageConsts()
|
|
|
|
// 5. Init()
|
|
|
|
// 6. GenerateType() // Called N times, once per type in the context's Order.
|
|
|
|
// 7. Imports()
|
|
|
|
//
|
|
|
|
// You may have multiple generators for the same file.
|
|
|
|
type Generator interface {
|
|
|
|
// The name of this generator. Will be included in generated comments.
|
|
|
|
Name() string
|
|
|
|
|
|
|
|
// Filter should return true if this generator cares about this type.
|
|
|
|
// (otherwise, GenerateType will not be called.)
|
|
|
|
//
|
|
|
|
// Filter is called before any of the generator's other functions;
|
|
|
|
// subsequent calls will get a context with only the types that passed
|
|
|
|
// this filter.
|
|
|
|
Filter(*Context, *types.Type) bool
|
|
|
|
|
|
|
|
// If this generator needs special namers, return them here. These will
|
|
|
|
// override the original namers in the context if there is a collision.
|
|
|
|
// You may return nil if you don't need special names. These names will
|
|
|
|
// be available in the context passed to the rest of the generator's
|
|
|
|
// functions.
|
|
|
|
//
|
|
|
|
// A use case for this is to return a namer that tracks imports.
|
|
|
|
Namers(*Context) namer.NameSystems
|
|
|
|
|
|
|
|
// Init should write an init function, and any other content that's not
|
|
|
|
// generated per-type. (It's not intended for generator specific
|
|
|
|
// initialization! Do that when your Package constructs the
|
|
|
|
// Generators.)
|
|
|
|
Init(*Context, io.Writer) error
|
|
|
|
|
|
|
|
// Finalize should write finish up functions, and any other content that's not
|
|
|
|
// generated per-type.
|
|
|
|
Finalize(*Context, io.Writer) error
|
|
|
|
|
|
|
|
// PackageVars should emit an array of variable lines. They will be
|
|
|
|
// placed in a var ( ... ) block. There's no need to include a leading
|
|
|
|
// \t or trailing \n.
|
|
|
|
PackageVars(*Context) []string
|
|
|
|
|
|
|
|
// PackageConsts should emit an array of constant lines. They will be
|
|
|
|
// placed in a const ( ... ) block. There's no need to include a leading
|
|
|
|
// \t or trailing \n.
|
|
|
|
PackageConsts(*Context) []string
|
|
|
|
|
|
|
|
// GenerateType should emit the code for a particular type.
|
|
|
|
GenerateType(*Context, *types.Type, io.Writer) error
|
|
|
|
|
|
|
|
// Imports should return a list of necessary imports. They will be
|
|
|
|
// formatted correctly. You do not need to include quotation marks,
|
|
|
|
// return only the package name; alternatively, you can also return
|
|
|
|
// imports in the format `name "path/to/pkg"`. Imports will be called
|
|
|
|
// after Init, PackageVars, PackageConsts, and GenerateType, to allow
|
|
|
|
// you to keep track of what imports you actually need.
|
|
|
|
Imports(*Context) []string
|
|
|
|
|
|
|
|
// Preferred file name of this generator, not including a path. It is
|
|
|
|
// allowed for multiple generators to use the same filename, but it's
|
|
|
|
// up to you to make sure they don't have colliding import names.
|
|
|
|
// TODO: provide per-file import tracking, removing the requirement
|
|
|
|
// that generators coordinate..
|
|
|
|
Filename() string
|
|
|
|
|
|
|
|
// A registered file type in the context to generate this file with. If
|
|
|
|
// the FileType is not found in the context, execution will stop.
|
|
|
|
FileType() string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Context is global context for individual generators to consume.
|
|
|
|
type Context struct {
|
|
|
|
// A map from the naming system to the names for that system. E.g., you
|
|
|
|
// might have public names and several private naming systems.
|
|
|
|
Namers namer.NameSystems
|
|
|
|
|
|
|
|
// All the types, in case you want to look up something.
|
|
|
|
Universe types.Universe
|
|
|
|
|
2019-12-12 01:27:03 +00:00
|
|
|
// Incoming imports, i.e. packages importing the given package.
|
|
|
|
incomingImports map[string][]string
|
|
|
|
|
|
|
|
// Incoming transitive imports, i.e. the transitive closure of IncomingImports
|
|
|
|
incomingTransitiveImports map[string][]string
|
|
|
|
|
2019-01-12 04:58:27 +00:00
|
|
|
// All the user-specified packages. This is after recursive expansion.
|
|
|
|
Inputs []string
|
|
|
|
|
|
|
|
// The canonical ordering of the types (will be filtered by both the
|
|
|
|
// Package's and Generator's Filter methods).
|
|
|
|
Order []*types.Type
|
|
|
|
|
|
|
|
// A set of types this context can process. If this is empty or nil,
|
|
|
|
// the default "golang" filetype will be provided.
|
|
|
|
FileTypes map[string]FileType
|
|
|
|
|
|
|
|
// If true, Execute* calls will just verify that the existing output is
|
|
|
|
// correct. (You may set this after calling NewContext.)
|
|
|
|
Verify bool
|
|
|
|
|
|
|
|
// Allows generators to add packages at runtime.
|
|
|
|
builder *parser.Builder
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewContext generates a context from the given builder, naming systems, and
|
|
|
|
// the naming system you wish to construct the canonical ordering from.
|
|
|
|
func NewContext(b *parser.Builder, nameSystems namer.NameSystems, canonicalOrderName string) (*Context, error) {
|
|
|
|
universe, err := b.FindTypes()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
c := &Context{
|
|
|
|
Namers: namer.NameSystems{},
|
|
|
|
Universe: universe,
|
|
|
|
Inputs: b.FindPackages(),
|
|
|
|
FileTypes: map[string]FileType{
|
|
|
|
GolangFileType: NewGolangFile(),
|
|
|
|
},
|
|
|
|
builder: b,
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, systemNamer := range nameSystems {
|
|
|
|
c.Namers[name] = systemNamer
|
|
|
|
if name == canonicalOrderName {
|
|
|
|
orderer := namer.Orderer{Namer: systemNamer}
|
|
|
|
c.Order = orderer.OrderUniverse(universe)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
2019-12-12 01:27:03 +00:00
|
|
|
// IncomingImports returns the incoming imports for each package. The map is lazily computed.
|
|
|
|
func (ctxt *Context) IncomingImports() map[string][]string {
|
|
|
|
if ctxt.incomingImports == nil {
|
|
|
|
incoming := map[string][]string{}
|
|
|
|
for _, pkg := range ctxt.Universe {
|
|
|
|
for imp := range pkg.Imports {
|
|
|
|
incoming[imp] = append(incoming[imp], pkg.Path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ctxt.incomingImports = incoming
|
|
|
|
}
|
|
|
|
return ctxt.incomingImports
|
|
|
|
}
|
|
|
|
|
|
|
|
// TransitiveIncomingImports returns the transitive closure of the incoming imports for each package.
|
|
|
|
// The map is lazily computed.
|
|
|
|
func (ctxt *Context) TransitiveIncomingImports() map[string][]string {
|
|
|
|
if ctxt.incomingTransitiveImports == nil {
|
|
|
|
ctxt.incomingTransitiveImports = transitiveClosure(ctxt.IncomingImports())
|
|
|
|
}
|
|
|
|
return ctxt.incomingTransitiveImports
|
|
|
|
}
|
|
|
|
|
2019-01-12 04:58:27 +00:00
|
|
|
// AddDir adds a Go package to the context. The specified path must be a single
|
|
|
|
// go package import path. GOPATH, GOROOT, and the location of your go binary
|
|
|
|
// (`which go`) will all be searched, in the normal Go fashion.
|
|
|
|
// Deprecated. Please use AddDirectory.
|
|
|
|
func (ctxt *Context) AddDir(path string) error {
|
2019-12-12 01:27:03 +00:00
|
|
|
ctxt.incomingImports = nil
|
|
|
|
ctxt.incomingTransitiveImports = nil
|
2019-01-12 04:58:27 +00:00
|
|
|
return ctxt.builder.AddDirTo(path, &ctxt.Universe)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddDirectory adds a Go package to the context. The specified path must be a
|
|
|
|
// single go package import path. GOPATH, GOROOT, and the location of your go
|
|
|
|
// binary (`which go`) will all be searched, in the normal Go fashion.
|
|
|
|
func (ctxt *Context) AddDirectory(path string) (*types.Package, error) {
|
2019-12-12 01:27:03 +00:00
|
|
|
ctxt.incomingImports = nil
|
|
|
|
ctxt.incomingTransitiveImports = nil
|
2019-01-12 04:58:27 +00:00
|
|
|
return ctxt.builder.AddDirectoryTo(path, &ctxt.Universe)
|
|
|
|
}
|