@@ -374,6 +374,7 @@ def collect_inputs(
374
374
- (File): An optional path to a generated environment file from a `cargo_build_script` target
375
375
- (list): All direct and transitive build flags from the current build info
376
376
- (list[File]): Linkstamp outputs.
377
+ - (File): Optional - the output directory containing all transitive crates that this crate depends on.
377
378
"""
378
379
linker_script = getattr (file , "linker_script" ) if hasattr (file , "linker_script" ) else None
379
380
@@ -449,10 +450,19 @@ def collect_inputs(
449
450
# If stamping is enabled include the volatile status info file
450
451
stamp_info = [ctx .version_file ] if stamp else []
451
452
453
+ # Windows is known to have a small limit to the length the command args can have (32k characters).
454
+ # To prevent the arguments to rustc from overflowing this length, rather than adding each transitive dep as a single library
455
+ # search path flag, symlink all transitive deps into a single directory that we can pass as a single path flag.
456
+ if toolchain .os == "windows" :
457
+ transitive_crates_dir , transitive_crates_links = symlink_transitive_crates (ctx , crate_info , dep_info )
458
+ else :
459
+ transitive_crates_dir , transitive_crates_links = None , []
460
+
452
461
compile_inputs = depset (
453
462
linkstamp_outs + stamp_info ,
454
463
transitive = [
455
464
nolinkstamp_compile_inputs ,
465
+ depset (transitive_crates_links ),
456
466
],
457
467
)
458
468
@@ -462,7 +472,7 @@ def collect_inputs(
462
472
build_env_files = [f for f in build_env_files ] + [build_env_file ]
463
473
compile_inputs = depset (build_env_files , transitive = [compile_inputs ])
464
474
465
- return compile_inputs , out_dir , build_env_files , build_flags_files , linkstamp_outs
475
+ return compile_inputs , out_dir , build_env_files , build_flags_files , linkstamp_outs , transitive_crates_dir
466
476
467
477
def construct_arguments (
468
478
ctx ,
@@ -481,6 +491,7 @@ def construct_arguments(
481
491
build_env_files ,
482
492
build_flags_files ,
483
493
emit = ["dep-info" , "link" ],
494
+ transitive_crates_dir = None ,
484
495
force_all_deps_direct = False ,
485
496
force_link = False ,
486
497
stamp = False ,
@@ -504,6 +515,7 @@ def construct_arguments(
504
515
build_env_files (list): Files containing rustc environment variables, for instance from `cargo_build_script` actions.
505
516
build_flags_files (list): The output files of a `cargo_build_script` actions containing rustc build flags
506
517
emit (list): Values for the --emit flag to rustc.
518
+ transitive_crates_dir (File, optional): The output directory containing all transitive crates that this crate depends on.
507
519
force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
508
520
to the commandline as opposed to -L.
509
521
force_link (bool, optional): Whether to add link flags to the command regardless of `emit`.
@@ -650,7 +662,7 @@ def construct_arguments(
650
662
_add_native_link_flags (rustc_flags , dep_info , linkstamp_outs , crate_info .type , toolchain , cc_toolchain , feature_configuration )
651
663
652
664
# These always need to be added, even if not linking this crate.
653
- add_crate_link_flags (rustc_flags , dep_info , force_all_deps_direct )
665
+ add_crate_link_flags (rustc_flags , dep_info , transitive_crates_dir , force_all_deps_direct )
654
666
655
667
needs_extern_proc_macro_flag = "proc-macro" in [crate_info .type , crate_info .wrapped_crate_type ] and \
656
668
crate_info .edition != "2015"
@@ -739,7 +751,7 @@ def rustc_compile_action(
739
751
# Determine if the build is currently running with --stamp
740
752
stamp = is_stamping_enabled (attr )
741
753
742
- compile_inputs , out_dir , build_env_files , build_flags_files , linkstamp_outs = collect_inputs (
754
+ compile_inputs , out_dir , build_env_files , build_flags_files , linkstamp_outs , transitive_crates_dir = collect_inputs (
743
755
ctx = ctx ,
744
756
file = ctx .file ,
745
757
files = ctx .files ,
@@ -769,6 +781,7 @@ def rustc_compile_action(
769
781
out_dir = out_dir ,
770
782
build_env_files = build_env_files ,
771
783
build_flags_files = build_flags_files ,
784
+ transitive_crates_dir = transitive_crates_dir ,
772
785
force_all_deps_direct = force_all_deps_direct ,
773
786
stamp = stamp ,
774
787
)
@@ -1040,12 +1053,54 @@ def _get_dir_names(files):
1040
1053
dirs [f .dirname ] = None
1041
1054
return dirs .keys ()
1042
1055
1043
- def add_crate_link_flags (args , dep_info , force_all_deps_direct = False ):
1056
+ def symlink_transitive_crates (ctx , crate_info , dep_info ):
1057
+ """Collect and symlink the transitive crates into a single directory.
1058
+
1059
+ The reason for this is so that we can pass a single entry for the library search path, which greatly shortens
1060
+ the length of the command line args that we pass to `rustc` and and prevents it from overflowing the limit set
1061
+ by the host platform (which is really only a concern on Windows).
1062
+
1063
+ Args:
1064
+ ctx (ctx): The rule's context object
1065
+ crate_info (CrateInfo): The CrateInfo provider of the target crate
1066
+ dep_info (DepInfo): The current target's dependency info
1067
+
1068
+ Returns:
1069
+ tuple: A tuple of the following items
1070
+ - (File): Optional - the output directory containing all transitive crates that this crate depends on.
1071
+ - (list): The list of transitive crates files that should be used as input to the build action.
1072
+ """
1073
+ deps = dep_info .transitive_crates .to_list ()
1074
+ if not deps :
1075
+ return None , []
1076
+
1077
+ output_dirname = crate_info .output .basename + ".transitive_crates"
1078
+ links = []
1079
+
1080
+ # Keep a list of the crates that were currently added so that we can uniquify them.
1081
+ names = {}
1082
+
1083
+ for dep in deps :
1084
+ name = dep .output .basename
1085
+ if name in names :
1086
+ continue
1087
+
1088
+ link = ctx .actions .declare_file (output_dirname + "/" + name )
1089
+ ctx .actions .symlink (output = link , target_file = dep .output , is_executable = True )
1090
+
1091
+ names [name ] = True
1092
+ links .append (link )
1093
+
1094
+ return links [0 ].dirname , links
1095
+
1096
+ def add_crate_link_flags (args , dep_info , transitive_crates_dir = None , force_all_deps_direct = False ):
1044
1097
"""Adds link flags to an Args object reference
1045
1098
1046
1099
Args:
1047
1100
args (Args): An arguments object reference
1048
1101
dep_info (DepInfo): The current target's dependency info
1102
+ transitive_crates_dir (File, optional): An output directory containing all transitive crates that this crate depends on.
1103
+ If this argument is set, only it will be added as a `-Ldependency=` flag, otherwise all rlibs will be set individually.
1049
1104
force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
1050
1105
to the commandline as opposed to -L.
1051
1106
"""
@@ -1064,12 +1119,17 @@ def add_crate_link_flags(args, dep_info, force_all_deps_direct = False):
1064
1119
else :
1065
1120
# nb. Direct crates are linked via --extern regardless of their crate_type
1066
1121
args .add_all (dep_info .direct_crates , map_each = _crate_to_link_flag )
1067
- args .add_all (
1068
- dep_info .transitive_crates ,
1069
- map_each = _get_crate_dirname ,
1070
- uniquify = True ,
1071
- format_each = "-Ldependency=%s" ,
1072
- )
1122
+
1123
+ # If transitive rlibs have been collected into this single directory, only set this directory
1124
+ if transitive_crates_dir :
1125
+ args .add ("-Ldependency={}" .format (transitive_crates_dir ))
1126
+ else :
1127
+ args .add_all (
1128
+ dep_info .transitive_crates ,
1129
+ map_each = _get_crate_dirname ,
1130
+ uniquify = True ,
1131
+ format_each = "-Ldependency=%s" ,
1132
+ )
1073
1133
1074
1134
def _crate_to_link_flag (crate ):
1075
1135
"""A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
@@ -1091,11 +1151,10 @@ def _crate_to_link_flag(crate):
1091
1151
return ["--extern={}={}" .format (name , crate_info .output .path )]
1092
1152
1093
1153
def _get_crate_dirname (crate ):
1094
- """A helper macro used by `add_crate_link_flags` for getting the directory name of the current crate's output path
1154
+ """A helper macro used by `add_crate_link_flags` for getting the directory name of the current crate's output path.
1095
1155
1096
1156
Args:
1097
1157
crate (CrateInfo): A CrateInfo provider from the current rule
1098
-
1099
1158
Returns:
1100
1159
str: The directory name of the the output File that will be produced.
1101
1160
"""
0 commit comments