Use new config API.
authorEric Huss <eric@huss.org>
Thu, 31 May 2018 16:54:37 +0000 (09:54 -0700)
committerEric Huss <eric@huss.org>
Thu, 31 May 2018 17:20:38 +0000 (10:20 -0700)
Also, require "override" feature if used in a config.

src/cargo/core/profiles.rs
src/cargo/util/config.rs
src/cargo/util/toml/mod.rs
tests/testsuite/profile_config.rs

index c9feafaed7f28c4e9b07d0dc58f4482a5e9b2dfe..24fa37d3b6a536251deef0ba872ab9d97360fa54 100644 (file)
@@ -1,13 +1,13 @@
-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)]
@@ -29,18 +29,20 @@ impl Profiles {
         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(),
@@ -137,71 +139,6 @@ impl Profiles {
         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.
@@ -449,20 +386,6 @@ pub struct Profile {
     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 {
@@ -605,3 +528,26 @@ impl ProfileFor {
         &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(())
+    }
+}
index e4b93828f15b3912dd7b107baa0b130b667dd4ec..6af6dc91919260aee3cec81b9169847369383720 100644 (file)
@@ -22,6 +22,7 @@ use lazycell::LazyCell;
 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;
@@ -77,6 +78,8 @@ pub struct Config {
     target_dir: Option<Filesystem>,
     /// Environment variables, separated to assist testing.
     env: HashMap<String, String>,
+    /// Profiles loaded from config.
+    profiles: LazyCell<ConfigProfiles>,
 }
 
 impl Config {
@@ -132,6 +135,7 @@ impl Config {
             creation_time: Instant::now(),
             target_dir: None,
             env,
+            profiles: LazyCell::new(),
         }
     }
 
@@ -246,6 +250,25 @@ impl Config {
             .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())
     }
@@ -1380,7 +1403,7 @@ impl ConfigValue {
         }
     }
 
-    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),
@@ -1425,6 +1448,7 @@ impl ConfigValue {
                     };
                 }
             }
+            // Allow switching types except for tables or arrays.
             (expected @ &mut CV::List(_, _), found)
             | (expected @ &mut CV::Table(_, _), found)
             | (expected, found @ CV::List(_, _))
index ac2a452c194389c300698164b6940b4ae8611375..0d4cd89d96a00b44f7adce34e2869cdea3cda655 100644 (file)
@@ -5,7 +5,6 @@ use std::path::{Path, PathBuf};
 use std::rc::Rc;
 use std::str;
 
-use failure::Error;
 use semver::{self, VersionReq};
 use serde::de::{self, Deserialize};
 use serde::ser;
@@ -22,7 +21,7 @@ use core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRoot
 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;
@@ -248,19 +247,19 @@ pub struct TomlProfiles {
 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(())
     }
@@ -420,71 +419,18 @@ impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
 }
 
 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()?;
             }
index 2644d85d7124e925f6b5e223128ced2b9b0d5ea1..73613be9e5738f6e8ff624f4da489c5b236e7cce 100644 (file)
@@ -18,18 +18,30 @@ fn profile_config_gated() {
 
     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",
@@ -53,34 +65,24 @@ fn profile_config_validate_warnings() {
         .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", "")
@@ -108,10 +110,7 @@ fn profile_config_error_paths() {
 [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
 ",
         ),
     );
@@ -120,7 +119,16 @@ Caused by:
 #[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",
@@ -139,7 +147,7 @@ fn profile_config_validate_errors() {
 [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.
@@ -170,10 +178,7 @@ fn profile_config_syntax_errors() {
 [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
 ",
         ),
     );
@@ -206,7 +211,16 @@ fn profile_config_override_spec_multiple() {
         "#,
         )
         .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();
 
@@ -290,7 +304,16 @@ fn profile_config_override_precedence() {
         "#,
         )
         .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",
@@ -318,7 +341,16 @@ fn profile_config_override_precedence() {
 #[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",