Add named config profiles.

This commit is contained in:
Eric Huss 2019-12-26 19:59:19 -08:00
parent 56a5503268
commit 77ee608de3
27 changed files with 771 additions and 646 deletions

View File

@ -1,5 +1,4 @@
use crate::command_prelude::*;
use cargo::ops::{self, TestOptions};
pub fn cli() -> App {
@ -80,11 +79,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
ProfileChecking::Checked,
)?;
compile_opts.build_config.profile_kind = args.get_profile_kind(
config,
ProfileKind::Custom("bench".to_owned()),
ProfileChecking::Checked,
)?;
compile_opts.build_config.requested_profile =
args.get_profile_name(config, "bench", ProfileChecking::Checked)?;
let ops = TestOptions {
no_run: args.is_present("no-run"),

View File

@ -29,7 +29,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
config,
spec: values(args, "package"),
target: args.target(),
profile_kind: args.get_profile_kind(config, ProfileKind::Dev, ProfileChecking::Checked)?,
requested_profile: args.get_profile_name(config, "dev", ProfileChecking::Checked)?,
profile_specified: args.is_present("profile") || args.is_present("release"),
doc: args.is_present("doc"),
};

View File

@ -116,8 +116,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
ProfileChecking::Checked,
)?;
compile_opts.build_config.profile_kind =
args.get_profile_kind(config, ProfileKind::Release, ProfileChecking::Checked)?;
compile_opts.build_config.requested_profile =
args.get_profile_name(config, "release", ProfileChecking::Checked)?;
let krates = args
.values_of("crate")

View File

@ -108,11 +108,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
ProfileChecking::Checked,
)?;
compile_opts.build_config.profile_kind = args.get_profile_kind(
config,
ProfileKind::Custom("test".to_owned()),
ProfileChecking::Checked,
)?;
compile_opts.build_config.requested_profile =
args.get_profile_name(config, "test", ProfileChecking::Checked)?;
// `TESTNAME` is actually an argument of the test binary, but it's
// important, so we explicitly mention it and reconfigure.

View File

