Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lib/compatibility.bzl #381

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fa9f398
Create compatibility.bzl
trybka Mar 1, 2021
e86f542
Update compatlib and add some tests.
trybka Mar 23, 2021
b6a5584
Merge remote-tracking branch 'origin/main' into HEAD
philsc Aug 8, 2022
52b2e62
Tweak stuff a bit
philsc Aug 8, 2022
8190b2e
simplify some
philsc Aug 9, 2022
b3d4463
Consolidate tests
philsc Aug 9, 2022
977a070
Simplify some more
philsc Aug 9, 2022
63d9712
Clean up some more
philsc Aug 9, 2022
e202c2b
Get started on docs
philsc Aug 9, 2022
4b34b67
Expand docs a bit
philsc Aug 9, 2022
6abd9ad
Fix some syntax highlighting issues
philsc Aug 9, 2022
dc846a0
regenerate docs
philsc Aug 9, 2022
73b0ed8
Incorporate feedback
philsc Aug 10, 2022
5b4c97c
clean up some more
philsc Aug 10, 2022
b77d66e
Clean up some more
philsc Aug 10, 2022
7568466
Add composed incompatibility to test
philsc Aug 10, 2022
bf4a2cd
Run buildifier
philsc Aug 10, 2022
ee916b0
Fix test
philsc Aug 10, 2022
7939c78
Merge remote-tracking branch 'origin/main' into HEAD
philsc Aug 17, 2022
c333bc4
Remove `bazel shutdown` which is causing segfault on Windows
philsc Aug 17, 2022
d935d67
Switch to *settings
philsc Aug 25, 2022
ded484c
Merge remote-tracking branch 'origin/main' into HEAD
philsc Aug 30, 2022
8d07e16
Switch to predetermined `constraint_value` targets
philsc Aug 30, 2022
0ee8b41
update docs
philsc Aug 30, 2022
04caadf
Fix lint issues
philsc Aug 30, 2022
6765506
Merge remote-tracking branch 'origin/main' into HEAD
philsc Sep 14, 2022
76a22f4
Incorporate feedback
philsc Sep 14, 2022
0d87298
Rename to incompatible_in_*
philsc Sep 14, 2022
9eb896e
Switch everything to @platforms//:incompatible
philsc Sep 18, 2022
d27b58d
delete PATH manipulation
philsc Sep 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ s = shell.quote(p)
## List of modules (in lib/)

* [collections](docs/collections_doc.md)
* [compatibility](docs/compatibility_doc.md)
* [dicts](docs/dicts_doc.md)
* [partial](docs/partial_doc.md)
* [paths](docs/paths_doc.md)
Expand Down
5 changes: 5 additions & 0 deletions docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ stardoc_with_diff_test(
out_label = "//docs:common_settings_doc.md",
)

stardoc_with_diff_test(
bzl_library_target = "//lib:compatibility",
out_label = "//docs:compatibility_doc.md",
)

