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

Create compatibility.bzl #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Create compatibility.bzl #1

wants to merge 2 commits into from

Conversation

trybka
Copy link
Owner

@trybka trybka commented Mar 1, 2021

No description provided.

@trybka
Copy link
Owner Author

trybka commented Mar 1, 2021

The "verbose" operators all use a named incompatible target, which means you get error messages like this:

bazel test //base:port_test --cpu=armv7a

(10:25:47) ERROR: Target //base:port_test is incompatible and cannot be built, but was explicitly requested.
Dependency chain:
    //base:port_test
    //base:base   <-- target platform didn't satisfy constraint //base:compatible_with_all_of_linux-ubuntu_and_x86_64

The named constraint is meaningless, and just a placeholder.

Alternatively, we can rely on the select no_match_error:

Analysis of target '//base:port_test' failed; build aborted: Configurable attribute "target_compatible_with" doesn't match this configuration: Didn't match all conditions.

Here, Didn't match all conditions. is just a placeholder which can actually express all the config_setting's .

@trybka
Copy link
Owner Author

trybka commented Mar 2, 2021

I hate myself for even suggesting this... but using some clever unicode...

We can yield this instead:

Dependency chain:
    //base:port_test
    //base:base   <-- target platform didn't satisfy constraint //base:compatible_with_all_of··· ∕∕tools∕cc_target_os∶linux-ubuntu and ∕∕tools∕target_cpu∶x86_64

Copy link

@philsc philsc left a comment

Choose a reason for hiding this comment

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

Very cool! Would you happen to have an accompanying test file that you're testing these out in? Would be fun to play with this a bit. Obviously it's easy to set something up manually to play with this, but I was just curious.


def _any_of(settings):
no_match_error = "Didn't match any conditions." # TODO(trybka): concat setting names?
return selects.with_or({tuple(settings): [],}, no_match_error = no_match_error)
Copy link

Choose a reason for hiding this comment

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

Avoiding no_match_error would be good I think. Otherwise you get analysis errors instead of target skipping.

I.e. this should probably be "//conditions:default": [INCOMPATIBLE_TARGET], no?

Copy link
Owner Author

Choose a reason for hiding this comment

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

d'oh -- good point. I was mostly down the rabbit hole testing on a single broken target.

yeah, this is a non-starter for skipping.

settings_name = "_and_".join([s.split(":")[1] for s in settings])
name = settings_name.replace(":", "-").replace("//", "").replace("/", "_")
if not native.existing_rule(name):
selects.config_setting_group(name = name, match_all = settings)
Copy link

Choose a reason for hiding this comment

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

Yeah, this makes sense. I never tried to implement all_of before, but it makes sense that you can't do it with a single select() statement.

Choose a reason for hiding this comment

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

It's cool to see composition of Starlark libs actually working. I always worry the "non-native" part of selects means using config_setting_group as a library call could blow up in unexpected ways. Happy to see it all remains smooth.

Copy link
Owner Author

Choose a reason for hiding this comment

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

I think the only sharp/weird edge here is the fact that this has to be a named rule (which necessitated the existing_rule check). If you have alternatives for how to make unique or unnamed targets here, that would improve things, I think.

Copy link

Choose a reason for hiding this comment

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

So... I had a thought. Would a simple implementation of all_of look like this?

def _all_of(settings):
    return settings

The target_compatible_with attribute already implements AND logic by default.

Maybe as per your comment below, maybe don't provide all_of at all? I like it there for consistency, but maybe it's superfluous?

Copy link
Owner Author

Choose a reason for hiding this comment

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

That would only work if settings contained only constraint_values. In the case I want to support, raw config_settings don't work here. (Apparently anything that provides ConstraintValueInfo would work, but currently nothing else does, and that provider is behind some flag anyhow.)

Copy link

Choose a reason for hiding this comment

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

Ah dang. Yeah, I keep forgetting about that, apologies!

name = settings_name.replace(":", "-").replace("//", "").replace("/", "_")
if not native.existing_rule(name):
selects.config_setting_group(name = name, match_all = settings)
compat_name = "compatible_with_all_of_" + name
Copy link

Choose a reason for hiding this comment

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

Neat idea!

Choose a reason for hiding this comment

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

What's the difference between this new value and just @platforms//:incompatible?

Copy link
Owner Author

Choose a reason for hiding this comment

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

@platforms//:incompatible cannot appear twice in this attribute--so if two selects resolve to incompatible, there will be an error.

@trybka
Copy link
Owner Author

trybka commented Mar 2, 2021

Very cool! Would you happen to have an accompanying test file that you're testing these out in? Would be fun to play with this a bit. Obviously it's easy to set something up manually to play with this, but I was just curious.

I'll try to add a test here.

@trybka trybka changed the base branch from compatlib to master March 3, 2021 22:32

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.

Copy link

@philsc philsc Mar 23, 2021

Choose a reason for hiding this comment

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

Could you expand this to talk about constraint_value targets not working with all_of because of bazelbuild/bazel#13047?

As a work around, folks should use something like this if they need to mix constraint_value and config_setting targets:

target_compatible_with = [":constraint1", "constraint2"] + compatbility.all_of([":config1", ":config2"]),

Choose a reason for hiding this comment

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

I commented on the bug but I'm really hoping that bug is fixable without a major effort. It's ultimately a silly circular dependency problem. Those are always awkward, but conceptually not too hard to think about ways of short circuiting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants