blob: 05a3f65fe87c4a9be2f0c89e52c5d03884d815b3 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2015 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package generator
18
19import (
20 "bytes"
21 "io"
22
23 "k8s.io/gengo/namer"
24 "k8s.io/gengo/parser"
25 "k8s.io/gengo/types"
26)
27
28// Package contains the contract for generating a package.
29type Package interface {
30 // Name returns the package short name.
31 Name() string
32 // Path returns the package import path.
33 Path() string
34
35 // Filter should return true if this package cares about this type.
36 // Otherwise, this type will be omitted from the type ordering for
37 // this package.
38 Filter(*Context, *types.Type) bool
39
40 // Header should return a header for the file, including comment markers.
41 // Useful for copyright notices and doc strings. Include an
42 // autogeneration notice! Do not include the "package x" line.
43 Header(filename string) []byte
44
45 // Generators returns the list of generators for this package. It is
46 // allowed for more than one generator to write to the same file.
47 // A Context is passed in case the list of generators depends on the
48 // input types.
49 Generators(*Context) []Generator
50}
51
52type File struct {
53 Name string
54 FileType string
55 PackageName string
56 Header []byte
57 Imports map[string]struct{}
58 Vars bytes.Buffer
59 Consts bytes.Buffer
60 Body bytes.Buffer
61}
62
63type FileType interface {
64 AssembleFile(f *File, path string) error
65 VerifyFile(f *File, path string) error
66}
67
68// Packages is a list of packages to generate.
69type Packages []Package
70
71// Generator is the contract for anything that wants to do auto-generation.
72// It's expected that the io.Writers passed to the below functions will be
73// ErrorTrackers; this allows implementations to not check for io errors,
74// making more readable code.
75//
76// The call order for the functions that take a Context is:
77// 1. Filter() // Subsequent calls see only types that pass this.
78// 2. Namers() // Subsequent calls see the namers provided by this.
79// 3. PackageVars()
80// 4. PackageConsts()
81// 5. Init()
82// 6. GenerateType() // Called N times, once per type in the context's Order.
83// 7. Imports()
84//
85// You may have multiple generators for the same file.
86type Generator interface {
87 // The name of this generator. Will be included in generated comments.
88 Name() string
89
90 // Filter should return true if this generator cares about this type.
91 // (otherwise, GenerateType will not be called.)
92 //
93 // Filter is called before any of the generator's other functions;
94 // subsequent calls will get a context with only the types that passed
95 // this filter.
96 Filter(*Context, *types.Type) bool
97
98 // If this generator needs special namers, return them here. These will
99 // override the original namers in the context if there is a collision.
100 // You may return nil if you don't need special names. These names will
101 // be available in the context passed to the rest of the generator's
102 // functions.
103 //
104 // A use case for this is to return a namer that tracks imports.
105 Namers(*Context) namer.NameSystems
106
107 // Init should write an init function, and any other content that's not
108 // generated per-type. (It's not intended for generator specific
109 // initialization! Do that when your Package constructs the
110 // Generators.)
111 Init(*Context, io.Writer) error
112
113 // Finalize should write finish up functions, and any other content that's not
114 // generated per-type.
115 Finalize(*Context, io.Writer) error
116
117 // PackageVars should emit an array of variable lines. They will be
118 // placed in a var ( ... ) block. There's no need to include a leading
119 // \t or trailing \n.
120 PackageVars(*Context) []string
121
122 // PackageConsts should emit an array of constant lines. They will be
123 // placed in a const ( ... ) block. There's no need to include a leading
124 // \t or trailing \n.
125 PackageConsts(*Context) []string
126
127 // GenerateType should emit the code for a particular type.
128 GenerateType(*Context, *types.Type, io.Writer) error
129
130 // Imports should return a list of necessary imports. They will be
131 // formatted correctly. You do not need to include quotation marks,
132 // return only the package name; alternatively, you can also return
133 // imports in the format `name "path/to/pkg"`. Imports will be called
134 // after Init, PackageVars, PackageConsts, and GenerateType, to allow
135 // you to keep track of what imports you actually need.
136 Imports(*Context) []string
137
138 // Preferred file name of this generator, not including a path. It is
139 // allowed for multiple generators to use the same filename, but it's
140 // up to you to make sure they don't have colliding import names.
141 // TODO: provide per-file import tracking, removing the requirement
142 // that generators coordinate..
143 Filename() string
144
145 // A registered file type in the context to generate this file with. If
146 // the FileType is not found in the context, execution will stop.
147 FileType() string
148}
149
150// Context is global context for individual generators to consume.
151type Context struct {
152 // A map from the naming system to the names for that system. E.g., you
153 // might have public names and several private naming systems.
154 Namers namer.NameSystems
155
156 // All the types, in case you want to look up something.
157 Universe types.Universe
158
159 // All the user-specified packages. This is after recursive expansion.
160 Inputs []string
161
162 // The canonical ordering of the types (will be filtered by both the
163 // Package's and Generator's Filter methods).
164 Order []*types.Type
165
166 // A set of types this context can process. If this is empty or nil,
167 // the default "golang" filetype will be provided.
168 FileTypes map[string]FileType
169
170 // If true, Execute* calls will just verify that the existing output is
171 // correct. (You may set this after calling NewContext.)
172 Verify bool
173
174 // Allows generators to add packages at runtime.
175 builder *parser.Builder
176}
177
178// NewContext generates a context from the given builder, naming systems, and
179// the naming system you wish to construct the canonical ordering from.
180func NewContext(b *parser.Builder, nameSystems namer.NameSystems, canonicalOrderName string) (*Context, error) {
181 universe, err := b.FindTypes()
182 if err != nil {
183 return nil, err
184 }
185
186 c := &Context{
187 Namers: namer.NameSystems{},
188 Universe: universe,
189 Inputs: b.FindPackages(),
190 FileTypes: map[string]FileType{
191 GolangFileType: NewGolangFile(),
192 },
193 builder: b,
194 }
195
196 for name, systemNamer := range nameSystems {
197 c.Namers[name] = systemNamer
198 if name == canonicalOrderName {
199 orderer := namer.Orderer{Namer: systemNamer}
200 c.Order = orderer.OrderUniverse(universe)
201 }
202 }
203 return c, nil
204}
205
206// AddDir adds a Go package to the context. The specified path must be a single
207// go package import path. GOPATH, GOROOT, and the location of your go binary
208// (`which go`) will all be searched, in the normal Go fashion.
209// Deprecated. Please use AddDirectory.
210func (ctxt *Context) AddDir(path string) error {
211 return ctxt.builder.AddDirTo(path, &ctxt.Universe)
212}
213
214// AddDirectory adds a Go package to the context. The specified path must be a
215// single go package import path. GOPATH, GOROOT, and the location of your go
216// binary (`which go`) will all be searched, in the normal Go fashion.
217func (ctxt *Context) AddDirectory(path string) (*types.Package, error) {
218 return ctxt.builder.AddDirectoryTo(path, &ctxt.Universe)
219}