stardoc_with_diff_test(
bzl_library_target = "//rules:copy_directory",
out_label = "//docs:copy_directory_doc.md",
Expand Down
145 changes: 145 additions & 0 deletions docs/compatibility_doc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<!-- Generated with Stardoc: http://skydoc.bazel.build -->

Skylib module of convenience functions for `target_compatible_with`.

Load the macros as follows in your `BUILD` files:
```python
load("@bazel_skylib//lib:compatibility.bzl", "compatibility")
```

See the [Platform docs](https://bazel.build/docs/platforms#skipping-incompatible-targets) for
more information.


<a id="#compatibility.all_of"></a>

## compatibility.all_of
comius marked this conversation as resolved.
Show resolved Hide resolved

<pre>
compatibility.all_of(<a href="#compatibility.all_of-settings">settings</a>)
</pre>

Create a `select()` for `target_compatible_with` which matches all of the given settings.

All of the settings must be true to get an empty list. Failure to match will result
in an incompatible `constraint_value` for the purpose of target skipping.

In other words, use this function to make a target incompatible unless all of the settings are
true.

Example:

```python
config_setting(
name = "dbg",
values = {"compilation_mode": "dbg"},
)

cc_binary(
name = "bin",
srcs = ["bin.cc"],
# This target can only be built for Linux in debug mode.
target_compatible_with = compatibility.all_of(
":dbg",
"@platforms//os:linux",
),
)
```

See also: `selects.config_setting_group(match_all)`


**PARAMETERS**


| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="compatibility.all_of-settings"></a>settings | The <code>config_setting</code> or <code>constraint_value</code> targets. | none |

**RETURNS**

A native series of `select()`s. The result is "incompatible" unless all settings are true.


<a id="#compatibility.any_of"></a>

## compatibility.any_of

<pre>
compatibility.any_of(<a href="#compatibility.any_of-settings">settings</a>)
</pre>

Create a `select()` for `target_compatible_with` which matches any of the given settings.

Any of the settings will resolve to an empty list, while the default condition will map to
an incompatible `constraint_value` for the purpose of target skipping.

In other words, use this function to make target incompatible unless one or more of the
settings are true.

```python
cc_binary(
name = "bin",
srcs = ["bin.cc"],
# This target can only be built for Linux or Mac.
target_compatible_with = compatibility.any_of(
"@platforms//os:linux",
"@platforms//os:macos",
),
)
```


**PARAMETERS**


| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="compatibility.any_of-settings"></a>settings | The <code>config_settings</code> or <code>constraint_value</code> targets. | none |

**RETURNS**

A native `select()` which maps any of the settings an empty list.


<a id="#compatibility.none_of"></a>

## compatibility.none_of

<pre>
compatibility.none_of(<a href="#compatibility.none_of-settings">settings</a>)
</pre>

Create a `select()` for `target_compatible_with` which matches none of the given settings.

Any of the settings will resolve to an incompatible `constraint_value` for the
purpose of target skipping.

In other words, use this function to make target incompatible if any of the settings are true.

```python
cc_binary(
name = "bin",
srcs = ["bin.cc"],
# This target cannot be built for Linux or Mac, but can be built for
# everything else.
target_compatible_with = compatibility.none_of(
"@platforms//os:linux",
"@platforms//os:macos",
),
)
```


**PARAMETERS**


| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="compatibility.none_of-settings"></a>settings | The <code>config_setting</code> or <code>constraint_value</code> targets. | none |

**RETURNS**

A native `select()` which maps any of the settings to the incompatible target.


9 changes: 9 additions & 0 deletions lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ bzl_library(
srcs = ["collections.bzl"],
)

bzl_library(
name = "compatibility",
srcs = ["compatibility.bzl"],
deps = [
":selects",
"//lib/compatibility:defs",
],
)

bzl_library(
name = "dicts",
srcs = ["dicts.bzl"],
Expand Down
130 changes: 130 additions & 0 deletions lib/compatibility.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Skylib module of convenience functions for `target_compatible_with`.

Load the macros as follows in your `BUILD` files:
```python
load("@bazel_skylib//lib:compatibility.bzl", "compatibility")
```

See the [Platform docs](https://bazel.build/docs/platforms#skipping-incompatible-targets) for
more information.
"""

load(":selects.bzl", "selects")
load("//lib/compatibility:defs.bzl", "MAX_NUM_ALL_OF_SETTINGS")

def _none_of(*settings):
"""Create a `select()` for `target_compatible_with` which matches none of the given settings.

Any of the settings will resolve to an incompatible `constraint_value` for the
purpose of target skipping.

In other words, use this function to make target incompatible if any of the settings are true.

```python
cc_binary(
name = "bin",
srcs = ["bin.cc"],
# This target cannot be built for Linux or Mac, but can be built for
# everything else.
target_compatible_with = compatibility.none_of(
"@platforms//os:linux",
"@platforms//os:macos",
),
)
```

Args:
*settings: The `config_setting` or `constraint_value` targets.

Returns:
A native `select()` which maps any of the settings to the incompatible target.
"""
return selects.with_or({
"//conditions:default": [],
tuple(settings): ["@bazel_skylib//lib/compatibility:none_of"],
philsc marked this conversation as resolved.
Show resolved Hide resolved
})

def _any_of(*settings):
"""Create a `select()` for `target_compatible_with` which matches any of the given settings.

Any of the settings will resolve to an empty list, while the default condition will map to
an incompatible `constraint_value` for the purpose of target skipping.

In other words, use this function to make target incompatible unless one or more of the
settings are true.

```python
cc_binary(
name = "bin",
srcs = ["bin.cc"],
# This target can only be built for Linux or Mac.
target_compatible_with = compatibility.any_of(
"@platforms//os:linux",
"@platforms//os:macos",
),
)
```

Args:
*settings: The `config_settings` or `constraint_value` targets.

Returns:
A native `select()` which maps any of the settings an empty list.
"""
return selects.with_or({
tuple(settings): [],
"//conditions:default": ["@bazel_skylib//lib/compatibility:any_of"],
})

def _all_of(*settings):
"""Create a `select()` for `target_compatible_with` which matches all of the given settings.

All of the settings must be true to get an empty list. Failure to match will result
in an incompatible `constraint_value` for the purpose of target skipping.

In other words, use this function to make a target incompatible unless all of the settings are
true.

Example:

```python
config_setting(
name = "dbg",
values = {"compilation_mode": "dbg"},
)

cc_binary(
name = "bin",
srcs = ["bin.cc"],
# This target can only be built for Linux in debug mode.
target_compatible_with = compatibility.all_of(
":dbg",
"@platforms//os:linux",
),
)
```

See also: `selects.config_setting_group(match_all)`

Args:
*settings: The `config_setting` or `constraint_value` targets.

Returns:
A native series of `select()`s. The result is "incompatible" unless all settings are true.
"""
if len(settings) > MAX_NUM_ALL_OF_SETTINGS:
fail("Cannot support more than {} arguments. Use selects.config_setting_group() instead.".format(MAX_NUM_ALL_OF_SETTINGS))

result = []
for i, setting in enumerate(settings):
result += select({
setting: [],
"//conditions:default": ["@bazel_skylib//lib/compatibility:all_of_{}".format(i)],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if you replace "@bazel_skylib//lib/compatibility:all_of_{}".format(i) with "@platforms//:incompatible" in this line?

It if works, it's much simpler.

Copy link
Author

@philsc philsc Sep 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as #381 (comment)

EDIT: Fixed link.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's one alternative, which I considered worse until now. How about:

result = []
for setting in enumerate(settings):
   result += select({
      setting: result,
       "//conditions:default": ["@bazel_skylib//lib/compatibility:incompatible_in_all_of"]

I think this should evaluate to a single label when a condition is not met.

First line could also be result = settings. That might work better in cquery-es.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to admit that I am struggling to follow your code snippet. Using setting: result, inside the dictionary means that we're nesting select() statements. Which is, as far as I can tell, not allowed. bazelbuild/bazel#1623 (comment)

Bazel also gets unhappy because the types of each select() path no longer match:

Error: '+' operator applied to incompatible types (select of list, select of select)

})
return result

compatibility = struct(
all_of = _all_of,
any_of = _any_of,
none_of = _none_of,
)
32 changes: 32 additions & 0 deletions lib/compatibility/BUILD
Original file line number Diff line number Diff line change
@@ -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",
)
philsc marked this conversation as resolved.
Show resolved Hide resolved

[constraint_value(
name = "all_of_" + str(i),
constraint_setting = "@platforms//:incompatible_setting",
) for i in range(MAX_NUM_ALL_OF_SETTINGS)]
4 changes: 4 additions & 0 deletions lib/compatibility/defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Implementation details for lib/compatibility.bzl."""

# The maximum number of arguments we support for compatibility.all_of().
MAX_NUM_ALL_OF_SETTINGS = 50
14 changes: 14 additions & 0 deletions tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,20 @@ sh_test(
tags = ["local"],
)

sh_test(
name = "compatibility_test",
srcs = ["compatibility_test.sh"],
data = [
":unittest.bash",
"//lib:compatibility",
"//lib/compatibility:build_file",
],
tags = ["local"],
deps = [
"@bazel_tools//tools/bash/runfiles",
],
)

shell_args_test_gen(
name = "shell_spawn_e2e_test_src",
)
Expand Down
Loading