Switch to predetermined `constraint_value` targets

This commit is contained in:
Philipp Schrader 2022-08-29 22:58:56 -07:00
parent ded484cd0a
commit 8d07e16678
6 changed files with 69 additions and 79 deletions

View File

@ -20,6 +20,7 @@ bzl_library(
srcs = ["compatibility.bzl"], srcs = ["compatibility.bzl"],
deps = [ deps = [
":selects", ":selects",
"//lib/compatibility:defs",
], ],
) )

View File

@ -10,51 +10,7 @@ more information.
""" """
load(":selects.bzl", "selects") load(":selects.bzl", "selects")
load("//lib/compatibility:defs.bzl", "MAX_NUM_ALL_OF_SETTINGS")
def _get_name_from_target_list(targets, joiner = " or "):
"""Join a list of strings into a string which is suitable as a target name.
The return value has a hash appended so that it is different between multiple "targets" values
that have the same name portion. For readability, we keep only the name portion of the
specified targets. But for uniqueness we need the hash. For example, the following two calls
may return strings as follows. The hashes are arbitrary in this example.
>>> _get_name_from_target_list(["@platforms//os:linux", "@platforms//cpu:x86_64"])
"linux or x86_64 (3ef2349)"
>>> _get_name_from_target_list(["//some/custom:linux", "//some/custom:x86_64"])
"linux or x86_64 (98ab64)"
Args:
targets: A list of target names.
joiner: An optional string to use for joining the list.
Returns:
A string which is a valid target name.
"""
# Compute absolute labels from the user-specified ones. They can be
# relative or absolute. We do this to make the final name as unique as
# possible via a hash.
package_label = Label("//" + native.package_name())
absolute_targets = sorted([package_label.relative(target) for target in targets])
joined_names = joiner.join([target.name for target in absolute_targets])
name = "%s (%x)" % (joined_names, hash(str(absolute_targets)))
return name
def _maybe_make_unique_incompatible_value(name):
"""Creates a `native.constraint_value` which is "incompatible."
When composing selects which could all resolve to "incompatible" we need distinct labels.
This will create a `constraint_value` with the given name, if it does not already exist.
Args:
name: A target name to check and use.
"""
if not native.existing_rule(name):
native.constraint_value(
name = name,
constraint_setting = "@platforms//:incompatible_setting",
)
def _none_of(*settings): def _none_of(*settings):
"""Create a `select()` for `target_compatible_with` which matches none of the given settings. """Create a `select()` for `target_compatible_with` which matches none of the given settings.
@ -83,12 +39,9 @@ def _none_of(*settings):
Returns: Returns:
A native `select()` which maps any of the settings to the incompatible target. A native `select()` which maps any of the settings to the incompatible target.
""" """
compat_name = " incompatible with " + _get_name_from_target_list(settings)
_maybe_make_unique_incompatible_value(compat_name)
return selects.with_or({ return selects.with_or({
"//conditions:default": [], "//conditions:default": [],
tuple(settings): [":" + compat_name], tuple(settings): ["@bazel_skylib//lib/compatibility:none_of"],
}) })
def _any_of(*settings): def _any_of(*settings):
@ -118,12 +71,9 @@ def _any_of(*settings):
Returns: Returns:
A native `select()` which maps any of the settings an empty list. A native `select()` which maps any of the settings an empty list.
""" """
compat_name = " compatible with any of " + _get_name_from_target_list(settings)
_maybe_make_unique_incompatible_value(compat_name)
return selects.with_or({ return selects.with_or({
tuple(settings): [], tuple(settings): [],
"//conditions:default": [":" + compat_name], "//conditions:default": ["@bazel_skylib//lib/compatibility:any_of"],
}) })
def _all_of(*settings): def _all_of(*settings):
@ -160,23 +110,18 @@ def _all_of(*settings):
*settings: The `config_setting` or `constraint_value` targets. *settings: The `config_setting` or `constraint_value` targets.
Returns: Returns:
A native `select()` which is "incompatible" unless all settings are true. A native series of `select()`s. The result is "incompatible" unless all settings are true.
""" """
group_name = _get_name_from_target_list(settings, joiner = " and ") if len(settings) > MAX_NUM_ALL_OF_SETTINGS:
compat_name = " compatible with all of " + group_name fail("Cannot support more than {} arguments. Use selects.config_setting_group() instead.".format(MAX_NUM_ALL_OF_SETTINGS))
_maybe_make_unique_incompatible_value(compat_name)
# all_of can only be accomplished with a config_setting_group.match_all. result = []
if not native.existing_rule(group_name): for i, setting in enumerate(settings):
selects.config_setting_group( result += select({
name = group_name, setting: [],
match_all = settings, "//conditions:default": ["@bazel_skylib//lib/compatibility:all_of_{}".format(i)],
) })
return result
return select({
":" + group_name: [],
"//conditions:default": [":" + compat_name],
})
compatibility = struct( compatibility = struct(
all_of = _all_of, all_of = _all_of,

32
lib/compatibility/BUILD Normal file
View File

@ -0,0 +1,32 @@
load(":defs.bzl", "MAX_NUM_ALL_OF_SETTINGS")
load("//:bzl_library.bzl", "bzl_library")
licenses(["notice"])
package(default_visibility = ["//visibility:public"])
bzl_library(
name = "defs",
srcs = ["defs.bzl"],
)
filegroup(
name = "build_file",
testonly = True,
srcs = ["BUILD"],
)
constraint_value(
name = "none_of",
constraint_setting = "@platforms//:incompatible_setting",
)
constraint_value(
name = "any_of",
constraint_setting = "@platforms//:incompatible_setting",
)
[constraint_value(
name = "all_of_" + str(i),
constraint_setting = "@platforms//:incompatible_setting",
) for i in range(50)]

View File

@ -0,0 +1,2 @@
# The maximum number of arguments we support for compatibility.all_of().
MAX_NUM_ALL_OF_SETTINGS = 50

View File

@ -98,6 +98,7 @@ sh_test(
data = [ data = [
":unittest.bash", ":unittest.bash",
"//lib:compatibility", "//lib:compatibility",
"//lib/compatibility:build_file",
], ],
tags = ["local"], tags = ["local"],
deps = [ deps = [

View File

@ -67,11 +67,21 @@ EOF
workspace(name = 'bazel_skylib') workspace(name = 'bazel_skylib')
EOF EOF
cat > BUILD <<EOF
EOF
cat > bzl_library.bzl <<EOF
def bzl_library(**kwargs):
"""A dummy implementation of bzl_library()."""
pass
EOF
mkdir -p lib mkdir -p lib
cat > lib/BUILD <<EOF cat > lib/BUILD <<EOF
EOF EOF
for file in compatibility.bzl selects.bzl; do for file in compatibility.bzl selects.bzl compatibility/BUILD compatibility/defs.bzl; do
mkdir -p "$(dirname "lib/${file}")" \
|| fail "couldn't mkdir for ${file}"
ln -sf "$(rlocation "bazel_skylib/lib/${file}")" "lib/${file}" \ ln -sf "$(rlocation "bazel_skylib/lib/${file}")" "lib/${file}" \
|| fail "couldn't symlink ${file}." || fail "couldn't symlink ${file}."
done done
@ -217,7 +227,7 @@ EOF
ensure_that_target_doesnt_build_for_platforms \ ensure_that_target_doesnt_build_for_platforms \
//target_skipping:pass_on_foo1_or_foo2_but_not_on_foo3 \ //target_skipping:pass_on_foo1_or_foo2_but_not_on_foo3 \
"didn't satisfy constraint //target_skipping: compatible with any of foo1 or foo2 (" \ "didn't satisfy constraint //lib/compatibility:any_of$" \
//target_skipping:foo3_platform \ //target_skipping:foo3_platform \
//target_skipping:bar1_platform //target_skipping:bar1_platform
} }
@ -240,7 +250,7 @@ EOF
ensure_that_target_doesnt_build_for_platforms \ ensure_that_target_doesnt_build_for_platforms \
//target_skipping:pass_on_everything_but_foo1_and_foo2 \ //target_skipping:pass_on_everything_but_foo1_and_foo2 \
"didn't satisfy constraint //target_skipping: incompatible with foo1 or foo2 (" \ "didn't satisfy constraint //lib/compatibility:none_of$" \
//target_skipping:foo1_bar1_platform \ //target_skipping:foo1_bar1_platform \
//target_skipping:foo2_bar1_platform \ //target_skipping:foo2_bar1_platform \
//target_skipping:foo2_bar2_platform //target_skipping:foo2_bar2_platform
@ -263,7 +273,7 @@ EOF
ensure_that_target_doesnt_build_for_platforms \ ensure_that_target_doesnt_build_for_platforms \
//target_skipping:pass_on_only_foo1_and_bar1 \ //target_skipping:pass_on_only_foo1_and_bar1 \
"didn't satisfy constraint //target_skipping: compatible with all of bar1 and foo1 (" \ "didn't satisfy constraints\\? \\[\\?//lib/compatibility:all_of_" \
//target_skipping:foo2_bar1_platform \ //target_skipping:foo2_bar1_platform \
//target_skipping:foo2_bar2_platform \ //target_skipping:foo2_bar2_platform \
//target_skipping:foo3_platform \ //target_skipping:foo3_platform \
@ -291,24 +301,23 @@ EOF
ensure_that_target_doesnt_build_for_platforms \ ensure_that_target_doesnt_build_for_platforms \
//target_skipping:pass_on_foo1_or_foo2_but_not_bar1 \ //target_skipping:pass_on_foo1_or_foo2_but_not_bar1 \
"didn't satisfy constraint //target_skipping: incompatible with bar1 (" \ "didn't satisfy constraint //lib/compatibility:none_of$" \
//target_skipping:foo1_bar1_platform \ //target_skipping:foo1_bar1_platform \
//target_skipping:foo2_bar1_platform \ //target_skipping:foo2_bar1_platform
ensure_that_target_doesnt_build_for_platforms \ ensure_that_target_doesnt_build_for_platforms \
//target_skipping:pass_on_foo1_or_foo2_but_not_bar1 \ //target_skipping:pass_on_foo1_or_foo2_but_not_bar1 \
"didn't satisfy constraint //target_skipping: compatible with any of foo1 or foo2 (" \ "didn't satisfy constraint //lib/compatibility:any_of$" \
//target_skipping:foo3_platform //target_skipping:foo3_platform
ensure_that_target_doesnt_build_for_platforms \ ensure_that_target_doesnt_build_for_platforms \
//target_skipping:pass_on_foo1_or_foo2_but_not_bar1 \ //target_skipping:pass_on_foo1_or_foo2_but_not_bar1 \
"didn't satisfy constraints \\[" \ "didn't satisfy constraints \\[//lib/compatibility:" \
//target_skipping:bar1_platform //target_skipping:bar1_platform
# Since the order of constraints isn't guaranteed until # Since the order of constraints isn't guaranteed until
# 72787a1267a6087923aca83bf161f93c0a1323e0, we do two individual checks here. # 72787a1267a6087923aca83bf161f93c0a1323e0, we do two individual checks here.
expect_log "//target_skipping: compatible with any of foo1 or foo2 (" expect_log "//lib/compatibility:any_of\\>"
expect_log "//target_skipping: incompatible with bar1 (" expect_log "//lib/compatibility:none_of\\>"
} }
cd "$TEST_TMPDIR" cd "$TEST_TMPDIR"