Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config Profiles (RFC 2282 Part 2) #5506

Merged
merged 2 commits into from
Jun 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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