Skip to content

Feature Request: lib/selects to generate all combinations. #573

@laramiel

Description

@laramiel

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions