diff --git a/Cargo.lock b/Cargo.lock index f4020c4f4da..ec49c24f463 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "cargo" -version = "0.2.0" +version = "0.3.0" dependencies = [ "advapi32-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bufstream 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index f4340c43b3c..f5ae50edd0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [project] name = "cargo" -version = "0.2.0" +version = "0.3.0" authors = ["Yehuda Katz ", "Carl Lerche ", "Alex Crichton "] diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 6c2c5edfc28..9b7907cc7de 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -14,8 +14,6 @@ use util::{CargoResult, human}; pub struct Manifest { summary: Summary, targets: Vec, - target_dir: PathBuf, - doc_dir: PathBuf, links: Option, warnings: Vec, exclude: Vec, @@ -51,8 +49,6 @@ pub struct SerializedManifest { version: String, dependencies: Vec, targets: Vec, - target_dir: String, - doc_dir: String, } impl Encodable for Manifest { @@ -64,8 +60,6 @@ impl Encodable for Manifest { SerializedDependency::from_dependency(d) }).collect(), targets: self.targets.clone(), - target_dir: self.target_dir.display().to_string(), - doc_dir: self.doc_dir.display().to_string(), }.encode(s) } } @@ -181,7 +175,6 @@ impl Encodable for Target { impl Manifest { pub fn new(summary: Summary, targets: Vec, - target_dir: PathBuf, doc_dir: PathBuf, exclude: Vec, include: Vec, links: Option, @@ -190,8 +183,6 @@ impl Manifest { Manifest { summary: summary, targets: targets, - target_dir: target_dir, - doc_dir: doc_dir, warnings: Vec::new(), exclude: exclude, include: include, @@ -202,14 +193,12 @@ impl Manifest { } pub fn dependencies(&self) -> &[Dependency] { self.summary.dependencies() } - pub fn doc_dir(&self) -> &Path { &self.doc_dir } pub fn exclude(&self) -> &[String] { &self.exclude } pub fn include(&self) -> &[String] { &self.include } pub fn metadata(&self) -> &ManifestMetadata { &self.metadata } pub fn name(&self) -> &str { self.package_id().name() } pub fn package_id(&self) -> &PackageId { self.summary.package_id() } pub fn summary(&self) -> &Summary { &self.summary } - pub fn target_dir(&self) -> &Path { &self.target_dir } pub fn targets(&self) -> &[Target] { &self.targets } pub fn version(&self) -> &Version { self.package_id().version() } pub fn warnings(&self) -> &[String] { &self.warnings } @@ -225,10 +214,6 @@ impl Manifest { pub fn set_summary(&mut self, summary: Summary) { self.summary = summary; } - - pub fn set_target_dir(&mut self, target_dir: PathBuf) { - self.target_dir = target_dir; - } } impl Target { diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index e7ed17fa6d9..fa5bcc06b7c 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -76,14 +76,9 @@ impl Package { pub fn package_id(&self) -> &PackageId { self.manifest.package_id() } pub fn root(&self) -> &Path { self.manifest_path.parent().unwrap() } pub fn summary(&self) -> &Summary { self.manifest.summary() } - pub fn target_dir(&self) -> &Path { self.manifest.target_dir() } pub fn targets(&self) -> &[Target] { self.manifest().targets() } pub fn version(&self) -> &Version { self.package_id().version() } - pub fn absolute_target_dir(&self) -> PathBuf { - self.root().join(self.target_dir()) - } - pub fn has_custom_build(&self) -> bool { self.targets().iter().any(|t| t.is_custom_build()) } diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index dbbf0412c91..03b0575556d 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -21,13 +21,13 @@ pub fn clean(manifest_path: &Path, opts: &CleanOptions) -> CargoResult<()> { opts.config)); try!(src.update()); let root = try!(src.root_package()); - let manifest = root.manifest(); + let target_dir = opts.config.target_dir(&root); // If we have a spec, then we need to delete some package,s otherwise, just // remove the whole target directory and be done with it! let spec = match opts.spec { Some(spec) => spec, - None => return rm_rf(manifest.target_dir()), + None => return rm_rf(&target_dir), }; // Load the lockfile (if one's available), and resolve spec to a pkgid @@ -52,14 +52,14 @@ pub fn clean(manifest_path: &Path, opts: &CleanOptions) -> CargoResult<()> { let pkgs = PackageSet::new(&[]); let profiles = Profiles::default(); let cx = try!(Context::new(&resolve, &srcs, &pkgs, opts.config, - Layout::at(root.absolute_target_dir()), + Layout::at(target_dir), None, &pkg, BuildConfig::default(), &profiles)); // And finally, clean everything out! for target in pkg.targets().iter() { // TODO: `cargo clean --release` - let layout = Layout::new(&root, opts.target, "debug"); + let layout = Layout::new(opts.config, &root, opts.target, "debug"); try!(rm_rf(&layout.fingerprint(&pkg))); let profiles = [Profile::default_dev(), Profile::default_test()]; for profile in profiles.iter() { diff --git a/src/cargo/ops/cargo_doc.rs b/src/cargo/ops/cargo_doc.rs index 5c2439e8751..8090a35fe18 100644 --- a/src/cargo/ops/cargo_doc.rs +++ b/src/cargo/ops/cargo_doc.rs @@ -54,8 +54,8 @@ pub fn doc(manifest_path: &Path, } }; - let path = package.absolute_target_dir().join("doc").join(&name) - .join("index.html"); + let target_dir = options.compile_opts.config.target_dir(&package); + let path = target_dir.join("doc").join(&name).join("index.html"); if fs::metadata(&path).is_ok() { open_docs(&path); } diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index ccae355dee9..07a85512e2f 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -49,7 +49,8 @@ pub fn package(manifest_path: &Path, } let filename = format!("package/{}-{}.crate", pkg.name(), pkg.version()); - let dst = pkg.absolute_target_dir().join(&filename); + let target_dir = config.target_dir(&pkg); + let dst = target_dir.join(&filename); if fs::metadata(&dst).is_ok() { return Ok(Some(dst)) } let mut bomb = Bomb { path: Some(dst.clone()) }; @@ -174,7 +175,6 @@ fn run_verify(config: &Config, pkg: &Package, tar: &Path) }); let mut new_manifest = pkg.manifest().clone(); new_manifest.set_summary(new_summary.override_id(new_pkgid)); - new_manifest.set_target_dir(dst.join("target")); let new_pkg = Package::new(new_manifest, &manifest_path, &new_src); // Now that we've rewritten all our path dependencies, compile it! diff --git a/src/cargo/ops/cargo_rustc/layout.rs b/src/cargo/ops/cargo_rustc/layout.rs index 33ea8af692a..58c644a82e8 100644 --- a/src/cargo/ops/cargo_rustc/layout.rs +++ b/src/cargo/ops/cargo_rustc/layout.rs @@ -50,6 +50,7 @@ use std::io; use std::path::{PathBuf, Path}; use core::Package; +use util::Config; use util::hex::short_hash; pub struct Layout { @@ -67,8 +68,9 @@ pub struct LayoutProxy<'a> { } impl Layout { - pub fn new(pkg: &Package, triple: Option<&str>, dest: &str) -> Layout { - let mut path = pkg.absolute_target_dir(); + pub fn new(config: &Config, pkg: &Package, triple: Option<&str>, + dest: &str) -> Layout { + let mut path = config.target_dir(pkg); // Flexible target specifications often point at filenames, so interpret // the target triple as a Path and then just use the file stem as the // component for the directory name. diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index e6660ce2071..162930be9a5 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -100,9 +100,9 @@ pub fn compile_targets<'a, 'cfg: 'a>(targets: &[(&'a Target, &'a Profile)], } else { deps.iter().find(|p| p.package_id() == resolve.root()).unwrap() }; - let host_layout = Layout::new(root, None, &dest); + let host_layout = Layout::new(config, root, None, &dest); let target_layout = build_config.requested_target.as_ref().map(|target| { - layout::Layout::new(root, Some(&target), &dest) + layout::Layout::new(config, root, Some(&target), &dest) }); let mut cx = try!(Context::new(resolve, sources, deps, config, @@ -514,20 +514,19 @@ fn prepare_rustc(package: &Package, target: &Target, profile: &Profile, fn rustdoc(package: &Package, target: &Target, profile: &Profile, cx: &mut Context) -> CargoResult { let kind = Kind::Target; - let mut doc_dir = cx.get_package(cx.resolve.root()).absolute_target_dir(); let mut rustdoc = try!(process(CommandType::Rustdoc, package, target, cx)); rustdoc.arg(&root_path(cx, package, target)) .cwd(cx.config.cwd()) .arg("--crate-name").arg(&target.crate_name()); + let mut doc_dir = cx.config.target_dir(cx.get_package(cx.resolve.root())); if let Some(target) = cx.requested_target() { rustdoc.arg("--target").arg(target); doc_dir.push(target); } doc_dir.push("doc"); - - rustdoc.arg("-o").arg(&doc_dir); + rustdoc.arg("-o").arg(doc_dir); match cx.resolve.features(package.package_id()) { Some(features) => { diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 35924a70365..90c971e8338 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; use rustc_serialize::{Encodable,Encoder}; use toml; -use core::MultiShell; +use core::{MultiShell, Package}; use ops; use util::{CargoResult, ChainError, internal, human}; @@ -30,6 +30,7 @@ pub struct Config { cwd: PathBuf, rustc: PathBuf, rustdoc: PathBuf, + target_dir: Option, } impl Config { @@ -51,13 +52,12 @@ impl Config { values_loaded: Cell::new(false), rustc: PathBuf::from("rustc"), rustdoc: PathBuf::from("rustdoc"), + target_dir: None, }; - cfg.rustc = try!(cfg.get_tool("rustc")); - cfg.rustdoc = try!(cfg.get_tool("rustdoc")); - let (rustc_version, rustc_host) = try!(ops::rustc_version(cfg.rustc())); - cfg.rustc_version = rustc_version; - cfg.rustc_host = rustc_host; + try!(cfg.scrape_tool_config()); + try!(cfg.scrape_rustc_version()); + try!(cfg.scrape_target_dir_config()); Ok(cfg) } @@ -108,6 +108,12 @@ impl Config { pub fn cwd(&self) -> &Path { &self.cwd } + pub fn target_dir(&self, pkg: &Package) -> PathBuf { + self.target_dir.clone().unwrap_or_else(|| { + pkg.root().join("target") + }) + } + pub fn get(&self, key: &str) -> CargoResult> { let vals = try!(self.values()); let mut parts = key.split('.').enumerate(); @@ -206,6 +212,32 @@ impl Config { Ok(()) } + fn scrape_tool_config(&mut self) -> CargoResult<()> { + self.rustc = try!(self.get_tool("rustc")); + self.rustdoc = try!(self.get_tool("rustdoc")); + Ok(()) + } + + fn scrape_rustc_version(&mut self) -> CargoResult<()> { + let (rustc_version, rustc_host) = try!(ops::rustc_version(&self.rustc)); + self.rustc_version = rustc_version; + self.rustc_host = rustc_host; + Ok(()) + } + + fn scrape_target_dir_config(&mut self) -> CargoResult<()> { + if let Some((dir, dir2)) = try!(self.get_string("build.target-dir")) { + let mut path = PathBuf::from(dir2); + path.pop(); + path.pop(); + path.push(dir); + self.target_dir = Some(path); + } else if let Some(dir) = env::var_os("CARGO_TARGET_DIR") { + self.target_dir = Some(self.cwd.join(dir)); + } + Ok(()) + } + fn get_tool(&self, tool: &str) -> CargoResult { let var = format!("build.{}", tool); if let Some((tool, path)) = try!(self.get_string(&var)) { diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index 37857bbb8cf..7a39c50f643 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -519,8 +519,6 @@ impl TomlManifest { let profiles = build_profiles(&self.profile); let mut manifest = Manifest::new(summary, targets, - layout.root.join("target"), - layout.root.join("doc"), exclude, include, project.links.clone(), diff --git a/src/doc/config.md b/src/doc/config.md index 3261e5cde17..b3f885c1fdf 100644 --- a/src/doc/config.md +++ b/src/doc/config.md @@ -76,9 +76,10 @@ proxy = "..." # HTTP proxy to use for HTTP requests (defaults to none) timeout = 60000 # Timeout for each HTTP request, in milliseconds [build] -jobs = 1 # number of jobs to run by default (default to # cpus) -rustc = "rustc" # path to the compiler to execute -rustdoc = "rustdoc" # path to the doc generator to execute +jobs = 1 # number of jobs to run by default (default to # cpus) +rustc = "rustc" # path to the compiler to execute +rustdoc = "rustdoc" # path to the doc generator to execute +target-dir = "target" # path of where to place all generated artifacts ``` # Environment Variables @@ -92,3 +93,5 @@ Cargo recognizes a few global environment variables to configure how it runs: compiler instead. * `RUSTDOC` - Instead of running `rustdoc`, Cargo will execute this specified `rustdoc` instance instead. +* `CARGO_TARGET_DIR` - Location of where to place all generated artifacts, + relative to the current working directory. diff --git a/tests/test_cargo_compile.rs b/tests/test_cargo_compile.rs index 0583479581b..d80e51aa6f6 100644 --- a/tests/test_cargo_compile.rs +++ b/tests/test_cargo_compile.rs @@ -1821,3 +1821,45 @@ test!(ignore_dotfile { assert_that(p.cargo("build"), execs().with_status(0)); }); + +test!(custom_target_dir { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", "fn main() {}"); + p.build(); + + let exe_name = format!("foo{}", env::consts::EXE_SUFFIX); + + assert_that(p.cargo("build").env("CARGO_TARGET_DIR", "foo/target"), + execs().with_status(0)); + assert_that(&p.root().join("foo/target/debug").join(&exe_name), + existing_file()); + assert_that(&p.root().join("target/debug").join(&exe_name), + is_not(existing_file())); + + assert_that(p.cargo("build"), + execs().with_status(0)); + assert_that(&p.root().join("foo/target/debug").join(&exe_name), + existing_file()); + assert_that(&p.root().join("target/debug").join(&exe_name), + existing_file()); + + fs::create_dir(p.root().join(".cargo")).unwrap(); + File::create(p.root().join(".cargo/config")).unwrap().write_all(br#" + [build] + target-dir = "bar/target" + "#).unwrap(); + assert_that(p.cargo("build").env("CARGO_TARGET_DIR", "foo/target"), + execs().with_status(0)); + assert_that(&p.root().join("bar/target/debug").join(&exe_name), + existing_file()); + assert_that(&p.root().join("foo/target/debug").join(&exe_name), + existing_file()); + assert_that(&p.root().join("target/debug").join(&exe_name), + existing_file()); +});