@ -1,27 +1,9 @@
use std::cell::RefCell;
use serde::ser;
use crate::core::compiler::{CompileKind, CompileTarget};
use crate::core::interning::InternedString;
use crate::util::ProcessBuilder;
use crate::util::{CargoResult, Config, RustfixDiagnosticServer};
#[derive(Debug, Clone)]
pub enum ProfileKind {
Dev,
Release,
Custom(String),
}
impl ProfileKind {
pub fn name(&self) -> &str {
match self {
ProfileKind::Dev => "dev",
ProfileKind::Release => "release",
ProfileKind::Custom(name) => name,
}
}
}
use serde::ser;
use std::cell::RefCell;
/// Configuration information for a rustc build.
#[derive(Debug)]
@ -31,7 +13,7 @@ pub struct BuildConfig {
/// Number of rustc jobs to run in parallel.
pub jobs: u32,
/// Build profile
pub profile_kind: ProfileKind,
pub requested_profile: InternedString,
/// The mode we are compiling in.
pub mode: CompileMode,
/// `true` to print stdout in JSON format (for machine reading).
@ -92,7 +74,7 @@ impl BuildConfig {
Ok(BuildConfig {
requested_kind,
jobs,
profile_kind: ProfileKind::Dev,
requested_profile: InternedString::new("dev"),
mode,
message_format: MessageFormat::Human,
force_rebuild: false,
@ -111,10 +93,6 @@ impl BuildConfig {
}
}
pub fn profile_name(&self) -> &str {
self.profile_kind.name()
}
pub fn test(&self) -> bool {
self.mode == CompileMode::Test || self.mode == CompileMode::Bench
}

View File

@ -26,7 +26,7 @@ pub struct BuildContext<'a, 'cfg> {
pub ws: &'a Workspace<'cfg>,
/// The cargo configuration.
pub config: &'cfg Config,
pub profiles: &'a Profiles,
pub profiles: Profiles,
pub build_config: &'a BuildConfig,
/// Extra compiler args for either `rustc` or `rustdoc`.
pub extra_compiler_args: HashMap<Unit<'a>, Vec<String>>,
@ -58,7 +58,7 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> {
packages: &'a PackageSet<'cfg>,
config: &'cfg Config,
build_config: &'a BuildConfig,
profiles: &'a Profiles,
profiles: Profiles,
units: &'a UnitInterner<'a>,
extra_compiler_args: HashMap<Unit<'a>, Vec<String>>,
) -> CargoResult<BuildContext<'a, 'cfg>> {

View File

@ -281,8 +281,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
export_dir: Option<PathBuf>,
units: &[Unit<'a>],
) -> CargoResult<()> {
let profile_kind = &self.bcx.build_config.profile_kind;
let dest = self.bcx.profiles.get_dir_name(profile_kind);
let dest = self.bcx.profiles.get_dir_name();
let host_layout = Layout::new(self.bcx.ws, None, &dest)?;
let mut targets = HashMap::new();
if let CompileKind::Target(target) = self.bcx.build_config.requested_kind {

View File

@ -19,7 +19,6 @@ use super::job::{
};
use super::timings::Timings;
use super::{BuildContext, BuildPlan, CompileMode, Context, Unit};
use crate::core::compiler::ProfileKind;
use crate::core::{PackageId, TargetKind};
use crate::handle_error;
use crate::util;
@ -44,7 +43,6 @@ pub struct JobQueue<'a, 'cfg> {
progress: Progress<'cfg>,
next_id: u32,
timings: Timings<'a, 'cfg>,
profile_kind: ProfileKind,
}
pub struct JobState<'a> {
@ -148,7 +146,6 @@ impl<'a, 'cfg> JobQueue<'a, 'cfg> {
progress,
next_id: 0,
timings,
profile_kind: bcx.build_config.profile_kind.clone(),
}
}
@ -415,7 +412,7 @@ impl<'a, 'cfg> JobQueue<'a, 'cfg> {
}
self.progress.clear();
let build_type = self.profile_kind.name();
let profile_name = cx.bcx.build_config.requested_profile;
// NOTE: this may be a bit inaccurate, since this may not display the
// profile for what was actually built. Profile overrides can change
// these settings, and in some cases different targets are built with
@ -423,7 +420,7 @@ impl<'a, 'cfg> JobQueue<'a, 'cfg> {
// list of Units built, and maybe display a list of the different
// profiles used. However, to keep it simple and compatible with old
// behavior, we just display what the base profile is.
let profile = cx.bcx.profiles.base_profile(&self.profile_kind)?;
let profile = cx.bcx.profiles.base_profile();
let mut opt_type = String::from(if profile.opt_level.as_str() == "0" {
"unoptimized"
} else {
@ -440,7 +437,7 @@ impl<'a, 'cfg> JobQueue<'a, 'cfg> {
} else if self.queue.is_empty() && queue.is_empty() {
let message = format!(
"{} [{}] target(s) in {}",
build_type, opt_type, time_elapsed
profile_name, opt_type, time_elapsed
);
if !cx.bcx.build_config.build_plan {
cx.bcx.config.shell().status("Finished", message)?;

View File

@ -27,7 +27,7 @@ use anyhow::Error;
use lazycell::LazyCell;
use log::debug;
pub use self::build_config::{BuildConfig, CompileMode, MessageFormat, ProfileKind};
pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
pub use self::build_context::{BuildContext, FileFlavor, TargetInfo};
use self::build_plan::BuildPlan;
pub use self::compilation::{Compilation, Doctest};

View File

@ -66,8 +66,7 @@ pub fn resolve_std<'cfg>(
/*replace*/ Vec::new(),
patch,
ws_config,
// Profiles are not used here, but we need something to pass in.
ws.profiles().clone(),
/*profiles*/ None,
crate::core::Features::default(),
);
@ -139,7 +138,6 @@ pub fn generate_std_roots<'a>(
/*is_member*/ false,
unit_for,
mode,
bcx.build_config.profile_kind.clone(),
);
let features = std_resolve.features_sorted(pkg.package_id());
Ok(bcx.units.intern(

View File

@ -116,7 +116,7 @@ impl<'a, 'cfg> Timings<'a, 'cfg> {
})
.collect();
let start_str = humantime::format_rfc3339_seconds(SystemTime::now()).to_string();
let profile = bcx.build_config.profile_kind.name().to_owned();
let profile = bcx.build_config.requested_profile.to_string();
Timings {
config: bcx.config,

View File

@ -546,7 +546,6 @@ fn new_unit_dep<'a>(
state.bcx.ws.is_member(pkg),
unit_for,
mode,
state.bcx.build_config.profile_kind.clone(),
);
new_unit_dep_with_profile(state, parent, pkg, target, unit_for, kind, mode, profile)
}

View File

@ -48,6 +48,18 @@ impl PartialEq for InternedString {
}
}
impl PartialEq<str> for InternedString {
fn eq(&self, other: &str) -> bool {
*self == other
}
}
impl<'a> PartialEq<&'a str> for InternedString {
fn eq(&self, other: &&str) -> bool {
**self == **other
}
}
impl Eq for InternedString {}
impl InternedString {

View File

@ -10,11 +10,10 @@ use serde::Serialize;
use url::Url;
use crate::core::interning::InternedString;
use crate::core::profiles::Profiles;
use crate::core::{Dependency, PackageId, PackageIdSpec, SourceId, Summary};
use crate::core::{Edition, Feature, Features, WorkspaceConfig};
use crate::util::errors::*;
use crate::util::toml::TomlManifest;
use crate::util::toml::{TomlManifest, TomlProfiles};
use crate::util::{short_hash, Config, Filesystem};
pub enum EitherManifest {
@ -33,7 +32,7 @@ pub struct Manifest {
include: Vec<String>,
metadata: ManifestMetadata,
custom_metadata: Option<toml::Value>,
profiles: Profiles,
profiles: Option<TomlProfiles>,
publish: Option<Vec<String>>,
publish_lockfile: bool,
replace: Vec<(PackageIdSpec, Dependency)>,
@ -64,7 +63,7 @@ pub struct VirtualManifest {
replace: Vec<(PackageIdSpec, Dependency)>,
patch: HashMap<Url, Vec<Dependency>>,
workspace: WorkspaceConfig,
profiles: Profiles,
profiles: Option<TomlProfiles>,
warnings: Warnings,
features: Features,
}
@ -399,7 +398,7 @@ impl Manifest {
links: Option<String>,
metadata: ManifestMetadata,
custom_metadata: Option<toml::Value>,
profiles: Profiles,
profiles: Option<TomlProfiles>,
publish: Option<Vec<String>>,
publish_lockfile: bool,
replace: Vec<(PackageIdSpec, Dependency)>,
@ -475,8 +474,8 @@ impl Manifest {
pub fn warnings(&self) -> &Warnings {
&self.warnings
}
pub fn profiles(&self) -> &Profiles {
&self.profiles
pub fn profiles(&self) -> Option<&TomlProfiles> {
self.profiles.as_ref()
}
pub fn publish(&self) -> &Option<Vec<String>> {
&self.publish
@ -563,7 +562,7 @@ impl VirtualManifest {
replace: Vec<(PackageIdSpec, Dependency)>,
patch: HashMap<Url, Vec<Dependency>>,
workspace: WorkspaceConfig,
profiles: Profiles,
profiles: Option<TomlProfiles>,
features: Features,
) -> VirtualManifest {
VirtualManifest {
@ -588,8 +587,8 @@ impl VirtualManifest {
&self.workspace
}
pub fn profiles(&self) -> &Profiles {
&self.profiles
pub fn profiles(&self) -> Option<&TomlProfiles> {
self.profiles.as_ref()
}
pub fn warnings_mut(&mut self) -> &mut Warnings {

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,6 @@ use log::debug;
use url::Url;
use crate::core::features::Features;
use crate::core::profiles::Profiles;
use crate::core::registry::PackageRegistry;
use crate::core::{Dependency, PackageId, PackageIdSpec};
use crate::core::{EitherManifest, Package, SourceId, VirtualManifest};
@ -17,7 +16,7 @@ use crate::ops;
use crate::sources::PathSource;
use crate::util::errors::{CargoResult, CargoResultExt, ManifestError};
use crate::util::paths;
use crate::util::toml::read_manifest;
use crate::util::toml::{read_manifest, TomlProfiles};
use crate::util::{Config, Filesystem};
/// The core abstraction in Cargo for working with a workspace of crates.
@ -273,7 +272,7 @@ impl<'cfg> Workspace<'cfg> {
self.config
}
pub fn profiles(&self) -> &Profiles {
pub fn profiles(&self) -> Option<&TomlProfiles> {
match self.root_maybe() {
MaybePackage::Package(p) => p.manifest().profiles(),
MaybePackage::Virtual(vm) => vm.profiles(),
@ -583,14 +582,6 @@ impl<'cfg> Workspace<'cfg> {
/// 2. All workspace members agree on this one root as the root.
/// 3. The current crate is a member of this workspace.
fn validate(&mut self) -> CargoResult<()> {
// Validate config profiles only once per workspace.
let features = self.features();
let mut warnings = Vec::new();
self.config.profiles()?.validate(features, &mut warnings)?;
for warning in warnings {
self.config.shell().warn(&warning)?;
}
// The rest of the checks require a VirtualManifest or multiple members.
if self.root_manifest.is_none() {
return Ok(());

View File

@ -1,13 +1,12 @@
use crate::core::InternedString;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use crate::core::compiler::unit_dependencies;
use crate::core::compiler::UnitInterner;
use crate::core::compiler::{
BuildConfig, BuildContext, CompileKind, CompileMode, Context, ProfileKind,
};
use crate::core::profiles::UnitFor;
use crate::core::compiler::{BuildConfig, BuildContext, CompileKind, CompileMode, Context};
use crate::core::profiles::{Profiles, UnitFor};
use crate::core::Workspace;
use crate::ops;
use crate::util::errors::{CargoResult, CargoResultExt};
@ -23,7 +22,7 @@ pub struct CleanOptions<'a> {
/// Whether to clean the release directory
pub profile_specified: bool,
/// Whether to clean the directory of a certain build profile
pub profile_kind: ProfileKind,
pub requested_profile: InternedString,
/// Whether to just clean the doc directory
pub doc: bool,
}
@ -39,16 +38,13 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
return rm_rf(&target_dir.into_path_unlocked(), config);
}
let profiles = ws.profiles();
// Check for whether the profile is defined.
let _ = profiles.base_profile(&opts.profile_kind)?;
let profiles = Profiles::new(ws.profiles(), config, opts.requested_profile, ws.features())?;
if opts.profile_specified {
// After parsing profiles we know the dir-name of the profile, if a profile
// was passed from the command line. If so, delete only the directory of
// that profile.
let dir_name = profiles.get_dir_name(&opts.profile_kind);
let dir_name = profiles.get_dir_name();
target_dir = target_dir.join(dir_name);
}
@ -64,8 +60,7 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
let interner = UnitInterner::new();
let mut build_config = BuildConfig::new(config, Some(1), &opts.target, CompileMode::Build)?;
let profile_kind = opts.profile_kind.clone();
build_config.profile_kind = profile_kind.clone();
build_config.requested_profile = opts.requested_profile;
let bcx = BuildContext::new(
ws,
&packages,
@ -88,20 +83,19 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
for mode in CompileMode::all_modes() {
for unit_for in UnitFor::all_values() {
let profile = if mode.is_run_custom_build() {
profiles.get_profile_run_custom_build(&profiles.get_profile(
pkg.package_id(),
ws.is_member(pkg),
*unit_for,
CompileMode::Build,
profile_kind.clone(),
))
bcx.profiles
.get_profile_run_custom_build(&bcx.profiles.get_profile(
pkg.package_id(),
ws.is_member(pkg),
*unit_for,
CompileMode::Build,
))
} else {
profiles.get_profile(
bcx.profiles.get_profile(
pkg.package_id(),
ws.is_member(pkg),
*unit_for,
*mode,
profile_kind.clone(),
)
};
let features = resolve.features_sorted(pkg.package_id());

View File

@ -300,10 +300,12 @@ pub fn compile_ws<'a>(
}
}
let profiles = ws.profiles();
// Early check for whether the profile is defined.
let _ = profiles.base_profile(&build_config.profile_kind)?;
let profiles = Profiles::new(
ws.profiles(),
config,
build_config.requested_profile,
ws.features(),
)?;
let specs = spec.to_package_id_specs(ws)?;
let dev_deps = ws.require_optional_deps() || filter.need_dev_deps(build_config.mode);
@ -381,6 +383,7 @@ pub fn compile_ws<'a>(
}
profiles.validate_packages(
ws.profiles(),
&mut config.shell(),
workspace_resolve.as_ref().unwrap_or(&resolve),
)?;
@ -397,7 +400,6 @@ pub fn compile_ws<'a>(
)?;
let units = generate_targets(
ws,
profiles,
&to_builds,
filter,
build_config.requested_kind,
@ -652,7 +654,6 @@ struct Proposal<'a> {
/// compile. Dependencies for these targets are computed later in `unit_dependencies`.
fn generate_targets<'a>(
ws: &Workspace<'_>,
profiles: &Profiles,
packages: &[&'a Package],
filter: &CompileFilter,
default_arch_kind: CompileKind,
@ -716,13 +717,9 @@ fn generate_targets<'a>(
_ => target_mode,
};
let kind = default_arch_kind.for_target(target);
let profile = profiles.get_profile(
pkg.package_id(),
ws.is_member(pkg),
unit_for,
target_mode,
bcx.build_config.profile_kind.clone(),
);
let profile =
bcx.profiles
.get_profile(pkg.package_id(), ws.is_member(pkg), unit_for, target_mode);
let features = resolve.features_sorted(pkg.package_id());
bcx.units.intern(
pkg,

View File

@ -427,7 +427,7 @@ impl CrateListingV2 {
info.features = feature_set(&opts.features);
info.all_features = opts.all_features;
info.no_default_features = opts.no_default_features;
info.profile = opts.build_config.profile_name().to_string();
info.profile = opts.build_config.requested_profile.to_string();
info.target = Some(target.to_string());
info.rustc = Some(rustc.to_string());
} else {
@ -439,7 +439,7 @@ impl CrateListingV2 {
features: feature_set(&opts.features),
all_features: opts.all_features,
no_default_features: opts.no_default_features,
profile: opts.build_config.profile_name().to_string(),
profile: opts.build_config.requested_profile.to_string(),
target: Some(target.to_string()),
rustc: Some(rustc.to_string()),
other: BTreeMap::new(),
@ -499,7 +499,7 @@ impl InstallInfo {
self.features == feature_set(&opts.features)
&& self.all_features == opts.all_features
&& self.no_default_features == opts.no_default_features
&& self.profile == opts.build_config.profile_name()
&& self.profile.as_str() == opts.build_config.requested_profile.as_str()
&& (self.target.is_none() || self.target.as_ref().map(|t| t.as_ref()) == Some(target))
&& &self.bins == exes
}

View File

@ -1,4 +1,5 @@
use crate::core::compiler::{BuildConfig, MessageFormat};
use crate::core::InternedString;
use crate::core::Workspace;
use crate::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionControl};
use crate::sources::CRATES_IO_REGISTRY;
@ -15,7 +16,7 @@ use std::ffi::{OsStr, OsString};
use std::fs;
use std::path::PathBuf;
pub use crate::core::compiler::{CompileMode, ProfileKind};
pub use crate::core::compiler::CompileMode;
pub use crate::{CliError, CliResult, Config};
pub use clap::{AppSettings, Arg, ArgMatches};
@ -307,19 +308,17 @@ pub trait ArgMatchesExt {
self._value_of("target").map(|s| s.to_string())
}
fn get_profile_kind(
fn get_profile_name(
&self,
config: &Config,
default: ProfileKind,
default: &str,
profile_checking: ProfileChecking,
) -> CargoResult<ProfileKind> {
) -> CargoResult<InternedString> {
let specified_profile = match self._value_of("profile") {
None => None,
Some("dev") => Some(ProfileKind::Dev),
Some("release") => Some(ProfileKind::Release),
Some(name) => {
TomlProfile::validate_name(name, "profile name")?;
Some(ProfileKind::Custom(name.to_string()))
Some(InternedString::new(name))
}
};
@ -334,24 +333,28 @@ pub trait ArgMatchesExt {
if self._is_present("release") {
if !config.cli_unstable().unstable_options {
Ok(ProfileKind::Release)
Ok(InternedString::new("release"))
} else {
match specified_profile {
None | Some(ProfileKind::Release) => Ok(ProfileKind::Release),
_ => anyhow::bail!("Conflicting usage of --profile and --release"),
Some(name) if name != "release" => {
anyhow::bail!("Conflicting usage of --profile and --release")
}
_ => Ok(InternedString::new("release")),
}
}
} else if self._is_present("debug") {
if !config.cli_unstable().unstable_options {
Ok(ProfileKind::Dev)
Ok(InternedString::new("dev"))
} else {
match specified_profile {
None | Some(ProfileKind::Dev) => Ok(ProfileKind::Dev),
_ => anyhow::bail!("Conflicting usage of --profile and --debug"),
Some(name) if name != "dev" => {
anyhow::bail!("Conflicting usage of --profile and --debug")
}
_ => Ok(InternedString::new("dev")),
}
}
} else {
Ok(specified_profile.unwrap_or(default))
Ok(specified_profile.unwrap_or_else(|| InternedString::new(default)))
}
}
@ -433,8 +436,7 @@ pub trait ArgMatchesExt {
let mut build_config = BuildConfig::new(config, self.jobs()?, &self.target(), mode)?;
build_config.message_format = message_format.unwrap_or(MessageFormat::Human);
build_config.profile_kind =
self.get_profile_kind(config, ProfileKind::Dev, profile_checking)?;
build_config.requested_profile = self.get_profile_name(config, "dev", profile_checking)?;
build_config.build_plan = self._is_present("build-plan");
if build_config.build_plan {
config

View File

@ -425,13 +425,11 @@ impl<'config> ValueDeserializer<'config> {
cv.definition().clone()
}
}
(true, None) => env_def,
(false, Some(cv)) => cv.definition().clone(),
(false, None) => {
return Err(
anyhow::format_err!("failed to find definition of `{}`", de.key).into(),
)
}
// Assume it is an environment, even if the key is not set.
// This can happen for intermediate tables, like
// CARGO_FOO_BAR_* where `CARGO_FOO_BAR` is not set.
(_, None) => env_def,
}
};
Ok(ValueDeserializer {

View File

@ -70,7 +70,6 @@ use serde::Deserialize;
use url::Url;
use self::ConfigValue as CV;
use crate::core::profiles::ConfigProfiles;
use crate::core::shell::Verbosity;
use crate::core::{nightly_features_allowed, CliUnstable, Shell, SourceId, Workspace};
use crate::ops;
@ -163,8 +162,6 @@ pub struct Config {
target_dir: Option<Filesystem>,
/// Environment variables, separated to assist testing.
env: HashMap<String, String>,
/// Profiles loaded from config.
profiles: LazyCell<ConfigProfiles>,
/// Tracks which sources have been updated to avoid multiple updates.
updated_sources: LazyCell<RefCell<HashSet<SourceId>>>,
/// Lock, if held, of the global package cache along with the number of
@ -238,7 +235,6 @@ impl Config {
creation_time: Instant::now(),
target_dir: None,
env,
profiles: LazyCell::new(),
updated_sources: LazyCell::new(),
package_cache_lock: RefCell::new(None),
http_config: LazyCell::new(),
@ -372,26 +368,6 @@ impl Config {
.map(AsRef::as_ref)
}
/// Gets profiles defined in config files.
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())
}
})
}
/// Which package sources have been updated, used to ensure it is only done once.
pub fn updated_sources(&self) -> RefMut<'_, HashSet<SourceId>> {
self.updated_sources

View File

@ -16,7 +16,6 @@ use url::Url;
use crate::core::dependency::DepKind;
use crate::core::manifest::{LibKind, ManifestMetadata, TargetSourcePath, Warnings};
use crate::core::profiles::Profiles;
use crate::core::{Dependency, InternedString, Manifest, PackageId, Summary, Target};
use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest};
use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig};
@ -270,23 +269,19 @@ pub struct TomlManifest {
}
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct TomlProfiles(BTreeMap<String, TomlProfile>);
pub struct TomlProfiles(BTreeMap<InternedString, TomlProfile>);
impl TomlProfiles {
pub fn get_all(&self) -> &BTreeMap<String, TomlProfile> {
pub fn get_all(&self) -> &BTreeMap<InternedString, TomlProfile> {
&self.0
}
pub fn get(&self, name: &'static str) -> Option<&TomlProfile> {
self.0.get(&String::from(name))
pub fn get(&self, name: &str) -> Option<&TomlProfile> {
self.0.get(name)
}
pub fn validate(&self, features: &Features, warnings: &mut Vec<String>) -> CargoResult<()> {
for (name, profile) in &self.0 {
if name == "debug" {
warnings.push("use `[profile.dev]` to configure debug builds".to_string());
}
profile.validate(name, features, warnings)?;
}
Ok(())
@ -408,13 +403,10 @@ pub struct TomlProfile {
pub panic: Option<String>,
pub overflow_checks: Option<bool>,
pub incremental: Option<bool>,
// `overrides` has been renamed to `package`, this should be removed when
// stabilized.
pub overrides: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
pub package: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
pub build_override: Option<Box<TomlProfile>>,
pub dir_name: Option<String>,
pub inherits: Option<String>,
pub dir_name: Option<InternedString>,
pub inherits: Option<InternedString>,
}
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
@ -458,21 +450,14 @@ impl TomlProfile {
features: &Features,
warnings: &mut Vec<String>,
) -> CargoResult<()> {
if name == "debug" {
warnings.push("use `[profile.dev]` to configure debug builds".to_string());
}
if let Some(ref profile) = self.build_override {
features.require(Feature::profile_overrides())?;
profile.validate_override("build-override")?;
}
if let Some(ref override_map) = self.overrides {
warnings.push(
"profile key `overrides` has been renamed to `package`, \
please update the manifest to the new key name"
.to_string(),
);
features.require(Feature::profile_overrides())?;
for profile in override_map.values() {
profile.validate_override("package")?;
}
}
if let Some(ref packages) = self.package {
features.require(Feature::profile_overrides())?;
for profile in packages.values() {
@ -570,7 +555,7 @@ impl TomlProfile {
}
fn validate_override(&self, which: &str) -> CargoResult<()> {
if self.overrides.is_some() || self.package.is_some() {
if self.package.is_some() {
bail!("package-specific profiles cannot be nested");
}
if self.build_override.is_some() {
@ -588,6 +573,7 @@ impl TomlProfile {
Ok(())
}
/// Overwrite self's values with the given profile.
pub fn merge(&mut self, profile: &TomlProfile) {
if let Some(v) = &profile.opt_level {
self.opt_level = Some(v.clone());
@ -625,16 +611,27 @@ impl TomlProfile {
self.incremental = Some(v);
}
if let Some(v) = &profile.overrides {
self.overrides = Some(v.clone());
if let Some(other_package) = &profile.package {
match &mut self.package {
Some(self_package) => {
for (spec, other_pkg_profile) in other_package {
match self_package.get_mut(spec) {
Some(p) => p.merge(other_pkg_profile),
None => {
self_package.insert(spec.clone(), other_pkg_profile.clone());
}
}
}
}
None => self.package = Some(other_package.clone()),
}
}
if let Some(v) = &profile.package {
self.package = Some(v.clone());
}
if let Some(v) = &profile.build_override {
self.build_override = Some(v.clone());
if let Some(other_bo) = &profile.build_override {
match &mut self.build_override {
Some(self_bo) => self_bo.merge(other_bo),
None => self.build_override = Some(other_bo.clone()),
}
}
if let Some(v) = &profile.inherits {
@ -1173,7 +1170,10 @@ impl TomlManifest {
`[workspace]`, only one can be specified"
),
};
let profiles = Profiles::new(me.profile.as_ref(), config, &features, &mut warnings)?;
let profiles = me.profile.clone();
if let Some(profiles) = &profiles {
profiles.validate(&features, &mut warnings)?;
}
let publish = match project.publish {
Some(VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
Some(VecStringOrBool::Bool(false)) => Some(vec![]),
@ -1321,7 +1321,10 @@ impl TomlManifest {
};
(me.replace(&mut cx)?, me.patch(&mut cx)?)
};
let profiles = Profiles::new(me.profile.as_ref(), config, &features, &mut warnings)?;
let profiles = me.profile.clone();
if let Some(profiles) = &profiles {
profiles.validate(&features, &mut warnings)?;
}
let workspace_config = match me.workspace {
Some(ref config) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
root,

View File

@ -1,5 +1,11 @@
//! Tests for config settings.
use cargo::core::{enable_nightly_features, InternedString, Shell};
use cargo::util::config::{self, Config, SslVersionConfig, StringList};
use cargo::util::toml::{self, VecStringOrBool as VSOB};
use cargo::CargoResult;
use cargo_test_support::{normalized_lines_match, paths, project, t};
use serde::Deserialize;
use std::borrow::Borrow;
use std::collections::{BTreeMap, HashMap};
use std::fs;
@ -7,13 +13,6 @@ use std::io;
use std::os;
use std::path::{Path, PathBuf};
use cargo::core::{enable_nightly_features, Shell};
use cargo::util::config::{self, Config, SslVersionConfig, StringList};
use cargo::util::toml::{self, VecStringOrBool as VSOB};
use cargo::CargoResult;
use cargo_test_support::{normalized_lines_match, paths, project, t};
use serde::Deserialize;
/// Helper for constructing a `Config` object.
pub struct ConfigBuilder {
env: HashMap<String, String>,
@ -424,8 +423,8 @@ lto = false
p,
toml::TomlProfile {
lto: Some(toml::StringOrBool::Bool(false)),
dir_name: Some("without-lto".to_string()),
inherits: Some("dev".to_string()),
dir_name: Some(InternedString::new("without-lto")),
inherits: Some(InternedString::new("dev")),
..Default::default()
}
);

View File

@ -1,5 +1,6 @@
//! Tests for profiles defined in config files.
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::{basic_lib_manifest, paths, project};
#[cargo_test]
@ -19,13 +20,44 @@ fn profile_config_gated() {
p.cargo("build -v")
.with_stderr_contains(
"\
[WARNING] profiles in config files require `-Z config-profile` command-line option
[WARNING] config profiles require the `-Z config-profile` command-line option \
(found profile `dev` in [..]/foo/.cargo/config)
",
)
.with_stderr_contains("[..]-C debuginfo=2[..]")
.run();
}
#[cargo_test]
fn named_profile_gated() {
// Named profile in config requires enabling in Cargo.toml.
let p = project()
.file("src/lib.rs", "")
.file(
".cargo/config",
r#"
[profile.foo]
inherits = 'dev'
opt-level = 1
"#,
)
.build();
p.cargo("build --profile foo -Zunstable-options -Zconfig-profile")
.masquerade_as_nightly_cargo()
.with_stderr(
"\
[ERROR] config profile `foo` is not valid (defined in `[..]/foo/.cargo/config`)
Caused by:
feature `named-profiles` is required
consider adding `cargo-features = [\"named-profiles\"]` to the manifest
",
)
.with_status(101)
.run();
}
#[cargo_test]
fn profile_config_validate_warnings() {
let p = project()
@ -56,8 +88,6 @@ fn profile_config_validate_warnings() {
.masquerade_as_nightly_cargo()
.with_stderr_unordered(
"\
[WARNING] unused config key `profile.asdf` in `[..].cargo/config`
[WARNING] unused config key `profile.test` in `[..].cargo/config`
[WARNING] unused config key `profile.dev.bad-key` in `[..].cargo/config`
[WARNING] unused config key `profile.dev.package.bar.bad-key-bar` in `[..].cargo/config`
[WARNING] unused config key `profile.dev.build-override.bad-key-bo` in `[..].cargo/config`
@ -70,6 +100,7 @@ fn profile_config_validate_warnings() {
#[cargo_test]
fn profile_config_error_paths() {
// Errors in config show where the error is located.
let p = project()
.file("Cargo.toml", &basic_lib_manifest("foo"))
.file("src/lib.rs", "")
@ -94,10 +125,10 @@ fn profile_config_error_paths() {
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
error in [..].cargo/config: `profile.dev.rpath` expected true/false, but found a string
[ERROR] error in [..]/foo/.cargo/config: \
could not load config key `profile.dev`: \
error in [..]/home/.cargo/config: \
`profile.dev.rpath` expected true/false, but found a string
",
)
.run();
@ -122,7 +153,7 @@ fn profile_config_validate_errors() {
.with_status(101)
.with_stderr(
"\
[ERROR] config profile `profile.dev` is not valid
[ERROR] config profile `dev` is not valid (defined in `[..]/foo/.cargo/config`)
Caused by:
`panic` may not be specified in a `package` profile
@ -150,10 +181,10 @@ fn profile_config_syntax_errors() {
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at [..]
Caused by:
error in [..].cargo/config: `profile.dev.codegen-units` expected an integer, but found a string
[ERROR] error in [..]/foo/.cargo/config: \
could not load config key `profile.dev`: \
error in [..]/foo/.cargo/config: \
`profile.dev.codegen-units` expected an integer, but found a string
",
)
.run();
@ -337,3 +368,123 @@ fn profile_config_mixed_types() {
.with_stderr_contains("[..]-C opt-level=3 [..]")
.run();
}
#[cargo_test]
fn named_config_profile() {
// Exercises config named profies.
// foo -> middle -> bar -> dev
// middle exists in Cargo.toml, the others in .cargo/config
use super::config::ConfigBuilder;
use cargo::core::compiler::CompileMode;
use cargo::core::enable_nightly_features;
use cargo::core::features::Features;
use cargo::core::profiles::{Profiles, UnitFor};
use cargo::core::{InternedString, PackageId};
use cargo::util::toml::TomlProfiles;
use std::fs;
enable_nightly_features();
paths::root().join(".cargo").mkdir_p();
fs::write(
paths::root().join(".cargo/config"),
r#"
[profile.foo]
inherits = "middle"
codegen-units = 2
[profile.foo.build-override]
codegen-units = 6
[profile.foo.package.dep]
codegen-units = 7
[profile.middle]
inherits = "bar"
codegen-units = 3
[profile.bar]
inherits = "dev"
codegen-units = 4
debug = 1
"#,
)
.unwrap();
let config = ConfigBuilder::new().unstable_flag("config-profile").build();
let mut warnings = Vec::new();
let features = Features::new(&["named-profiles".to_string()], &mut warnings).unwrap();
assert_eq!(warnings.len(), 0);
let profile_name = InternedString::new("foo");
let toml = r#"
[profile.middle]
inherits = "bar"
codegen-units = 1
opt-level = 1
[profile.middle.package.dep]
overflow-checks = false
[profile.foo.build-override]
codegen-units = 5
debug-assertions = false
[profile.foo.package.dep]
codegen-units = 8
"#;
#[derive(serde::Deserialize)]
struct TomlManifest {
profile: Option<TomlProfiles>,
}
let manifest: TomlManifest = toml::from_str(toml).unwrap();
let profiles =
Profiles::new(manifest.profile.as_ref(), &config, profile_name, &features).unwrap();
let crates_io = cargo::core::source::SourceId::crates_io(&config).unwrap();
let a_pkg = PackageId::new("a", "0.1.0", crates_io).unwrap();
let dep_pkg = PackageId::new("dep", "0.1.0", crates_io).unwrap();
// normal package
let p = profiles.get_profile(a_pkg, true, UnitFor::new_normal(), CompileMode::Build);
assert_eq!(p.name, "foo");
assert_eq!(p.codegen_units, Some(2)); // "foo" from config
assert_eq!(p.opt_level, "1"); // "middle" from manifest
assert_eq!(p.debuginfo, Some(1)); // "bar" from config
assert_eq!(p.debug_assertions, true); // "dev" built-in (ignore build-override)
assert_eq!(p.overflow_checks, true); // "dev" built-in (ignore package override)
// build-override
let bo = profiles.get_profile(a_pkg, true, UnitFor::new_build(), CompileMode::Build);
assert_eq!(bo.name, "foo");
assert_eq!(bo.codegen_units, Some(6)); // "foo" build override from config
assert_eq!(bo.opt_level, "1"); // SAME as normal
assert_eq!(bo.debuginfo, Some(1)); // SAME as normal
assert_eq!(bo.debug_assertions, false); // "foo" build override from manifest
assert_eq!(bo.overflow_checks, true); // SAME as normal
// package overrides
let po = profiles.get_profile(dep_pkg, false, UnitFor::new_normal(), CompileMode::Build);
assert_eq!(po.name, "foo");
assert_eq!(po.codegen_units, Some(7)); // "foo" package override from config
assert_eq!(po.opt_level, "1"); // SAME as normal
assert_eq!(po.debuginfo, Some(1)); // SAME as normal
assert_eq!(po.debug_assertions, true); // SAME as normal
assert_eq!(po.overflow_checks, false); // "middle" package override from manifest
}
#[cargo_test]
fn named_env_profile() {
// Environment variables used to define a named profile.
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["named-profiles"]
[package]
name = "foo"
version = "0.1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("build -v -Zconfig-profile -Zunstable-options --profile=other")
.masquerade_as_nightly_cargo()
.env("CARGO_PROFILE_OTHER_CODEGEN_UNITS", "1")
.env("CARGO_PROFILE_OTHER_INHERITS", "dev")
.with_stderr_contains("[..]-C codegen-units=1 [..]")
.run();
}

View File

@ -27,10 +27,7 @@ fn inherits_on_release() {
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at [..]
Caused by:
An 'inherits' must not specified root profile 'release'
[ERROR] `inherits` must not be specified in root profile `release`
",
)
.run();
@ -61,10 +58,9 @@ fn missing_inherits() {
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at [..]
Caused by:
An 'inherits' directive is needed for all profiles that are not 'dev' or 'release'. Here it is missing from 'release-lto'",
[ERROR] profile `release-lto` is missing an `inherits` directive \
(`inherits` is required for all profiles except `dev` or `release`)
",
)
.run();
}
@ -198,10 +194,8 @@ fn non_existent_inherits() {
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at [..]
Caused by:
Profile 'non-existent' not found in Cargo.toml",
[ERROR] profile `release-lto` inherits from `non-existent`, but that profile is not defined
",
)
.run();
}
@ -232,10 +226,8 @@ fn self_inherits() {
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at [..]
Caused by:
Inheritance loop of profiles cycles with profile 'release-lto'",
[ERROR] profile inheritance loop detected with profile `release-lto` inheriting `release-lto`
",
)
.run();
}
@ -270,10 +262,8 @@ fn inherits_loop() {
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at [..]
Caused by:
Inheritance loop of profiles cycles with profile 'release-lto'",
[ERROR] profile inheritance loop detected with profile `release-lto2` inheriting `release-lto`
",
)
.run();
}
@ -477,3 +467,32 @@ fn clean_custom_dirname() {
assert!(p.build_dir().join("debug").is_dir());
assert!(!p.build_dir().join("other").is_dir());
}
#[cargo_test]
fn unknown_profile() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["named-profiles"]
[package]
name = "foo"
version = "0.0.1"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("build --profile alpha -Zunstable-options")
.masquerade_as_nightly_cargo()
.with_stderr("[ERROR] profile `alpha` is not defined")
.with_status(101)
.run();
// Clean has a separate code path, need to check it too.
p.cargo("clean --profile alpha -Zunstable-options")
.masquerade_as_nightly_cargo()
.with_stderr("[ERROR] profile `alpha` is not defined")
.with_status(101)
.run();
}

View File

@ -68,13 +68,16 @@ fn profile_override_warnings() {
.file("bar/src/lib.rs", "")
.build();
p.cargo("build").with_stderr_contains(
p.cargo("build")
.with_stderr_contains(
"\
[WARNING] version or URL in package profile spec `bar:1.2.3` does not match any of the packages: bar v0.5.0 ([..])
[WARNING] package profile spec `bart` did not match any packages
[WARNING] profile package spec `bar:1.2.3` in profile `dev` \
has a version or URL that does not match any of the packages: \
bar v0.5.0 ([..]/foo/bar)
[WARNING] profile package spec `bart` in profile `dev` did not match any packages
<tab>Did you mean `bar`?
[WARNING] package profile spec `no-suggestion` did not match any packages
[WARNING] profile package spec `no-suggestion` in profile `dev` did not match any packages
[COMPILING] [..]
",
)
@ -96,10 +99,7 @@ fn profile_override_bad_settings() {
"rpath = true",
"`rpath` may not be specified in a `package` profile",
),
(
"overrides = {}",
"package-specific profiles cannot be nested",
),
("package = {}", "package-specific profiles cannot be nested"),
];
for &(snippet, expected) in bad_values.iter() {
let p = project()
@ -394,42 +394,6 @@ fn override_proc_macro() {
.run();
}
#[cargo_test]
fn override_package_rename() {
// backwards-compatibility test
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = {path = "bar"}
[profile.dev]
opt-level = 1
[profile.dev.overrides.bar]
opt-level = 3
"#,
)
.file("src/lib.rs", "")
.file("bar/Cargo.toml", &basic_lib_manifest("bar"))
.file("bar/src/lib.rs", "")
.build();
p.cargo("check")
.with_stderr("\
[WARNING] profile key `overrides` has been renamed to `package`, please update the manifest to the new key name
[CHECKING] bar [..]
[CHECKING] foo [..]
[FINISHED] [..]
")
.run();
}
#[cargo_test]
fn no_warning_ws() {
// https://github.com/rust-lang/cargo/issues/7378, avoid warnings in a workspace.