Also, require "override" feature if used in a config.
-use std::collections::{HashMap, HashSet};
-use std::sync::atomic;
+use std::collections::HashSet;
use std::{cmp, fmt, hash};
use core::compiler::CompileMode;
use core::interning::InternedString;
use core::{Features, PackageId, PackageIdSpec, PackageSet, Shell};
+use util::errors::CargoResultExt;
use util::lev_distance::lev_distance;
use util::toml::{ProfilePackageSpec, StringOrBool, TomlProfile, TomlProfiles, U32OrBool};
-use util::{CargoResult, Config, ConfigValue};
+use util::{CargoResult, Config};
/// Collection of all user profiles.
#[derive(Clone, Debug)]
if let Some(profiles) = profiles {
profiles.validate(features, warnings)?;
}
- Profiles::validate_config(config, warnings)?;
+
+ let config_profiles = config.profiles()?;
+ config_profiles.validate(features, warnings)?;
Ok(Profiles {
dev: ProfileMaker {
default: Profile::default_dev(),
toml: profiles.and_then(|p| p.dev.clone()),
- config: TomlProfile::from_config(config, "dev", warnings)?,
+ config: config_profiles.dev.clone(),
},
release: ProfileMaker {
default: Profile::default_release(),
toml: profiles.and_then(|p| p.release.clone()),
- config: TomlProfile::from_config(config, "release", warnings)?,
+ config: config_profiles.release.clone(),
},
test: ProfileMaker {
default: Profile::default_test(),
self.doc.validate_packages(shell, packages)?;
Ok(())
}
-
- fn validate_config(config: &Config, warnings: &mut Vec<String>) -> CargoResult<()> {
- static VALIDATE_ONCE: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;
-
- if VALIDATE_ONCE.swap(true, atomic::Ordering::SeqCst) {
- return Ok(());
- }
-
- // cv: Value<HashMap<String, CV>>
- if let Some(cv) = config.get_table("profile")? {
- // Warn if config profiles without CLI option.
- if !config.cli_unstable().config_profile {
- warnings.push(format!(
- "profile in config `{}` requires `-Z config-profile` command-line option",
- cv.definition
- ));
- // Ignore the rest.
- return Ok(());
- }
- // Warn about unsupported profile names.
- for (key, profile_cv) in cv.val.iter() {
- if key != "dev" && key != "release" {
- warnings.push(format!(
- "profile `{}` in config `{}` is not supported",
- key,
- profile_cv.definition_path().display()
- ));
- }
- }
- // Warn about incorrect key names.
- for profile_cv in cv.val.values() {
- if let ConfigValue::Table(ref profile, _) = *profile_cv {
- validate_profile_keys(profile, warnings);
- if let Some(&ConfigValue::Table(ref bo_profile, _)) =
- profile.get("build-override")
- {
- validate_profile_keys(bo_profile, warnings);
- }
- if let Some(&ConfigValue::Table(ref os, _)) = profile.get("overrides") {
- for o_profile_cv in os.values() {
- if let ConfigValue::Table(ref o_profile, _) = *o_profile_cv {
- validate_profile_keys(o_profile, warnings);
- }
- }
- }
- }
- }
- }
- return Ok(());
-
- fn validate_profile_keys(
- profile: &HashMap<String, ConfigValue>,
- warnings: &mut Vec<String>,
- ) {
- for (key, value_cv) in profile.iter() {
- if !TOML_PROFILE_KEYS.iter().any(|k| k == key) {
- warnings.push(format!(
- "unused profile key `{}` in config `{}`",
- key,
- value_cv.definition_path().display()
- ));
- }
- }
- }
- }
}
/// An object used for handling the profile override hierarchy.
pub panic: Option<InternedString>,
}
-const TOML_PROFILE_KEYS: [&str; 11] = [
- "opt-level",
- "lto",
- "codegen-units",
- "debug",
- "debug-assertions",
- "rpath",
- "panic",
- "overflow-checks",
- "incremental",
- "overrides",
- "build-override",
-];
-
impl Default for Profile {
fn default() -> Profile {
Profile {
&ALL
}
}
+
+/// Profiles loaded from .cargo/config files.
+#[derive(Clone, Debug, Deserialize, Default)]
+pub struct ConfigProfiles {
+ dev: Option<TomlProfile>,
+ release: Option<TomlProfile>,
+}
+
+impl ConfigProfiles {
+ pub fn validate(&self, features: &Features, warnings: &mut Vec<String>) -> CargoResult<()> {
+ if let Some(ref profile) = self.dev {
+ profile
+ .validate("dev", features, warnings)
+ .chain_err(|| format_err!("config profile `profile.dev` is not valid"))?;
+ }
+ if let Some(ref profile) = self.release {
+ profile
+ .validate("release", features, warnings)
+ .chain_err(|| format_err!("config profile `profile.release` is not valid"))?;
+ }
+ Ok(())
+ }
+}
use serde::{de, de::IntoDeserializer, Serialize, Serializer};
use toml;
+use core::profiles::ConfigProfiles;
use core::shell::Verbosity;
use core::{CliUnstable, Shell, SourceId, Workspace};
use ops;
target_dir: Option<Filesystem>,
/// Environment variables, separated to assist testing.
env: HashMap<String, String>,
+ /// Profiles loaded from config.
+ profiles: LazyCell<ConfigProfiles>,
}
impl Config {
creation_time: Instant::now(),
target_dir: None,
env,
+ profiles: LazyCell::new(),
}
}
.map(AsRef::as_ref)
}
+ pub fn profiles(&self) -> CargoResult<&ConfigProfiles> {
+ self.profiles.try_borrow_with(|| {
+ let ocp = self.get::<Option<ConfigProfiles>>("profile")?;
+ if let Some(config_profiles) = ocp {
+ // Warn if config profiles without CLI option.
+ if !self.cli_unstable().config_profile {
+ self.shell().warn(
+ "profiles in config files require `-Z config-profile` \
+ command-line option",
+ )?;
+ return Ok(ConfigProfiles::default());
+ }
+ Ok(config_profiles)
+ } else {
+ Ok(ConfigProfiles::default())
+ }
+ })
+ }
+
pub fn values(&self) -> CargoResult<&HashMap<String, ConfigValue>> {
self.values.try_borrow_with(|| self.load_values())
}
}
}
- pub fn into_toml(self) -> toml::Value {
+ fn into_toml(self) -> toml::Value {
match self {
CV::Boolean(s, _) => toml::Value::Boolean(s),
CV::String(s, _) => toml::Value::String(s),
};
}
}
+ // Allow switching types except for tables or arrays.
(expected @ &mut CV::List(_, _), found)
| (expected @ &mut CV::Table(_, _), found)
| (expected, found @ CV::List(_, _))
use std::rc::Rc;
use std::str;
-use failure::Error;
use semver::{self, VersionReq};
use serde::de::{self, Deserialize};
use serde::ser;
use sources::CRATES_IO;
use util::errors::{CargoError, CargoResult, CargoResultExt};
use util::paths;
-use util::{self, Config, ConfigValue, ToUrl};
+use util::{self, Config, ToUrl};
mod targets;
use self::targets::targets;
impl TomlProfiles {
pub fn validate(&self, features: &Features, warnings: &mut Vec<String>) -> CargoResult<()> {
if let Some(ref test) = self.test {
- test.validate("test", Some(features), warnings)?;
+ test.validate("test", features, warnings)?;
}
if let Some(ref doc) = self.doc {
- doc.validate("doc", Some(features), warnings)?;
+ doc.validate("doc", features, warnings)?;
}
if let Some(ref bench) = self.bench {
- bench.validate("bench", Some(features), warnings)?;
+ bench.validate("bench", features, warnings)?;
}
if let Some(ref dev) = self.dev {
- dev.validate("dev", Some(features), warnings)?;
+ dev.validate("dev", features, warnings)?;
}
if let Some(ref release) = self.release {
- release.validate("release", Some(features), warnings)?;
+ release.validate("release", features, warnings)?;
}
Ok(())
}
}
impl TomlProfile {
- pub fn from_config(
- config: &Config,
- name: &str,
- warnings: &mut Vec<String>,
- ) -> CargoResult<Option<TomlProfile>> {
- if !config.cli_unstable().config_profile {
- return Ok(None);
- }
- if let Some(util::config::Value { val, .. }) =
- config.get_table(&format!("profile.{}", name))?
- {
- let cv = ConfigValue::Table(val.clone(), PathBuf::new());
- let toml = cv.into_toml();
- let profile: TomlProfile =
- Deserialize::deserialize(toml).chain_err(|| error_path(&val))?;
- profile
- .validate(name, None, warnings)
- .chain_err(|| error_path(&val))?;
- return Ok(Some(profile));
- }
- return Ok(None);
-
- fn error_path(table: &HashMap<String, ConfigValue>) -> Error {
- let mut paths = HashSet::new();
- error_path_rec(table, &mut paths);
- if paths.len() == 1 {
- format_err!(
- "error in config profile `{}`",
- paths.into_iter().next().unwrap()
- )
- } else {
- let mut ps = paths.into_iter().collect::<Vec<_>>();
- ps.sort(); // to help with testing
- format_err!(
- "error in config profile, possible locations: {}",
- ps.join(", ")
- )
- }
- }
- fn error_path_rec(table: &HashMap<String, ConfigValue>, paths: &mut HashSet<String>) {
- for cv in table.values() {
- paths.insert(cv.definition_path().display().to_string());
- if let &ConfigValue::Table(ref t, _) = cv {
- error_path_rec(t, paths);
- }
- }
- }
- }
-
pub fn validate(
&self,
name: &str,
- features: Option<&Features>,
+ features: &Features,
warnings: &mut Vec<String>,
) -> CargoResult<()> {
if let Some(ref profile) = self.build_override {
- if let Some(features) = features {
- features.require(Feature::profile_overrides())?;
- }
+ features.require(Feature::profile_overrides())?;
profile.validate_override()?;
}
if let Some(ref override_map) = self.overrides {
- if let Some(features) = features {
- features.require(Feature::profile_overrides())?;
- }
+ features.require(Feature::profile_overrides())?;
for profile in override_map.values() {
profile.validate_override()?;
}
assert_that(
p.cargo("build -v"),
- execs().with_status(0).with_stderr_contains(
- "\
-[WARNING] profile in config `[..]foo[/].cargo[/]config` requires `-Z config-profile` command-line option
+ execs()
+ .with_status(0)
+ .with_stderr_contains(
+ "\
+[WARNING] profiles in config files require `-Z config-profile` command-line option
",
- ).with_stderr_contains("[..]-C debuginfo=2[..]"),
+ )
+ .with_stderr_contains("[..]-C debuginfo=2[..]"),
);
}
#[test]
fn profile_config_validate_warnings() {
let p = project("foo")
- .file("Cargo.toml", &basic_lib_manifest("foo"))
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["profile-overrides"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ )
.file("src/lib.rs", "")
.file(
".cargo/config",
.build();
assert_that(
- p.cargo("build -v -Z config-profile")
+ p.cargo("build -Z config-profile")
.masquerade_as_nightly_cargo(),
- execs()
- .with_status(0)
- .with_stderr_contains(
- "\
-[WARNING] profile `test` in config `[..]foo[/].cargo[/]config` is not supported
-",
- )
- .with_stderr_contains(
- "\
-[WARNING] profile `asdf` in config `[..]foo[/].cargo[/]config` is not supported
-",
- )
- .with_stderr_contains(
- "\
-[WARNING] unused profile key `bad-key` in config `[..]foo[/].cargo[/]config`
-[WARNING] unused profile key `bad-key-bo` in config `[..]foo[/].cargo[/]config`
-[WARNING] unused profile key `bad-key-bar` in config `[..]foo[/].cargo[/]config`
+ execs().with_status(0).with_stderr_unordered(
+ "\
+[WARNING] unused key `profile.asdf` in config file `[..].cargo[/]config`
+[WARNING] unused key `profile.test` in config file `[..].cargo[/]config`
+[WARNING] unused key `profile.dev.bad-key` in config file `[..].cargo[/]config`
+[WARNING] unused key `profile.dev.overrides.bar.bad-key-bar` in config file `[..].cargo[/]config`
+[WARNING] unused key `profile.dev.build-override.bad-key-bo` in config file `[..].cargo[/]config`
+[COMPILING] foo [..]
+[FINISHED] [..]
",
- ),
+ ),
);
}
#[test]
fn profile_config_error_paths() {
- // Due to how it's implemented, we are uncertain where a merged error
- // comes from.
let p = project("foo")
.file("Cargo.toml", &basic_lib_manifest("foo"))
.file("src/lib.rs", "")
[ERROR] failed to parse manifest at `[..]foo[/]Cargo.toml`
Caused by:
- error in config profile, possible locations: [..]foo[/].cargo[/]config, [..]home[/].cargo[/]config
-
-Caused by:
- invalid type: string \"foo\", expected a boolean for key `rpath`
+ error in [..].cargo[/]config: `profile.dev.rpath` expected true/false, but found a string
",
),
);
#[test]
fn profile_config_validate_errors() {
let p = project("foo")
- .file("Cargo.toml", &basic_lib_manifest("foo"))
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["profile-overrides"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ )
.file("src/lib.rs", "")
.file(
".cargo/config",
[ERROR] failed to parse manifest at `[..]foo[/]Cargo.toml`
Caused by:
- error in config profile `[..]foo[/].cargo[/]config`
+ config profile `profile.dev` is not valid
Caused by:
`panic` may not be specified in a profile override.
[ERROR] failed to parse manifest at [..]
Caused by:
- error in config profile `[..]foo[/].cargo[/]config`
-
-Caused by:
- invalid type: string \"foo\", expected u32 for key `codegen-units`
+ error in [..].cargo[/]config: `profile.dev.codegen-units` expected an integer, but found a string
",
),
);
"#,
)
.file("src/lib.rs", "")
- .file("bar/Cargo.toml", &basic_lib_manifest("bar"))
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ cargo-features = ["profile-overrides"]
+
+ [package]
+ name = "bar"
+ version = "0.5.0"
+ "#,
+ )
.file("bar/src/lib.rs", "")
.build();
"#,
)
.file("src/lib.rs", "")
- .file("bar/Cargo.toml", &basic_lib_manifest("bar"))
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ cargo-features = ["profile-overrides"]
+
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ "#,
+ )
.file("bar/src/lib.rs", "")
.file(
".cargo/config",
#[test]
fn profile_config_no_warn_unknown_override() {
let p = project("foo")
- .file("Cargo.toml", &basic_lib_manifest("foo"))
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["profile-overrides"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ )
.file("src/lib.rs", "")
.file(
".cargo/config",