| 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'. |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 174 | # |
| 175 | # The optional `copyout` argument configures which files to copy out from the |
| 176 | # GOPATH into declared files. The contents of the list should be strings that |
| 177 | # refer to a file within GOPATH/src, eg. source.monogon.dev/foo/bar/baz.go. The |
| 178 | # files is then going to be copied into the path corresponding to a declared |
| 179 | # file of the same basename. |
| 180 | def _gotool_run(cg, executable, arguments, copyout=[], **kwargs): |
| 181 | ctx = cg.ctx |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 182 | go = go_context(ctx) |
| 183 | gopath = ctx.attr.gopath[0][GoPath] |
| 184 | |
| 185 | inputs = [ |
| 186 | gopath.gopath_file, |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 187 | ] + kwargs.get("inputs", []) |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 188 | |
| 189 | tools = [ |
| 190 | executable, |
| 191 | go.sdk.go, |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 192 | ] + go.sdk.srcs + go.sdk.tools + kwargs.get("tools", []) |
| 193 | |
| 194 | copyout_env = [] |
| 195 | for path in copyout: |
| 196 | to = cg.output_root + "/" + path |
| 197 | copyout_env.append("{}:{}".format(path, to)) |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 198 | |
| 199 | env = { |
| 200 | "GOTOOLWRAP_GOPATH": gopath.gopath_file.path, |
| 201 | "GOTOOLWRAP_GOROOT": go.sdk.root_file.dirname, |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 202 | "GOTOOLWRAP_COPYOUT": ';'.join(copyout_env), |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 203 | } |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 204 | env.update(kwargs.get("env", {})) |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 205 | |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 206 | kwargs_ = dict([(k, v) for (k, v) in kwargs.items() if k not in [ |
| 207 | "executable", |
| 208 | "arguments", |
| 209 | "inputs", |
| 210 | "env", |
| 211 | "tools", |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 212 | ]]) |
| 213 | |
| 214 | ctx.actions.run( |
| 215 | executable = ctx.executable._gotoolwrap, |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 216 | arguments = [executable.path] + arguments, |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 217 | env = env, |
| 218 | inputs = inputs, |
| 219 | tools = tools, |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 220 | **kwargs_ |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 221 | ) |
| 222 | |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 223 | # _output_directory returns the relative path into which |
| 224 | # ctx.action.declare_file writes are rooted. This is used as code-generators |
| 225 | # require a root path for all outputted files, instead of a list of files to |
| 226 | # emit. |
| 227 | def _output_directory(ctx): |
| 228 | # We combine bin_dir, the BUILDfile path and the target name. This seems |
| 229 | # wrong. Is there no simpler way to do this? |
| 230 | buildfile_path = ctx.build_file_path |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 231 | parts = buildfile_path.split("/") |
| 232 | if not parts[-1].startswith("BUILD"): |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 233 | fail("internal error: unexpected BUILD file path: {}", parts[-1]) |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 234 | package_path = "/".join(parts[:-1]) |
| 235 | return "/".join([ctx.bin_dir.path, package_path, ctx.attr.name]) |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 236 | |
| 237 | # _cg returns a 'codegen context', a struct that's used to accumulate the |
| 238 | # results of code generation. It assumes all output will be rooted in a |
| 239 | # generated importpath (with more 'shortened' importpaths underneath the root), |
| 240 | # and collects outputs to pass to the codegen execution action. It also |
| 241 | # collects a map of importpaths to outputs that make it up. |
| 242 | def _cg(ctx, importpath): |
| 243 | output_root = _output_directory(ctx) |
| 244 | |
| 245 | return struct( |
| 246 | # The 'root' importpath, under which 'shortened' importpaths reside. |
| 247 | importpath = importpath, |
| 248 | # The prefix into which all files will be emitted. We use the target |
| 249 | # name for convenience. |
| 250 | output_prefix = ctx.attr.name, |
| 251 | # The full relative path visible to the codegen, pointing to the same |
| 252 | # directory as output_prefix (just from the point of view of the |
| 253 | # runtime filesystem, not the ctx.actions filepath declaration API). |
| 254 | output_root = output_root, |
| 255 | # The list of outputs that have to be generated by the codegen. |
| 256 | outputs = [], |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 257 | # List of files compatible with the `copyout` argument of _gotool_run. |
| 258 | # Populated by calls to _declare_library. Should be used when the tool |
| 259 | # generates files within the GOPATH instead of directly at declared |
| 260 | # output file locations. |
| 261 | copyout = [], |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 262 | # A map of importpath to list of outputs (from the above list) that |
| 263 | # make up a generated Go package/library. |
| 264 | libraries = {}, |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 265 | ctx = ctx, |
| 266 | ) |
| 267 | |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 268 | # _declare_library adds a single Go package/library at importpath to the |
| 269 | # codegen context with the given file paths (rooted in the importpath). |
| 270 | def _declare_library(cg, importpath, files): |
| 271 | importpath = cg.importpath + "/" + importpath |
| 272 | cg.libraries[importpath] = [] |
| 273 | for f in files: |
| 274 | output = cg.ctx.actions.declare_file("{}/{}/{}".format( |
| 275 | cg.output_prefix, |
| 276 | importpath, |
| 277 | f, |
| 278 | )) |
| 279 | cg.outputs.append(output) |
| 280 | cg.libraries[importpath].append(output) |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 281 | cg.copyout.append("{}/{}".format(importpath, f)) |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 282 | |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 283 | # _declare_libraries declares multiple Go package/libraries to the codegen |
| 284 | # context. The key of the dictionary is the importpath of the library, and the |
| 285 | # value are the file names of generated outputs. |
| 286 | def _declare_libraries(cg, libraries): |
| 287 | for k, v in libraries.items(): |
| 288 | _declare_library(cg, k, v) |
| 289 | |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 290 | # _codegen_clientset runs the clientset codegenerator. |
| 291 | def _codegen_clientset(ctx): |
| 292 | cg = _cg(ctx, ctx.attr.importpath) |
| 293 | |
| 294 | _declare_libraries(cg, { |
| Lorenz Brun | 6211e4d | 2023-11-14 19:09:40 +0100 | [diff] [blame] | 295 | "clientset/versioned": ["clientset.go"], |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 296 | "clientset/versioned/fake": ["register.go", "clientset_generated.go"], |
| 297 | "clientset/versioned/scheme": ["register.go", "doc.go"], |
| 298 | }) |
| 299 | |
| 300 | for api, types in ctx.attr.apis.items(): |
| 301 | client_name = api.split("/")[-2] |
| 302 | _declare_libraries(cg, { |
| 303 | "clientset/versioned/typed/{}".format(api): [ |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 304 | "doc.go", |
| 305 | "generated_expansion.go", |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 306 | "{}_client.go".format(client_name), |
| 307 | ] + [ |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 308 | "{}.go".format(t) |
| 309 | for t in types |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 310 | ], |
| 311 | "clientset/versioned/typed/{}/fake".format(api): [ |
| 312 | "doc.go", |
| 313 | "fake_{}_client.go".format(client_name), |
| 314 | ], |
| 315 | }) |
| 316 | |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 317 | _gotool_run( |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 318 | cg, |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 319 | mnemonic = "ClientsetGen", |
| 320 | executable = ctx.executable._client_gen, |
| 321 | arguments = [ |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 322 | "--clientset-name", |
| 323 | "versioned", |
| 324 | "--input-base", |
| 325 | ctx.attr.apipath, |
| 326 | "--input", |
| 327 | ",".join(ctx.attr.apis), |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 328 | "--output-pkg", |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 329 | cg.importpath + "/clientset", |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 330 | "--output-dir", |
| 331 | cg.output_root + "/" + cg.importpath + "/clientset", |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 332 | "--go-header-file", |
| 333 | ctx.file.boilerplate.path, |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 334 | ], |
| 335 | inputs = [ |
| 336 | ctx.file.boilerplate, |
| 337 | ], |
| 338 | outputs = cg.outputs, |
| 339 | ) |
| 340 | |
| 341 | return cg.libraries |
| 342 | |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 343 | # _codegen_deepcopy runs the deepcopy codegenerator (outputting to the apipath, |
| 344 | # not the importpath). |
| 345 | def _codegen_deepcopy(ctx): |
| 346 | cg = _cg(ctx, ctx.attr.apipath) |
| 347 | |
| 348 | for api, types in ctx.attr.apis.items(): |
| 349 | _declare_libraries(cg, { |
| 350 | api: ["zz_generated.deepcopy.go"], |
| 351 | }) |
| 352 | |
| 353 | _gotool_run( |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 354 | cg, |
| 355 | copyout = cg.copyout, |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 356 | mnemonic = "DeepcopyGen", |
| 357 | executable = ctx.executable._deepcopy_gen, |
| 358 | arguments = [ |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 359 | "--go-header-file", |
| 360 | ctx.file.boilerplate.path, |
| 361 | "--stderrthreshold", |
| 362 | "0", |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 363 | "--output-file", |
| 364 | "zz_generated.deepcopy.go", |
| 365 | ] + ["{}/{}".format(ctx.attr.apipath, api) for api in ctx.attr.apis], |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 366 | inputs = [ |
| 367 | ctx.file.boilerplate, |
| 368 | ], |
| 369 | outputs = cg.outputs, |
| 370 | ) |
| 371 | return cg.libraries |
| 372 | |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 373 | # _codegen_informer runs the informer codegenerator. |
| 374 | def _codegen_informer(ctx): |
| 375 | cg = _cg(ctx, ctx.attr.importpath) |
| 376 | |
| 377 | _declare_libraries(cg, { |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 378 | "informers/externalversions": ["factory.go", "generic.go"], |
| 379 | "informers/externalversions/internalinterfaces": ["factory_interfaces.go"], |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 380 | }) |
| 381 | |
| 382 | for api, types in ctx.attr.apis.items(): |
| 383 | client_name = api.split("/")[-2] |
| 384 | _declare_libraries(cg, { |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 385 | "informers/externalversions/{}".format(client_name): ["interface.go"], |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 386 | "informers/externalversions/{}".format(api): [ |
| 387 | "interface.go", |
| 388 | ] + [ |
| 389 | "{}.go".format(t) |
| 390 | for t in types |
| 391 | ], |
| 392 | }) |
| 393 | |
| 394 | _gotool_run( |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 395 | cg, |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 396 | mnemonic = "InformerGen", |
| 397 | executable = ctx.executable._informer_gen, |
| 398 | arguments = [ |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 399 | "--versioned-clientset-package", |
| 400 | "{}/clientset/versioned".format(ctx.attr.importpath), |
| 401 | "--listers-package", |
| 402 | "{}/listers".format(ctx.attr.importpath), |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 403 | "--output-pkg", |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 404 | "{}/informers".format(ctx.attr.importpath), |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 405 | "--output-dir", |
| 406 | cg.output_root + "/" + cg.importpath + "/informers", |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 407 | "--go-header-file", |
| 408 | ctx.file.boilerplate.path, |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 409 | ] + ["{}/{}".format(ctx.attr.apipath, api) for api in ctx.attr.apis], |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 410 | inputs = [ |
| 411 | ctx.file.boilerplate, |
| 412 | ], |
| 413 | outputs = cg.outputs, |
| 414 | ) |
| 415 | |
| 416 | return cg.libraries |
| 417 | |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 418 | # _codegen_lister runs the lister codegenerator. |
| 419 | def _codegen_lister(ctx): |
| 420 | cg = _cg(ctx, ctx.attr.importpath) |
| 421 | |
| 422 | for api, types in ctx.attr.apis.items(): |
| 423 | client_name = api.split("/")[-2] |
| 424 | _declare_libraries(cg, { |
| 425 | "listers/{}".format(api): [ |
| 426 | "expansion_generated.go", |
| 427 | ] + [ |
| 428 | "{}.go".format(t) |
| 429 | for t in types |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 430 | ], |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 431 | }) |
| 432 | |
| 433 | _gotool_run( |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 434 | cg, |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 435 | mnemonic = "ListerGen", |
| 436 | executable = ctx.executable._lister_gen, |
| 437 | arguments = [ |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 438 | "--output-pkg", |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 439 | "{}/listers".format(ctx.attr.importpath), |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 440 | "--output-dir", |
| 441 | cg.output_root + "/" + cg.importpath + "/listers", |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 442 | "--go-header-file", |
| 443 | ctx.file.boilerplate.path, |
| 444 | "-v", |
| 445 | "10", |
| Tim Windelschmidt | ddc5e6a | 2024-04-23 23:44:34 +0200 | [diff] [blame^] | 446 | ] + ["{}/{}".format(ctx.attr.apipath, api) for api in ctx.attr.apis], |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 447 | inputs = [ |
| 448 | ctx.file.boilerplate, |
| 449 | ], |
| 450 | outputs = cg.outputs, |
| 451 | ) |
| 452 | |
| 453 | return cg.libraries |
| 454 | |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 455 | # _update_dict_check is a helper function that updates dict a with dict b, |
| 456 | # ensuring there's no overwritten keys. |
| 457 | def _update_dict_check(a, b): |
| 458 | for k in b.keys(): |
| 459 | if k in a: |
| 460 | fail("internal error: repeat importpath {}", k) |
| 461 | a.update(b) |
| 462 | |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 463 | def _go_kubernetes_resource_bundle_impl(ctx): |
| 464 | go = go_context(ctx) |
| 465 | |
| 466 | all_gens = {} |
| 467 | _update_dict_check(all_gens, _codegen_clientset(ctx)) |
| 468 | _update_dict_check(all_gens, _codegen_deepcopy(ctx)) |
| 469 | _update_dict_check(all_gens, _codegen_informer(ctx)) |
| 470 | _update_dict_check(all_gens, _codegen_lister(ctx)) |
| 471 | |
| 472 | libraries = [] |
| 473 | for importpath, srcs in all_gens.items(): |
| 474 | library = go.new_library( |
| 475 | go, |
| 476 | srcs = srcs, |
| 477 | importpath = importpath, |
| 478 | ) |
| 479 | libraries.append(library) |
| 480 | |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 481 | return [KubeResourceBundle(libraries = libraries)] |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 482 | |
| 483 | # go_kubernetes_resource_bundle runs kubernetes code-generators on a codepath |
| 484 | # for some requested APIs, and whose output can be made into Go library targets |
| 485 | # via go_kubernetes_library. This bundle corresponds to a single Kubernetes API |
| 486 | # resource group. |
| 487 | go_kubernetes_resource_bundle = rule( |
| 488 | implementation = _go_kubernetes_resource_bundle_impl, |
| 489 | attrs = { |
| 490 | "gopath": attr.label( |
| 491 | mandatory = True, |
| 492 | providers = [GoPath], |
| 493 | cfg = preprocessing_transition, |
| 494 | doc = "A rules_go go_path that contains all the API libraries for which codegen should be run.", |
| 495 | ), |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 496 | "importpath": attr.string( |
| 497 | mandatory = True, |
| 498 | doc = """ |
| 499 | The root importpath of the generated code (apart from deepcopy |
| 500 | codegen). The Bazel target path corresponding to this |
| 501 | importpath needs to contain the go_kubernetes_library and |
| 502 | go_library targets that allow to actually build against the |
| 503 | generated code. |
| 504 | """, |
| 505 | ), |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 506 | "apipath": attr.string( |
| 507 | mandatory = True, |
| 508 | doc = "The root importpath of the APIs for which to generate code.", |
| 509 | ), |
| 510 | "apis": attr.string_list_dict( |
| 511 | mandatory = True, |
| 512 | doc = """ |
| 513 | The APIs underneath importpath for which to generated code, |
| 514 | eg. foo/v1, mapping into a list of lowercased types generated |
| 515 | from each (eg. widget for `type Widget struct`). |
| 516 | """, |
| 517 | ), |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 518 | "boilerplate": attr.label( |
| 519 | default = "//metropolis/build/kube-code-generator:boilerplate.go.txt", |
| 520 | allow_single_file = True, |
| 521 | doc = "Header that will be used in the generated code.", |
| 522 | ), |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 523 | "_go_context_data": attr.label( |
| 524 | default = "@io_bazel_rules_go//:go_context_data", |
| 525 | ), |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 526 | "_gotoolwrap": attr.label( |
| 527 | default = Label("//metropolis/build/gotoolwrap"), |
| 528 | allow_single_file = True, |
| 529 | executable = True, |
| 530 | cfg = "exec", |
| 531 | ), |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 532 | "_deepcopy_gen": attr.label( |
| 533 | default = Label("@io_k8s_code_generator//cmd/deepcopy-gen"), |
| 534 | allow_single_file = True, |
| 535 | executable = True, |
| 536 | cfg = "exec", |
| 537 | ), |
| 538 | "_client_gen": attr.label( |
| 539 | default = Label("@io_k8s_code_generator//cmd/client-gen"), |
| 540 | allow_single_file = True, |
| 541 | executable = True, |
| 542 | cfg = "exec", |
| 543 | ), |
| 544 | "_informer_gen": attr.label( |
| 545 | default = Label("@io_k8s_code_generator//cmd/informer-gen"), |
| 546 | allow_single_file = True, |
| 547 | executable = True, |
| 548 | cfg = "exec", |
| 549 | ), |
| 550 | "_lister_gen": attr.label( |
| 551 | default = Label("@io_k8s_code_generator//cmd/lister-gen"), |
| 552 | allow_single_file = True, |
| 553 | executable = True, |
| 554 | cfg = "exec", |
| 555 | ), |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 556 | "_allowlist_function_transition": attr.label( |
| Lorenz Brun | 4e0dba6 | 2021-05-17 15:25:15 +0200 | [diff] [blame] | 557 | default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| 558 | ), |
| Serge Bazanski | 7353e17 | 2021-03-31 22:09:22 +0200 | [diff] [blame] | 559 | }, |
| 560 | toolchains = ["@io_bazel_rules_go//go:toolchain"], |
| 561 | ) |