Skip to content

Commit 882bd59

Browse files
Add annotation_selects tag class (#3541)
Adds an `annotation_select` tag class. This annotation will add common environment variables: ``` crate.annotation( crate = "foo", build_scipt_data = [ "@bar", ], build_script_env = { "BAR": "bar", }, ) ``` This annotation will append a platform dependent select statement to the above variables: ``` crate.annotation_select( crate = "foo", triples = ["x86_64-unknown-linux-gnu"], build_script_data = [ "@baz", ], build_script_env = { "BAZ": "baz", }, ) ``` Attempts to improve #3511 --------- Co-authored-by: Daniel Wagner-Hall <[email protected]>
1 parent 40e97b1 commit 882bd59

File tree

1 file changed

+169
-14
lines changed

1 file changed

+169
-14
lines changed

crate_universe/extensions.bzl

Lines changed: 169 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,109 @@ def _get_host_cargo_rustc(module_ctx, host_triple, host_tools_repo):
858858

859859
return cargo_path, rustc_path
860860

861+
def _update_annotations(annotations, crate, version, triples, data, env):
862+
"""Insert build-script data/env for each target triple if keys exist."""
863+
crate_info = annotations.get(crate)
864+
if not crate_info:
865+
fail("Crate {} not found in annotations".format(crate))
866+
867+
version_info = crate_info.get(version)
868+
if not version_info:
869+
fail("Version {} not found in annotations for crate {}".format(version, crate))
870+
871+
for triple in triples:
872+
entry = _insert_build_script_data(version_info, data, triple, crate, version)
873+
entry = _insert_build_script_env(entry, env, triple, crate, version)
874+
crate_info[version] = entry
875+
876+
return crate_info
877+
878+
def _insert_annotation(store, crate, annotation):
879+
"""Insert `annotation` (dict) into `store[crate][version]`.
880+
881+
Raises `fail()` if that (crate, version) pair already exists.
882+
"""
883+
version = annotation["version"]
884+
crate_map = store.setdefault(crate, {}) # create nested map if needed
885+
886+
if version in crate_map:
887+
fail("Duplicate annotation for crate {} version {}".format(crate, version))
888+
889+
crate_map[version] = annotation
890+
891+
def _insert_select(annotation, field, value, triple, crate, version):
892+
"""
893+
Add a per-triple override to annotation[field].
894+
895+
If annotation[field] is in a list or dict, we wrap that into the common field. Then
896+
897+
On later calls we just update the existing selects.
898+
"""
899+
current = annotation.get(field)
900+
901+
if type(current) != "struct":
902+
annotation[field] = struct(
903+
common = current,
904+
selects = {triple: value},
905+
)
906+
else:
907+
if triple in current.selects:
908+
fail("Duplicate annotation for crate {} version {} triple {}".format(crate, version, triple))
909+
current.selects.update({triple: value})
910+
911+
return annotation
912+
913+
def _insert_build_script_data(annotation, build_script_data, triple, crate, version):
914+
return _insert_select(
915+
annotation,
916+
"build_script_data",
917+
build_script_data,
918+
triple,
919+
crate,
920+
version,
921+
)
922+
923+
def _insert_build_script_env(annotation, build_script_env, triple, crate, version):
924+
return _insert_select(
925+
annotation,
926+
"build_script_env",
927+
build_script_env,
928+
triple,
929+
crate,
930+
version,
931+
)
932+
933+
def _create_annotation(annotation_dict):
934+
return _crate_universe_crate.annotation(**{
935+
k: v
936+
for k, v in annotation_dict.items()
937+
# Tag classes can't take in None, but the function requires None
938+
# instead of the empty values in many cases.
939+
# https://github.com/bazelbuild/bazel/issues/20744
940+
if v != "" and v != [] and v != {}
941+
})
942+
943+
def _collapse_crate_versions(crates, create = _create_annotation):
944+
"""{crate: {version: annotation}} -> {crate: [annotation,…]}"""
945+
return {
946+
crate: [create(v) for v in versions.values()]
947+
for crate, versions in crates.items()
948+
}
949+
950+
def _collapse_versions(repo_specific, module, create = _create_annotation):
951+
"""
952+
Return new (repo_specific, module) dicts with versions collapsed.
953+
954+
repo_specific: {repo: {crate: {version: annotation}}}
955+
module : {crate: {version: annotation}}
956+
"""
957+
new_repo_specific = {
958+
repo: _collapse_crate_versions(crates, create)
959+
for repo, crates in repo_specific.items()
960+
}
961+
new_module = _collapse_crate_versions(module, create)
962+
return new_repo_specific, new_module
963+
861964
def _crate_impl(module_ctx):
862965
reproducible = True
863966
host_triple = get_host_triple(module_ctx, abi = {
@@ -927,23 +1030,45 @@ def _crate_impl(module_ctx):
9271030
if replacement:
9281031
annotation_dict["override_targets"]["bin"] = str(replacement)
9291032

930-
annotation = _crate_universe_crate.annotation(**{
931-
k: v
932-
for k, v in annotation_dict.items()
933-
# Tag classes can't take in None, but the function requires None
934-
# instead of the empty values in many cases.
935-
# https://github.com/bazelbuild/bazel/issues/20744
936-
if v != "" and v != [] and v != {}
937-
})
9381033
if not repositories:
939-
_get_or_insert(module_annotations, crate, []).append(annotation)
940-
for repo in repositories:
941-
_get_or_insert(
942-
_get_or_insert(repo_specific_annotations, repo, {}),
1034+
_insert_annotation(module_annotations, crate, annotation_dict)
1035+
else:
1036+
for repo in repositories:
1037+
# each repo gets its own top-level dict:
1038+
repo_map = repo_specific_annotations.setdefault(repo, {})
1039+
_insert_annotation(repo_map, crate, annotation_dict)
1040+
1041+
for annotation_select_tag in mod.tags.annotation_select:
1042+
annotation_select_dict = structs.to_dict(annotation_select_tag)
1043+
repositories = annotation_select_dict.pop("repositories")
1044+
crate = annotation_select_dict.pop("crate")
1045+
triples = annotation_select_dict.pop("triples")
1046+
version = annotation_select_dict["version"]
1047+
build_script_data = annotation_select_dict["build_script_data"]
1048+
build_script_env = annotation_select_dict["build_script_env"]
1049+
1050+
if repositories:
1051+
for repo in repositories:
1052+
_update_annotations(
1053+
repo_specific_annotations.get(repo, {}),
1054+
crate,
1055+
version,
1056+
triples,
1057+
build_script_data,
1058+
build_script_env,
1059+
)
1060+
else:
1061+
_update_annotations(
1062+
module_annotations,
9431063
crate,
944-
[],
945-
).append(annotation)
1064+
version,
1065+
triples,
1066+
build_script_data,
1067+
build_script_env,
1068+
)
9461069

1070+
# Now transform the annotations into a format that the `_generate_hub_and_spokes` function expects.
1071+
repo_specific_annotations, module_annotations = _collapse_versions(repo_specific_annotations, module_annotations)
9471072
common_specs = []
9481073
repo_specific_specs = {}
9491074
for spec in mod.tags.spec:
@@ -1389,6 +1514,35 @@ can be found below where the supported keys for each template can be found in th
13891514
},
13901515
)
13911516

1517+
_annotation_select = tag_class(
1518+
doc = "A constructor for a crate dependency with selectable attributes.",
1519+
attrs = {
1520+
"crate": attr.string(
1521+
doc = "The name of the crate the annotation is applied to",
1522+
mandatory = True,
1523+
),
1524+
"repositories": attr.string_list(
1525+
doc = "A list of repository names specified from `crate.from_cargo(name=...)` that this annotation is applied to. Defaults to all repositories.",
1526+
default = [],
1527+
),
1528+
"version": attr.string(
1529+
doc = "The versions of the crate the annotation is applied to. Defaults to all versions.",
1530+
default = "*",
1531+
),
1532+
} | {
1533+
"build_script_data": attr.string_list(
1534+
doc = "A list of labels to add to a crate's `cargo_build_script::data` attribute.",
1535+
),
1536+
"build_script_env": attr.string_dict(
1537+
doc = "Additional environment variables to set on a crate's `cargo_build_script::env` attribute.",
1538+
),
1539+
"triples": attr.string_list(
1540+
doc = "A list of triples to apply the annotation to.",
1541+
mandatory = True,
1542+
),
1543+
},
1544+
)
1545+
13921546
crate = module_extension(
13931547
doc = """\
13941548
Crate universe module extensions.
@@ -1408,6 +1562,7 @@ Environment Variables:
14081562
implementation = _crate_impl,
14091563
tag_classes = {
14101564
"annotation": _annotation,
1565+
"annotation_select": _annotation_select,
14111566
"from_cargo": _from_cargo,
14121567
"from_specs": _from_specs,
14131568
"render_config": _render_config,

0 commit comments

Comments
 (0)