Skip to content
This repository has been archived by the owner on Dec 29, 2022. It is now read-only.

Rebuild necessary packages when reusing build plan #969

Merged
merged 13 commits into from
Aug 1, 2018

Conversation

Xanewok
Copy link
Member

@Xanewok Xanewok commented Jul 29, 2018

Somewhat fixes #876.
Relies on the Cargo fork branch pulling in rust-lang/cargo#5830.

Firstly, it fixes the package autodetection logic. Previously it calculated bare -p and compared it against previous cached version. Now we try to build default set of packages for Cargo, create a set out of these packages and if we find ourselves modifying a package not included in the cached build plan, we ask Cargo to rebuild the sum of previous packages and the new one. In short, the build plan is additive and if we don't have what we need, we try to rebuild it including the package we just modified.

Secondly, we distinguish more unit types in the dep graph. This changes unit distinction from (PackageId, TargetKind) to (PackageId, Target, CompileMode).
Previous key type was not enough a) when compiling different targets of the same kind (e.g. differently named bin targets) b) when compiling exact same crate target in a regular mode and in the unit test/harness mode. The latter distinction is important, because previously we could have assigned a unit test harness invocation to a regular lib on which other units depended - when we invalidated library, the unit test harness was reran, but the lib was not rebuilt, resulting in missing/invalid data across multiple crate targets.

This also somewhat improves test harness in few places - I think it'd be great to focus on it now and provide a nice set of bulletproof regression tests against cases such as this. This would definitely help bring the 1.0 quality!

@Xanewok Xanewok requested review from nrc and matklad July 29, 2018 20:39
.map(|(idx, _)| idx + 1)
.and_then(|idx| args.get(idx))
.map(|x| x.as_os_str())
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ? should help redability I think

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe FP/Haskell spoiled me recently, I like the chain of simple transformations 😢
Do you mean doing sth like let idx = ...?; args.get(idx).map(|x| x.as_os_str())?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let idx = args.iter().enumenrate().find(...)?;
args.get(idx + 1)?.as_os_str()

prev_package_arg: Option<PackageArg>,
/// Packages (names) for which this build plan was prepared.
/// Used to detect if the plan can reused when building certain packages.
built_packages: HashSet<String>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The might be several packages with the same name. Ideally, we should use something like package_id here I think

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OTOH, I'm all for not peeking into Cargo's internals unless absolutely necessary, and I think using strings for know is the best approach.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on packages with same name; came to the conclusion that having packages with same name in the primary workspace will be rare enough and these strings we're passing to Cargo's Packages::from_flags, so I think these get converted and resolved later to PackageIdSpecs internally?

Re Cargo internals I'd love to stabilise a small subset of public-facing structures like PackageId but that's a talk for another time 😄

crate fn cache_compiler_job(&mut self, id: &PackageId, target: &Target, cmd: &ProcessBuilder) {
let pkg_key = (id.clone(), target.kind().clone());
crate fn cache_compiler_job(&mut self, id: &PackageId, target: &Target, mode: CompileMode, cmd: &ProcessBuilder) {
let pkg_key = (id.clone(), target.clone(), mode);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/pkg_key/unit_key/

// collect every unit having the longest path prefix.
let max_matching_prefix = other_targets.values()
.map(|src_dir| matching_prefix_components(modified, src_dir))
.max().unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not obvious that this unwrap will never trip :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, thanks!

let dirty_units = other_targets.iter()
.filter(|(_, src_dir)| max_matching_prefix ==
matching_prefix_components(modified, src_dir)
).map(|(unit, _)| unit);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is an amusing way to lay out a comparison operator over several lines :)

@Xanewok Xanewok changed the title WIP: Rebuild necessary packages when reusing build plan Rebuild necessary packages when reusing build plan Jul 29, 2018
@Xanewok
Copy link
Member Author

Xanewok commented Jul 29, 2018

@nrc @matklad 003e437#diff-80398c5faae3c069e4e6aa2ed11b28c0R16 bumped pulled Cargo commit to the most recent one to include required rust-lang/cargo@90a5ba3 commit.

Is that a problem? Or do we only need to coordinate that for Rust distributions?

@matklad
Copy link
Member

matklad commented Jul 29, 2018

@Xanewok no, when building as a part of rust-lang/rust, RLS is forced to use the right cargo, rustfmt and clippy:

https://github.com/rust-lang/rust/blob/70cac59031d5c33962a1f53cdca9359c0dcd1f9f/src/Cargo.toml#L47-L62

@Xanewok
Copy link
Member Author

Xanewok commented Jul 29, 2018

Right then, so if this lands it seems we need to be careful to update Cargo before RLS at rust-lang/rust 👍

@matklad
Copy link
Member

matklad commented Jul 29, 2018

Right then, so if this lands it seems we need to be careful to update Cargo before RLS at rust-lang/rust

We'll need to update them simultaneously, b/c the new Cargo won't work with old RLS. We can just add an _: CompileMode to RLS though.

@Xanewok
Copy link
Member Author

Xanewok commented Jul 29, 2018

Ah right, obviously. Thanks for the info! I'll do that if/when this lands.

Copy link
Member

@nrc nrc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, thanks!

A couple of nits inline.

@@ -214,7 +213,7 @@ impl Plan {
match unit {
None => trace!(
"Modified file {:?} doesn't correspond to any package!",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/{:?}/{}

@@ -471,6 +471,15 @@ impl PackageMap {
#[derive(Debug)]
crate struct JobQueue(Vec<ProcessBuilder>);

fn proc_arg<T: AsRef<OsStr>>(prc: &ProcessBuilder, key: T) -> Option<&std::ffi::OsStr> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this function have a more descriptive name? And/or a comment.

@Xanewok
Copy link
Member Author

Xanewok commented Aug 1, 2018

@nrc addressed the feedback and it's green (with the exception of recently busted AppVeyor) so I'm gonna merge this one now.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RLS only compiles the first member of a Cargo workspace
3 participants