@@ -60,17 +60,17 @@ crate struct Plan {
60
60
// An object for finding the package which a file belongs to and this inferring
61
61
// a package argument.
62
62
package_map : Option < PackageMap > ,
63
- /// Packages for which this build plan was prepared.
63
+ /// Packages (names) for which this build plan was prepared.
64
64
/// Used to detect if the plan can reused when building certain packages.
65
- built_packages : PackageArg ,
65
+ built_packages : HashSet < String > ,
66
66
}
67
67
68
68
impl Plan {
69
69
crate fn new ( ) -> Plan {
70
- Self :: for_packages ( PackageArg :: All )
70
+ Self :: for_packages ( HashSet :: new ( ) )
71
71
}
72
72
73
- crate fn for_packages ( pkgs : PackageArg ) -> Plan {
73
+ crate fn for_packages ( pkgs : HashSet < String > ) -> Plan {
74
74
Plan {
75
75
units : HashMap :: new ( ) ,
76
76
dep_graph : HashMap :: new ( ) ,
@@ -309,14 +309,29 @@ impl Plan {
309
309
self . package_map = Some ( PackageMap :: new ( manifest_path) ) ;
310
310
}
311
311
312
- let package_arg = self . package_map
312
+ if !self . is_ready ( ) || requested_cargo {
313
+ return WorkStatus :: NeedsCargo ( PackageArg :: Default ) ;
314
+ }
315
+
316
+ let dirty_packages = self . package_map
313
317
. as_ref ( )
314
318
. unwrap ( )
315
- . compute_package_arg ( modified) ;
316
- let package_arg_changed = self . built_packages != package_arg;
319
+ . compute_dirty_packages ( modified) ;
320
+
321
+ let needs_more_packages = dirty_packages
322
+ . difference ( & self . built_packages )
323
+ . next ( )
324
+ . is_some ( ) ;
317
325
318
- if !self . is_ready ( ) || requested_cargo || package_arg_changed {
319
- return WorkStatus :: NeedsCargo ( package_arg) ;
326
+ let needed_packages = self . built_packages
327
+ . union ( & dirty_packages)
328
+ . cloned ( )
329
+ . collect ( ) ;
330
+
331
+ // We modified a file from a packages, that are not included in the
332
+ // cached build plan - run Cargo to recreate the build plan including them
333
+ if needs_more_packages {
334
+ return WorkStatus :: NeedsCargo ( PackageArg :: Packages ( needed_packages) ) ;
320
335
}
321
336
322
337
let dirties = self . fetch_dirty_units ( modified) ;
@@ -330,13 +345,13 @@ impl Plan {
330
345
. iter ( )
331
346
. any ( |& ( _, ref kind) | * kind == TargetKind :: CustomBuild )
332
347
{
333
- WorkStatus :: NeedsCargo ( package_arg )
348
+ WorkStatus :: NeedsCargo ( PackageArg :: Packages ( needed_packages ) )
334
349
} else {
335
350
let graph = self . dirty_rev_dep_graph ( & dirties) ;
336
351
trace ! ( "Constructed dirty rev dep graph: {:?}" , graph) ;
337
352
338
353
if graph. is_empty ( ) {
339
- return WorkStatus :: NeedsCargo ( package_arg ) ;
354
+ return WorkStatus :: NeedsCargo ( PackageArg :: Default ) ;
340
355
}
341
356
342
357
let queue = self . topological_sort ( & graph) ;
@@ -355,9 +370,8 @@ impl Plan {
355
370
// crates within the crate that depend on the error-ing one have never been built.
356
371
// In that case we need to build from scratch so that everything is in our cache, or
357
372
// we cope with the error. In the error case, jobs will be None.
358
-
359
373
match jobs {
360
- None => WorkStatus :: NeedsCargo ( package_arg ) ,
374
+ None => WorkStatus :: NeedsCargo ( PackageArg :: Default ) ,
361
375
Some ( jobs) => {
362
376
assert ! ( !jobs. is_empty( ) ) ;
363
377
WorkStatus :: Execute ( JobQueue ( jobs) )
@@ -372,17 +386,13 @@ crate enum WorkStatus {
372
386
Execute ( JobQueue ) ,
373
387
}
374
388
375
- // The point of the PackageMap is to compute the minimal work for Cargo to do,
376
- // given a list of changed files. That is, if things have changed all over the
377
- // place we have to do a `--all` build, but if they've only changed in one
378
- // package, then we can do `-p foo` which should mean less work.
379
- //
380
- // However, when we change package we throw away our build plan and must rebuild
381
- // it, so we must be careful that compiler jobs we expect to exist will in fact
382
- // exist. There is some wasted work too, but I don't think we can predict when
383
- // we definitely need to do this and it should be quick compared to compilation.
384
-
385
- // Maps paths to packages.
389
+ /// Maps paths to packages.
390
+ ///
391
+ /// The point of the PackageMap is detect if additional packages need to be
392
+ /// included in the cached build plan. The cache can represent only a subset of
393
+ /// the entire workspace, hence why we need to detect if a package was modified
394
+ /// that's outside the cached build plan - if so, we need to recreate it,
395
+ /// including the new package.
386
396
#[ derive( Debug ) ]
387
397
struct PackageMap {
388
398
// A map from a manifest directory to the package name.
@@ -418,19 +428,12 @@ impl PackageMap {
418
428
. collect ( )
419
429
}
420
430
421
- // Compute the `-p` argument to pass to Cargo by examining the current dirty
422
- // set of files and finding their package.
423
- fn compute_package_arg < T : AsRef < Path > + fmt:: Debug > ( & self , modified_files : & [ T ] ) -> PackageArg {
424
- let mut packages: Option < HashSet < String > > = modified_files
431
+ /// Given modified set of files, returns a set of corresponding dirty packages.
432
+ fn compute_dirty_packages < T : AsRef < Path > + fmt:: Debug > ( & self , modified_files : & [ T ] ) -> HashSet < String > {
433
+ modified_files
425
434
. iter ( )
426
- . map ( |p| self . map ( p. as_ref ( ) ) )
427
- . collect ( ) ;
428
- match packages {
429
- Some ( ref mut packages) if packages. len ( ) == 1 => {
430
- PackageArg :: Package ( packages. drain ( ) . next ( ) . unwrap ( ) )
431
- }
432
- _ => PackageArg :: All ,
433
- }
435
+ . filter_map ( |p| self . map ( p. as_ref ( ) ) )
436
+ . collect ( )
434
437
}
435
438
436
439
// Map a file to the package which it belongs to.
0 commit comments