@@ -858,6 +858,109 @@ def _get_host_cargo_rustc(module_ctx, host_triple, host_tools_repo):
858
858
859
859
return cargo_path , rustc_path
860
860
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
+
861
964
def _crate_impl (module_ctx ):
862
965
reproducible = True
863
966
host_triple = get_host_triple (module_ctx , abi = {
@@ -927,23 +1030,45 @@ def _crate_impl(module_ctx):
927
1030
if replacement :
928
1031
annotation_dict ["override_targets" ]["bin" ] = str (replacement )
929
1032
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
- })
938
1033
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 ,
943
1063
crate ,
944
- [],
945
- ).append (annotation )
1064
+ version ,
1065
+ triples ,
1066
+ build_script_data ,
1067
+ build_script_env ,
1068
+ )
946
1069
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 )
947
1072
common_specs = []
948
1073
repo_specific_specs = {}
949
1074
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
1389
1514
},
1390
1515
)
1391
1516
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
+
1392
1546
crate = module_extension (
1393
1547
doc = """\
1394
1548
Crate universe module extensions.
@@ -1408,6 +1562,7 @@ Environment Variables:
1408
1562
implementation = _crate_impl ,
1409
1563
tag_classes = {
1410
1564
"annotation" : _annotation ,
1565
+ "annotation_select" : _annotation_select ,
1411
1566
"from_cargo" : _from_cargo ,
1412
1567
"from_specs" : _from_specs ,
1413
1568
"render_config" : _render_config ,
0 commit comments