23
23
//! repeats until the queue is empty.
24
24
25
25
use std:: collections:: { BTreeSet , HashMap , HashSet } ;
26
+ use std:: hash:: { Hash , Hasher } ;
26
27
use std:: iter:: FromIterator ;
27
28
use std:: sync:: Arc ;
28
29
30
+ use crate :: core:: compiler:: standard_lib;
29
31
use crate :: core:: compiler:: unit_dependencies:: build_unit_dependencies;
30
- use crate :: core:: compiler:: { standard_lib , unit_graph } ;
32
+ use crate :: core:: compiler:: unit_graph :: { self , UnitDep , UnitGraph } ;
31
33
use crate :: core:: compiler:: { BuildConfig , BuildContext , Compilation , Context } ;
32
- use crate :: core:: compiler:: { CompileKind , CompileMode , RustcTargetData , Unit } ;
34
+ use crate :: core:: compiler:: { CompileKind , CompileMode , CompileTarget , RustcTargetData , Unit } ;
33
35
use crate :: core:: compiler:: { DefaultExecutor , Executor , UnitInterner } ;
34
36
use crate :: core:: profiles:: { Profiles , UnitFor } ;
35
37
use crate :: core:: resolver:: features:: { self , FeaturesFor } ;
@@ -39,7 +41,7 @@ use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
39
41
use crate :: ops;
40
42
use crate :: ops:: resolve:: WorkspaceResolve ;
41
43
use crate :: util:: config:: Config ;
42
- use crate :: util:: { closest_msg, profile, CargoResult } ;
44
+ use crate :: util:: { closest_msg, profile, CargoResult , StableHasher } ;
43
45
44
46
/// Contains information about how a package should be compiled.
45
47
///
@@ -410,11 +412,24 @@ pub fn create_bcx<'a, 'cfg>(
410
412
workspace_resolve. as_ref ( ) . unwrap_or ( & resolve) ,
411
413
) ?;
412
414
413
- let units = generate_targets (
415
+ // If `--target` has not been specified, then the unit graph is built
416
+ // assuming `--target $HOST` was specified. See
417
+ // `rebuild_unit_graph_shared` for more on why this is done.
418
+ let explicit_host_kind = CompileKind :: Target ( CompileTarget :: new ( & target_data. rustc . host ) ?) ;
419
+ let explicit_host_kinds: Vec < _ > = build_config
420
+ . requested_kinds
421
+ . iter ( )
422
+ . map ( |kind| match kind {
423
+ CompileKind :: Host => explicit_host_kind,
424
+ CompileKind :: Target ( t) => CompileKind :: Target ( * t) ,
425
+ } )
426
+ . collect ( ) ;
427
+
428
+ let mut units = generate_targets (
414
429
ws,
415
430
& to_builds,
416
431
filter,
417
- & build_config . requested_kinds ,
432
+ & explicit_host_kinds ,
418
433
build_config. mode ,
419
434
& resolve,
420
435
& workspace_resolve,
@@ -442,7 +457,7 @@ pub fn create_bcx<'a, 'cfg>(
442
457
& crates,
443
458
std_resolve,
444
459
std_features,
445
- & build_config . requested_kinds ,
460
+ & explicit_host_kinds ,
446
461
& pkg_set,
447
462
interner,
448
463
& profiles,
@@ -451,6 +466,34 @@ pub fn create_bcx<'a, 'cfg>(
451
466
Default :: default ( )
452
467
} ;
453
468
469
+ let mut unit_graph = build_unit_dependencies (
470
+ ws,
471
+ & pkg_set,
472
+ & resolve,
473
+ & resolved_features,
474
+ std_resolve_features. as_ref ( ) ,
475
+ & units,
476
+ & std_roots,
477
+ build_config. mode ,
478
+ & target_data,
479
+ & profiles,
480
+ interner,
481
+ ) ?;
482
+
483
+ if build_config
484
+ . requested_kinds
485
+ . iter ( )
486
+ . any ( CompileKind :: is_host)
487
+ {
488
+ // Rebuild the unit graph, replacing the explicit host targets with
489
+ // CompileKind::Host, merging any dependencies shared with build
490
+ // dependencies.
491
+ let new_graph = rebuild_unit_graph_shared ( interner, unit_graph, & units, explicit_host_kind) ;
492
+ // This would be nicer with destructuring assignment.
493
+ units = new_graph. 0 ;
494
+ unit_graph = new_graph. 1 ;
495
+ }
496
+
454
497
let mut extra_compiler_args = HashMap :: new ( ) ;
455
498
if let Some ( args) = extra_args {
456
499
if units. len ( ) != 1 {
@@ -485,20 +528,6 @@ pub fn create_bcx<'a, 'cfg>(
485
528
}
486
529
}
487
530
488
- let unit_graph = build_unit_dependencies (
489
- ws,
490
- & pkg_set,
491
- & resolve,
492
- & resolved_features,
493
- std_resolve_features. as_ref ( ) ,
494
- & units,
495
- & std_roots,
496
- build_config. mode ,
497
- & target_data,
498
- & profiles,
499
- interner,
500
- ) ?;
501
-
502
531
let bcx = BuildContext :: new (
503
532
ws,
504
533
pkg_set,
@@ -789,6 +818,7 @@ fn generate_targets(
789
818
target_mode,
790
819
features. clone ( ) ,
791
820
/*is_std*/ false ,
821
+ /*dep_hash*/ 0 ,
792
822
) ;
793
823
units. insert ( unit) ;
794
824
}
@@ -1169,3 +1199,93 @@ fn filter_targets<'a>(
1169
1199
}
1170
1200
proposals
1171
1201
}
1202
+
1203
+ /// This is used to rebuild the unit graph, sharing host dependencies if possible.
1204
+ ///
1205
+ /// This will translate any unit's `CompileKind::Target(host)` to
1206
+ /// `CompileKind::Host` if the kind is equal to `to_host`. This also handles
1207
+ /// generating the unit `dep_hash`, and merging shared units if possible.
1208
+ ///
1209
+ /// This is necessary because if normal dependencies used `CompileKind::Host`,
1210
+ /// there would be no way to distinguish those units from build-dependency
1211
+ /// units. This can cause a problem if a shared normal/build dependency needs
1212
+ /// to link to another dependency whose features differ based on whether or
1213
+ /// not it is a normal or build dependency. If both units used
1214
+ /// `CompileKind::Host`, then they would end up being identical, causing a
1215
+ /// collision in the `UnitGraph`, and Cargo would end up randomly choosing one
1216
+ /// value or the other.
1217
+ ///
1218
+ /// The solution is to keep normal and build dependencies separate when
1219
+ /// building the unit graph, and then run this second pass which will try to
1220
+ /// combine shared dependencies safely. By adding a hash of the dependencies
1221
+ /// to the `Unit`, this allows the `CompileKind` to be changed back to `Host`
1222
+ /// without fear of an unwanted collision.
1223
+ fn rebuild_unit_graph_shared (
1224
+ interner : & UnitInterner ,
1225
+ unit_graph : UnitGraph ,
1226
+ roots : & [ Unit ] ,
1227
+ to_host : CompileKind ,
1228
+ ) -> ( Vec < Unit > , UnitGraph ) {
1229
+ let mut result = UnitGraph :: new ( ) ;
1230
+ // Map of the old unit to the new unit, used to avoid recursing into units
1231
+ // that have already been computed to improve performance.
1232
+ let mut memo = HashMap :: new ( ) ;
1233
+ let new_roots = roots
1234
+ . iter ( )
1235
+ . map ( |root| {
1236
+ traverse_and_share ( interner, & mut memo, & mut result, & unit_graph, root, to_host)
1237
+ } )
1238
+ . collect ( ) ;
1239
+ ( new_roots, result)
1240
+ }
1241
+
1242
+ /// Recursive function for rebuilding the graph.
1243
+ ///
1244
+ /// This walks `unit_graph`, starting at the given `unit`. It inserts the new
1245
+ /// units into `new_graph`, and returns a new updated version of the given
1246
+ /// unit (`dep_hash` is filled in, and `kind` switched if necessary).
1247
+ fn traverse_and_share (
1248
+ interner : & UnitInterner ,
1249
+ memo : & mut HashMap < Unit , Unit > ,
1250
+ new_graph : & mut UnitGraph ,
1251
+ unit_graph : & UnitGraph ,
1252
+ unit : & Unit ,
1253
+ to_host : CompileKind ,
1254
+ ) -> Unit {
1255
+ if let Some ( new_unit) = memo. get ( unit) {
1256
+ // Already computed, no need to recompute.
1257
+ return new_unit. clone ( ) ;
1258
+ }
1259
+ let mut dep_hash = StableHasher :: new ( ) ;
1260
+ let new_deps: Vec < _ > = unit_graph[ unit]
1261
+ . iter ( )
1262
+ . map ( |dep| {
1263
+ let new_dep_unit =
1264
+ traverse_and_share ( interner, memo, new_graph, unit_graph, & dep. unit , to_host) ;
1265
+ new_dep_unit. hash ( & mut dep_hash) ;
1266
+ UnitDep {
1267
+ unit : new_dep_unit,
1268
+ ..dep. clone ( )
1269
+ }
1270
+ } )
1271
+ . collect ( ) ;
1272
+ let new_dep_hash = dep_hash. finish ( ) ;
1273
+ let new_kind = if unit. kind == to_host {
1274
+ CompileKind :: Host
1275
+ } else {
1276
+ unit. kind
1277
+ } ;
1278
+ let new_unit = interner. intern (
1279
+ & unit. pkg ,
1280
+ & unit. target ,
1281
+ unit. profile ,
1282
+ new_kind,
1283
+ unit. mode ,
1284
+ unit. features . clone ( ) ,
1285
+ unit. is_std ,
1286
+ new_dep_hash,
1287
+ ) ;
1288
+ assert ! ( memo. insert( unit. clone( ) , new_unit. clone( ) ) . is_none( ) ) ;
1289
+ new_graph. entry ( new_unit. clone ( ) ) . or_insert ( new_deps) ;
1290
+ new_unit
1291
+ }
0 commit comments