Skip to content

Commit 468c774

Browse files
committed
Added label_injection to crate_universe annotations
1 parent 4461a8a commit 468c774

File tree

6 files changed

+712
-7
lines changed

6 files changed

+712
-7
lines changed

crate_universe/extensions.bzl

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,9 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
377377
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
378378
load(
379379
"//crate_universe/private:common_utils.bzl",
380+
"apply_label_injections",
380381
"new_cargo_bazel_fn",
382+
"sanitize_label_injections",
381383
)
382384
load("//crate_universe/private:crates_repository.bzl", "SUPPORTED_PLATFORM_TRIPLES")
383385
load(
@@ -407,13 +409,6 @@ load("//rust/platform:triple.bzl", "get_host_triple")
407409
load("//rust/platform:triple_mappings.bzl", "system_to_binary_ext")
408410
load(":defs.bzl", _crate_universe_crate = "crate")
409411

410-
# A list of labels which may be relative (and if so, is within the repo the rule is generated in).
411-
#
412-
# If I were to write ":foo", with attr.label_list, it would evaluate to
413-
# "@@//:foo". However, for a tag such as deps, ":foo" should refer to
414-
# "@@rules_rust~crates~<crate>//:foo".
415-
_relative_label_list = attr.string_list
416-
417412
_OPT_BOOL_VALUES = {
418413
"auto": None,
419414
"off": False,
@@ -1010,6 +1005,7 @@ def _crate_impl(module_ctx):
10101005
repositories = annotation_dict.pop("repositories")
10111006
crate = annotation_dict.pop("crate")
10121007
version = annotation_dict.pop("version")
1008+
label_injections = sanitize_label_injections(annotation_dict.pop("label_injections", {}))
10131009

10141010
# The crate.annotation function can take in either a list or a bool.
10151011
# For the tag-based method, because it has type safety, we have to
@@ -1042,6 +1038,16 @@ def _crate_impl(module_ctx):
10421038
if replacement:
10431039
annotation_dict["override_targets"]["bin"] = str(replacement)
10441040

1041+
# Replace any apparent labels with canonical ones. This is important for cases where an apparent label
1042+
# (e.g. `@foo` vs canonical `@@rules_rust+0.1.2+toolchains~foo`) is used in an annotation. To match the
1043+
# user intent of the annotation, these labels must be resolved at the same module level the annotation
1044+
# is defined.
1045+
for attr_name in _ANNOTATION_SELECT_ATTRS:
1046+
annotation_dict[attr_name] = apply_label_injections(
1047+
label_mapping = label_injections,
1048+
attribute = annotation_dict[attr_name],
1049+
)
1050+
10451051
if not repositories:
10461052
_insert_annotation(module_annotations, crate, version, annotation_dict)
10471053
else:
@@ -1305,6 +1311,13 @@ _ANNOTATION_NORMAL_ATTRS = {
13051311
values = _OPT_BOOL_VALUES.keys(),
13061312
default = "auto",
13071313
),
1314+
"label_injections": attr.label_keyed_string_dict(
1315+
doc = (
1316+
"A mapping of labels to stable names used in the annotation. This is necessary for cases where a `build_script_data` annotation is given and the new label is used in location " +
1317+
"expansion via `build_script_env`. E.g. `build_script_data = [\"@xz//:lzma\"]` and `build_script_env = {\"LZMA_BIN\": \"$(execpath @xz//:lzma)\"}`. This example would require " +
1318+
"`label_injections = {\"@xz\": \"@xz\"}` where the key will be resolved to a canonical label and the value is the apparent label used in annotations."
1319+
),
1320+
),
13081321
"override_target_bin": attr.label(
13091322
doc = "An optional alternate target to use when something depends on this crate to allow the parent repo to provide its own version of this dependency.",
13101323
),
@@ -1331,6 +1344,13 @@ _ANNOTATION_NORMAL_ATTRS = {
13311344
),
13321345
}
13331346

1347+
# A list of labels which may be relative (and if so, is within the repo the rule is generated in).
1348+
#
1349+
# If I were to write ":foo", with attr.label_list, it would evaluate to
1350+
# "@@//:foo". However, for a tag such as deps, ":foo" should refer to
1351+
# "@@rules_rust~crates~<crate>//:foo".
1352+
_relative_label_list = attr.string_list
1353+
13341354
_ANNOTATION_SELECT_ATTRS = {
13351355
"build_script_compile_data": _relative_label_list(
13361356
doc = "A list of labels to add to a crate's `cargo_build_script::compile_data` attribute.",

crate_universe/private/common_utils.bzl

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,96 @@ def parse_alias_rule(value):
211211
bzl = str(bzl),
212212
rule = rule,
213213
)
214+
215+
def sanitize_label_injections(label_injections):
216+
"""Load the `label_injections` attribute and sanitize it for use in replacements.
217+
218+
Args:
219+
label_injections (dict[Label, str]): A mapping of labels (canonical) to string labels (apparent).
220+
221+
Returns:
222+
dict[str, str]: A mapping of string
223+
"""
224+
updated = {}
225+
for canonical, apparent in label_injections.items():
226+
canon_repo, _, _ = str(canonical).partition("//")
227+
_, _, target = apparent.partition("//")
228+
if target:
229+
target = "//" + target
230+
231+
updated["{}{}".format(canon_repo, target)] = apparent
232+
233+
return updated
234+
235+
def apply_label_injections(*, label_mapping, attribute):
236+
"""Apply label_injections to convert canonical labels to apparent labels.
237+
238+
Args:
239+
label_mapping: The mapping of canonical label to apparent label.
240+
attribute: The attribute to update.
241+
242+
Returns:
243+
[dict | list | str]: The updated attribute based on `label_mapping`.
244+
"""
245+
if not label_mapping:
246+
return attribute
247+
248+
if not attribute:
249+
return attribute
250+
251+
attr_type = type(attribute)
252+
253+
if attr_type == "dict":
254+
updated = dict(attribute.items())
255+
value_type = type(updated.values()[0])
256+
257+
if value_type == "string":
258+
updated = dict(attribute.items())
259+
for canonical, apparent in label_mapping.items():
260+
updated = {
261+
key.replace(apparent, canonical): value.replace(apparent, canonical)
262+
for key, value in updated.items()
263+
}
264+
return updated
265+
266+
if value_type == "list":
267+
for canonical, apparent in label_mapping.items():
268+
updated = {
269+
key.replace(apparent, canonical): [v.replace(apparent, canonical) for v in value]
270+
for key, value in updated.items()
271+
}
272+
return updated
273+
274+
if value_type == "dict":
275+
for canonical, apparent in label_mapping.items():
276+
updated = {
277+
key.replace(apparent, canonical): {
278+
k.replace(apparent, canonical): v.replace(apparent, canonical)
279+
for k, v in value.items()
280+
}
281+
for key, value in updated.items()
282+
}
283+
return updated
284+
285+
fail("Unknown dict value type `{}` for: {}".format(
286+
value_type,
287+
attribute,
288+
))
289+
290+
if attr_type == "list":
291+
updated = [i for i in attribute]
292+
for canonical, apparent in label_mapping.items():
293+
updated = [entry.replace(apparent, canonical) for entry in updated]
294+
return updated
295+
296+
if attr_type == "string":
297+
updated = str(attribute)
298+
for canonical, apparent in label_mapping.items():
299+
updated = updated.replace(apparent, canonical)
300+
301+
return updated
302+
303+
fail("Unexpected attribute type `{}` for: {}".format(
304+
attr_type,
305+
attribute,
306+
))

crate_universe/private/tests/BUILD.bazel

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
load(":label_injections_test.bzl", "label_injections_test_suite")
2+
3+
############################ UNIT TESTS #############################
4+
label_injections_test_suite(name = "label_injections_test_suite")

0 commit comments

Comments
 (0)