Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upAdd dep-info generation #3557
Conversation
rust-highfive
assigned
brson
Jan 18, 2017
This comment has been minimized.
This comment has been minimized.
rust-highfive
commented
Jan 18, 2017
|
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @brson (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
This comment has been minimized.
This comment has been minimized.
|
This is very rough, but I wanted to checkpoint my work in progress. Comments are welcome (though I'm out most of the rest of this week). See https://internals.rust-lang.org/t/proposal-make-cargo-output-dep-info/4603/ for background discussion. I also want to create a tracking issue, but haven't done that yet. One limitation is that it doesn't follow path dependencies for libraries. I agree this would be a good thing. Another discussion question: most paths will be absolute, but it might be more desirable to make them relative to some base directory. The code has a mechanism for this, but I haven't plumbed it through from the command line, out of concern for proliferating options. Other issues are identified by TODO. |
alexcrichton
reviewed
Jan 18, 2017
|
I'm curious, what do you think about avoiding an explicit argument for this and doing it unconditionally? We could always emit |
| } else { | ||
| &profiles.dev | ||
| }; | ||
| let mut context = Context::new(&ws, &resolve, &packages, &config, build_config, &profiles)?; |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 18, 2017
Member
Perhaps this could be moved into cargo_rustc to avoid creating multiple contexts? It's not expensive or anything but it tends to be subtle enough to introduce lots of bugs, so deduplicating the logic would be best.
This comment has been minimized.
This comment has been minimized.
raphlinus
Jan 23, 2017
Author
Contributor
Yes, probably moving into cargo_rustc would be better. By way of context, this started out as a plugin using cargo as a lib, so it makes sense to change things to be more integrated.
| profile: &profile, | ||
| kind: Kind::Target, | ||
| }; | ||
| let filename = ["dep-", kind, "-", &context.file_stem(&unit)].concat(); |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 18, 2017
Member
Here I think we may want to tweak how fingerprints are managed slightly. Could we maintain a map of fingerprints for units (e.g. active paths) on the Context and then we scrape that list afterwards? That way we don't have to re-parse anything or re-guess filenames and such.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Yes, I'm totally fine with always writing the depfile. It occurs to me it could potentially be useful for speeding up a null build, assuming we get it precise enough. |
This comment has been minimized.
This comment has been minimized.
|
Ok! In that case we can probably drop the command line argument for now and start out with just |
This comment has been minimized.
This comment has been minimized.
|
Ok, I uploaded a new version, for discussion. I believe the functionality of this is about what we want (though happy to discuss), but no doubt the implementation can be cleaned up, as it still shows signs of being an external tool scraping cargo's internals. Happily, this version does traverse path dependencies. Build script inputs are still missing, but I think I understand better how to add them. Also, I see the merge problem; TargetKind::Example has been split. I'll rebase. |
raphlinus
added some commits
Jan 17, 2017
raphlinus
force-pushed the
raphlinus:master
branch
from
6e9124e
to
1ed7f69
Jan 25, 2017
This comment has been minimized.
This comment has been minimized.
|
Rebased. I ended up doing |
alexcrichton
reviewed
Jan 25, 2017
| let mut outfile = File::create(output_path)?; | ||
| write!(outfile, "{}:", target_fn)?; | ||
| for dep in &deps { | ||
| write!(outfile, " {}", render_filename(dep, basedir)?)?; |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 25, 2017
Member
I think here we'll want to escape spaces in paths, right? (that's done elsewhere I believe)
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
raphlinus
Jan 26, 2017
Author
Contributor
Yeah, I do need to re-escape spaces. Thanks for the heads-up!
alexcrichton
reviewed
Jan 25, 2017
| pub fn output_depinfo(context: &mut Context, unit: &Unit) -> CargoResult<()> { | ||
| let mut deps = HashSet::new(); | ||
| add_deps_for_unit(&mut deps, context, unit)?; | ||
| for dep_unit in &context.dep_targets(unit)? { |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 25, 2017
Member
I think this'll want to be recursive to pick up path dependencies of path dependencies
This comment has been minimized.
This comment has been minimized.
alexcrichton
reviewed
Jan 25, 2017
| } | ||
| } | ||
|
|
||
| // TODO: probably better to use Context::target_filenames for this |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 25, 2017
Member
Yeah I think we'll just want to emit one .d file with the same contents for each filename returned from target_filenames
This comment has been minimized.
This comment has been minimized.
alexcrichton
reviewed
Jan 25, 2017
| relpath.to_str().ok_or(internal("path not utf-8")).map(ToOwned::to_owned) | ||
| } | ||
|
|
||
| fn read_dep_file<P: AsRef<Path>>(path: P) -> CargoResult<DepFile> { |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 25, 2017
Member
Could this implementation be shared with the fingerprint module? I think the fingerprint module could store information in a side table in the context perhaps to avoid rereading the filesystem here.
This comment has been minimized.
This comment has been minimized.
raphlinus
Jan 26, 2017
Author
Contributor
I shared the implementation, but have not yet implemented a change to stash the info so we can avoid rereading the file system. I don't think that's hard, but for reference here are some measurements from a null build of cargo: a null build takes about 215ms. The time between reading the input fingerprints and writing the output .d is about 400us, repeated twice. Also, for perspective, this only reads the deps for path dependencies. So I think the impact on performance is pretty minimal. I'll happily stash if you wanna chase that down, though.
(However, in looking at the strace, I see I'm losing several ms by not buffering the file writing - fixing that now!)
raphlinus
added some commits
Jan 26, 2017
This comment has been minimized.
This comment has been minimized.
|
Latest version has a bit of performance tuning, and also picks up dependencies generated by build scripts. My personal feeling is that this is ready now, but I'd very much welcome any feedback. |
alexcrichton
reviewed
Jan 26, 2017
| if visited.contains(unit) { | ||
| return Ok(()); | ||
| } | ||
| visited.insert(unit.clone()); |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 26, 2017
Member
You can combine these together as:
if !visited.insert(unit.clone()) {
return Ok(())
}I believe clone is just copying something like 4 pointers, so it shouldn't cost anything.
alexcrichton
reviewed
Jan 26, 2017
|
|
||
| // Add dependencies from rustc dep-info output (stored in fingerprint directory) | ||
| let dep_info_loc = fingerprint::dep_info_loc(context, unit); | ||
| if let Some(paths) = fingerprint::parse_dep_info(&dep_info_loc)? { |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 26, 2017
Member
I think here we'd want to return an error in the None case, right? If we fail to read dep info we expect to exists, that seems fatal for this operation at least.
This comment has been minimized.
This comment has been minimized.
raphlinus
Jan 26, 2017
Author
Contributor
So if I error on None here, the following tests fail:
cargo_platform_specific_dependency
freshness_ignores_excluded
I haven't dug into exactly why; might be worth investigating.
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 26, 2017
Member
Odd! I'd be fine ignoring the error case here, but I think that may want to translate to not writing out a dependency file at all or perhaps a warning, it seems semi-serious to not list deps by accident.
alexcrichton
reviewed
Jan 26, 2017
| let basedir = None; // TODO | ||
| for (_filename, link_dst, _linkable) in context.target_filenames(unit)? { | ||
| if let Some(link_dst) = link_dst { | ||
| let output_path = link_dst.with_extension("d"); |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 26, 2017
Member
I think we may want to unconditionally append .d here to handle case like libfoo.so.d, right?
This comment has been minimized.
This comment has been minimized.
raphlinus
Jan 26, 2017
Author
Contributor
I had that before, but I changed it to follow https://github.com/rust-lang/cargo/blob/master/src/cargo/ops/cargo_rustc/mod.rs#L281 which does it this way. I'm ok either way but feel it should be consistent.
This comment has been minimized.
This comment has been minimized.
alexcrichton
Jan 26, 2017
Member
Eh that sounds reasonable to me! If the compiler's doing it we may as well mirror that.
This comment has been minimized.
This comment has been minimized.
|
Looks great! I think that rereading from the filesystem is very well factored here, so let's keep as-is. Could you also be sure to add a test to make sure these files are generated? Other than that I'm all for this! |
This comment has been minimized.
This comment has been minimized.
|
Ok, here's where I am regarding the strict checks that dependencies are present. I tracked down the failure for That didn't quite fix things. The unit that runs the build script depends on another unit to build it. However, that wasn't getting picked up, as I'm still getting some test failures when I have strict checking, apparently in the interaction between build script and features. What I'd like to do is clean up my current patch, turn off the strict checking, and file an issue regarding the failures when strict checking is on (and that the feature should be considered experimental until then). I think it's going to take some fairly deep digging to get all of them, and I believe it now works well enough for all our needs for a while. Sound good? |
This comment has been minimized.
This comment has been minimized.
|
Thanks for tracking that down! I think though that instead of adding I'm ok not having strict checking as well, yeah, but what do you think is the best way to represent that in the dep-info file? If we missed a dependency b/c we couldn't find the files associated with it, should we just leave out those files? Delete the dep-info file? |
This comment has been minimized.
This comment has been minimized.
|
I agree grabbing the graph is a good idea, but it doesn't seem simple to me. We'd need to grab the graph before running any of the compilation steps, then gather up the depfiles after. Where should the graph be stored? What type would it have? I'm considering deferring the build script stuff for now, if it's going to be really complicated. In the targets we're building now, we're not using build scripts in the primary targets or their path deps. But I'm definitely willing to put in some more work if there's a way to get it right that's not too hard. Deleting the dep-info file seems most correct; in my quick test with ninja, it causes the build to always be re-run. I'll do that next. |
This comment has been minimized.
This comment has been minimized.
|
I believe the latest version is in a mergeable state. It doesn't try to do anything fancy in cases where it can't find the dep-info output (like the build scripts), just deletes the .d file. This works for our cases and shouldn't cause incorrect behavior. If we merge this, I'll definitely document the further work in an issue. |
This comment has been minimized.
This comment has been minimized.
|
@bors: r+ Looks great to me! I'll try to poke around the failure cases as well soon. |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
bors
added a commit
that referenced
this pull request
Jan 27, 2017
This comment has been minimized.
This comment has been minimized.
|
|
raphlinus commentedJan 18, 2017
Work in progress: add a --dep-info flag to cargo build (and also
rustc) that outputs dependency information in a form compatible with
make and ninja, to a specified file. This will help in integrating
into other build systems.