bazelbuild / bazelbuild/bazel-skylib

Feature Request: lib/selects to generate all combinations.

Open
#573 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Starlark
Stars
444
Forks
202
PR merge metrics
No merged PRs in 30d

Description

When writing bzlmod rules we often need to construct config settings groups for a wide selection of cpu, os, compiler combinations. It would, IMO, be useful to provide a utility to generate this:

```
settings.generate_combinations(
groups = [
{ "@platforms//os:macos", "macos" },
{ "@platforms//cpu:x86_64", "x86_64",
"@platforms//cpu:arm64", "arm64" },
{ "@rules_cc//cc/compiler:clang", "clang" },
]
)

settings.generate_combinations(
groups = [
{ "@platforms//os:linux", "linux" },
{ "@platforms//cpu:x86_64", "x86_64",
"@platforms//cpu:arm64", "arm64" },
{ "@rules_cc//cc/compiler:clang", "gcc",
"@rules_cc//cc/compiler:clang", "clang" },
]
)

settings.generate_combinations(
groups = [
{ "@platforms//os:windows", "windows" },
{ "@platforms//cpu:x86_64", "x86_64",
"@platforms//cpu:arm64", "arm64" },
{ "@rules_cc//cc/compiler:msvc-cl", "msvc-cl" ,
"@rules_cc//cc/compiler:mingw-gcc", "mingw-gcc" },
]
)
```

This would generate the select configs for `:macos_x86_64_clang`,`:linux_x86_64_gcc`, etc...

Included below is a sketch of a possible implementation.

```
def _generate_combinations_impl(args):
if not args:
return [ [] ]

for l in _generate_combinations(args[1:]):
for t in args[0].items():
if t[0] and t[1]:
result.append([t] + l)
return result

def _generate_combinations(prefix, groups, visibility):

for l in _generate_combinations(groups):
if not l:
continue
m = [c[0] for c in l ]
n = [prefix] if prefix else []
name = "_".join(n + [str(c[1]) for c in l])

if native.existing_rule(name) == None:
selects.config_setting_group(
name = name,
match_all = m,
visibility = visibility,
)

def generate_combinations(*, groups, prefix=None, visibility=None):
"""Generates config_setting_group for all provided combinations.

Groups is a list of dictionaries, where each key is a condition
and each value is a name slug. The generated group names are of the
form: prefix_slug1_slug2_slug3

Example:

```build
config_setting(name = "one", define_values = {"foo": "true"})
config_setting(name = "two", define_values = {"bar": "false"})
config_setting(name = "three", define_values = {"baz": "more_false"})

generate_combinations(
prefix = "pre",
groups = [
{ ":one", "one" },
{ ":two", "two" },
{ ":three", "three" ,
"//conditions:default", "any" },
]
)

cc_binary(
name = "myapp",
srcs = ["myapp.cc"],
deps = select({
":pre_one_two_three": [":special_deps"],
":pre_one_two_any": [":other_deps"],
"//conditions:default": [":default_deps"]
})
```

Args:
prefix: The prefix of the generated group names.
groups: A list of dictionaries. The keys are the conditions to pass to config_setting_group
as match_all conditions, the values are name slugs.
visibility: Visibility of the config_setting_group.
"""
_generate_combinations_impl(prefix, groups, visibility)
```

Contributor guide

Open the contributing guide

Research direction

Start by inspecting lib/selects and the existing settings.generate_combinations and selects.config_setting_group entry points. Confirm how native.existing_rule, naming, visibility, and group inputs are handled; done means every valid combination produces the requested config_setting_group names without duplicating existing rules.

Written by the indexing model from the issue text.

Assessment

Domain
build-system
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.