From f688e9c2f55f196547fd24ff953208d2c9fbb6f4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 8 Dec 2017 07:43:45 -0800 Subject: [PATCH] Change Cargo's own dep-file format This commit alters the format of the dependency info that Cargo keeps track of for each crate. In order to be more resilient against directory renames and such Cargo will now postprocess the compiler's dep-info output and serialize into its own format. This format is intended to primarily list relative paths *to the root of the relevant package* rather than absolute or relative to some other location. If paths aren't actually relative to the package root they're still stored as absolute, but there's not much we can do about that! --- src/cargo/ops/cargo_rustc/fingerprint.rs | 104 +++++++++++++------- src/cargo/ops/cargo_rustc/mod.rs | 27 ++--- src/cargo/ops/cargo_rustc/output_depinfo.rs | 2 +- 3 files changed, 84 insertions(+), 49 deletions(-) diff --git a/src/cargo/ops/cargo_rustc/fingerprint.rs b/src/cargo/ops/cargo_rustc/fingerprint.rs index ae6e051cc..5c3284bab 100644 --- a/src/cargo/ops/cargo_rustc/fingerprint.rs +++ b/src/cargo/ops/cargo_rustc/fingerprint.rs @@ -1,8 +1,6 @@ use std::env; -use std::fs::{self, File}; +use std::fs; use std::hash::{self, Hasher}; -use std::io::prelude::*; -use std::io::BufReader; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; @@ -615,47 +613,28 @@ fn log_compare(unit: &Unit, compare: &CargoResult<()>) { } // Parse the dep-info into a list of paths -pub fn parse_dep_info(cx: &Context, dep_info: &Path) +pub fn parse_dep_info(pkg: &Package, dep_info: &Path) -> CargoResult>> { - macro_rules! fs_try { - ($e:expr) => (match $e { Ok(e) => e, Err(..) => return Ok(None) }) - } - let f = BufReader::new(fs_try!(File::open(dep_info))); - let line = match f.lines().next() { - Some(Ok(line)) => line, - _ => return Ok(None), + let data = match paths::read_bytes(dep_info) { + Ok(data) => data, + Err(_) => return Ok(None), }; - let pos = line.find(": ").ok_or_else(|| { - internal(format!("dep-info not in an understood format: {}", - dep_info.display())) - })?; - let deps = &line[pos + 2..]; - - let mut paths = Vec::new(); - let mut deps = deps.split(' ').map(|s| s.trim()).filter(|s| !s.is_empty()); - while let Some(s) = deps.next() { - let mut file = s.to_string(); - while file.ends_with('\\') { - file.pop(); - file.push(' '); - file.push_str(deps.next().ok_or_else(|| { - internal("malformed dep-info format, trailing \\".to_string()) - })?); - } - - // Note that paths emitted in dep info files may be relative, but due to - // `path_args` in the module above this the relative paths are always - // relative to the root of a workspace. - paths.push(cx.ws.root().join(&file)); + let paths = data.split(|&x| x == 0) + .filter(|x| !x.is_empty()) + .map(|p| util::bytes2path(p).map(|p| pkg.root().join(p))) + .collect::, _>>()?; + if paths.len() == 0 { + Ok(None) + } else { + Ok(Some(paths)) } - Ok(Some(paths)) } -fn dep_info_mtime_if_fresh(cx: &Context, dep_info: &Path) +fn dep_info_mtime_if_fresh(pkg: &Package, dep_info: &Path) -> CargoResult> { - if let Some(paths) = parse_dep_info(cx, dep_info)? { + if let Some(paths) = parse_dep_info(pkg, dep_info)? { Ok(mtime_if_fresh(dep_info, paths.iter())) } else { Ok(None) @@ -730,3 +709,56 @@ fn filename<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> String { }; format!("{}{}-{}", flavor, kind, file_stem) } + +/// Parses the dep-info file coming out of rustc into a Cargo-specific format. +/// +/// This function will parse `rustc_dep_info` as a makefile-style dep info to +/// learn about the all files which a crate depends on. This is then +/// re-serialized into the `cargo_dep_info` path in a Cargo-specific format. +/// +/// The `pkg_root` argument here is the absolute path to the directory +/// containing `Cargo.toml` for this crate that was compiled. The paths listed +/// in the rustc dep-info file may or may not be absolute but we'll want to +/// consider all of them relative to the `root` specified. +/// +/// The `rustc_cwd` argument is the absolute path to the cwd of the compiler +/// when it was invoked. +/// +/// The serialized Cargo format will contain a list of files, all of which are +/// relative if they're under `root`. or absolute if they're elsewehre. +pub fn translate_dep_info(rustc_dep_info: &Path, + cargo_dep_info: &Path, + pkg_root: &Path, + rustc_cwd: &Path) -> CargoResult<()> { + let contents = paths::read(rustc_dep_info)?; + let line = match contents.lines().next() { + Some(line) => line, + None => return Ok(()), + }; + let pos = line.find(": ").ok_or_else(|| { + internal(format!("dep-info not in an understood format: {}", + rustc_dep_info.display())) + })?; + let deps = &line[pos + 2..]; + + let mut new_contents = Vec::new(); + let mut deps = deps.split(' ').map(|s| s.trim()).filter(|s| !s.is_empty()); + while let Some(s) = deps.next() { + let mut file = s.to_string(); + while file.ends_with('\\') { + file.pop(); + file.push(' '); + file.push_str(deps.next().ok_or_else(|| { + internal("malformed dep-info format, trailing \\".to_string()) + })?); + } + + let absolute = rustc_cwd.join(file); + let path = absolute.strip_prefix(pkg_root).unwrap_or(&absolute); + new_contents.extend(util::path2bytes(path)?); + new_contents.push(0); + } + paths::write(cargo_dep_info, &new_contents)?; + Ok(()) +} + diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index fc96ddb5a..17603f7c3 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -357,6 +357,8 @@ fn rustc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, let exec = exec.clone(); let root_output = cx.target_root().to_path_buf(); + let pkg_root = unit.pkg.root().to_path_buf(); + let cwd = rustc.get_cwd().unwrap_or(cx.config.cwd()).to_path_buf(); return Ok(Work::new(move |state| { // Only at runtime have we discovered what the extra -L and -l @@ -437,12 +439,15 @@ fn rustc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, } } - if fs::metadata(&rustc_dep_info_loc).is_ok() { - info!("Renaming dep_info {:?} to {:?}", rustc_dep_info_loc, dep_info_loc); - fs::rename(&rustc_dep_info_loc, &dep_info_loc).chain_err(|| { - internal(format!("could not rename dep info: {:?}", - rustc_dep_info_loc)) - })?; + if rustc_dep_info_loc.exists() { + fingerprint::translate_dep_info(&rustc_dep_info_loc, + &dep_info_loc, + &pkg_root, + &cwd) + .chain_err(|| { + internal(format!("could not parse/generate dep info at: {}", + rustc_dep_info_loc.display())) + })?; } Ok(()) @@ -713,22 +718,20 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, // // The first returned value here is the argument to pass to rustc, and the // second is the cwd that rustc should operate in. -fn path_args(cx: &Context, unit: &Unit) -> (PathBuf, Option) { +fn path_args(cx: &Context, unit: &Unit) -> (PathBuf, PathBuf) { let ws_root = cx.ws.root(); let src = unit.target.src_path(); assert!(src.is_absolute()); match src.strip_prefix(ws_root) { - Ok(path) => (path.to_path_buf(), Some(ws_root.to_path_buf())), - Err(_) => (src.to_path_buf(), None), + Ok(path) => (path.to_path_buf(), ws_root.to_path_buf()), + Err(_) => (src.to_path_buf(), unit.pkg.root().to_path_buf()), } } fn add_path_args(cx: &Context, unit: &Unit, cmd: &mut ProcessBuilder) { let (arg, cwd) = path_args(cx, unit); cmd.arg(arg); - if let Some(cwd) = cwd { - cmd.cwd(cwd); - } + cmd.cwd(cwd); } fn build_base_args<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, diff --git a/src/cargo/ops/cargo_rustc/output_depinfo.rs b/src/cargo/ops/cargo_rustc/output_depinfo.rs index 94b460463..bfcebd5aa 100644 --- a/src/cargo/ops/cargo_rustc/output_depinfo.rs +++ b/src/cargo/ops/cargo_rustc/output_depinfo.rs @@ -36,7 +36,7 @@ fn add_deps_for_unit<'a, 'b>( if !unit.profile.run_custom_build { // 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(context, &dep_info_loc)? { + if let Some(paths) = fingerprint::parse_dep_info(unit.pkg, &dep_info_loc)? { for path in paths { deps.insert(path); } -- 2.30.2