-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #3557 - raphlinus:master, r=alexcrichton
Add dep-info generation 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.
- Loading branch information
Showing
4 changed files
with
185 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
use std::collections::HashSet; | ||
use std::io::{Write, BufWriter, ErrorKind}; | ||
use std::fs::{self, File}; | ||
use std::path::{Path, PathBuf}; | ||
|
||
use ops::{Context, Unit}; | ||
use util::{CargoResult, internal}; | ||
use ops::cargo_rustc::fingerprint; | ||
|
||
fn render_filename<P: AsRef<Path>>(path: P, basedir: Option<&str>) -> CargoResult<String> { | ||
let path = path.as_ref(); | ||
let relpath = match basedir { | ||
None => path, | ||
Some(base) => match path.strip_prefix(base) { | ||
Ok(relpath) => relpath, | ||
_ => path, | ||
} | ||
}; | ||
relpath.to_str().ok_or(internal("path not utf-8")).map(|f| f.replace(" ", "\\ ")) | ||
} | ||
|
||
fn add_deps_for_unit<'a, 'b>(deps: &mut HashSet<PathBuf>, context: &mut Context<'a, 'b>, | ||
unit: &Unit<'a>, visited: &mut HashSet<Unit<'a>>) -> CargoResult<()> | ||
{ | ||
if !visited.insert(unit.clone()) { | ||
return Ok(()); | ||
} | ||
|
||
// 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)? { | ||
for path in paths { | ||
deps.insert(path); | ||
} | ||
} else { | ||
debug!("can't find dep_info for {:?} {:?}", | ||
unit.pkg.package_id(), unit.profile); | ||
return Err(internal("dep_info missing")); | ||
} | ||
|
||
// Add rerun-if-changed dependencies | ||
let key = (unit.pkg.package_id().clone(), unit.kind); | ||
if let Some(output) = context.build_state.outputs.lock().unwrap().get(&key) { | ||
for path in &output.rerun_if_changed { | ||
deps.insert(path.into()); | ||
} | ||
} | ||
|
||
// Recursively traverse all transitive dependencies | ||
for dep_unit in &context.dep_targets(unit)? { | ||
let source_id = dep_unit.pkg.package_id().source_id(); | ||
if source_id.is_path() { | ||
add_deps_for_unit(deps, context, dep_unit, visited)?; | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
pub fn output_depinfo<'a, 'b>(context: &mut Context<'a, 'b>, unit: &Unit<'a>) -> CargoResult<()> { | ||
let mut deps = HashSet::new(); | ||
let mut visited = HashSet::new(); | ||
let success = add_deps_for_unit(&mut deps, context, unit, &mut visited).is_ok(); | ||
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"); | ||
if success { | ||
let mut outfile = BufWriter::new(File::create(output_path)?); | ||
let target_fn = render_filename(link_dst, basedir)?; | ||
write!(outfile, "{}:", target_fn)?; | ||
for dep in &deps { | ||
write!(outfile, " {}", render_filename(dep, basedir)?)?; | ||
} | ||
writeln!(outfile, "")?; | ||
} else { | ||
// dep-info generation failed, so delete output file. This will usually | ||
// cause the build system to always rerun the build rule, which is correct | ||
// if inefficient. | ||
match fs::remove_file(output_path) { | ||
Err(err) => { | ||
if err.kind() != ErrorKind::NotFound { | ||
return Err(err.into()); | ||
} | ||
} | ||
_ => () | ||
} | ||
} | ||
} | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
extern crate cargotest; | ||
extern crate hamcrest; | ||
|
||
use cargotest::support::{basic_bin_manifest, main_file, execs, project}; | ||
use hamcrest::{assert_that, existing_file}; | ||
|
||
#[test] | ||
fn build_dep_info() { | ||
let p = project("foo") | ||
.file("Cargo.toml", &basic_bin_manifest("foo")) | ||
.file("src/foo.rs", &main_file(r#""i am foo""#, &[])); | ||
|
||
assert_that(p.cargo_process("build"), execs().with_status(0)); | ||
|
||
let depinfo_bin_path = &p.bin("foo").with_extension("d"); | ||
|
||
assert_that(depinfo_bin_path, existing_file()); | ||
} | ||
|
||
#[test] | ||
fn build_dep_info_lib() { | ||
let p = project("foo") | ||
.file("Cargo.toml", r#" | ||
[package] | ||
name = "foo" | ||
version = "0.0.1" | ||
authors = [] | ||
[[example]] | ||
name = "ex" | ||
crate-type = ["lib"] | ||
"#) | ||
.file("src/lib.rs", "") | ||
.file("examples/ex.rs", ""); | ||
|
||
assert_that(p.cargo_process("build").arg("--example=ex"), execs().with_status(0)); | ||
assert_that(&p.example_lib("ex", "lib").with_extension("d"), existing_file()); | ||
} | ||
|
||
|
||
#[test] | ||
fn build_dep_info_rlib() { | ||
let p = project("foo") | ||
.file("Cargo.toml", r#" | ||
[package] | ||
name = "foo" | ||
version = "0.0.1" | ||
authors = [] | ||
[[example]] | ||
name = "ex" | ||
crate-type = ["rlib"] | ||
"#) | ||
.file("src/lib.rs", "") | ||
.file("examples/ex.rs", ""); | ||
|
||
assert_that(p.cargo_process("build").arg("--example=ex"), execs().with_status(0)); | ||
assert_that(&p.example_lib("ex", "rlib").with_extension("d"), existing_file()); | ||
} | ||
|
||
#[test] | ||
fn build_dep_info_dylib() { | ||
let p = project("foo") | ||
.file("Cargo.toml", r#" | ||
[package] | ||
name = "foo" | ||
version = "0.0.1" | ||
authors = [] | ||
[[example]] | ||
name = "ex" | ||
crate-type = ["dylib"] | ||
"#) | ||
.file("src/lib.rs", "") | ||
.file("examples/ex.rs", ""); | ||
|
||
assert_that(p.cargo_process("build").arg("--example=ex"), execs().with_status(0)); | ||
assert_that(&p.example_lib("ex", "dylib").with_extension("d"), existing_file()); | ||
} |