2323//! repeats until the queue is empty.
2424
2525use std:: collections:: { BTreeSet , HashMap , HashSet } ;
26+ use std:: hash:: { Hash , Hasher } ;
2627use std:: iter:: FromIterator ;
2728use std:: sync:: Arc ;
2829
30+ use crate :: core:: compiler:: standard_lib;
2931use 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 } ;
3133use 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 } ;
3335use crate :: core:: compiler:: { DefaultExecutor , Executor , UnitInterner } ;
3436use crate :: core:: profiles:: { Profiles , UnitFor } ;
3537use crate :: core:: resolver:: features:: { self , FeaturesFor } ;
@@ -39,7 +41,7 @@ use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
3941use crate :: ops;
4042use crate :: ops:: resolve:: WorkspaceResolve ;
4143use crate :: util:: config:: Config ;
42- use crate :: util:: { closest_msg, profile, CargoResult } ;
44+ use crate :: util:: { closest_msg, profile, CargoResult , StableHasher } ;
4345
4446/// Contains information about how a package should be compiled.
4547///
@@ -410,11 +412,24 @@ pub fn create_bcx<'a, 'cfg>(
410412 workspace_resolve. as_ref ( ) . unwrap_or ( & resolve) ,
411413 ) ?;
412414
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 (
414429 ws,
415430 & to_builds,
416431 filter,
417- & build_config . requested_kinds ,
432+ & explicit_host_kinds ,
418433 build_config. mode ,
419434 & resolve,
420435 & workspace_resolve,
@@ -442,7 +457,7 @@ pub fn create_bcx<'a, 'cfg>(
442457 & crates,
443458 std_resolve,
444459 std_features,
445- & build_config . requested_kinds ,
460+ & explicit_host_kinds ,
446461 & pkg_set,
447462 interner,
448463 & profiles,
@@ -451,6 +466,34 @@ pub fn create_bcx<'a, 'cfg>(
451466 Default :: default ( )
452467 } ;
453468
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+
454497 let mut extra_compiler_args = HashMap :: new ( ) ;
455498 if let Some ( args) = extra_args {
456499 if units. len ( ) != 1 {
@@ -485,20 +528,6 @@ pub fn create_bcx<'a, 'cfg>(
485528 }
486529 }
487530
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-
502531 let bcx = BuildContext :: new (
503532 ws,
504533 pkg_set,
@@ -789,6 +818,7 @@ fn generate_targets(
789818 target_mode,
790819 features. clone ( ) ,
791820 /*is_std*/ false ,
821+ /*dep_hash*/ 0 ,
792822 ) ;
793823 units. insert ( unit) ;
794824 }
@@ -1169,3 +1199,93 @@ fn filter_targets<'a>(
11691199 }
11701200 proposals
11711201}
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