Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 1 | # Copyright 2020 The Monogon Project Authors. |
| 2 | # |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | # Bazel rules for generating Kubernetes-style API types using |
| 18 | # github.com/kubernetes/code-generator. |
| 19 | # |
| 20 | # k8s.io/code-gen generators target a peculiar filesystem/package structure for |
| 21 | # generated code: |
| 22 | # |
| 23 | # example.com/project/apis/goodbye/v1/doc.go - hand written |
| 24 | # /types.go - hand written |
| 25 | # /zz_generated.go - generated |
| 26 | # example.com/project/apis/hello/v1/doc.go - hand written |
| 27 | # /types.go - hand written |
| 28 | # /zz_generated.go - generated |
| 29 | # example.com/project/generated/clientset/... - generated |
| 30 | # example.com/project/generated/informers/... - generated |
| 31 | # example.com/project/generated/listers/... - generated |
| 32 | # |
| 33 | # This means, that usually the generated files are both colocated directly |
| 34 | # with the package (for zz_generated.deepcopy.go, generated by deepcopy) |
| 35 | # and have their own package (for files generated by clientset, informers, |
| 36 | # listers). |
| 37 | # |
| 38 | # Most importantly, however, multiple Go packages (in the above example, |
| 39 | # goodbye/v1 and hello/v1) are used to in turn generate multiple output Go |
| 40 | # packages. This proves problematic when generating code for Bazel, as we have |
| 41 | # to consume the result of code generation from multiple Bazel targets (each |
| 42 | # representing a different generated Go package). |
| 43 | # |
| 44 | # To handle this, we split up the code generation into four main steps. These |
| 45 | # need to be manually instantiated by any use who wants to consume these rules. |
| 46 | # |
| 47 | # 1. Create a rules_go go_path that contains all hand-written API packages. |
| 48 | # 2. Parse hand written API packages using go_kubernetes_resource_bundle, via |
| 49 | # the generated go_path. This prepares outputs (ie., 'runs' generators), |
| 50 | # and creates an internal provider (KubeResourceBundle) that contains |
| 51 | # informations about generated Go packages. |
| 52 | # 3. For every output package, create a go_kubernetes_library target, |
| 53 | # specifying the bundle from which it's supposed to be created, and which |
| 54 | # particular library from that bundle should be used. |
| 55 | # 4. Next to every go_kubernetes_library, create a go_library with the same |
| 56 | # importpath which embeds the go_kubernetes_library. The split between |
| 57 | # go_kubernetes_library and go_library is required to let Gazelle know |
| 58 | # about the availability of given importpaths at given Bazel targets |
| 59 | # (unfortunately, it seems like Gazelle is unable to parse rules that emit |
| 60 | # the same providers as go_library does, instead requiring us to use a full |
| 61 | # go_library rule instead). |
| 62 | # |
| 63 | # Point 3. is somwhat different for the generated deepcopy code, which has to |
| 64 | # live alongside (in the same importpath) as the hand-written API go_library, |
| 65 | # and needs to be embedded into that. Special care has to be taken to not cause |
| 66 | # a cycle (handwritten API -> go_path -> bundle -> kubernetes_library -> |
| 67 | # go_library) in this case. This is done via a select() which selectively |
| 68 | # enables the inclusion of the generated deepcopy code within the hand-written |
| 69 | # API library, only enabling it for the target build, not the preprocessing |
| 70 | # done by go_kubernetes_resource_bundle. |
| 71 | # |
| 72 | # Or, in graphical form: |
| 73 | # |
| 74 | # .------------. .------------. |
| 75 | # | go_library | | go_library | |
| 76 | # |------------| |------------| |
| 77 | # | goodbye/v1 | | hello/v2 | |
| 78 | # '------------' '------------' |
| 79 | # '------. .--------' |
| 80 | # .---------. |
| 81 | # | go_path | |
| 82 | # '---------' |
| 83 | # | (preprocessing transition) |
| 84 | # .-------------------------------. |
| 85 | # | go_kubernetes_resource_bundle | |
| 86 | # '-------------------------------' |
| 87 | # | '--------------------------. |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 88 | # .---------------------------. .--------------------------. |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 89 | # | go_kubernetes_library | | go_kubernetes_library | |
| 90 | # |---------------------------| |--------------------------| ... others ... |
| 91 | # | clientset/verioned/typed | | clientset/verioned/fake | |
| 92 | # '---------------------------' '--------------------------' |
| 93 | # | | |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 94 | # .---------------------------. .--------------------------. |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 95 | # | go_library | | go_library | |
| 96 | # |---------------------------| |--------------------------| ... others ... |
| 97 | # | clientset/versioned/typed | | clientset/versioned/fake | |
| 98 | # '---------------------------' '--------------------------' |
| 99 | # |
| 100 | |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 101 | load("@io_bazel_rules_go//go:def.bzl", "GoLibrary", "GoPath", "go_context") |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 102 | |
| 103 | def _preprocessing_transition_impl(settings, attr): |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 104 | return {"//metropolis/build/kube-code-generator:preprocessing": "yes"} |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 105 | |
| 106 | # preprocessing_transition is attached to the incoming go_path in |
| 107 | # go_kubernetes_resource_bundle, unsets the |
| 108 | # //metropolis/build/kube-code-generator:embed_deepcopy config setting. |
| 109 | # This allows go_libraries that make up the handwritten API libraries to only |
| 110 | # embed the generated deepcopy when they are pulled in for build reasons, not |
| 111 | # when the graph is being traversed in order to generate the deepcopy itself. |
| 112 | # This breaks up the cycle that would happen otherwise. |
| 113 | preprocessing_transition = transition( |
| 114 | implementation = _preprocessing_transition_impl, |
| 115 | inputs = [], |
| 116 | outputs = ["//metropolis/build/kube-code-generator:preprocessing"], |
| 117 | ) |
| 118 | |
| 119 | # KubeResourceBundle is emitted by go_kubernetes_resource_bundle and contains |
| 120 | # informations about libraries generated by the kubernetes code-generators. |
| 121 | KubeResourceBundle = provider( |
| 122 | "Information about the generated Go sources of a k8s.io/code-generator-built library.", |
| 123 | fields = { |
| 124 | "libraries": "Map from Go importpath to list of Files that make up this importpath.", |
| 125 | }, |
| 126 | ) |
| 127 | |
| 128 | def _go_kubernetes_library_impl(ctx): |
| 129 | go = go_context(ctx) |
| 130 | bundle = ctx.attr.bundle[KubeResourceBundle] |
| 131 | libraries = bundle.libraries |
| 132 | |
| 133 | found_importpaths = [l.importpath for l in libraries] |
| 134 | |
| 135 | libraries = [l for l in libraries if l.importpath == ctx.attr.importpath] |
| 136 | if len(libraries) < 1: |
| 137 | fail("importpath {} not found in bundle (have {})".format(ctx.attr.importpath, ", ".join(found_importpaths))) |
| 138 | if len(libraries) > 1: |
| 139 | fail("internal error: multiple libraries with importpath {} found in bundle".format(ctx.attr.importpath)) |
| 140 | library = libraries[0] |
| 141 | |
| 142 | source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented()) |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 143 | return [library, source, OutputGroupInfo(go_generated_srcs = depset(library.srcs))] |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 144 | |
| 145 | # go_kubernetes_library picks a single Go library from a kube_resource_bundle |
| 146 | # and prepares it for being embedded into a go_library. |
| 147 | go_kubernetes_library = rule( |
| 148 | implementation = _go_kubernetes_library_impl, |
| 149 | attrs = { |
| 150 | "bundle": attr.label( |
| 151 | mandatory = True, |
| 152 | providers = [KubeResourceBundle], |
| 153 | doc = "A go_kubernetes_resource_bundle that contains the result of a kubernetes code-generation run.", |
| 154 | ), |
| 155 | "importpath": attr.string( |
| 156 | mandatory = True, |
| 157 | doc = "The importpath of the library picked from the bundle, same as the importpath of the go_library that embeds it.", |
| 158 | ), |
| 159 | "deps": attr.label_list( |
| 160 | providers = [GoLibrary], |
| 161 | doc = "All build dependencies of this library.", |
| 162 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 163 | "_go_context_data": attr.label( |
| 164 | default = "@io_bazel_rules_go//:go_context_data", |
| 165 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 166 | }, |
| 167 | toolchains = ["@io_bazel_rules_go//go:toolchain"], |
| 168 | ) |
| 169 | |
| 170 | # _gotool_run is a helper function which runs an executable under |
| 171 | # //metropolis/build/gotoolwrap, effectively setting up everything required to |
| 172 | # use standard Go tooling on the monogon workspace (ie. GOPATH/GOROOT). This is |
| 173 | # required by generators to run 'go fmt'. |
| 174 | def _gotool_run(ctx, executable, arguments, **kwargs): |
| 175 | go = go_context(ctx) |
| 176 | gopath = ctx.attr.gopath[0][GoPath] |
| 177 | |
| 178 | inputs = [ |
| 179 | gopath.gopath_file, |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 180 | ] + kwargs.get("inputs", []) |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 181 | |
| 182 | tools = [ |
| 183 | executable, |
| 184 | go.sdk.go, |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 185 | ] + go.sdk.tools + kwargs.get("tools", []) |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 186 | |
| 187 | env = { |
| 188 | "GOTOOLWRAP_GOPATH": gopath.gopath_file.path, |
| 189 | "GOTOOLWRAP_GOROOT": go.sdk.root_file.dirname, |
| 190 | } |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 191 | env.update(kwargs.get("env", {})) |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 192 | |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 193 | kwargs_ = dict([(k, v) for (k, v) in kwargs.items() if k not in [ |
| 194 | "executable", |
| 195 | "arguments", |
| 196 | "inputs", |
| 197 | "env", |
| 198 | "tools", |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 199 | ]]) |
| 200 | |
| 201 | ctx.actions.run( |
| 202 | executable = ctx.executable._gotoolwrap, |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 203 | arguments = [executable.path] + arguments, |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 204 | env = env, |
| 205 | inputs = inputs, |
| 206 | tools = tools, |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 207 | **kwargs_ |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 208 | ) |
| 209 | |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 210 | # _output_directory returns the relative path into which |
| 211 | # ctx.action.declare_file writes are rooted. This is used as code-generators |
| 212 | # require a root path for all outputted files, instead of a list of files to |
| 213 | # emit. |
| 214 | def _output_directory(ctx): |
| 215 | # We combine bin_dir, the BUILDfile path and the target name. This seems |
| 216 | # wrong. Is there no simpler way to do this? |
| 217 | buildfile_path = ctx.build_file_path |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 218 | parts = buildfile_path.split("/") |
| 219 | if not parts[-1].startswith("BUILD"): |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 220 | fail("internal error: unexpected BUILD file path: {}", parts[-1]) |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 221 | package_path = "/".join(parts[:-1]) |
| 222 | return "/".join([ctx.bin_dir.path, package_path, ctx.attr.name]) |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 223 | |
| 224 | # _cg returns a 'codegen context', a struct that's used to accumulate the |
| 225 | # results of code generation. It assumes all output will be rooted in a |
| 226 | # generated importpath (with more 'shortened' importpaths underneath the root), |
| 227 | # and collects outputs to pass to the codegen execution action. It also |
| 228 | # collects a map of importpaths to outputs that make it up. |
| 229 | def _cg(ctx, importpath): |
| 230 | output_root = _output_directory(ctx) |
| 231 | |
| 232 | return struct( |
| 233 | # The 'root' importpath, under which 'shortened' importpaths reside. |
| 234 | importpath = importpath, |
| 235 | # The prefix into which all files will be emitted. We use the target |
| 236 | # name for convenience. |
| 237 | output_prefix = ctx.attr.name, |
| 238 | # The full relative path visible to the codegen, pointing to the same |
| 239 | # directory as output_prefix (just from the point of view of the |
| 240 | # runtime filesystem, not the ctx.actions filepath declaration API). |
| 241 | output_root = output_root, |
| 242 | # The list of outputs that have to be generated by the codegen. |
| 243 | outputs = [], |
| 244 | # A map of importpath to list of outputs (from the above list) that |
| 245 | # make up a generated Go package/library. |
| 246 | libraries = {}, |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 247 | ctx = ctx, |
| 248 | ) |
| 249 | |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 250 | # _declare_library adds a single Go package/library at importpath to the |
| 251 | # codegen context with the given file paths (rooted in the importpath). |
| 252 | def _declare_library(cg, importpath, files): |
| 253 | importpath = cg.importpath + "/" + importpath |
| 254 | cg.libraries[importpath] = [] |
| 255 | for f in files: |
| 256 | output = cg.ctx.actions.declare_file("{}/{}/{}".format( |
| 257 | cg.output_prefix, |
| 258 | importpath, |
| 259 | f, |
| 260 | )) |
| 261 | cg.outputs.append(output) |
| 262 | cg.libraries[importpath].append(output) |
| 263 | |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 264 | # _declare_libraries declares multiple Go package/libraries to the codegen |
| 265 | # context. The key of the dictionary is the importpath of the library, and the |
| 266 | # value are the file names of generated outputs. |
| 267 | def _declare_libraries(cg, libraries): |
| 268 | for k, v in libraries.items(): |
| 269 | _declare_library(cg, k, v) |
| 270 | |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 271 | # _codegen_clientset runs the clientset codegenerator. |
| 272 | def _codegen_clientset(ctx): |
| 273 | cg = _cg(ctx, ctx.attr.importpath) |
| 274 | |
| 275 | _declare_libraries(cg, { |
| 276 | "clientset/versioned": ["clientset.go", "doc.go"], |
| 277 | "clientset/versioned/fake": ["register.go", "clientset_generated.go"], |
| 278 | "clientset/versioned/scheme": ["register.go", "doc.go"], |
| 279 | }) |
| 280 | |
| 281 | for api, types in ctx.attr.apis.items(): |
| 282 | client_name = api.split("/")[-2] |
| 283 | _declare_libraries(cg, { |
| 284 | "clientset/versioned/typed/{}".format(api): [ |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 285 | "doc.go", |
| 286 | "generated_expansion.go", |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 287 | "{}_client.go".format(client_name), |
| 288 | ] + [ |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 289 | "{}.go".format(t) |
| 290 | for t in types |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 291 | ], |
| 292 | "clientset/versioned/typed/{}/fake".format(api): [ |
| 293 | "doc.go", |
| 294 | "fake_{}_client.go".format(client_name), |
| 295 | ], |
| 296 | }) |
| 297 | |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 298 | _gotool_run( |
| 299 | ctx, |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 300 | mnemonic = "ClientsetGen", |
| 301 | executable = ctx.executable._client_gen, |
| 302 | arguments = [ |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 303 | "--clientset-name", |
| 304 | "versioned", |
| 305 | "--input-base", |
| 306 | ctx.attr.apipath, |
| 307 | "--input", |
| 308 | ",".join(ctx.attr.apis), |
| 309 | "--output-package", |
| 310 | cg.importpath + "/clientset", |
| 311 | "--output-base", |
| 312 | cg.output_root, |
| 313 | "--go-header-file", |
| 314 | ctx.file.boilerplate.path, |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 315 | ], |
| 316 | inputs = [ |
| 317 | ctx.file.boilerplate, |
| 318 | ], |
| 319 | outputs = cg.outputs, |
| 320 | ) |
| 321 | |
| 322 | return cg.libraries |
| 323 | |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 324 | # _codegen_deepcopy runs the deepcopy codegenerator (outputting to the apipath, |
| 325 | # not the importpath). |
| 326 | def _codegen_deepcopy(ctx): |
| 327 | cg = _cg(ctx, ctx.attr.apipath) |
| 328 | |
| 329 | for api, types in ctx.attr.apis.items(): |
| 330 | _declare_libraries(cg, { |
| 331 | api: ["zz_generated.deepcopy.go"], |
| 332 | }) |
| 333 | |
| 334 | _gotool_run( |
| 335 | ctx, |
| 336 | mnemonic = "DeepcopyGen", |
| 337 | executable = ctx.executable._deepcopy_gen, |
| 338 | arguments = [ |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 339 | "--input-dirs", |
| 340 | ",".join(["{}/{}".format(ctx.attr.apipath, api) for api in ctx.attr.apis]), |
| 341 | "--go-header-file", |
| 342 | ctx.file.boilerplate.path, |
| 343 | "--stderrthreshold", |
| 344 | "0", |
| 345 | "-O", |
| 346 | "zz_generated.deepcopy", |
| 347 | "--output-base", |
| 348 | cg.output_root, |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 349 | ctx.attr.apipath, |
| 350 | ], |
| 351 | inputs = [ |
| 352 | ctx.file.boilerplate, |
| 353 | ], |
| 354 | outputs = cg.outputs, |
| 355 | ) |
| 356 | return cg.libraries |
| 357 | |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 358 | # _codegen_informer runs the informer codegenerator. |
| 359 | def _codegen_informer(ctx): |
| 360 | cg = _cg(ctx, ctx.attr.importpath) |
| 361 | |
| 362 | _declare_libraries(cg, { |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 363 | "informers/externalversions": ["factory.go", "generic.go"], |
| 364 | "informers/externalversions/internalinterfaces": ["factory_interfaces.go"], |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 365 | }) |
| 366 | |
| 367 | for api, types in ctx.attr.apis.items(): |
| 368 | client_name = api.split("/")[-2] |
| 369 | _declare_libraries(cg, { |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 370 | "informers/externalversions/{}".format(client_name): ["interface.go"], |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 371 | "informers/externalversions/{}".format(api): [ |
| 372 | "interface.go", |
| 373 | ] + [ |
| 374 | "{}.go".format(t) |
| 375 | for t in types |
| 376 | ], |
| 377 | }) |
| 378 | |
| 379 | _gotool_run( |
| 380 | ctx, |
| 381 | mnemonic = "InformerGen", |
| 382 | executable = ctx.executable._informer_gen, |
| 383 | arguments = [ |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 384 | "--input-dirs", |
| 385 | ",".join(["{}/{}".format(ctx.attr.apipath, api) for api in ctx.attr.apis]), |
| 386 | "--versioned-clientset-package", |
| 387 | "{}/clientset/versioned".format(ctx.attr.importpath), |
| 388 | "--listers-package", |
| 389 | "{}/listers".format(ctx.attr.importpath), |
| 390 | "--output-package", |
| 391 | "{}/informers".format(ctx.attr.importpath), |
| 392 | "--output-base", |
| 393 | cg.output_root, |
| 394 | "--go-header-file", |
| 395 | ctx.file.boilerplate.path, |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 396 | ], |
| 397 | inputs = [ |
| 398 | ctx.file.boilerplate, |
| 399 | ], |
| 400 | outputs = cg.outputs, |
| 401 | ) |
| 402 | |
| 403 | return cg.libraries |
| 404 | |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 405 | # _codegen_lister runs the lister codegenerator. |
| 406 | def _codegen_lister(ctx): |
| 407 | cg = _cg(ctx, ctx.attr.importpath) |
| 408 | |
| 409 | for api, types in ctx.attr.apis.items(): |
| 410 | client_name = api.split("/")[-2] |
| 411 | _declare_libraries(cg, { |
| 412 | "listers/{}".format(api): [ |
| 413 | "expansion_generated.go", |
| 414 | ] + [ |
| 415 | "{}.go".format(t) |
| 416 | for t in types |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 417 | ], |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 418 | }) |
| 419 | |
| 420 | _gotool_run( |
| 421 | ctx, |
| 422 | mnemonic = "ListerGen", |
| 423 | executable = ctx.executable._lister_gen, |
| 424 | arguments = [ |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 425 | "--input-dirs", |
| 426 | ",".join(["{}/{}".format(ctx.attr.apipath, api) for api in ctx.attr.apis]), |
| 427 | "--output-package", |
| 428 | "{}/listers".format(ctx.attr.importpath), |
| 429 | "--output-base", |
| 430 | cg.output_root, |
| 431 | "--go-header-file", |
| 432 | ctx.file.boilerplate.path, |
| 433 | "-v", |
| 434 | "10", |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 435 | ], |
| 436 | inputs = [ |
| 437 | ctx.file.boilerplate, |
| 438 | ], |
| 439 | outputs = cg.outputs, |
| 440 | ) |
| 441 | |
| 442 | return cg.libraries |
| 443 | |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 444 | # _update_dict_check is a helper function that updates dict a with dict b, |
| 445 | # ensuring there's no overwritten keys. |
| 446 | def _update_dict_check(a, b): |
| 447 | for k in b.keys(): |
| 448 | if k in a: |
| 449 | fail("internal error: repeat importpath {}", k) |
| 450 | a.update(b) |
| 451 | |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 452 | def _go_kubernetes_resource_bundle_impl(ctx): |
| 453 | go = go_context(ctx) |
| 454 | |
| 455 | all_gens = {} |
| 456 | _update_dict_check(all_gens, _codegen_clientset(ctx)) |
| 457 | _update_dict_check(all_gens, _codegen_deepcopy(ctx)) |
| 458 | _update_dict_check(all_gens, _codegen_informer(ctx)) |
| 459 | _update_dict_check(all_gens, _codegen_lister(ctx)) |
| 460 | |
| 461 | libraries = [] |
| 462 | for importpath, srcs in all_gens.items(): |
| 463 | library = go.new_library( |
| 464 | go, |
| 465 | srcs = srcs, |
| 466 | importpath = importpath, |
| 467 | ) |
| 468 | libraries.append(library) |
| 469 | |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 470 | return [KubeResourceBundle(libraries = libraries)] |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 471 | |
| 472 | # go_kubernetes_resource_bundle runs kubernetes code-generators on a codepath |
| 473 | # for some requested APIs, and whose output can be made into Go library targets |
| 474 | # via go_kubernetes_library. This bundle corresponds to a single Kubernetes API |
| 475 | # resource group. |
| 476 | go_kubernetes_resource_bundle = rule( |
| 477 | implementation = _go_kubernetes_resource_bundle_impl, |
| 478 | attrs = { |
| 479 | "gopath": attr.label( |
| 480 | mandatory = True, |
| 481 | providers = [GoPath], |
| 482 | cfg = preprocessing_transition, |
| 483 | doc = "A rules_go go_path that contains all the API libraries for which codegen should be run.", |
| 484 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 485 | "importpath": attr.string( |
| 486 | mandatory = True, |
| 487 | doc = """ |
| 488 | The root importpath of the generated code (apart from deepcopy |
| 489 | codegen). The Bazel target path corresponding to this |
| 490 | importpath needs to contain the go_kubernetes_library and |
| 491 | go_library targets that allow to actually build against the |
| 492 | generated code. |
| 493 | """, |
| 494 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 495 | "apipath": attr.string( |
| 496 | mandatory = True, |
| 497 | doc = "The root importpath of the APIs for which to generate code.", |
| 498 | ), |
| 499 | "apis": attr.string_list_dict( |
| 500 | mandatory = True, |
| 501 | doc = """ |
| 502 | The APIs underneath importpath for which to generated code, |
| 503 | eg. foo/v1, mapping into a list of lowercased types generated |
| 504 | from each (eg. widget for `type Widget struct`). |
| 505 | """, |
| 506 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 507 | "boilerplate": attr.label( |
| 508 | default = "//metropolis/build/kube-code-generator:boilerplate.go.txt", |
| 509 | allow_single_file = True, |
| 510 | doc = "Header that will be used in the generated code.", |
| 511 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 512 | "_go_context_data": attr.label( |
| 513 | default = "@io_bazel_rules_go//:go_context_data", |
| 514 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 515 | "_gotoolwrap": attr.label( |
| 516 | default = Label("//metropolis/build/gotoolwrap"), |
| 517 | allow_single_file = True, |
| 518 | executable = True, |
| 519 | cfg = "exec", |
| 520 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 521 | "_deepcopy_gen": attr.label( |
| 522 | default = Label("@io_k8s_code_generator//cmd/deepcopy-gen"), |
| 523 | allow_single_file = True, |
| 524 | executable = True, |
| 525 | cfg = "exec", |
| 526 | ), |
| 527 | "_client_gen": attr.label( |
| 528 | default = Label("@io_k8s_code_generator//cmd/client-gen"), |
| 529 | allow_single_file = True, |
| 530 | executable = True, |
| 531 | cfg = "exec", |
| 532 | ), |
| 533 | "_informer_gen": attr.label( |
| 534 | default = Label("@io_k8s_code_generator//cmd/informer-gen"), |
| 535 | allow_single_file = True, |
| 536 | executable = True, |
| 537 | cfg = "exec", |
| 538 | ), |
| 539 | "_lister_gen": attr.label( |
| 540 | default = Label("@io_k8s_code_generator//cmd/lister-gen"), |
| 541 | allow_single_file = True, |
| 542 | executable = True, |
| 543 | cfg = "exec", |
| 544 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 545 | "_allowlist_function_transition": attr.label( |
Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame^] | 546 | default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| 547 | ), |
Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 548 | }, |
| 549 | toolchains = ["@io_bazel_rules_go//go:toolchain"], |
| 550 | ) |