Skip to content

Commit

Permalink
Auto merge of #5506 - ehuss:config-profile, r=alexcrichton
Browse files Browse the repository at this point in the history
Config Profiles (RFC 2282 Part 2)

Notes:
- `-Z config-profile` CLI option is required to use.
- Config values no longer reject mixed base types (integer, string, boolean) in order to support the mixed types in profiles.
  • Loading branch information
bors committed Jun 2, 2018
2 parents cddf9ad + 84a80d8 commit 9f09778
Show file tree
Hide file tree
Showing 9 changed files with 594 additions and 129 deletions.
1 change: 1 addition & 0 deletions src/bin/cargo/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Available unstable (nightly-only) flags:
-Z no-index-update -- Do not update the registry, avoids a network request for benchmarking
-Z offline -- Offline mode that does not perform network requests
-Z unstable-options -- Allow the usage of unstable options such as --registry
-Z config-profile -- Read profiles from .cargo/config files
Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
);
Expand Down
2 changes: 2 additions & 0 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ pub struct CliUnstable {
pub minimal_versions: bool,
pub package_features: bool,
pub advanced_env: bool,
pub config_profile: bool,
}

impl CliUnstable {
Expand Down Expand Up @@ -344,6 +345,7 @@ impl CliUnstable {
"minimal-versions" => self.minimal_versions = true,
"package-features" => self.package_features = true,
"advanced-env" => self.advanced_env = true,
"config-profile" => self.config_profile = true,
_ => bail!("unknown `-Z` flag specified: {}", k),
}

Expand Down
190 changes: 129 additions & 61 deletions src/cargo/core/profiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ use std::{cmp, fmt, hash};

use core::compiler::CompileMode;
use core::interning::InternedString;
use core::{PackageId, PackageIdSpec, PackageSet, Shell};
use core::{Features, PackageId, PackageIdSpec, PackageSet, Shell};
use util::errors::CargoResultExt;
use util::lev_distance::lev_distance;
use util::toml::{ProfilePackageSpec, StringOrBool, TomlProfile, U32OrBool};
use util::CargoResult;
use util::toml::{ProfilePackageSpec, StringOrBool, TomlProfile, TomlProfiles, U32OrBool};
use util::{CargoResult, Config};

/// Collection of all user profiles.
#[derive(Clone, Debug)]
Expand All @@ -20,34 +21,45 @@ pub struct Profiles {

impl Profiles {
pub fn new(
dev: Option<TomlProfile>,
release: Option<TomlProfile>,
test: Option<TomlProfile>,
bench: Option<TomlProfile>,
doc: Option<TomlProfile>,
) -> Profiles {
Profiles {
profiles: Option<&TomlProfiles>,
config: &Config,
features: &Features,
warnings: &mut Vec<String>,
) -> CargoResult<Profiles> {
if let Some(profiles) = profiles {
profiles.validate(features, warnings)?;
}

let config_profiles = config.profiles()?;
config_profiles.validate(features, warnings)?;

Ok(Profiles {
dev: ProfileMaker {
default: Profile::default_dev(),
toml: dev,
toml: profiles.and_then(|p| p.dev.clone()),
config: config_profiles.dev.clone(),
},
release: ProfileMaker {
default: Profile::default_release(),
toml: release,
toml: profiles.and_then(|p| p.release.clone()),
config: config_profiles.release.clone(),
},
test: ProfileMaker {
default: Profile::default_test(),
toml: test,
toml: profiles.and_then(|p| p.test.clone()),
config: None,
},
bench: ProfileMaker {
default: Profile::default_bench(),
toml: bench,
toml: profiles.and_then(|p| p.bench.clone()),
config: None,
},
doc: ProfileMaker {
default: Profile::default_doc(),
toml: doc,
toml: profiles.and_then(|p| p.doc.clone()),
config: None,
},
}
})
}

/// Retrieve the profile for a target.
Expand Down Expand Up @@ -86,7 +98,7 @@ impl Profiles {
CompileMode::Bench => &self.bench,
CompileMode::Doc { .. } => &self.doc,
};
let mut profile = maker.profile_for(Some(pkg_id), is_member, profile_for);
let mut profile = maker.get_profile(Some(pkg_id), is_member, profile_for);
// `panic` should not be set for tests/benches, or any of their
// dependencies.
if profile_for == ProfileFor::TestDependency || mode.is_any_test() {
Expand All @@ -112,9 +124,9 @@ impl Profiles {
/// select for the package that was actually built.
pub fn base_profile(&self, release: bool) -> Profile {
if release {
self.release.profile_for(None, true, ProfileFor::Any)
self.release.get_profile(None, true, ProfileFor::Any)
} else {
self.dev.profile_for(None, true, ProfileFor::Any)
self.dev.get_profile(None, true, ProfileFor::Any)
}
}

Expand All @@ -132,6 +144,7 @@ impl Profiles {
/// An object used for handling the profile override hierarchy.
///
/// The precedence of profiles are (first one wins):
/// - Profiles in .cargo/config files (using same order as below).
/// - [profile.dev.overrides.name] - A named package.
/// - [profile.dev.overrides."*"] - This cannot apply to workspace members.
/// - [profile.dev.build-override] - This can only apply to `build.rs` scripts
Expand All @@ -140,60 +153,45 @@ impl Profiles {
/// - Default (hard-coded) values.
#[derive(Debug, Clone)]
struct ProfileMaker {
/// The starting, hard-coded defaults for the profile.
default: Profile,
/// The profile from the `Cargo.toml` manifest.
toml: Option<TomlProfile>,
/// Profile loaded from `.cargo/config` files.
config: Option<TomlProfile>,
}

impl ProfileMaker {
fn profile_for(
fn get_profile(
&self,
pkg_id: Option<&PackageId>,
is_member: bool,
profile_for: ProfileFor,
) -> Profile {
let mut profile = self.default;
if let Some(ref toml) = self.toml {
merge_profile(&mut profile, toml);
if profile_for == ProfileFor::CustomBuild {
if let Some(ref build_override) = toml.build_override {
merge_profile(&mut profile, build_override);
}
}
if let Some(ref overrides) = toml.overrides {
if !is_member {
if let Some(all) = overrides.get(&ProfilePackageSpec::All) {
merge_profile(&mut profile, all);
}
}
if let Some(pkg_id) = pkg_id {
let mut matches = overrides.iter().filter_map(
|(key, spec_profile)| match key {
&ProfilePackageSpec::All => None,
&ProfilePackageSpec::Spec(ref s) => if s.matches(pkg_id) {
Some(spec_profile)
} else {
None
},
},
);
if let Some(spec_profile) = matches.next() {
merge_profile(&mut profile, spec_profile);
// `validate_packages` should ensure that there are
// no additional matches.
assert!(
matches.next().is_none(),
"package `{}` matched multiple profile overrides",
pkg_id
);
}
}
}
merge_toml(pkg_id, is_member, profile_for, &mut profile, toml);
}
if let Some(ref toml) = self.config {
merge_toml(pkg_id, is_member, profile_for, &mut profile, toml);
}
profile
}

fn validate_packages(&self, shell: &mut Shell, packages: &PackageSet) -> CargoResult<()> {
let toml = match self.toml {
self.validate_packages_toml(shell, packages, &self.toml, true)?;
self.validate_packages_toml(shell, packages, &self.config, false)?;
Ok(())
}

fn validate_packages_toml(
&self,
shell: &mut Shell,
packages: &PackageSet,
toml: &Option<TomlProfile>,
warn_unmatched: bool,
) -> CargoResult<()> {
let toml = match *toml {
Some(ref toml) => toml,
None => return Ok(()),
};
Expand All @@ -206,9 +204,9 @@ impl ProfileMaker {
for pkg_id in packages.package_ids() {
let matches: Vec<&PackageIdSpec> = overrides
.keys()
.filter_map(|key| match key {
&ProfilePackageSpec::All => None,
&ProfilePackageSpec::Spec(ref spec) => if spec.matches(pkg_id) {
.filter_map(|key| match *key {
ProfilePackageSpec::All => None,
ProfilePackageSpec::Spec(ref spec) => if spec.matches(pkg_id) {
Some(spec)
} else {
None
Expand Down Expand Up @@ -237,9 +235,12 @@ impl ProfileMaker {
}
}

if !warn_unmatched {
return Ok(());
}
// Verify every override matches at least one package.
let missing_specs = overrides.keys().filter_map(|key| {
if let &ProfilePackageSpec::Spec(ref spec) = key {
if let ProfilePackageSpec::Spec(ref spec) = *key {
if !found.contains(spec) {
return Some(spec);
}
Expand All @@ -258,7 +259,7 @@ impl ProfileMaker {
}
})
.collect();
if name_matches.len() == 0 {
if name_matches.is_empty() {
let suggestion = packages
.package_ids()
.map(|p| (lev_distance(spec.name(), &p.name()), p.name()))
Expand Down Expand Up @@ -289,6 +290,50 @@ impl ProfileMaker {
}
}

fn merge_toml(
pkg_id: Option<&PackageId>,
is_member: bool,
profile_for: ProfileFor,
profile: &mut Profile,
toml: &TomlProfile,
) {
merge_profile(profile, toml);
if profile_for == ProfileFor::CustomBuild {
if let Some(ref build_override) = toml.build_override {
merge_profile(profile, build_override);
}
}
if let Some(ref overrides) = toml.overrides {
if !is_member {
if let Some(all) = overrides.get(&ProfilePackageSpec::All) {
merge_profile(profile, all);
}
}
if let Some(pkg_id) = pkg_id {
let mut matches = overrides
.iter()
.filter_map(|(key, spec_profile)| match *key {
ProfilePackageSpec::All => None,
ProfilePackageSpec::Spec(ref s) => if s.matches(pkg_id) {
Some(spec_profile)
} else {
None
},
});
if let Some(spec_profile) = matches.next() {
merge_profile(profile, spec_profile);
// `validate_packages` should ensure that there are
// no additional matches.
assert!(
matches.next().is_none(),
"package `{}` matched multiple profile overrides",
pkg_id
);
}
}
}
}

fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
if let Some(ref opt_level) = toml.opt_level {
profile.opt_level = InternedString::new(&opt_level.0);
Expand Down Expand Up @@ -483,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(())
}
}
Loading

0 comments on commit 9f09778

Please sign in to comment.