Change Cargo's own dep-file format
authorAlex Crichton <alex@alexcrichton.com>
Fri, 8 Dec 2017 15:43:45 +0000 (07:43 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 12 Dec 2017 19:00:07 +0000 (11:00 -0800)
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
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/ops/cargo_rustc/output_depinfo.rs

index ae6e051ccac47e39e5c2a5a3b0360a2e3811a235..5c3284bab324ecce453fd2d5d756079382b8d4f3 100644 (file)
@@ -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<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)
@@ -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(())
+}
+
index fc96ddb5a89b9f074b185b1ab03991d31f3834d6..17603f7c3868c98847b70c78d4d6e2bb049ab18f 100644 (file)
@@ -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<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>,
index 94b46046322566bea457653e295e3f35e788e3cc..bfcebd5aad135158c16d43a547a05f69d5dea4f9 100644 (file)
@@ -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);
             }