Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/cargo/core/compiler/build_context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,17 +160,22 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> {
self.build_config.jobs
}

pub fn rustflags_args(&self, unit: &Unit<'_>) -> CargoResult<Vec<String>> {
/// Get the RUSTFLAGS argument by its kind
pub fn rustflags_args_for(&self, kind:Kind) -> CargoResult<Vec<String>> {
env_args(
self.config,
&self.build_config.requested_target,
self.host_triple(),
self.info(unit.kind).cfg(),
unit.kind,
self.info(kind).cfg(),
kind,
"RUSTFLAGS",
)
}

pub fn rustflags_args(&self, unit: &Unit<'_>) -> CargoResult<Vec<String>> {
self.rustflags_args_for(unit.kind)
}

pub fn rustdocflags_args(&self, unit: &Unit<'_>) -> CargoResult<Vec<String>> {
env_args(
self.config,
Expand Down
53 changes: 52 additions & 1 deletion src/cargo/core/compiler/context/compilation_files.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::env;
use std::fmt;
use std::fs::metadata;
use std::hash::{Hash, Hasher, SipHasher};
use std::path::{Path, PathBuf};
use std::sync::Arc;
Expand Down Expand Up @@ -31,10 +32,15 @@ pub struct CompilationFiles<'a, 'cfg: 'a> {
/// The root targets requested by the user on the command line (does not
/// include dependencies).
roots: Vec<Unit<'a>>,
primary: HashSet<Unit<'a>>,
ws: &'a Workspace<'cfg>,
metas: HashMap<Unit<'a>, Option<Metadata>>,
/// For each Unit, a list all files produced.
outputs: HashMap<Unit<'a>, LazyCell<Arc<Vec<OutputFile>>>>,
/// The directory that is used as the shared targets
shared_target_dir: Option<PathBuf>,
/// Flags indicates if we should use the shared dir
shared_dir_enabled: bool
}

#[derive(Debug)]
Expand Down Expand Up @@ -70,14 +76,32 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
cx: &Context<'a, 'cfg>,
) -> CompilationFiles<'a, 'cfg> {
let mut metas = HashMap::new();
let mut primary = HashSet::new();
for unit in roots {
metadata_of(unit, cx, &mut metas);
if cx.is_primary_package(unit) {
primary.insert(unit.clone());
}
}

let has_rustflags = [Kind::Host, Kind::Target].iter()
.any(|kind| {
cx.bcx
.rustflags_args_for(*kind)
.ok()
.map_or(true, |args| args.len() > 0)
});

let outputs = metas
.keys()
.cloned()
.map(|unit| (unit, LazyCell::new()))
.collect();

let shared_target_dir = env::var("CARGO_SHARED_TARGET_DIR")
.ok()
.map_or(None, |path| Some(PathBuf::from(path)));

CompilationFiles {
ws,
host,
Expand All @@ -86,6 +110,9 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
roots: roots.to_vec(),
metas,
outputs,
primary,
shared_target_dir,
shared_dir_enabled: !has_rustflags
}
}

Expand Down Expand Up @@ -122,10 +149,29 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
} else if unit.target.is_example() {
self.layout(unit.kind).examples().to_path_buf()
} else {
if let Some(ref shared_dir) = self.shared_target_dir {
if self.shared_dir_enabled && !self.primary.contains(unit) {
return shared_dir.clone();
}
}
self.deps_dir(unit).to_path_buf()
}
}

/// Check if the unit is in the shared dir
pub fn in_shared_dir(&self, unit: &Unit<'a>, bcx: &BuildContext<'a, 'cfg>) -> bool {
if self.shared_dir_enabled && self.shared_target_dir.is_some() && !self.primary.contains(unit) {
if let Ok(outputs) = self.outputs(unit, bcx) {
return !outputs.iter().any(|out| !metadata(out.path.as_path()).is_ok());
}
}
false
}

pub fn shared_dir(&self) -> Option<&Path> {
self.shared_target_dir.as_ref().map(|p| p.as_path())
}

pub fn export_dir(&self) -> Option<PathBuf> {
self.export_dir.clone()
}
Expand All @@ -150,6 +196,11 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
/// Returns the directories where Rust crate dependencies are found for the
/// specified unit.
pub fn deps_dir(&self, unit: &Unit<'_>) -> &Path {
if let Some(ref shared_dir) = self.shared_target_dir {
if self.shared_dir_enabled && !self.primary.contains(unit) {
return shared_dir.as_path();
}
}
self.layout(unit.kind).deps()
}

Expand Down
5 changes: 5 additions & 0 deletions src/cargo/core/compiler/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,11 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
self.files.as_ref().unwrap().outputs(unit, self.bcx)
}

