generator.go 8.9 KB

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