Skip to content

Commit

Permalink
Add build profile.
Browse files Browse the repository at this point in the history
  • Loading branch information
ehuss committed Apr 2, 2019
1 parent c866f48 commit d6b26a2
Show file tree
Hide file tree
Showing 6 changed files with 635 additions and 39 deletions.
3 changes: 3 additions & 0 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ features! {
// Overriding profiles for dependencies.
[unstable] profile_overrides: bool,

// Build profile.
[unstable] build_profile: bool,

// Separating the namespaces for features and dependencies
[unstable] namespaced_features: bool,

Expand Down
132 changes: 96 additions & 36 deletions src/cargo/core/profiles.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::collections::HashSet;
use std::path::Path;
use std::{cmp, env, fmt, hash};

use serde::Deserialize;

use crate::core::compiler::CompileMode;
use crate::core::interning::InternedString;
use crate::core::{Features, PackageId, PackageIdSpec, PackageSet, Shell};
use crate::core::{Feature, Features, PackageId, PackageIdSpec, PackageSet, Shell};
use crate::util::errors::CargoResultExt;
use crate::util::lev_distance::lev_distance;
use crate::util::toml::{ProfilePackageSpec, StringOrBool, TomlProfile, TomlProfiles, U32OrBool};
Expand All @@ -19,10 +20,13 @@ pub struct Profiles {
test: ProfileMaker,
bench: ProfileMaker,
doc: ProfileMaker,
build: ProfileMaker,
/// Incremental compilation can be overridden globally via:
/// - `CARGO_INCREMENTAL` environment variable.
/// - `build.incremental` config value.
incremental: Option<bool>,
// Temporary flag to detect if unstable feature is enabled.
build_enabled: bool,
}

impl Profiles {
Expand All @@ -31,9 +35,13 @@ impl Profiles {
config: &Config,
features: &Features,
warnings: &mut Vec<String>,
manifest_path: &Path,
) -> CargoResult<Profiles> {
if let Some(profiles) = profiles {
profiles.validate(features, warnings)?;
if profiles.build.is_some() {
features.require(Feature::build_profile())?;
}
}

let config_profiles = config.profiles()?;
Expand All @@ -43,6 +51,18 @@ impl Profiles {
None => config.get::<Option<bool>>("build.incremental")?,
};

let config_build_profile =
if !features.is_enabled(Feature::build_profile()) && config_profiles.build.is_some() {
warnings.push(format!(
"profile `build` in config file will be ignored for \
manifest `{}` because \"build-profile\" feature is not enabled",
manifest_path.display()
));
None
} else {
config_profiles.build.clone()
};

Ok(Profiles {
dev: ProfileMaker {
default: Profile::default_dev(),
Expand All @@ -69,7 +89,13 @@ impl Profiles {
toml: profiles.and_then(|p| p.doc.clone()),
config: None,
},
build: ProfileMaker {
default: Profile::default_build(),
toml: profiles.and_then(|p| p.build.clone()),
config: config_build_profile,
},
incremental,
build_enabled: features.is_enabled(Feature::build_profile()),
})
}

Expand Down Expand Up @@ -100,10 +126,14 @@ impl Profiles {
// `build_unit_profiles` normally ensures that it selects the
// ancestor's profile. However, `cargo clean -p` can hit this
// path.
if release {
&self.release
let maker = if release { &self.release } else { &self.dev };
if (unit_for.build)
&& self.build_enabled
&& !maker.has_build_override()
{
&self.build
} else {
&self.dev
maker
}
}
CompileMode::Doc { .. } => &self.doc,
Expand Down Expand Up @@ -148,9 +178,11 @@ impl Profiles {
/// select for the package that was actually built.
pub fn base_profile(&self, release: bool) -> Profile {
if release {
self.release.get_profile(None, true, UnitFor::new_normal())
self.release
.get_profile(None, true, UnitFor::new_normal())
} else {
self.dev.get_profile(None, true, UnitFor::new_normal())
self.dev
.get_profile(None, true, UnitFor::new_normal())
}
}

Expand All @@ -165,6 +197,7 @@ impl Profiles {
self.test.validate_packages(shell, packages)?;
self.bench.validate_packages(shell, packages)?;
self.doc.validate_packages(shell, packages)?;
self.build.validate_packages(shell, packages)?;
Ok(())
}
}
Expand Down Expand Up @@ -198,10 +231,22 @@ impl ProfileMaker {
) -> Profile {
let mut profile = self.default;
if let Some(ref toml) = self.toml {
merge_toml(pkg_id, is_member, unit_for, &mut profile, toml);
merge_toml(
pkg_id,
is_member,
unit_for,
&mut profile,
toml,
);
}
if let Some(ref toml) = self.config {
merge_toml(pkg_id, is_member, unit_for, &mut profile, toml);
merge_toml(
pkg_id,
is_member,
unit_for,
&mut profile,
toml,
);
}
profile
}
Expand Down Expand Up @@ -318,6 +363,13 @@ impl ProfileMaker {
}
Ok(())
}

fn has_build_override(&self) -> bool {
self.toml
.as_ref()
.map(|tp| tp.build_override.is_some())
.unwrap_or(false)
}
}

fn merge_toml(
Expand Down Expand Up @@ -449,6 +501,7 @@ compact_debug! {
"test" => (Profile::default_test(), "default_test()"),
"bench" => (Profile::default_bench(), "default_bench()"),
"doc" => (Profile::default_doc(), "default_doc()"),
"build" => (Profile::default_build(), "default_build()"),
_ => (Profile::default(), "default()"),
};
[debug_the_fields(
Expand Down Expand Up @@ -529,6 +582,13 @@ impl Profile {
}
}

fn default_build() -> Profile {
Profile {
name: "build",
..Profile::default()
}
}

/// Compares all fields except `name`, which doesn't affect compilation.
/// This is necessary for `Unit` deduplication for things like "test" and
/// "dev" which are essentially the same.
Expand Down Expand Up @@ -604,31 +664,33 @@ pub struct UnitFor {
impl UnitFor {
/// A unit for a normal target/dependency (i.e., not custom build,
/// proc macro/plugin, or test/bench).
pub fn new_normal() -> UnitFor {
pub const fn new_normal() -> UnitFor {
UnitFor {
build: false,
panic_abort_ok: true,
}
}

/// A unit for a custom build script or its dependencies.
pub fn new_build() -> UnitFor {
pub const fn new_build() -> UnitFor {
UnitFor {
build: true,
panic_abort_ok: false,
}
}

/// A unit for a proc macro or compiler plugin or their dependencies.
pub fn new_compiler() -> UnitFor {
pub const fn new_compiler() -> UnitFor {
// Note: This is currently the same as `new_build`, but keeping it
// separate for now in case it is useful in the future.
UnitFor {
build: false,
build: true,
panic_abort_ok: false,
}
}

/// A unit for a test/bench target or their dependencies.
pub fn new_test() -> UnitFor {
pub const fn new_test() -> UnitFor {
UnitFor {
build: false,
panic_abort_ok: false,
Expand Down Expand Up @@ -660,18 +722,9 @@ impl UnitFor {
/// All possible values, used by `clean`.
pub fn all_values() -> &'static [UnitFor] {
static ALL: [UnitFor; 3] = [
UnitFor {
build: false,
panic_abort_ok: true,
},
UnitFor {
build: true,
panic_abort_ok: false,
},
UnitFor {
build: false,
panic_abort_ok: false,
},
UnitFor::new_normal(),
UnitFor::new_build(),
UnitFor::new_test(),
];
&ALL
}
Expand All @@ -682,22 +735,29 @@ impl UnitFor {
pub struct ConfigProfiles {
dev: Option<TomlProfile>,
release: Option<TomlProfile>,
build: 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(|| failure::format_err!("config profile `profile.dev` is not valid"))?;
}
if let Some(ref profile) = self.release {
profile
.validate("release", features, warnings)
.chain_err(|| {
failure::format_err!("config profile `profile.release` is not valid")
})?;
macro_rules! check_profile {
($name:ident) => {
if let Some(ref profile) = self.$name {
profile
.validate(stringify!($name), features, warnings)
.chain_err(|| {
failure::format_err!(
"config profile `profile.{}` is not valid",
stringify!($name)
)
})?;
}
};
}

check_profile!(dev);
check_profile!(release);
check_profile!(build);
Ok(())
}
}
27 changes: 24 additions & 3 deletions src/cargo/util/toml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ pub struct TomlProfiles {
pub bench: Option<TomlProfile>,
pub dev: Option<TomlProfile>,
pub release: Option<TomlProfile>,
pub build: Option<TomlProfile>,
}

impl TomlProfiles {
Expand All @@ -294,6 +295,9 @@ impl TomlProfiles {
if let Some(ref release) = self.release {
release.validate("release", features, warnings)?;
}
if let Some(ref build) = self.build {
build.validate("build", features, warnings)?;
}
Ok(())
}
}
Expand Down Expand Up @@ -470,7 +474,7 @@ impl TomlProfile {
}

match name {
"dev" | "release" => {}
"dev" | "release" | "build" => {}
_ => {
if self.overrides.is_some() || self.build_override.is_some() {
bail!(
Expand All @@ -491,6 +495,11 @@ impl TomlProfile {
warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
}
}
"build" => {
if self.build_override.is_some() {
failure::bail!("`build` profile cannot specify build overrides.")
}
}
_ => {}
}

Expand Down Expand Up @@ -1023,7 +1032,13 @@ impl TomlManifest {
`[workspace]`, only one can be specified"
),
};
let profiles = Profiles::new(me.profile.as_ref(), config, &features, &mut warnings)?;
let profiles = Profiles::new(
me.profile.as_ref(),
config,
&features,
&mut warnings,
&package_root.join("Cargo.toml"),
)?;
let publish = match project.publish {
Some(VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
Some(VecStringOrBool::Bool(false)) => Some(vec![]),
Expand Down Expand Up @@ -1154,7 +1169,13 @@ impl TomlManifest {
};
(me.replace(&mut cx)?, me.patch(&mut cx)?)
};
let profiles = Profiles::new(me.profile.as_ref(), config, &features, &mut warnings)?;
let profiles = Profiles::new(
me.profile.as_ref(),
config,
&features,
&mut warnings,
&root.join("Cargo.toml"),
)?;
let workspace_config = match me.workspace {
Some(ref config) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
root,
Expand Down
30 changes: 30 additions & 0 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,36 @@ cargo +nightly build -Z config-profile
```


### Build Profile
* Tracking Issue: [rust-lang/rust#48683](https://github.com/rust-lang/rust/issues/48683)

The `build` profile controls the build settings for build scripts,
proc-macros, compiler plugins, and all of their dependencies. It requires the
`build-profile` feature to be enabled.

```toml
cargo-features = ["build-profile"]

[profile.build]
# The following illustrates the defaults.
opt-level = 0
debug = false
rpath = false
lto = false
debug-assertions = false
codegen-units = 16
panic = 'unwind'
incremental = false
overflow-checks = false
```

It is compatible with [Profile Overrides](#profile-overrides) and [Config
Profiles](#config-profiles). If `build-override` is specified in a dev or
release profile, that takes precedence over the `build` profile. Enabling
`build-profile` will cause `build-override` to also affect proc-macros and
plugins (normally it only affects build scripts).


### Namespaced features
* Original issue: [#1286](https://github.com/rust-lang/cargo/issues/1286)
* Tracking Issue: [#5565](https://github.com/rust-lang/cargo/issues/5565)
Expand Down
Loading

0 comments on commit d6b26a2

Please sign in to comment.