/// Examine if the target is already in the global cache
pub fn in_shared_dir(&self, unit: &Unit<'a>) -> bool {
self.files.as_ref().unwrap().in_shared_dir(unit, self.bcx)
}

/// For a package, return all targets which are registered as dependencies
/// for that package.
// TODO: this ideally should be `-> &[Unit<'a>]`
Expand Down
36 changes: 25 additions & 11 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ fn rustc<'a, 'cfg>(
.unwrap_or_else(|| cx.bcx.config.cwd())
.to_path_buf();

let found_shared = cx.in_shared_dir(unit);

return Ok(Work::new(move |state| {
// Only at runtime have we discovered what the extra -L and -l
// arguments are for native libraries, so we process those here. We
Expand Down Expand Up @@ -293,24 +295,28 @@ fn rustc<'a, 'cfg>(

state.running(&rustc);
if json_messages {
exec.exec_json(
rustc,
package_id,
&target,
mode,
&mut assert_is_empty,
&mut |line| json_stderr(line, package_id, &target),
)
.map_err(internal_if_simple_exit_code)
.chain_err(|| format!("Could not compile `{}`.", name))?;
if !found_shared
{
exec.exec_json(
rustc,
package_id,
&target,
mode,
&mut assert_is_empty,
&mut |line| json_stderr(line, package_id, &target),
)
.map_err(internal_if_simple_exit_code)
.chain_err(|| format!("Could not compile `{}`.", name))?;
}
} else if build_plan {
state.build_plan(buildkey, rustc.clone(), outputs.clone());
} else {
} else if !found_shared {
exec.exec_and_capture_output(rustc, package_id, &target, mode, state)
.map_err(internal_if_simple_exit_code)
.chain_err(|| format!("Could not compile `{}`.", name))?;
}

//TODO: handle the rename ?
if do_rename && real_name != crate_name {
let dst = &outputs[0].path;
let src = dst.with_file_name(
Expand Down Expand Up @@ -920,6 +926,14 @@ fn build_deps_args<'a, 'cfg>(
deps.push(cx.files().deps_dir(unit));
deps
});

if let Some(shared_dir) = cx.files().shared_dir() {
cmd.arg("-L").arg(&{
let mut deps = OsString::from("dependency=");
deps.push(shared_dir);
deps
});
}

// Be sure that the host path is also listed. This'll ensure that proc-macro
// dependencies are correctly found (for reexported macros).
Expand Down
85 changes: 85 additions & 0 deletions tests/testsuite/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4727,3 +4727,88 @@ Caused by:
.with_status(101)
.run();
}



#[test]
fn cargo_compile_with_shared_dir() {
Package::new("bar", "0.0.1")
.file("bar/Cargo.toml",
r#"
[package]
name = "bar"
version = "0.0.1"

[lib]
name = "bar"
"#
)
.file("src/lib.rs", r#"pub fn bar() { println!("I'm bar") }"#)
.publish();

let p = project()
.file("Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"

[[bin]]
name = "foo"

[dependencies]
bar = "*"
"#
)
.file("src/main.rs", r#"fn main() { bar::bar() }"#)
.file("cache/dummy", "")
.build();

let cache_dir = p.root().join("cache");

p.cargo("build").env("CARGO_SHARED_TARGET_DIR", &cache_dir).run();
assert!(p.bin("foo").is_file());

fn count_binary(path:&std::path::PathBuf) -> (u32, u32) {

let (mut n_bin_output, mut n_dep_output) = (0, 0);

if let Ok(dir_ent) = path.as_path().read_dir() {
for file in dir_ent {
if let Ok(entry) = file {
if let Some(file_name) = entry.path().file_name().map_or(None, |x| x.to_str()) {
if file_name.starts_with("libbar-") &&
file_name.ends_with(".rlib") {
n_bin_output += 1;
}
else if file_name.starts_with("bar-") &&
file_name.ends_with(".d") {
n_dep_output += 1;
}
}
}
}
}

(n_bin_output, n_dep_output)
}

let (cache_bins, cache_deps) = count_binary(&cache_dir);
let (target_bins, target_deps) = count_binary(&p.target_debug_dir().join("deps"));


assert_eq!(cache_bins, 1);
assert_eq!(cache_deps, 1);
assert_eq!(target_bins, 0);
assert_eq!(target_deps, 0);

p.process(&p.bin("foo")).with_stdout("I'm bar\n").run();

p.cargo("clean").run();
let (cache_bins, cache_deps) = count_binary(&cache_dir);
let (target_bins, target_deps) = count_binary(&p.target_debug_dir().join("deps"));
assert_eq!(cache_bins, 1);
assert_eq!(cache_deps, 1);
assert_eq!(target_bins, 0);
assert_eq!(target_deps, 0);
}