# Copyright 2024 The Bazel Authors. All rights reserved. # # 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. """Builders to make building complex objects easier.""" load("@bazel_skylib//lib:types.bzl", "types") def _DepsetBuilder(): """Create a builder for a depset.""" # buildifier: disable=uninitialized self = struct( _order = [None], add = lambda *a, **k: _DepsetBuilder_add(self, *a, **k), build = lambda *a, **k: _DepsetBuilder_build(self, *a, **k), direct = [], get_order = lambda *a, **k: _DepsetBuilder_get_order(self, *a, **k), set_order = lambda *a, **k: _DepsetBuilder_set_order(self, *a, **k), transitive = [], ) return self def _DepsetBuilder_add(self, *values): """Add value to the depset. Args: self: {type}`DepsetBuilder` implicitly added. *values: {type}`depset | list | object` Values to add to the depset. The values can be a depset, the non-depset value to add, or a list of such values to add. Returns: {type}`DepsetBuilder` """ for value in values: if types.is_list(value): for sub_value in value: if types.is_depset(sub_value): self.transitive.append(sub_value) else: self.direct.append(sub_value) elif types.is_depset(value): self.transitive.append(value) else: self.direct.append(value) return self def _DepsetBuilder_set_order(self, order): """Sets the order to use. Args: self: {type}`DepsetBuilder` implicitly added. order: {type}`str` One of the {obj}`depset` `order` values. Returns: {type}`DepsetBuilder` """ self._order[0] = order return self def _DepsetBuilder_get_order(self): """Gets the depset order that will be used. Args: self: {type}`DepsetBuilder` implicitly added. Returns: {type}`str | None` If not previously set, `None` is returned. """ return self._order[0] def _DepsetBuilder_build(self): """Creates a {obj}`depset` from the accumulated values. Args: self: {type}`DepsetBuilder` implicitly added. Returns: {type}`depset` """ if not self.direct and len(self.transitive) == 1 and self._order[0] == None: return self.transitive[0] else: kwargs = {} if self._order[0] != None: kwargs["order"] = self._order[0] return depset(direct = self.direct, transitive = self.transitive, **kwargs) def _RunfilesBuilder(): """Creates a `RunfilesBuilder`. Returns: {type}`RunfilesBuilder` """ # buildifier: disable=uninitialized self = struct( add = lambda *a, **k: _RunfilesBuilder_add(self, *a, **k), add_targets = lambda *a, **k: _RunfilesBuilder_add_targets(self, *a, **k), build = lambda *a, **k: _RunfilesBuilder_build(self, *a, **k), files = _DepsetBuilder(), root_symlinks = {}, runfiles = [], symlinks = {}, ) return self def _RunfilesBuilder_add(self, *values): """Adds a value to the runfiles. Args: self: {type}`RunfilesBuilder` implicitly added. *values: {type}`File | runfiles | list[File] | depset[File] | list[runfiles]` The values to add. Returns: {type}`RunfilesBuilder` """ for value in values: if types.is_list(value): for sub_value in value: _RunfilesBuilder_add_internal(self, sub_value) else: _RunfilesBuilder_add_internal(self, value) return self def _RunfilesBuilder_add_targets(self, targets): """Adds runfiles from targets Args: self: {type}`RunfilesBuilder` implicitly added. targets: {type}`list[Target]` targets whose default runfiles to add. Returns: {type}`RunfilesBuilder` """ for t in targets: self.runfiles.append(t[DefaultInfo].default_runfiles) return self def _RunfilesBuilder_add_internal(self, value): if _is_file(value): self.files.add(value) elif types.is_depset(value): self.files.add(value) elif _is_runfiles(value): self.runfiles.append(value) else: fail("Unhandled value: type {}: {}".format(type(value), value)) def _RunfilesBuilder_build(self, ctx, **kwargs): """Creates a {obj}`runfiles` from the accumulated values. Args: self: {type}`RunfilesBuilder` implicitly added. ctx: {type}`ctx` The rule context to use to create the runfiles object. **kwargs: additional args to pass along to {obj}`ctx.runfiles`. Returns: {type}`runfiles` """ return ctx.runfiles( transitive_files = self.files.build(), symlinks = self.symlinks, root_symlinks = self.root_symlinks, **kwargs ).merge_all(self.runfiles) # Skylib's types module doesn't have is_file, so roll our own def _is_file(value): return type(value) == "File" def _is_runfiles(value): return type(value) == "runfiles" builders = struct( DepsetBuilder = _DepsetBuilder, RunfilesBuilder = _RunfilesBuilder, )