the idea is to have testonly-esque checks
consider a project where we want to partition content (for an existing rule) into three categories:
- "open-source"; a.k.a.
oss - "proprietary"; a.k.a.
prp - "development-only"; a.k.a.
dev
such that these categories are supersets: dev ⊃ prp ⊃ oss
i.e. the following dep edges are allowed:
- content of kind
osscan depend on content of kindoss - content of kind
prpcan depend on content of kindprpORoss - content of kind
devcan depend on content of kinddevORprpORoss
and the following are not allowed:
ossdepending onprpORdevprpdepending ondev
more concretely:
cc_library(name = "external_lib")
cc_library(name = "oss1") # kind = "oss"
cc_library(name = "proprietary1") # kind = "prp"
cc_library(name = "dev1") # kind = "dev"
cc_library( # kind = "oss"
name = "oss",
deps = [
":external_lib", # fine, not tagged
":oss1", # okay
":proprietary1", # !!! not allowed
":dev1", # !!! not allowed
],
)
cc_library( # kind = "prp"
name = "proprietary",
deps = [
":external_lib", # fine, not tagged
":oss1", # okay
":proprietary1", # okay
":dev1", # !!! not allowed
],
)
cc_library( # kind = "dev"
name = "dev",
deps = [
":external_lib", # fine, not tagged
":oss1", # okay
":proprietary1", # okay
":dev1", # okay
],
)
# and, ideally:
cc_library(name = "laundered", deps = [":dev1"])
cc_library(name = "oss2", deps = [":laundered"]) # !!! not alloweduhhh... didn't actually end up implementing this; I think environments are a better fit for a few reasons:
- applies to all rules; don't need to extend rules or add aspects to run using CLI flags
- better error messages? though this is something that you could have the aspects do with enough work
selectawareness: support for "refinements"
this would look like:
- an aspect that walks your dep graph checking that dep edges match the desired rules
- targets communicate their "kind" by any of:
- rule extensions that have an extra attribute (non-configurable, we'd hope) or an extra provider
aspect_hints- using
tags
for background on environments see:
- Kristina Chodorow's blog post
- this stackoverflow post's overview
- docs on:
- the common
compatible_withandrestricted_torule attributes environment_groupandenvironment(within the codebase overview)
- the common
- definitions for the
environmentandenvironment_grouprules in the Bazel source code - this source code comment on
ConstraintSemanticswithin Bazel - this github issue discussing the state of
environments in Bazel and potential alternatives - the
--target_environmentflag docs - other misc undocumented attributes like:
restrictedToandcompatibleWithon rule definitions- these have no starlark-accessible equivalent (i.e. no corresponding attributes on
rule(...)) - buuuut we can fake it by mimicking the effects; i.e. setting the private (starts with
$) attributes that the environments given torestrictedToandcompatibleWithare lowered onto the rule instantiation as - UPDATE: nevermind, this doesn't work; those attributes are only added if
restrictedTois set so we can't set them from the starlark side... need to just do the regular old macro thing - it's unfortunate because the behavior of
restrictedTo/compatibleWithon rule declarations and how it interacts withrestricted_toandcompatible_withattrs specified on targets is pretty good and not trivial to replicate in the macro layer
- these have no starlark-accessible equivalent (i.e. no corresponding attributes on
default_restricted_toanddefault_compatible_withinpackage(...)
Tip
You can use package(default_restricted_to = [...]) and package(default_compatible_with = [...]) to set restricted_to/compatible_with for all targets in a package!
(these attributes to package are not documented; see here)
Important
The way default_restricted_to and default_compatible_with interact with the corresponding attributes set on targets is that restricted_to/compatible_with take precedence: no merging of values happens.
See here for details.
Tip
You can do a "restricted environment build" by setting environments to "check" targets against
i.e. --target_environment=//:oss_e1 will flag targets that cannot be built for OSS:
❯ bazel build //... --target_environment=//:oss_e1
INFO: Build option --target_environment has changed, discarding analysis cache.
ERROR: This is a restricted-environment build.
//:dev_e1_2 declares compatibility with:
[//:dev_e1]
but does not support:
//:oss_e1
//:prp_e1_2 declares compatibility with:
[//:prp_e1, //:dev_e1]
but does not support:
//:oss_e1
//:prp_e1_1 declares compatibility with:
[//:prp_e1, //:dev_e1]
but does not support:
//:oss_e1
//:dev_e1_1 declares compatibility with:
[//:dev_e1]
but does not support:
//:oss_e1--target_environment=//:dev_e1 will flag targets that can't be built for dev (i.e. nothing)
--target_environment=//:oss_e1,//:prp_e1 will flag targets that can't be built for oss AND prp:
❯ bazel build //... --target_environment=//:oss_e1,//:prp_e1
INFO: Build option --target_environment has changed, discarding analysis cache.
ERROR: This is a restricted-environment build.
//:prp_e1_2 declares compatibility with:
[//:prp_e1, //:dev_e1]
but does not support:
//:oss_e1
//:dev_e1_2 declares compatibility with:
[//:dev_e1]
but does not support:
//:oss_e1
//:prp_e1
//:prp_e1_1 declares compatibility with:
[//:prp_e1, //:dev_e1]
but does not support:
//:oss_e1
//:dev_e1_1 declares compatibility with:
[//:dev_e1]
but does not support:
//:oss_e1
//:prp_e1[!TIP]
refined environments
- selects: union (refinement)
TODO: restricted_to and compatible_with on rules?
- can get close with macros but not perfect
- fortunately: non-configurable so can check stuff in your macro
see BUILD.bazel
see BUILD.bazel