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