diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 5a4a48182b4..f9c21624dc4 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -1,4 +1,5 @@ use std::default::Default; +use std::collections::HashMap; use std::fmt; use std::path::{PathBuf, Path}; @@ -20,7 +21,7 @@ pub struct Manifest { include: Vec, metadata: ManifestMetadata, profiles: Profiles, - publish: bool + publish: bool, } /// General metadata about a package which is just blindly uploaded to the @@ -100,7 +101,7 @@ impl Encodable for TargetKind { } } -#[derive(RustcEncodable, RustcDecodable, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct Profile { pub opt_level: u32, pub lto: bool, @@ -113,6 +114,12 @@ pub struct Profile { pub test: bool, pub doc: bool, pub run_custom_build: bool, + pub deps_profile: Option, +} + +#[derive(Clone, Hash, PartialEq, Eq, Debug)] +pub struct ProfileId { + name: String } #[derive(Default, Clone, Debug)] @@ -466,6 +473,7 @@ impl Default for Profile { test: false, doc: false, run_custom_build: false, + deps_profile: None, } } } @@ -484,3 +492,27 @@ impl fmt::Display for Profile { } } + +impl Profiles { + pub fn name_to_id(name: &str) -> Option { + let all_profiles = ["dev", "release", "test", "bench", "doc"]; + if all_profiles.iter().any(|&profile| profile == name) { + Some(ProfileId { name: name.to_owned() }) + } else { + None + } + } + + pub fn by_id(&self, id: &ProfileId) -> &Profile { + let key: &str = &id.name; + self.all_profiles()[key] + } + + fn all_profiles(&self) -> HashMap<&str, &Profile> { + [("dev", &self.dev), + ("release", &self.release), + ("test", &self.test), + ("bench", &self.bench), + ("doc", &self.doc)].iter().map(|&x| x).collect() + } +} diff --git a/src/cargo/core/mod.rs b/src/cargo/core/mod.rs index b0b0895f0ef..63ab74a6190 100644 --- a/src/cargo/core/mod.rs +++ b/src/cargo/core/mod.rs @@ -1,5 +1,5 @@ pub use self::dependency::{Dependency, DependencyInner}; -pub use self::manifest::{Manifest, Target, TargetKind, Profile, LibKind, Profiles}; +pub use self::manifest::{Manifest, Target, TargetKind, Profile, ProfileId, LibKind, Profiles}; pub use self::package::{Package, PackageSet}; pub use self::package_id::{PackageId, Metadata}; pub use self::package_id_spec::PackageIdSpec; diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index 9aa3c86699c..293838c91e2 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -88,6 +88,10 @@ impl Package { pub fn generate_metadata(&self) -> Metadata { self.package_id().generate_metadata(self.root()) } + + pub fn is_local(&self) -> bool { + self.manifest.summary().source_id().is_path() + } } impl fmt::Display for Package { diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index ba7716f12b5..629dc1b3018 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -240,6 +240,9 @@ pub fn compile_pkg<'a>(root_package: &Package, let mut build_config = try!(scrape_build_config(config, jobs, target)); build_config.exec_engine = exec_engine.clone(); build_config.release = release; + build_config.deps_profile = if release { &profiles.release } else { &profiles.dev } + .deps_profile.clone(); + if let CompileMode::Doc { deps } = mode { build_config.doc_all = deps; } diff --git a/src/cargo/ops/cargo_rustc/context.rs b/src/cargo/ops/cargo_rustc/context.rs index c85de27c4c8..c87407be239 100644 --- a/src/cargo/ops/cargo_rustc/context.rs +++ b/src/cargo/ops/cargo_rustc/context.rs @@ -374,7 +374,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Unit { pkg: pkg, target: t, - profile: self.lib_profile(id), + profile: self.lib_profile(pkg), kind: unit.kind.for_target(t), } }) @@ -404,7 +404,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Unit { pkg: unit.pkg, target: t, - profile: self.lib_profile(id), + profile: self.lib_profile(unit.pkg), kind: unit.kind.for_target(t), } })); @@ -477,7 +477,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { ret.push(Unit { pkg: dep, target: lib, - profile: self.lib_profile(dep.package_id()), + profile: self.lib_profile(dep), kind: unit.kind.for_target(lib), }); if self.build_config.doc_all { @@ -523,7 +523,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Unit { pkg: unit.pkg, target: t, - profile: self.lib_profile(unit.pkg.package_id()), + profile: self.lib_profile(unit.pkg), kind: unit.kind.for_target(t), } }) @@ -576,12 +576,13 @@ impl<'a, 'cfg> Context<'a, 'cfg> { self.build_config.requested_target.as_ref().map(|s| &s[..]) } - pub fn lib_profile(&self, _pkg: &PackageId) -> &'a Profile { - if self.build_config.release { - &self.profiles.release - } else { - &self.profiles.dev + pub fn lib_profile(&self, pkg: &Package) -> &'a Profile { + if !pkg.is_local() { + if let Some(ref id) = self.build_config.deps_profile { + return self.profiles.by_id(id) + } } + if self.build_config.release { &self.profiles.release } else { &self.profiles.dev } } pub fn build_script_profile(&self, _pkg: &PackageId) -> &'a Profile { diff --git a/src/cargo/ops/cargo_rustc/custom_build.rs b/src/cargo/ops/cargo_rustc/custom_build.rs index bc83ecbdc5a..05512a350fc 100644 --- a/src/cargo/ops/cargo_rustc/custom_build.rs +++ b/src/cargo/ops/cargo_rustc/custom_build.rs @@ -95,7 +95,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) // environment variables. Note that the profile-related environment // variables are not set with this the build script's profile but rather the // package's library profile. - let profile = cx.lib_profile(unit.pkg.package_id()); + let profile = cx.lib_profile(unit.pkg); let to_exec = to_exec.into_os_string(); let mut p = try!(super::process(CommandType::Host(to_exec), unit.pkg, cx)); p.env("OUT_DIR", &build_output) diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index f76ba09e5c9..c4e72ad8be9 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -7,7 +7,7 @@ use std::path::{self, PathBuf}; use std::sync::Arc; use core::{SourceMap, Package, PackageId, PackageSet, Target, Resolve}; -use core::{Profile, Profiles}; +use core::{Profile, ProfileId, Profiles}; use util::{self, CargoResult, human}; use util::{Config, internal, ChainError, profile, join_paths}; @@ -42,6 +42,7 @@ pub struct BuildConfig { pub exec_engine: Option>>, pub release: bool, pub doc_all: bool, + pub deps_profile: Option, } #[derive(Clone, Default)] @@ -448,7 +449,7 @@ fn build_base_args(cx: &Context, let Profile { opt_level, lto, codegen_units, ref rustc_args, debuginfo, debug_assertions, rpath, test, doc: _doc, run_custom_build, - rustdoc_args: _, + rustdoc_args: _, deps_profile: _, } = *unit.profile; assert!(!run_custom_build); diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index 47b67aa0c64..c6321e760a4 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -243,6 +243,7 @@ pub struct TomlProfile { debug: Option, debug_assertions: Option, rpath: Option, + dependencies_profile: Option, } #[derive(RustcDecodable)] @@ -562,7 +563,7 @@ impl TomlManifest { repository: project.repository.clone(), keywords: project.keywords.clone().unwrap_or(Vec::new()), }; - let profiles = build_profiles(&self.profile); + let profiles = try!(build_profiles(&self.profile)); let publish = project.publish.unwrap_or(true); let mut manifest = Manifest::new(summary, targets, @@ -957,30 +958,40 @@ fn normalize(lib: &Option, ret } -fn build_profiles(profiles: &Option) -> Profiles { +fn build_profiles(profiles: &Option) -> CargoResult { let profiles = profiles.as_ref(); - return Profiles { - release: merge(Profile::default_release(), - profiles.and_then(|p| p.release.as_ref())), - dev: merge(Profile::default_dev(), - profiles.and_then(|p| p.dev.as_ref())), - test: merge(Profile::default_test(), - profiles.and_then(|p| p.test.as_ref())), - bench: merge(Profile::default_bench(), - profiles.and_then(|p| p.bench.as_ref())), - doc: merge(Profile::default_doc(), - profiles.and_then(|p| p.doc.as_ref())), + return Ok(Profiles { + release: try!(merge(Profile::default_release(), + profiles.and_then(|p| p.release.as_ref()))), + dev: try!(merge(Profile::default_dev(), + profiles.and_then(|p| p.dev.as_ref()))), + test: try!(merge(Profile::default_test(), + profiles.and_then(|p| p.test.as_ref()))), + bench: try!(merge(Profile::default_bench(), + profiles.and_then(|p| p.bench.as_ref()))), + doc: try!(merge(Profile::default_doc(), + profiles.and_then(|p| p.doc.as_ref()))), custom_build: Profile::default_custom_build(), - }; + }); - fn merge(profile: Profile, toml: Option<&TomlProfile>) -> Profile { + fn merge(profile: Profile, toml: Option<&TomlProfile>) -> CargoResult { let &TomlProfile { - opt_level, lto, codegen_units, debug, debug_assertions, rpath + opt_level, lto, codegen_units, debug, + debug_assertions, rpath, ref dependencies_profile } = match toml { Some(toml) => toml, - None => return profile, + None => return Ok(profile), + }; + let deps_profile = match *dependencies_profile { + None => None, + Some(ref name) => if let Some(id) = Profiles::name_to_id(name) { + Some(id) + } else { + bail!("no such profile: {}", name) + } }; - Profile { + + Ok(Profile { opt_level: opt_level.unwrap_or(profile.opt_level), lto: lto.unwrap_or(profile.lto), codegen_units: codegen_units, @@ -992,6 +1003,7 @@ fn build_profiles(profiles: &Option) -> Profiles { test: profile.test, doc: profile.doc, run_custom_build: profile.run_custom_build, - } - } + deps_profile: deps_profile, + }) + }; } diff --git a/tests/test_cargo_profiles.rs b/tests/test_cargo_profiles.rs index b41d4e851d7..c47b343d14a 100644 --- a/tests/test_cargo_profiles.rs +++ b/tests/test_cargo_profiles.rs @@ -1,8 +1,9 @@ use std::env; use std::path::MAIN_SEPARATOR as SEP; +use support::registry::Package; +use support::{COMPILING, RUNNING, UPDATING, DOWNLOADING}; use support::{project, execs}; -use support::{COMPILING, RUNNING}; use hamcrest::assert_that; fn setup() { @@ -109,3 +110,130 @@ test!(top_level_overrides_deps { prefix = env::consts::DLL_PREFIX, suffix = env::consts::DLL_SUFFIX))); }); + + +test!(dependencies_profile_in_dev { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "test" + version = "0.0.0" + authors = [] + + [profile.dev] + dependencies-profile = "release" + + [profile.release] + opt-level = 3 + + [dependencies] + baz = "*" + "#) + .file("src/lib.rs", "extern crate baz;"); + Package::new("baz", "0.0.1").publish(); + + assert_that(p.cargo_process("build").arg("-v"), + execs().with_status(0).with_stdout(&format!("\ +{updating} registry [..] +{downloading} baz v0.0.1 ([..]) +{compiling} baz v0.0.1 ([..]) +{running} `rustc [..]lib.rs --crate-name baz --crate-type lib \ + -C opt-level=3 \ + -C metadata=[..] \ + -C extra-filename=[..] \ + --out-dir [..]deps \ + --emit=dep-info,link \ + -L dependency=[..]deps \ + -L dependency=[..]deps \ + --cap-lints allow` +{compiling} test v0.0.0 ([..]) +{running} `rustc src{sep}lib.rs --crate-name test --crate-type lib \ + -g \ + --out-dir {dir}{sep}target{sep}debug \ + --emit=dep-info,link \ + -L dependency={dir}{sep}target{sep}debug \ + -L dependency={dir}{sep}target{sep}debug{sep}deps \ + --extern baz=[..].rlib`", + updating = UPDATING, + downloading = DOWNLOADING, + running = RUNNING, + compiling = COMPILING, + sep = SEP, + dir = p.root().display()) + )); +}); + + +test!(dependencies_profile_in_release { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [profile.dev] + opt-level = 1 + + [profile.release] + dependencies-profile = "dev" + + [dependencies] + baz = "*" + "#) + .file("src/lib.rs", "extern crate baz;"); + Package::new("baz", "0.0.1").publish(); + + assert_that(p.cargo_process("build").arg("--release").arg("-v"), + execs().with_status(0).with_stdout(&format!("\ +{updating} registry [..] +{downloading} baz v0.0.1 ([..]) +{compiling} baz v0.0.1 ([..]) +{running} `rustc [..]lib.rs --crate-name baz --crate-type lib \ + -C opt-level=1 \ + -g \ + -C debug-assertions=on \ + -C metadata=[..] \ + -C extra-filename=[..] \ + --out-dir [..]deps \ + --emit=dep-info,link \ + -L dependency=[..]deps \ + -L dependency=[..]deps \ + --cap-lints allow` +{compiling} test v0.0.0 ([..]) +{running} `rustc src{sep}lib.rs --crate-name test --crate-type lib \ + -C opt-level=3 \ + --out-dir {dir}{sep}target{sep}release \ + --emit=dep-info,link \ + -L dependency={dir}{sep}target{sep}release \ + -L dependency={dir}{sep}target{sep}release{sep}deps \ + --extern baz=[..].rlib`", + updating = UPDATING, + downloading = DOWNLOADING, + running = RUNNING, + compiling = COMPILING, + sep = SEP, + dir = p.root().display()) + )); +}); + +test!(invalid_dependencies_profile { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "test" + version = "0.0.0" + authors = [] + + [profile.dev] + dependencies-profile = "spam" + "#); + + assert_that(p.cargo_process("build").arg("-v"), + execs().with_status(101).with_stderr("failed to parse manifest at [..] + +Caused by: +no such profile: spam")); +}); +