Skip to content

Commit

Permalink
Change Cargo's own dep-file format
Browse files Browse the repository at this point in the history
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!
  • Loading branch information
alexcrichton committed Dec 12, 2017
1 parent 8647a87 commit f688e9c
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 49 deletions.
104 changes: 68 additions & 36 deletions src/cargo/ops/cargo_rustc/fingerprint.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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<Option<Vec<PathBuf>>>
{
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::<Result<Vec<_>, _>>()?;
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<Option<FileTime>>
{
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)
Expand Down Expand Up @@ -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(())
}

27 changes: 15 additions & 12 deletions src/cargo/ops/cargo_rustc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(())
Expand Down Expand Up @@ -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<PathBuf>) {
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>,
Expand Down
2 changes: 1 addition & 1 deletion src/cargo/ops/cargo_rustc/output_depinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit f688e9c

Please sign in to comment.