From 575d6e819cfae17bea30987c9f1ec9509f8220ac Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 17 Apr 2018 18:46:44 -0700 Subject: [PATCH 01/23] Profile Overrides (RFC #2282 Part 1) --- .../compiler/context/compilation_files.rs | 20 +- src/cargo/core/compiler/context/mod.rs | 80 +- .../compiler/context/unit_dependencies.rs | 289 ++++--- src/cargo/core/compiler/custom_build.rs | 10 +- src/cargo/core/compiler/fingerprint.rs | 22 +- src/cargo/core/compiler/job_queue.rs | 41 +- src/cargo/core/compiler/mod.rs | 64 +- src/cargo/core/compiler/output_depinfo.rs | 6 +- src/cargo/core/features.rs | 3 + src/cargo/core/manifest.rs | 163 +--- src/cargo/core/mod.rs | 3 +- src/cargo/core/profiles.rs | 394 +++++++++ src/cargo/core/workspace.rs | 26 +- src/cargo/ops/cargo_clean.rs | 61 +- src/cargo/ops/cargo_compile.rs | 773 +++++++++--------- src/cargo/util/machine_message.rs | 16 +- src/cargo/util/toml/mod.rs | 207 +++-- src/doc/src/reference/unstable.md | 35 + tests/testsuite/check.rs | 6 +- tests/testsuite/doc.rs | 15 + tests/testsuite/profiles.rs | 113 +++ tests/testsuite/test.rs | 8 +- 22 files changed, 1400 insertions(+), 955 deletions(-) create mode 100644 src/cargo/core/profiles.rs diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index 88aa1cfc761..f8b0ca5c952 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -97,7 +97,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { /// Returns the appropriate output directory for the specified package and /// target. pub fn out_dir(&self, unit: &Unit<'a>) -> PathBuf { - if unit.profile.doc { + if unit.mode.is_doc() { self.layout(unit.kind).root().parent().unwrap().join("doc") } else if unit.target.is_custom_build() { self.build_script_dir(unit) @@ -148,7 +148,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { /// Returns the appropriate directory layout for either a plugin or not. pub fn build_script_dir(&self, unit: &Unit<'a>) -> PathBuf { assert!(unit.target.is_custom_build()); - assert!(!unit.profile.run_custom_build); + assert!(!unit.mode.is_run_custom_build()); let dir = self.pkg_dir(unit); self.layout(Kind::Host).build().join(dir) } @@ -156,7 +156,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { /// Returns the appropriate directory layout for either a plugin or not. pub fn build_script_out_dir(&self, unit: &Unit<'a>) -> PathBuf { assert!(unit.target.is_custom_build()); - assert!(unit.profile.run_custom_build); + assert!(unit.mode.is_run_custom_build()); let dir = self.pkg_dir(unit); self.layout(unit.kind).build().join(dir).join("out") } @@ -211,7 +211,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { } else { Some(( out_dir.parent().unwrap().to_owned(), - if unit.profile.test { + if unit.mode.is_any_test() { file_stem } else { bin_stem @@ -244,7 +244,10 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { let mut ret = Vec::new(); let mut unsupported = Vec::new(); { - if unit.profile.check { + if unit.mode.is_check() { + // This is not quite correct for non-lib targets. rustc + // currently does not emit rmeta files, so there is nothing to + // check for! See #3624. let path = out_dir.join(format!("lib{}.rmeta", file_stem)); let hardlink = link_stem .clone() @@ -296,7 +299,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { | TargetKind::Test => { add("bin", FileFlavor::Normal)?; } - TargetKind::Lib(..) | TargetKind::ExampleLib(..) if unit.profile.test => { + TargetKind::Lib(..) | TargetKind::ExampleLib(..) if unit.mode.is_any_test() => { add("bin", FileFlavor::Normal)?; } TargetKind::ExampleLib(ref kinds) | TargetKind::Lib(ref kinds) => { @@ -378,7 +381,7 @@ fn compute_metadata<'a, 'cfg>( // just here for rustbuild. We need a more principled method // doing this eventually. let __cargo_default_lib_metadata = env::var("__CARGO_DEFAULT_LIB_METADATA"); - if !(unit.profile.test || unit.profile.check) + if !(unit.mode.is_any_test() || unit.mode.is_check()) && (unit.target.is_dylib() || unit.target.is_cdylib() || (unit.target.is_bin() && cx.build_config.target_triple().starts_with("wasm32-"))) && unit.pkg.package_id().source_id().is_path() @@ -422,7 +425,8 @@ fn compute_metadata<'a, 'cfg>( // Throw in the profile we're compiling with. This helps caching // panic=abort and panic=unwind artifacts, additionally with various // settings like debuginfo and whatnot. - unit.profile.hash(&mut hasher); + cx.unit_profile(unit).hash(&mut hasher); + unit.mode.hash(&mut hasher); // Artifacts compiled for the host should have a different metadata // piece than those compiled for the target, so make sure we throw in diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index fdf47780b76..02380498908 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -1,5 +1,4 @@ #![allow(deprecated)] - use std::collections::{HashMap, HashSet}; use std::env; use std::path::{Path, PathBuf}; @@ -8,8 +7,10 @@ use std::sync::Arc; use jobserver::Client; -use core::{Package, PackageId, PackageSet, Profile, Resolve, Target}; -use core::{Dependency, Profiles, Workspace}; +use core::{Package, PackageId, PackageSet, Resolve, Target}; +use core::{Dependency, Workspace}; +use core::profiles::{Profile, ProfileFor, Profiles}; +use ops::CompileMode; use util::{internal, profile, Cfg, CfgExpr, Config}; use util::errors::{CargoResult, CargoResultExt}; @@ -45,7 +46,7 @@ pub use self::target_info::{FileFlavor, TargetInfo}; /// example, it needs to know the target architecture (OS, chip arch etc.) and it needs to know /// whether you want a debug or release build. There is enough information in this struct to figure /// all that out. -#[derive(Clone, Copy, Eq, PartialEq, Hash)] +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] pub struct Unit<'a> { /// Information about available targets, which files to include/exclude, etc. Basically stuff in /// `Cargo.toml`. @@ -54,9 +55,9 @@ pub struct Unit<'a> { /// to be confused with *target-triple* (or *target architecture* ...), the target arch for a /// build. pub target: &'a Target, - /// The profile contains information about *how* the build should be run, including debug - /// level, extra args to pass to rustc, etc. - pub profile: &'a Profile, + /// This indicates the purpose of the target for profile selection. See + /// `ProfileFor` for more details. + pub profile_for: ProfileFor, /// Whether this compilation unit is for the host or target architecture. /// /// For example, when @@ -64,6 +65,9 @@ pub struct Unit<'a> { /// the host architecture so the host rustc can use it (when compiling to the target /// architecture). pub kind: Kind, + /// The "mode" this unit is being compiled for. See `CompileMode` for + /// more details. + pub mode: CompileMode, } /// The build context, containing all information about a build task @@ -87,13 +91,19 @@ pub struct Context<'a, 'cfg: 'a> { pub links: Links<'a>, pub used_in_plugin: HashSet>, pub jobserver: Client, + pub profiles: &'a Profiles, + /// This is a workaround to carry the extra compiler args given on the + /// command-line for `cargo rustc` and `cargo rustdoc`. These commands + /// only support one target, but we don't want the args passed to any + /// dependencies. + pub extra_compiler_args: HashMap, Vec>, target_info: TargetInfo, host_info: TargetInfo, - profiles: &'a Profiles, incremental_env: Option, unit_dependencies: HashMap, Vec>>, + unit_profiles: HashMap, Profile>, files: Option>, } @@ -105,6 +115,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { config: &'cfg Config, build_config: BuildConfig, profiles: &'a Profiles, + extra_compiler_args: HashMap, Vec>, ) -> CargoResult> { let incremental_env = match env::var("CARGO_INCREMENTAL") { Ok(v) => Some(v == "1"), @@ -155,7 +166,9 @@ impl<'a, 'cfg> Context<'a, 'cfg> { build_script_overridden: HashSet::new(), unit_dependencies: HashMap::new(), + unit_profiles: HashMap::new(), files: None, + extra_compiler_args, }; cx.compilation.host_dylib_path = cx.host_info.sysroot_libdir.clone(); @@ -174,8 +187,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> { let mut queue = JobQueue::new(&self); self.prepare_units(export_dir, units)?; self.prepare()?; - self.build_used_in_plugin_map(&units)?; - custom_build::build_map(&mut self, &units)?; + self.build_used_in_plugin_map(units)?; + custom_build::build_map(&mut self, units)?; for unit in units.iter() { // Build up a list of pending jobs, each of which represent @@ -200,7 +213,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { None => &output.path, }; - if unit.profile.test { + if unit.mode.is_any_test() && !unit.mode.is_check() { self.compilation.tests.push(( unit.pkg.clone(), unit.target.kind().clone(), @@ -224,7 +237,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { continue; } - if dep.profile.run_custom_build { + if dep.mode.is_run_custom_build() { let out_dir = self.files().build_script_out_dir(dep).display().to_string(); self.compilation .extra_env @@ -236,7 +249,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { if !dep.target.is_lib() { continue; } - if dep.profile.doc { + if dep.mode.is_doc() { continue; } @@ -313,15 +326,16 @@ impl<'a, 'cfg> Context<'a, 'cfg> { None => None, }; - let deps = build_unit_dependencies(units, &self)?; + let deps = build_unit_dependencies(units, self)?; self.unit_dependencies = deps; + self.unit_profiles = self.profiles.build_unit_profiles(units, self); let files = CompilationFiles::new( units, host_layout, target_layout, export_dir, self.ws, - &self, + self, ); self.files = Some(files); Ok(()) @@ -414,7 +428,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { // dependencies. However, that code itself calls this method and // gets a full pre-filtered set of dependencies. This is not super // obvious, and clear, but it does work at the moment. - if unit.profile.run_custom_build { + if unit.target.is_custom_build() { let key = (unit.pkg.package_id().clone(), unit.kind); if self.build_script_overridden.contains(&key) { return Vec::new(); @@ -476,26 +490,11 @@ impl<'a, 'cfg> Context<'a, 'cfg> { self.build_config.jobs } - pub fn lib_profile(&self) -> &'a Profile { - let (normal, test) = if self.build_config.release { - (&self.profiles.release, &self.profiles.bench_deps) - } else { - (&self.profiles.dev, &self.profiles.test_deps) - }; - if self.build_config.test { - test - } else { - normal - } - } - - pub fn build_script_profile(&self, _pkg: &PackageId) -> &'a Profile { - // TODO: should build scripts always be built with the same library - // profile? How is this controlled at the CLI layer? - self.lib_profile() - } - - pub fn incremental_args(&self, unit: &Unit) -> CargoResult> { + pub fn incremental_args( + &self, + unit: &Unit, + profile_incremental: bool, + ) -> CargoResult> { // There's a number of ways to configure incremental compilation right // now. In order of descending priority (first is highest priority) we // have: @@ -518,7 +517,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { // have it enabled by default while release profiles have it disabled // by default. let global_cfg = self.config.get_bool("build.incremental")?.map(|c| c.val); - let incremental = match (self.incremental_env, global_cfg, unit.profile.incremental) { + let incremental = match (self.incremental_env, global_cfg, profile_incremental) { (Some(v), _, _) => v, (None, Some(false), _) => false, (None, _, other) => other, @@ -572,6 +571,13 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Kind::Target => &self.target_info, } } + + /// Returns the profile for a given unit. + /// This should not be called until profiles are computed in + /// `prepare_units`. + pub fn unit_profile(&self, unit: &Unit<'a>) -> &Profile { + &self.unit_profiles[unit] + } } /// Acquire extra flags to pass to the compiler from various locations. diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs index b65a55f15da..7460f13cace 100644 --- a/src/cargo/core/compiler/context/unit_dependencies.rs +++ b/src/cargo/core/compiler/context/unit_dependencies.rs @@ -16,11 +16,12 @@ //! graph of `Unit`s, which capture these properties. use super::{Context, Kind, Unit}; -use std::collections::HashMap; -use CargoResult; use core::dependency::Kind as DepKind; +use core::profiles::ProfileFor; use core::Target; -use core::Profile; +use ops::CompileMode; +use std::collections::HashMap; +use CargoResult; pub fn build_unit_dependencies<'a, 'cfg>( roots: &[Unit<'a>], @@ -28,7 +29,7 @@ pub fn build_unit_dependencies<'a, 'cfg>( ) -> CargoResult, Vec>>> { let mut deps = HashMap::new(); for unit in roots.iter() { - deps_of(unit, cx, &mut deps)?; + deps_of(unit, cx, &mut deps, unit.profile_for)?; } Ok(deps) @@ -38,12 +39,14 @@ fn deps_of<'a, 'b, 'cfg>( unit: &Unit<'a>, cx: &Context<'a, 'cfg>, deps: &'b mut HashMap, Vec>>, + profile_for: ProfileFor, ) -> CargoResult<&'b [Unit<'a>]> { if !deps.contains_key(unit) { - let unit_deps = compute_deps(unit, cx, deps)?; - deps.insert(*unit, unit_deps.clone()); - for unit in unit_deps { - deps_of(&unit, cx, deps)?; + let unit_deps = compute_deps(unit, cx, deps, profile_for)?; + let to_insert: Vec<_> = unit_deps.iter().map(|&(unit, _)| unit).collect(); + deps.insert(*unit, to_insert); + for (unit, profile_for) in unit_deps { + deps_of(&unit, cx, deps, profile_for)?; } } Ok(deps[unit].as_ref()) @@ -51,68 +54,72 @@ fn deps_of<'a, 'b, 'cfg>( /// For a package, return all targets which are registered as dependencies /// for that package. +/// This returns a vec of `(Unit, ProfileFor)` pairs. The `ProfileFor` +/// is the profile type that should be used for dependencies of the unit. fn compute_deps<'a, 'b, 'cfg>( unit: &Unit<'a>, cx: &Context<'a, 'cfg>, deps: &'b mut HashMap, Vec>>, -) -> CargoResult>> { - if unit.profile.run_custom_build { + profile_for: ProfileFor, +) -> CargoResult, ProfileFor)>> { + if unit.mode.is_run_custom_build() { return compute_deps_custom_build(unit, cx, deps); - } else if unit.profile.doc && !unit.profile.test { + } else if unit.mode.is_doc() && !unit.mode.is_any_test() { + // Note: This does not include Doctest. return compute_deps_doc(unit, cx); } let id = unit.pkg.package_id(); let deps = cx.resolve.deps(id); - let mut ret = deps - .filter(|&(_id, deps)| { - assert!(deps.len() > 0); - deps.iter().any(|dep| { - // If this target is a build command, then we only want build - // dependencies, otherwise we want everything *other than* build - // dependencies. - if unit.target.is_custom_build() != dep.is_build() { - return false; - } + let mut ret = deps.filter(|&(_id, deps)| { + assert!(deps.len() > 0); + deps.iter().any(|dep| { + // If this target is a build command, then we only want build + // dependencies, otherwise we want everything *other than* build + // dependencies. + if unit.target.is_custom_build() != dep.is_build() { + return false; + } - // If this dependency is *not* a transitive dependency, then it - // only applies to test/example targets - if !dep.is_transitive() && !unit.target.is_test() && !unit.target.is_example() - && !unit.profile.test - { - return false; - } + // If this dependency is *not* a transitive dependency, then it + // only applies to test/example targets + if !dep.is_transitive() && !unit.target.is_test() && !unit.target.is_example() + && !unit.mode.is_any_test() + { + return false; + } - // If this dependency is only available for certain platforms, - // make sure we're only enabling it for that platform. - if !cx.dep_platform_activated(dep, unit.kind) { - return false; - } + // If this dependency is only available for certain platforms, + // make sure we're only enabling it for that platform. + if !cx.dep_platform_activated(dep, unit.kind) { + return false; + } - // If the dependency is optional, then we're only activating it - // if the corresponding feature was activated - if dep.is_optional() && !cx.resolve.features(id).contains(&*dep.name()) { - return false; - } + // If the dependency is optional, then we're only activating it + // if the corresponding feature was activated + if dep.is_optional() && !cx.resolve.features(id).contains(&*dep.name()) { + return false; + } - // If we've gotten past all that, then this dependency is - // actually used! - true - }) + // If we've gotten past all that, then this dependency is + // actually used! + true }) - .filter_map(|(id, _)| { - match cx.get_package(id) { - Ok(pkg) => pkg.targets().iter().find(|t| t.is_lib()).map(|t| { - let unit = Unit { + }).filter_map(|(id, _)| match cx.get_package(id) { + Ok(pkg) => pkg.targets().iter().find(|t| t.is_lib()).map(|t| { + let mode = check_or_build_mode(&unit.mode, t); + Ok(( + Unit { pkg, target: t, - profile: lib_or_check_profile(unit, t, cx), + profile_for, kind: unit.kind.for_target(t), - }; - Ok(unit) - }), - Err(e) => Some(Err(e)), - } + mode, + }, + profile_for, + )) + }), + Err(e) => Some(Err(e)), }) .collect::>>()?; @@ -122,40 +129,17 @@ fn compute_deps<'a, 'b, 'cfg>( if unit.target.is_custom_build() { return Ok(ret); } - ret.extend(dep_build_script(unit, cx)); + ret.extend(dep_build_script(unit)); // If this target is a binary, test, example, etc, then it depends on // the library of the same package. The call to `resolve.deps` above // didn't include `pkg` in the return values, so we need to special case // it here and see if we need to push `(pkg, pkg_lib_target)`. - if unit.target.is_lib() && !unit.profile.doc { + if unit.target.is_lib() && unit.mode != CompileMode::Doctest { return Ok(ret); } - ret.extend(maybe_lib(unit, cx)); - - // Integration tests/benchmarks require binaries to be built - if unit.profile.test && (unit.target.is_test() || unit.target.is_bench()) { - ret.extend( - unit.pkg - .targets() - .iter() - .filter(|t| { - let no_required_features = Vec::new(); + ret.extend(maybe_lib(unit, profile_for)); - t.is_bin() && - // Skip binaries with required features that have not been selected. - t.required_features().unwrap_or(&no_required_features).iter().all(|f| { - cx.resolve.features(id).contains(f) - }) - }) - .map(|t| Unit { - pkg: unit.pkg, - target: t, - profile: lib_or_check_profile(unit, t, cx), - kind: unit.kind.for_target(t), - }), - ); - } Ok(ret) } @@ -167,10 +151,10 @@ fn compute_deps_custom_build<'a, 'cfg>( unit: &Unit<'a>, cx: &Context<'a, 'cfg>, deps: &mut HashMap, Vec>>, -) -> CargoResult>> { +) -> CargoResult, ProfileFor)>> { // When not overridden, then the dependencies to run a build script are: // - // 1. Compiling the build script itcx + // 1. Compiling the build script itself // 2. For each immediate dependency of our package which has a `links` // key, the execution of that build script. let not_custom_build = unit.pkg @@ -179,23 +163,32 @@ fn compute_deps_custom_build<'a, 'cfg>( .find(|t| !t.is_custom_build()) .unwrap(); let tmp = Unit { + pkg: unit.pkg, target: not_custom_build, - profile: &cx.profiles.dev, - ..*unit + // The profile here isn't critical. We are just using this temp unit + // for fetching dependencies that might have `links`. + profile_for: ProfileFor::Any, + kind: unit.kind, + mode: CompileMode::Build, }; - let deps = deps_of(&tmp, cx, deps)?; + let deps = deps_of(&tmp, cx, deps, ProfileFor::CustomBuild)?; Ok(deps.iter() .filter_map(|unit| { if !unit.target.linkable() || unit.pkg.manifest().links().is_none() { return None; } - dep_build_script(unit, cx) + dep_build_script(unit) }) - .chain(Some(Unit { - profile: cx.build_script_profile(unit.pkg.package_id()), - kind: Kind::Host, // build scripts always compiled for the host - ..*unit - })) + .chain(Some(( + Unit { + pkg: unit.pkg, + target: unit.target, + profile_for: ProfileFor::CustomBuild, + kind: Kind::Host, // build scripts always compiled for the host + mode: CompileMode::Build, + }, + ProfileFor::CustomBuild, + ))) .collect()) } @@ -203,15 +196,13 @@ fn compute_deps_custom_build<'a, 'cfg>( fn compute_deps_doc<'a, 'cfg>( unit: &Unit<'a>, cx: &Context<'a, 'cfg>, -) -> CargoResult>> { +) -> CargoResult, ProfileFor)>> { let deps = cx.resolve .deps(unit.pkg.package_id()) .filter(|&(_id, deps)| { - deps.iter().any(|dep| { - match dep.kind() { - DepKind::Normal => cx.dep_platform_activated(dep, unit.kind), - _ => false, - } + deps.iter().any(|dep| match dep.kind() { + DepKind::Normal => cx.dep_platform_activated(dep, unit.kind), + _ => false, }) }) .map(|(id, _deps)| cx.get_package(id)); @@ -226,43 +217,57 @@ fn compute_deps_doc<'a, 'cfg>( Some(lib) => lib, None => continue, }; - ret.push(Unit { - pkg: dep, - target: lib, - profile: lib_or_check_profile(unit, lib, cx), - kind: unit.kind.for_target(lib), - }); - if cx.build_config.doc_all { - ret.push(Unit { + // rustdoc only needs rmeta files for regular dependencies. + // However, for plugins/proc-macros, deps should be built like normal. + let mode = check_or_build_mode(&unit.mode, lib); + ret.push(( + Unit { pkg: dep, target: lib, - profile: &cx.profiles.doc, + profile_for: ProfileFor::Any, kind: unit.kind.for_target(lib), - }); + mode, + }, + ProfileFor::Any, + )); + if let CompileMode::Doc { deps: true } = unit.mode { + ret.push(( + Unit { + pkg: dep, + target: lib, + profile_for: ProfileFor::Any, + kind: unit.kind.for_target(lib), + mode: unit.mode, + }, + ProfileFor::Any, + )); } } // Be sure to build/run the build script for documented libraries as - ret.extend(dep_build_script(unit, cx)); + ret.extend(dep_build_script(unit)); // If we document a binary, we need the library available if unit.target.is_bin() { - ret.extend(maybe_lib(unit, cx)); + ret.extend(maybe_lib(unit, ProfileFor::Any)); } Ok(ret) } -fn maybe_lib<'a, 'cfg>(unit: &Unit<'a>, cx: &Context<'a, 'cfg>) -> Option> { - unit.pkg - .targets() - .iter() - .find(|t| t.linkable()) - .map(|t| Unit { - pkg: unit.pkg, - target: t, - profile: lib_or_check_profile(unit, t, cx), - kind: unit.kind.for_target(t), - }) +fn maybe_lib<'a>(unit: &Unit<'a>, profile_for: ProfileFor) -> Option<(Unit<'a>, ProfileFor)> { + let mode = check_or_build_mode(&unit.mode, unit.target); + unit.pkg.targets().iter().find(|t| t.linkable()).map(|t| { + ( + Unit { + pkg: unit.pkg, + target: t, + profile_for, + kind: unit.kind.for_target(t), + mode, + }, + profile_for, + ) + }) } /// If a build script is scheduled to be run for the package specified by @@ -272,28 +277,44 @@ fn maybe_lib<'a, 'cfg>(unit: &Unit<'a>, cx: &Context<'a, 'cfg>) -> Option(unit: &Unit<'a>, cx: &Context<'a, 'cfg>) -> Option> { +fn dep_build_script<'a>(unit: &Unit<'a>) -> Option<(Unit<'a>, ProfileFor)> { unit.pkg .targets() .iter() .find(|t| t.is_custom_build()) - .map(|t| Unit { - pkg: unit.pkg, - target: t, - profile: &cx.profiles.custom_build, - kind: unit.kind, + .map(|t| { + ( + Unit { + pkg: unit.pkg, + target: t, + // The profile for *running* the build script will actually be the + // target the build script is running for (so that the environment + // variables get set correctly). This is overridden in + // `Profiles::build_unit_profiles`, so the exact value here isn't + // critical. + profile_for: ProfileFor::CustomBuild, + kind: unit.kind, + mode: CompileMode::RunCustomBuild, + }, + ProfileFor::CustomBuild, + ) }) } -fn lib_or_check_profile<'a, 'cfg>( - unit: &Unit, - target: &Target, - cx: &Context<'a, 'cfg>, -) -> &'a Profile { - if !target.is_custom_build() && !target.for_host() - && (unit.profile.check || (unit.profile.doc && !unit.profile.test)) - { - return &cx.profiles.check; +/// Choose the correct mode for dependencies. +fn check_or_build_mode(mode: &CompileMode, target: &Target) -> CompileMode { + match *mode { + CompileMode::Check { .. } | CompileMode::Doc { .. } => { + if target.for_host() { + // Plugin and proc-macro targets should be compiled like + // normal. + CompileMode::Build + } else { + // Regular dependencies should not be checked with --test. + // Regular dependencies of doc targets should emit rmeta only. + CompileMode::Check { test: false } + } + } + _ => CompileMode::Build, } - cx.lib_profile() } diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index 320af640314..3b00c478c8c 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -102,11 +102,11 @@ pub fn prepare<'a, 'cfg>( } fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<(Work, Work)> { - assert!(unit.profile.run_custom_build); + assert!(unit.mode.is_run_custom_build()); let dependencies = cx.dep_targets(unit); let build_script_unit = dependencies .iter() - .find(|d| !d.profile.run_custom_build && d.target.is_custom_build()) + .find(|d| !d.mode.is_run_custom_build() && d.target.is_custom_build()) .expect("running a script not depending on an actual script"); let script_output = cx.files().build_script_dir(build_script_unit); let build_output = cx.files().build_script_out_dir(unit); @@ -118,9 +118,9 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes // environment variables. Note that the profile-related environment // variables are not set with this the build script's profile but rather the // package's library profile. - let profile = cx.lib_profile(); let to_exec = to_exec.into_os_string(); let mut cmd = cx.compilation.host_process(to_exec, unit.pkg)?; + let profile = cx.unit_profile(unit).clone(); cmd.env("OUT_DIR", &build_output) .env("CARGO_MANIFEST_DIR", unit.pkg.root()) .env("NUM_JOBS", &cx.jobs().to_string()) @@ -132,7 +132,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes }, ) .env("DEBUG", &profile.debuginfo.is_some().to_string()) - .env("OPT_LEVEL", &profile.opt_level) + .env("OPT_LEVEL", &profile.opt_level.to_string()) .env( "PROFILE", if cx.build_config.release { @@ -196,7 +196,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes dependencies .iter() .filter_map(|unit| { - if unit.profile.run_custom_build { + if unit.mode.is_run_custom_build() { Some(( unit.pkg.manifest().links().unwrap().to_string(), unit.pkg.package_id().clone(), diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 9d3fc4d503f..100912ceb3d 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -86,7 +86,7 @@ pub fn prepare_target<'a, 'cfg>( let root = cx.files().out_dir(unit); let mut missing_outputs = false; - if unit.profile.doc { + if unit.mode.is_doc() { missing_outputs = !root.join(unit.target.crate_name()) .join("index.html") .exists(); @@ -102,7 +102,7 @@ pub fn prepare_target<'a, 'cfg>( } } - let allow_failure = unit.profile.rustc_args.is_some(); + let allow_failure = cx.extra_compiler_args.get(unit).is_some(); let target_root = cx.files().target_root().to_path_buf(); let write_fingerprint = Work::new(move |_| { match fingerprint.update_local(&target_root) { @@ -449,15 +449,23 @@ fn calculate<'a, 'cfg>( }; let mut deps = deps; deps.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b)); - let extra_flags = if unit.profile.doc { + let extra_flags = if unit.mode.is_doc() { cx.rustdocflags_args(unit)? } else { cx.rustflags_args(unit)? }; + let profile_hash = { + let profile = cx.unit_profile(unit); + util::hash_u64(&( + profile, + unit.mode, + cx.incremental_args(unit, profile.incremental)?, + )) + }; let fingerprint = Arc::new(Fingerprint { rustc: util::hash_u64(&cx.build_config.rustc.verbose_version), target: util::hash_u64(&unit.target), - profile: util::hash_u64(&(&unit.profile, cx.incremental_args(unit)?)), + profile: profile_hash, // Note that .0 is hashed here, not .1 which is the cwd. That doesn't // actually affect the output artifact so there's no need to hash it. path: util::hash_u64(&super::path_args(cx, unit).0), @@ -478,7 +486,7 @@ fn calculate<'a, 'cfg>( // responsibility of the source) fn use_dep_info(unit: &Unit) -> bool { let path = unit.pkg.summary().source_id().is_path(); - !unit.profile.doc && path + !unit.mode.is_doc() && path } /// Prepare the necessary work for the fingerprint of a build command. @@ -758,9 +766,9 @@ fn filename<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> String { TargetKind::Bench => "bench", TargetKind::CustomBuild => "build-script", }; - let flavor = if unit.profile.test { + let flavor = if unit.mode.is_any_test() && !unit.mode.is_check() { "test-" - } else if unit.profile.doc { + } else if unit.mode.is_doc() { "doc-" } else { "" diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs index 9f33e9ca4bf..4fcaf4ea5ab 100644 --- a/src/cargo/core/compiler/job_queue.rs +++ b/src/cargo/core/compiler/job_queue.rs @@ -8,7 +8,9 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use crossbeam::{self, Scope}; use jobserver::{Acquired, HelperThread}; -use core::{PackageId, Profile, Target}; +use core::{PackageId, Target}; +use core::profiles::ProfileFor; +use ops::CompileMode; use util::{Config, DependencyQueue, Dirty, Fresh, Freshness}; use util::{internal, profile, CargoResult, CargoResultExt, ProcessBuilder}; use handle_error; @@ -46,8 +48,9 @@ struct PendingBuild { struct Key<'a> { pkg: &'a PackageId, target: &'a Target, - profile: &'a Profile, + profile_for: ProfileFor, kind: Kind, + mode: CompileMode, } pub struct JobState<'a> { @@ -231,7 +234,7 @@ impl<'a> JobQueue<'a> { Ok(()) => self.finish(key, cx)?, Err(e) => { let msg = "The following warnings were emitted during compilation:"; - self.emit_warnings(Some(msg), key, cx)?; + self.emit_warnings(Some(msg), &key, cx)?; if self.active > 0 { error = Some(format_err!("build failed")); @@ -253,8 +256,9 @@ impl<'a> JobQueue<'a> { } let build_type = if self.is_release { "release" } else { "dev" }; - let profile = cx.lib_profile(); - let mut opt_type = String::from(if profile.opt_level == "0" { + // TODO FIXME: We don't know which pkg to display this for! + let profile = cx.profiles.base_profile(self.is_release); + let mut opt_type = String::from(if profile.opt_level.as_str() == "0" { "unoptimized" } else { "optimized" @@ -316,7 +320,7 @@ impl<'a> JobQueue<'a> { Ok(()) } - fn emit_warnings(&self, msg: Option<&str>, key: Key<'a>, cx: &mut Context) -> CargoResult<()> { + fn emit_warnings(&self, msg: Option<&str>, key: &Key<'a>, cx: &mut Context) -> CargoResult<()> { let output = cx.build_state.outputs.lock().unwrap(); if let Some(output) = output.get(&(key.pkg.clone(), key.kind)) { if let Some(msg) = msg { @@ -339,8 +343,8 @@ impl<'a> JobQueue<'a> { } fn finish(&mut self, key: Key<'a>, cx: &mut Context) -> CargoResult<()> { - if key.profile.run_custom_build && cx.show_warnings(key.pkg) { - self.emit_warnings(None, key, cx)?; + if key.mode.is_run_custom_build() && cx.show_warnings(key.pkg) { + self.emit_warnings(None, &key, cx)?; } let state = self.pending.get_mut(&key).unwrap(); @@ -366,8 +370,8 @@ impl<'a> JobQueue<'a> { key: &Key<'a>, fresh: Freshness, ) -> CargoResult<()> { - if (self.compiled.contains(key.pkg) && !key.profile.doc) - || (self.documented.contains(key.pkg) && key.profile.doc) + if (self.compiled.contains(key.pkg) && !key.mode.is_doc()) + || (self.documented.contains(key.pkg) && key.mode.is_doc()) { return Ok(()); } @@ -376,14 +380,15 @@ impl<'a> JobQueue<'a> { // Any dirty stage which runs at least one command gets printed as // being a compiled package Dirty => { - if key.profile.doc { - if !key.profile.test { + if key.mode.is_doc() { + // Skip Doctest + if !key.mode.is_any_test() { self.documented.insert(key.pkg); config.shell().status("Documenting", key.pkg)?; } } else { self.compiled.insert(key.pkg); - if key.profile.check { + if key.mode.is_check() { config.shell().status("Checking", key.pkg)?; } else { config.shell().status("Compiling", key.pkg)?; @@ -405,8 +410,9 @@ impl<'a> Key<'a> { Key { pkg: unit.pkg.package_id(), target: unit.target, - profile: unit.profile, + profile_for: unit.profile_for, kind: unit.kind, + mode: unit.mode, } } @@ -414,8 +420,9 @@ impl<'a> Key<'a> { let unit = Unit { pkg: cx.get_package(self.pkg)?, target: self.target, - profile: self.profile, + profile_for: self.profile_for, kind: self.kind, + mode: self.mode, }; let targets = cx.dep_targets(&unit); Ok(targets @@ -437,8 +444,8 @@ impl<'a> fmt::Debug for Key<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "{} => {}/{} => {:?}", - self.pkg, self.target, self.profile, self.kind + "{} => {}/{:?} => {:?}", + self.pkg, self.target, self.mode, self.kind ) } } diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index fccdfb6c2c7..05c31e96acb 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -9,9 +9,10 @@ use std::sync::Arc; use same_file::is_same_file; use serde_json; -use core::{Feature, PackageId, Profile, Target}; -use core::manifest::Lto; +use core::{Feature, PackageId, Target}; +use core::profiles::{Lto, Profile}; use core::shell::ColorChoice; +use ops::CompileMode; use util::{self, machine_message, Config, Freshness, ProcessBuilder, Rustc}; use util::{internal, join_paths, profile}; use util::paths; @@ -59,10 +60,6 @@ pub struct BuildConfig { pub jobs: u32, /// Whether we are building for release pub release: bool, - /// Whether we are running tests - pub test: bool, - /// Whether we are building documentation - pub doc_all: bool, /// Whether to print std output in json format (for machine reading) pub json_messages: bool, } @@ -131,8 +128,6 @@ impl BuildConfig { host: host_config, target: target_config, release: false, - test: false, - doc_all: false, json_messages: false, }) } @@ -304,14 +299,14 @@ fn compile<'a, 'cfg: 'a>( fingerprint::prepare_init(cx, unit)?; cx.links.validate(cx.resolve, unit)?; - let (dirty, fresh, freshness) = if unit.profile.run_custom_build { + let (dirty, fresh, freshness) = if unit.mode.is_run_custom_build() { custom_build::prepare(cx, unit)? - } else if unit.profile.doc && unit.profile.test { + } else if unit.mode == CompileMode::Doctest { // we run these targets later, so this is just a noop for now (Work::noop(), Work::noop(), Freshness::Fresh) } else { let (mut freshness, dirty, fresh) = fingerprint::prepare_target(cx, unit)?; - let work = if unit.profile.doc { + let work = if unit.mode.is_doc() { rustdoc(cx, unit)? } else { rustc(cx, unit, exec)? @@ -369,7 +364,7 @@ fn rustc<'a, 'cfg>( // If we are a binary and the package also contains a library, then we // don't pass the `-l` flags. let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib()); - let do_rename = unit.target.allows_underscores() && !unit.profile.test; + let do_rename = unit.target.allows_underscores() && !unit.mode.is_any_test(); let real_name = unit.target.name().to_string(); let crate_name = unit.target.crate_name(); @@ -561,7 +556,8 @@ fn link_targets<'a, 'cfg>( let export_dir = cx.files().export_dir(unit); let package_id = unit.pkg.package_id().clone(); let target = unit.target.clone(); - let profile = unit.profile.clone(); + let profile = cx.unit_profile(unit).clone(); + let unit_mode = unit.mode; let features = cx.resolve .features_sorted(&package_id) .into_iter() @@ -601,10 +597,18 @@ fn link_targets<'a, 'cfg>( } if json_messages { + let art_profile = machine_message::ArtifactProfile { + opt_level: profile.opt_level.as_str(), + debuginfo: profile.debuginfo, + debug_assertions: profile.debug_assertions, + overflow_checks: profile.overflow_checks, + test: unit_mode.is_any_test(), + }; + machine_message::emit(&machine_message::Artifact { package_id: &package_id, target: &target, - profile: &profile, + profile: art_profile, features, filenames: destinations, fresh, @@ -770,7 +774,7 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult rustdoc.arg(format!("--edition={}", &manifest.edition())); } - if let Some(ref args) = unit.profile.rustdoc_args { + if let Some(args) = cx.extra_compiler_args.get(unit) { rustdoc.args(args); } @@ -835,23 +839,21 @@ fn build_base_args<'a, 'cfg>( unit: &Unit<'a>, crate_types: &[&str], ) -> CargoResult<()> { + assert!(!unit.mode.is_run_custom_build()); + let Profile { ref opt_level, ref lto, codegen_units, - ref rustc_args, debuginfo, debug_assertions, overflow_checks, rpath, - test, - doc: _doc, - run_custom_build, ref panic, - check, + incremental, .. - } = *unit.profile; - assert!(!run_custom_build); + } = *cx.unit_profile(unit); + let test = unit.mode.is_any_test(); cmd.arg("--crate-name").arg(&unit.target.crate_name()); @@ -877,7 +879,7 @@ fn build_base_args<'a, 'cfg>( } } - if check { + if unit.mode.is_check() { cmd.arg("--emit=dep-info,metadata"); } else { cmd.arg("--emit=dep-info,link"); @@ -889,7 +891,7 @@ fn build_base_args<'a, 'cfg>( cmd.arg("-C").arg("prefer-dynamic"); } - if opt_level != "0" { + if opt_level.as_str() != "0" { cmd.arg("-C").arg(&format!("opt-level={}", opt_level)); } @@ -938,14 +940,14 @@ fn build_base_args<'a, 'cfg>( cmd.arg("-C").arg(format!("debuginfo={}", debuginfo)); } - if let Some(ref args) = *rustc_args { + if let Some(args) = cx.extra_compiler_args.get(unit) { cmd.args(args); } // -C overflow-checks is implied by the setting of -C debug-assertions, // so we only need to provide -C overflow-checks if it differs from // the value of -C debug-assertions we would provide. - if opt_level != "0" { + if opt_level.as_str() != "0" { if debug_assertions { cmd.args(&["-C", "debug-assertions=on"]); if !overflow_checks { @@ -1020,7 +1022,7 @@ fn build_base_args<'a, 'cfg>( "linker=", cx.linker(unit.kind).map(|s| s.as_ref()), ); - cmd.args(&cx.incremental_args(unit)?); + cmd.args(&cx.incremental_args(unit, incremental)?); Ok(()) } @@ -1053,11 +1055,11 @@ fn build_deps_args<'a, 'cfg>( // error in the future, see PR #4797 if !dep_targets .iter() - .any(|u| !u.profile.doc && u.target.linkable()) + .any(|u| !u.mode.is_doc() && u.target.linkable()) { if let Some(u) = dep_targets .iter() - .find(|u| !u.profile.doc && u.target.is_lib()) + .find(|u| !u.mode.is_doc() && u.target.is_lib()) { cx.config.shell().warn(format!( "The package `{}` \ @@ -1072,10 +1074,10 @@ fn build_deps_args<'a, 'cfg>( } for dep in dep_targets { - if dep.profile.run_custom_build { + if dep.mode.is_run_custom_build() { cmd.env("OUT_DIR", &cx.files().build_script_out_dir(&dep)); } - if dep.target.linkable() && !dep.profile.doc { + if dep.target.linkable() && !dep.mode.is_doc() { link_to(cmd, cx, unit, &dep)?; } } diff --git a/src/cargo/core/compiler/output_depinfo.rs b/src/cargo/core/compiler/output_depinfo.rs index dcdbb95f8b7..242222ef31d 100644 --- a/src/cargo/core/compiler/output_depinfo.rs +++ b/src/cargo/core/compiler/output_depinfo.rs @@ -34,7 +34,7 @@ fn add_deps_for_unit<'a, 'b>( // units representing the execution of a build script don't actually // generate a dep info file, so we just keep on going below - if !unit.profile.run_custom_build { + if !unit.mode.is_run_custom_build() { // Add dependencies from rustc dep-info output (stored in fingerprint directory) let dep_info_loc = fingerprint::dep_info_loc(context, unit); if let Some(paths) = fingerprint::parse_dep_info(unit.pkg, &dep_info_loc)? { @@ -43,9 +43,9 @@ fn add_deps_for_unit<'a, 'b>( } } else { debug!( - "can't find dep_info for {:?} {:?}", + "can't find dep_info for {:?} {}", unit.pkg.package_id(), - unit.profile + unit.target ); return Err(internal("dep_info missing")); } diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index c9658a8aaed..406c969aa82 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -170,6 +170,9 @@ features! { // Whether a lock file is published with this crate [unstable] publish_lockfile: bool, + + // Overriding profiles for dependencies. + [unstable] profile_overrides: bool, } } diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index b4b50fc90a9..da7269c5faa 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -10,6 +10,7 @@ use toml; use url::Url; use core::{Dependency, PackageId, PackageIdSpec, SourceId, Summary}; +use core::profiles::Profiles; use core::{Edition, Feature, Features, WorkspaceConfig}; use core::interning::InternedString; use util::Config; @@ -152,59 +153,6 @@ impl ser::Serialize for TargetKind { } } -// Note that most of the fields here are skipped when serializing because we -// don't want to export them just yet (becomes a public API of Cargo). Others -// though are definitely needed! -#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize)] -pub struct Profile { - pub opt_level: String, - #[serde(skip_serializing)] - pub lto: Lto, - #[serde(skip_serializing)] - pub codegen_units: Option, // None = use rustc default - #[serde(skip_serializing)] - pub rustc_args: Option>, - #[serde(skip_serializing)] - pub rustdoc_args: Option>, - pub debuginfo: Option, - pub debug_assertions: bool, - pub overflow_checks: bool, - #[serde(skip_serializing)] - pub rpath: bool, - pub test: bool, - #[serde(skip_serializing)] - pub doc: bool, - #[serde(skip_serializing)] - pub run_custom_build: bool, - #[serde(skip_serializing)] - pub check: bool, - #[serde(skip_serializing)] - pub panic: Option, - #[serde(skip_serializing)] - pub incremental: bool, -} - -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub enum Lto { - Bool(bool), - Named(String), -} - -#[derive(Default, Clone, Debug, PartialEq, Eq)] -pub struct Profiles { - pub release: Profile, - pub dev: Profile, - pub test: Profile, - pub test_deps: Profile, - pub bench: Profile, - pub bench_deps: Profile, - pub doc: Profile, - pub custom_build: Profile, - pub check: Profile, - pub check_test: Profile, - pub doctest: Profile, -} - /// Information about a binary, a library, an example, etc. that is part of the /// package. #[derive(Clone, Hash, PartialEq, Eq, Debug)] @@ -726,112 +674,3 @@ impl fmt::Display for Target { } } } - -impl Profile { - pub fn default_dev() -> Profile { - Profile { - debuginfo: Some(2), - debug_assertions: true, - overflow_checks: true, - incremental: true, - ..Profile::default() - } - } - - pub fn default_release() -> Profile { - Profile { - opt_level: "3".to_string(), - debuginfo: None, - ..Profile::default() - } - } - - pub fn default_test() -> Profile { - Profile { - test: true, - ..Profile::default_dev() - } - } - - pub fn default_bench() -> Profile { - Profile { - test: true, - ..Profile::default_release() - } - } - - pub fn default_doc() -> Profile { - Profile { - doc: true, - ..Profile::default_dev() - } - } - - pub fn default_custom_build() -> Profile { - Profile { - run_custom_build: true, - ..Profile::default_dev() - } - } - - pub fn default_check() -> Profile { - Profile { - check: true, - ..Profile::default_dev() - } - } - - pub fn default_check_test() -> Profile { - Profile { - check: true, - test: true, - ..Profile::default_dev() - } - } - - pub fn default_doctest() -> Profile { - Profile { - doc: true, - test: true, - ..Profile::default_dev() - } - } -} - -impl Default for Profile { - fn default() -> Profile { - Profile { - opt_level: "0".to_string(), - lto: Lto::Bool(false), - codegen_units: None, - rustc_args: None, - rustdoc_args: None, - debuginfo: None, - debug_assertions: false, - overflow_checks: false, - rpath: false, - test: false, - doc: false, - run_custom_build: false, - check: false, - panic: None, - incremental: false, - } - } -} - -impl fmt::Display for Profile { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.test { - write!(f, "Profile(test)") - } else if self.doc { - write!(f, "Profile(doc)") - } else if self.run_custom_build { - write!(f, "Profile(run)") - } else if self.check { - write!(f, "Profile(check)") - } else { - write!(f, "Profile(build)") - } - } -} diff --git a/src/cargo/core/mod.rs b/src/cargo/core/mod.rs index d112050d08b..347772bb810 100644 --- a/src/cargo/core/mod.rs +++ b/src/cargo/core/mod.rs @@ -1,7 +1,7 @@ pub use self::dependency::Dependency; pub use self::features::{CliUnstable, Edition, Feature, Features}; pub use self::manifest::{EitherManifest, VirtualManifest}; -pub use self::manifest::{LibKind, Manifest, Profile, Profiles, Target, TargetKind}; +pub use self::manifest::{LibKind, Manifest, Target, TargetKind}; pub use self::package::{Package, PackageSet}; pub use self::package_id::PackageId; pub use self::package_id_spec::PackageIdSpec; @@ -22,6 +22,7 @@ pub mod summary; pub mod shell; pub mod registry; pub mod compiler; +pub mod profiles; mod interning; mod package_id_spec; mod workspace; diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs new file mode 100644 index 00000000000..615b0ac5486 --- /dev/null +++ b/src/cargo/core/profiles.rs @@ -0,0 +1,394 @@ +use std::collections::HashMap; +use std::{cmp, fmt, hash}; +use ops::CompileMode; +use util::toml::{StringOrBool, TomlProfile, U32OrBool}; +use core::compiler::Context; +use core::compiler::Unit; +use core::interning::InternedString; + +/// Collection of all user profiles. +#[derive(Clone, Debug)] +pub struct Profiles { + dev: ProfileMaker, + release: ProfileMaker, + test: ProfileMaker, + bench: ProfileMaker, + doc: ProfileMaker, +} + +impl Profiles { + pub fn new( + dev: Option, + release: Option, + test: Option, + bench: Option, + doc: Option, + ) -> Profiles { + Profiles { + dev: ProfileMaker { + default: Profile::default_dev(), + toml: dev, + }, + release: ProfileMaker { + default: Profile::default_release(), + toml: release, + }, + test: ProfileMaker { + default: Profile::default_test(), + toml: test, + }, + bench: ProfileMaker { + default: Profile::default_bench(), + toml: bench, + }, + doc: ProfileMaker { + default: Profile::default_doc(), + toml: doc, + }, + } + } + + /// Retrieve the profile for a target. + /// `is_member` is whether or not this package is a member of the + /// workspace. + fn get_profile( + &self, + pkg_name: &str, + is_member: bool, + profile_for: ProfileFor, + mode: CompileMode, + release: bool, + ) -> Profile { + let maker = match mode { + CompileMode::Test => { + if release { + &self.bench + } else { + &self.test + } + } + CompileMode::Build + | CompileMode::Check { .. } + | CompileMode::Doctest + | CompileMode::RunCustomBuild => { + // Note: RunCustomBuild doesn't normally use this code path. + // `build_unit_profiles` normally ensures that it selects the + // ancestor's profile. However `cargo clean -p` can hit this + // path. + // TODO: I think `cargo clean -p xxx` is not cleaning out + // the "OUT_DIR" directory. This is not a new bug. + if release { + &self.release + } else { + &self.dev + } + } + CompileMode::Bench => &self.bench, + CompileMode::Doc { .. } => &self.doc, + }; + let mut profile = maker.profile_for(pkg_name, 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() { + profile.panic = None; + } + profile + } + + /// This returns a generic base profile. This is currently used for the + /// `[Finished]` line. It is not entirely accurate, since it doesn't + /// select for the package that was actually built. + pub fn base_profile(&self, release: bool) -> Profile { + if release { + self.release.profile_for("", true, ProfileFor::Any) + } else { + self.dev.profile_for("", true, ProfileFor::Any) + } + } + + /// Build a mapping from Unit -> Profile for all the given units and all + /// of their dependencies. + pub fn build_unit_profiles<'a, 'cfg>( + &self, + units: &[Unit<'a>], + cx: &Context<'a, 'cfg>, + ) -> HashMap, Profile> { + let mut result = HashMap::new(); + for unit in units.iter() { + self.build_unit_profiles_rec(unit, None, cx, &mut result); + } + result + } + + fn build_unit_profiles_rec<'a, 'cfg>( + &self, + unit: &Unit<'a>, + parent: Option<&Unit<'a>>, + cx: &Context<'a, 'cfg>, + map: &mut HashMap, Profile>, + ) { + if !map.contains_key(unit) { + let for_unit = if unit.mode.is_run_custom_build() { + // The profile for *running* a custom build script is the + // target the script is running for. This allows + // `custom_build::build_work` to set the correct environment + // settings. + // + // In the case of `cargo clean -p`, it creates artificial + // units to compute filenames, without a dependency hierarchy, + // so we don't have a parent here. That should be OK, it + // only affects the environment variables used to *run* + // `build.rs`. + parent.unwrap_or(unit) + } else { + unit + }; + let profile = self.get_profile( + &for_unit.pkg.name(), + cx.ws.is_member(for_unit.pkg), + for_unit.profile_for, + for_unit.mode, + cx.build_config.release, + ); + map.insert(*unit, profile); + let deps = cx.dep_targets(unit); + for dep in &deps { + self.build_unit_profiles_rec(dep, Some(unit), cx, map); + } + } + } +} + +/// An object used for handling the profile override hierarchy. +/// +/// The precedence of profiles are (first one wins): +/// - [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 +/// and their dependencies. +/// - [profile.dev] +/// - Default (hard-coded) values. +#[derive(Debug, Clone)] +struct ProfileMaker { + default: Profile, + toml: Option, +} + +impl ProfileMaker { + fn profile_for(&self, pkg_name: &str, is_member: bool, profile_for: ProfileFor) -> Profile { + let mut profile = self.default.clone(); + 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(star) = overrides.get("*") { + merge_profile(&mut profile, star); + } + } + if let Some(byname) = overrides.get(pkg_name) { + merge_profile(&mut profile, byname); + } + } + } + profile + } +} + +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); + } + match toml.lto { + Some(StringOrBool::Bool(b)) => profile.lto = Lto::Bool(b), + Some(StringOrBool::String(ref n)) => profile.lto = Lto::Named(n.clone()), + None => {} + } + if toml.codegen_units.is_some() { + profile.codegen_units = toml.codegen_units; + } + match toml.debug { + Some(U32OrBool::U32(debug)) => profile.debuginfo = Some(debug), + Some(U32OrBool::Bool(true)) => profile.debuginfo = Some(2), + Some(U32OrBool::Bool(false)) => profile.debuginfo = None, + None => {} + } + if let Some(debug_assertions) = toml.debug_assertions { + profile.debug_assertions = debug_assertions; + } + if let Some(rpath) = toml.rpath { + profile.rpath = rpath; + } + if let Some(ref panic) = toml.panic { + profile.panic = Some(InternedString::new(panic)); + } + if let Some(overflow_checks) = toml.overflow_checks { + profile.overflow_checks = overflow_checks; + } + if let Some(incremental) = toml.incremental { + profile.incremental = incremental; + } +} + +/// Profile settings used to determine which compiler flags to use for a +/// target. +#[derive(Debug, Clone, Eq)] +pub struct Profile { + pub name: &'static str, + pub opt_level: InternedString, + pub lto: Lto, + // None = use rustc default + pub codegen_units: Option, + pub debuginfo: Option, + pub debug_assertions: bool, + pub overflow_checks: bool, + pub rpath: bool, + pub incremental: bool, + pub panic: Option, +} + +impl Default for Profile { + fn default() -> Profile { + Profile { + name: "", + opt_level: InternedString::new("0"), + lto: Lto::Bool(false), + codegen_units: None, + debuginfo: None, + debug_assertions: false, + overflow_checks: false, + rpath: false, + incremental: false, + panic: None, + } + } +} + +impl fmt::Display for Profile { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Profile({})", self.name) + } +} + +impl hash::Hash for Profile { + fn hash(&self, state: &mut H) + where + H: hash::Hasher, + { + self.comparable().hash(state); + } +} + +impl cmp::PartialEq for Profile { + fn eq(&self, other: &Self) -> bool { + self.comparable() == other.comparable() + } +} + +impl Profile { + fn default_dev() -> Profile { + Profile { + name: "dev", + debuginfo: Some(2), + debug_assertions: true, + overflow_checks: true, + incremental: true, + ..Profile::default() + } + } + + fn default_release() -> Profile { + Profile { + name: "release", + opt_level: InternedString::new("3"), + ..Profile::default() + } + } + + fn default_test() -> Profile { + Profile { + name: "test", + ..Profile::default_dev() + } + } + + fn default_bench() -> Profile { + Profile { + name: "bench", + ..Profile::default_release() + } + } + + fn default_doc() -> Profile { + Profile { + name: "doc", + ..Profile::default_dev() + } + } + + /// Compare 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. + fn comparable( + &self, + ) -> ( + &InternedString, + &Lto, + &Option, + &Option, + &bool, + &bool, + &bool, + &bool, + &Option, + ) { + ( + &self.opt_level, + &self.lto, + &self.codegen_units, + &self.debuginfo, + &self.debug_assertions, + &self.overflow_checks, + &self.rpath, + &self.incremental, + &self.panic, + ) + } +} + +/// The link-time-optimization setting. +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum Lto { + /// False = no LTO + /// True = "Fat" LTO + Bool(bool), + /// Named LTO settings like "thin". + Named(String), +} + +/// A flag used in `Unit` to indicate the purpose for the target. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum ProfileFor { + /// A general-purpose target. + Any, + /// A target for `build.rs` or any of its dependencies. This enables + /// `build_override` profiles for these targets. + CustomBuild, + /// A target that is a dependency of a test or benchmark. Currently this + /// enforces that the `panic` setting is not set. + TestDependency, +} + +impl ProfileFor { + pub fn all_values() -> Vec { + vec![ + ProfileFor::Any, + ProfileFor::CustomBuild, + ProfileFor::TestDependency, + ] + } +} diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index 72985eef3d5..0de6ef40ab8 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -8,8 +8,9 @@ use glob::glob; use url::Url; use core::registry::PackageRegistry; -use core::{Dependency, PackageIdSpec, Profile, Profiles}; +use core::{Dependency, PackageIdSpec}; use core::{EitherManifest, Package, SourceId, VirtualManifest}; +use core::profiles::Profiles; use ops; use sources::PathSource; use util::errors::{CargoResult, CargoResultExt}; @@ -307,6 +308,13 @@ impl<'cfg> Workspace<'cfg> { } } + /// Returns true if the package is a member of the workspace. + pub fn is_member(&self, pkg: &Package) -> bool { + // TODO: Implement this in a better way. + // Maybe make it part of Package? + self.members().any(|p| p == pkg) + } + pub fn is_ephemeral(&self) -> bool { self.is_ephemeral } @@ -645,24 +653,10 @@ impl<'cfg> Workspace<'cfg> { } if let Some(ref root_manifest) = self.root_manifest { - let default_profiles = Profiles { - release: Profile::default_release(), - dev: Profile::default_dev(), - test: Profile::default_test(), - test_deps: Profile::default_dev(), - bench: Profile::default_bench(), - bench_deps: Profile::default_release(), - doc: Profile::default_doc(), - custom_build: Profile::default_custom_build(), - check: Profile::default_check(), - check_test: Profile::default_check_test(), - doctest: Profile::default_doctest(), - }; - for pkg in self.members() .filter(|p| p.manifest_path() != root_manifest) { - if pkg.manifest().profiles() != &default_profiles { + if pkg.manifest().original().has_profiles() { let message = &format!( "profiles for the non root package will be ignored, \ specify profiles at the workspace root:\n\ diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index a8c388752dd..64eed368292 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -1,12 +1,14 @@ use std::fs; use std::path::Path; +use std::collections::HashMap; -use core::{Profiles, Workspace}; +use core::Workspace; use core::compiler::{BuildConfig, Context, Kind, Unit}; +use core::profiles::ProfileFor; use util::Config; use util::errors::{CargoResult, CargoResultExt}; use util::paths; -use ops; +use ops::{self, CompileMode}; pub struct CleanOptions<'a> { pub config: &'a Config, @@ -46,39 +48,16 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { // Generate all relevant `Unit` targets for this package for target in pkg.targets() { for kind in [Kind::Host, Kind::Target].iter() { - let Profiles { - ref release, - ref dev, - ref test, - ref bench, - ref doc, - ref custom_build, - ref test_deps, - ref bench_deps, - ref check, - ref check_test, - ref doctest, - } = *profiles; - let profiles = [ - release, - dev, - test, - bench, - doc, - custom_build, - test_deps, - bench_deps, - check, - check_test, - doctest, - ]; - for profile in profiles.iter() { - units.push(Unit { - pkg, - target, - profile, - kind: *kind, - }); + for mode in CompileMode::all_modes() { + for profile_for in ProfileFor::all_values() { + units.push(Unit { + pkg, + target, + profile_for, + kind: *kind, + mode, + }); + } } } } @@ -86,13 +65,21 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { let mut build_config = BuildConfig::new(config, Some(1), &opts.target, None)?; build_config.release = opts.release; - let mut cx = Context::new(ws, &resolve, &packages, opts.config, build_config, profiles)?; + let mut cx = Context::new( + ws, + &resolve, + &packages, + opts.config, + build_config, + profiles, + HashMap::new(), + )?; cx.prepare_units(None, &units)?; for unit in units.iter() { rm_rf(&cx.files().fingerprint_dir(unit), config)?; if unit.target.is_custom_build() { - if unit.profile.run_custom_build { + if unit.mode.is_run_custom_build() { rm_rf(&cx.files().build_script_out_dir(unit), config)?; } else { rm_rf(&cx.files().build_script_dir(unit), config)?; diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 85d58bd21fc..9850101b105 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -22,18 +22,19 @@ //! previously compiled dependency //! -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; use std::sync::Arc; -use core::{Package, Source, Target}; -use core::{PackageId, PackageIdSpec, Profile, Profiles, TargetKind, Workspace}; use core::compiler::{BuildConfig, Compilation, Context, DefaultExecutor, Executor}; use core::compiler::{Kind, Unit}; +use core::profiles::ProfileFor; use core::resolver::{Method, Resolve}; +use core::{Package, Source, Target}; +use core::{PackageId, PackageIdSpec, TargetKind, Workspace}; use ops; use util::config::Config; -use util::{profile, CargoResult, CargoResultExt}; +use util::{lev_distance, profile, CargoResult, CargoResultExt}; /// Contains information about how a package should be compiled. #[derive(Debug)] @@ -96,14 +97,84 @@ impl<'a> CompileOptions<'a> { } } -#[derive(Clone, Copy, PartialEq, Debug)] +/// The general "mode" of what to do. +/// This is used for two purposes. The commands themselves pass this in to +/// `compile_ws` to tell it the general execution strategy. This influences +/// the default targets selected. The other use is in the `Unit` struct +/// to indicate what is being done with a specific target. +#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)] pub enum CompileMode { + /// A target being built for a test. Test, + /// Building a target with `rustc` (lib or bin). Build, + /// Building a target with `rustc` to emit `rmeta` metadata only. If + /// `test` is true, then it is also compiled with `--test` to check it like + /// a test. Check { test: bool }, + /// A target being built for a benchmark. Bench, + /// A target that will be documented with `rustdoc`. + /// If `deps` is true, then it will also document all dependencies. Doc { deps: bool }, + /// A target that will be tested with `rustdoc`. Doctest, + /// A marker for Units that represent the execution of a `build.rs` + /// script. + RunCustomBuild, +} + +impl CompileMode { + /// Returns true if the unit is being checked. + pub fn is_check(&self) -> bool { + match *self { + CompileMode::Check { .. } => true, + _ => false, + } + } + + /// Returns true if this is a doc or doctest. Be careful using this. + /// Although both run rustdoc, the dependencies for those two modes are + /// very different. + pub fn is_doc(&self) -> bool { + match *self { + CompileMode::Doc { .. } | CompileMode::Doctest => true, + _ => false, + } + } + + /// Returns true if this is any type of test (test, benchmark, doctest, or + /// check-test). + pub fn is_any_test(&self) -> bool { + match *self { + CompileMode::Test + | CompileMode::Bench + | CompileMode::Check { test: true } + | CompileMode::Doctest => true, + _ => false, + } + } + + /// Returns true if this is the *execution* of a `build.rs` script. + pub fn is_run_custom_build(&self) -> bool { + *self == CompileMode::RunCustomBuild + } + + /// List of all modes (currently used by `cargo clean -p` for computing + /// all possible outputs). + pub fn all_modes() -> Vec { + vec![ + CompileMode::Test, + CompileMode::Build, + CompileMode::Check { test: true }, + CompileMode::Check { test: false }, + CompileMode::Bench, + CompileMode::Doc { deps: true }, + CompileMode::Doc { deps: false }, + CompileMode::Doctest, + CompileMode::RunCustomBuild, + ] + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -267,13 +338,12 @@ pub fn compile_ws<'a>( .into_path_unlocked(); let mut build_config = BuildConfig::new(config, jobs, &target, Some(rustc_info_cache))?; build_config.release = release; - build_config.test = mode == CompileMode::Test || mode == CompileMode::Bench; build_config.json_messages = message_format == MessageFormat::Json; - if let CompileMode::Doc { deps } = mode { - build_config.doc_all = deps; - } - - let profiles = ws.profiles(); + let default_arch_kind = if build_config.requested_target.is_some() { + Kind::Target + } else { + Kind::Host + }; let specs = spec.into_package_id_specs(ws)?; let features = Method::split_features(features); @@ -296,62 +366,42 @@ pub fn compile_ws<'a>( }) .collect::>>()?; - let mut general_targets = Vec::new(); - let mut package_targets = Vec::new(); - - match (target_rustc_args, target_rustdoc_args) { - (&Some(..), _) | (_, &Some(..)) if to_builds.len() != 1 => { - panic!("`rustc` and `rustdoc` should not accept multiple `-p` flags") - } - (&Some(ref args), _) => { - let all_features = - resolve_all_features(&resolve_with_overrides, to_builds[0].package_id()); - let targets = - generate_targets(to_builds[0], profiles, mode, filter, &all_features, release)?; - if targets.len() == 1 { - let (target, profile) = targets[0]; - let mut profile = profile.clone(); - profile.rustc_args = Some(args.to_vec()); - general_targets.push((target, profile)); - } else { - bail!( - "extra arguments to `rustc` can only be passed to one \ - target, consider filtering\nthe package by passing \ - e.g. `--lib` or `--bin NAME` to specify a single target" - ) - } - } - (&None, &Some(ref args)) => { - let all_features = - resolve_all_features(&resolve_with_overrides, to_builds[0].package_id()); - let targets = - generate_targets(to_builds[0], profiles, mode, filter, &all_features, release)?; - if targets.len() == 1 { - let (target, profile) = targets[0]; - let mut profile = profile.clone(); - profile.rustdoc_args = Some(args.to_vec()); - general_targets.push((target, profile)); - } else { - bail!( - "extra arguments to `rustdoc` can only be passed to one \ - target, consider filtering\nthe package by passing e.g. \ - `--lib` or `--bin NAME` to specify a single target" - ) - } - } - (&None, &None) => for &to_build in to_builds.iter() { - let all_features = resolve_all_features(&resolve_with_overrides, to_build.package_id()); - let targets = - generate_targets(to_build, profiles, mode, filter, &all_features, release)?; - package_targets.push((to_build, targets)); - }, + let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) { + (&Some(ref args), _) => (Some(args.clone()), "rustc"), + (_, &Some(ref args)) => (Some(args.clone()), "rustdoc"), + _ => (None, ""), }; - for &(target, ref profile) in &general_targets { - for &to_build in to_builds.iter() { - package_targets.push((to_build, vec![(target, profile)])); + if extra_args.is_some() && to_builds.len() != 1 { + panic!( + "`{}` should not accept multiple `-p` flags", + extra_args_name + ); + } + + let profiles = ws.profiles(); + let mut extra_compiler_args = HashMap::new(); + + let units = generate_targets( + &to_builds, + filter, + default_arch_kind, + mode, + &resolve_with_overrides, + )?; + + if let Some(args) = extra_args { + if units.len() != 1 { + bail!( + "extra arguments to `{}` can only be passed to one \ + target, consider filtering\nthe package by passing \ + e.g. `--lib` or `--bin NAME` to specify a single target", + extra_args_name + ); } + extra_compiler_args.insert(units[0], args); } + let mut ret = { let _p = profile::start("compiling"); let mut cx = Context::new( @@ -361,50 +411,14 @@ pub fn compile_ws<'a>( config, build_config, profiles, + extra_compiler_args, )?; - let units = package_targets - .iter() - .flat_map(|&(pkg, ref targets)| { - let default_kind = if cx.build_config.requested_target.is_some() { - Kind::Target - } else { - Kind::Host - }; - targets.iter().map(move |&(target, profile)| Unit { - pkg, - target, - profile, - kind: if target.for_host() { - Kind::Host - } else { - default_kind - }, - }) - }) - .collect::>(); cx.compile(&units, export_dir.clone(), &exec)? }; ret.to_doc_test = to_builds.into_iter().cloned().collect(); return Ok(ret); - - fn resolve_all_features( - resolve_with_overrides: &Resolve, - package_id: &PackageId, - ) -> HashSet { - let mut features = resolve_with_overrides.features(package_id).clone(); - - // Include features enabled for use by dependencies so targets can also use them with the - // required-features field when deciding whether to be built or skipped. - for (dep, _) in resolve_with_overrides.deps(package_id) { - for feature in resolve_with_overrides.features(dep) { - features.insert(dep.name().to_string() + "/" + feature); - } - } - - features - } } impl FilterRule { @@ -496,6 +510,7 @@ impl CompileFilter { .. } => examples.is_specific() || tests.is_specific() || benches.is_specific(), }, + CompileMode::RunCustomBuild => panic!("Invalid mode"), } } @@ -533,307 +548,307 @@ impl CompileFilter { } } -#[derive(Clone, Copy, Debug)] -struct BuildProposal<'a> { - target: &'a Target, - profile: &'a Profile, - required: bool, -} - -fn generate_default_targets<'a>( +/// Generates all the base targets for the packages the user has requested to +/// compile. Dependencies for these targets are computed later in +/// `unit_dependencies`. +fn generate_targets<'a>( + packages: &[&'a Package], + filter: &CompileFilter, + default_arch_kind: Kind, mode: CompileMode, - targets: &'a [Target], - profile: &'a Profile, - dep: &'a Profile, - required_features_filterable: bool, -) -> Vec> { - match mode { - CompileMode::Bench => targets - .iter() - .filter(|t| t.benched()) - .map(|t| BuildProposal { - target: t, - profile, - required: !required_features_filterable, - }) - .collect::>(), - CompileMode::Test => { - let mut base = targets - .iter() - .filter(|t| t.tested()) - .map(|t| BuildProposal { - target: t, - profile: if t.is_example() { dep } else { profile }, - required: !required_features_filterable, - }) - .collect::>(); - - // Always compile the library if we're testing everything as - // it'll be needed for doctests - if let Some(t) = targets.iter().find(|t| t.is_lib()) { - if t.doctested() { - base.push(BuildProposal { - target: t, - profile: dep, - required: !required_features_filterable, - }); + resolve: &Resolve, +) -> CargoResult>> { + let mut units = Vec::new(); + + // Helper for creating a Unit struct. + let new_unit = + |pkg: &'a Package, target: &'a Target, mode: CompileMode, profile_for: ProfileFor| { + let actual_profile_for = if profile_for != ProfileFor::Any { + profile_for + } else if mode.is_any_test() { + // Force dependencies of this unit to not set `panic`. + ProfileFor::TestDependency + } else { + profile_for + }; + let actual_mode = match mode { + CompileMode::Test => { + if target.is_example() { + // Examples are included as regular binaries to verify + // that they compile. + CompileMode::Build + } else { + CompileMode::Test + } } + CompileMode::Build => match *target.kind() { + TargetKind::Test => CompileMode::Test, + TargetKind::Bench => CompileMode::Bench, + _ => CompileMode::Build, + }, + _ => mode, + }; + let kind = if target.for_host() { + Kind::Host + } else { + default_arch_kind + }; + Unit { + pkg, + target, + profile_for: actual_profile_for, + kind, + mode: actual_mode, } - base - } - CompileMode::Build | CompileMode::Check { .. } => targets - .iter() - .filter(|t| t.is_bin() || t.is_lib()) - .map(|t| BuildProposal { - target: t, - profile, - required: !required_features_filterable, - }) - .collect(), - CompileMode::Doc { .. } => targets - .iter() - .filter(|t| { - t.documented() - && (!t.is_bin() || !targets.iter().any(|l| l.is_lib() && l.name() == t.name())) - }) - .map(|t| BuildProposal { - target: t, - profile, - required: !required_features_filterable, - }) - .collect(), - CompileMode::Doctest => { - if let Some(t) = targets.iter().find(|t| t.is_lib()) { - if t.doctested() { - return vec![ - BuildProposal { - target: t, - profile, - required: !required_features_filterable, - }, - ]; + }; + + for pkg in packages { + let features = resolve_all_features(resolve, pkg.package_id()); + // Create a list of proposed targets. The `bool` value indicates + // whether or not all required features *must* be present. If false, + // and the features are not available, then it will be silently + // skipped. Generally, targets specified by name (`--bin foo`) are + // required, all others can be silently skipped if features are + // missing. + let mut proposals: Vec<(Unit<'a>, bool)> = Vec::new(); + + match *filter { + CompileFilter::Default { + required_features_filterable, + } => { + let default_units = generate_default_targets(pkg.targets(), mode) + .iter() + .map(|t| { + ( + new_unit(pkg, t, mode, ProfileFor::Any), + !required_features_filterable, + ) + }) + .collect::>(); + proposals.extend(default_units); + if mode == CompileMode::Test { + // Include the lib as it will be required for doctests. + if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) { + proposals.push(( + new_unit(pkg, t, CompileMode::Build, ProfileFor::TestDependency), + false, + )); + } } } + CompileFilter::Only { + all_targets, + lib, + ref bins, + ref examples, + ref tests, + ref benches, + } => { + if lib { + if let Some(target) = pkg.targets().iter().find(|t| t.is_lib()) { + proposals.push((new_unit(pkg, target, mode, ProfileFor::Any), false)); + } else if !all_targets { + bail!("no library targets found") + } + } + // If --tests was specified, add all targets that would be + // generated by `cargo test`. + let test_filter = match *tests { + FilterRule::All => Target::tested, + FilterRule::Just(_) => Target::is_test, + }; + let test_mode = match mode { + CompileMode::Build => CompileMode::Test, + CompileMode::Check { .. } => CompileMode::Check { test: true }, + _ => mode, + }; + // If --benches was specified, add all targets that would be + // generated by `cargo bench`. + let bench_filter = match *benches { + FilterRule::All => Target::benched, + FilterRule::Just(_) => Target::is_bench, + }; + let bench_mode = match mode { + CompileMode::Build => CompileMode::Bench, + CompileMode::Check { .. } => CompileMode::Check { test: true }, + _ => mode, + }; - Vec::new() + proposals.extend( + list_rule_targets(pkg, bins, "bin", Target::is_bin)? + .into_iter() + .map(|(t, required)| (new_unit(pkg, t, mode, ProfileFor::Any), required)) + .chain( + list_rule_targets(pkg, examples, "example", Target::is_example)? + .into_iter() + .map(|(t, required)| { + (new_unit(pkg, t, mode, ProfileFor::Any), required) + }), + ) + .chain( + list_rule_targets(pkg, tests, "test", test_filter)? + .into_iter() + .map(|(t, required)| { + (new_unit(pkg, t, test_mode, ProfileFor::Any), required) + }), + ) + .chain( + list_rule_targets(pkg, benches, "bench", bench_filter)? + .into_iter() + .map(|(t, required)| { + (new_unit(pkg, t, bench_mode, ProfileFor::Any), required) + }), + ) + .collect::>(), + ); + } } - } -} -/// Given a filter rule and some context, propose a list of targets -fn propose_indicated_targets<'a>( - pkg: &'a Package, - rule: &FilterRule, - desc: &'static str, - is_expected_kind: fn(&Target) -> bool, - profile: &'a Profile, -) -> CargoResult>> { - match *rule { - FilterRule::All => { - let result = pkg.targets() - .iter() - .filter(|t| is_expected_kind(t)) - .map(|t| BuildProposal { - target: t, - profile, - required: false, - }); - Ok(result.collect()) + // If any integration tests/benches are being tested, make sure that + // binaries are built as well. + if !mode.is_check() && proposals.iter().any(|&(ref unit, _)| { + unit.mode.is_any_test() && (unit.target.is_test() || unit.target.is_bench()) + }) { + proposals.extend( + pkg.targets() + .iter() + .filter(|t| t.is_bin()) + .map(|t| (new_unit(pkg, t, CompileMode::Build, ProfileFor::Any), false)), + ); } - FilterRule::Just(ref names) => { - let mut targets = Vec::new(); - for name in names { - let target = pkg.targets() + + // Only include targets that are libraries or have all required + // features available. + for (unit, required) in proposals { + let unavailable_features = match unit.target.required_features() { + Some(rf) => rf.iter().filter(|f| !features.contains(*f)).collect(), + None => Vec::new(), + }; + if unit.target.is_lib() || unavailable_features.is_empty() { + units.push(unit); + } else if required { + let required_features = unit.target.required_features().unwrap(); + let quoted_required_features: Vec = required_features .iter() - .find(|t| t.name() == *name && is_expected_kind(t)); - let t = match target { - Some(t) => t, - None => { - let suggestion = pkg.find_closest_target(name, is_expected_kind); - match suggestion { - Some(s) => { - let suggested_name = s.name(); - bail!( - "no {} target named `{}`\n\nDid you mean `{}`?", - desc, - name, - suggested_name - ) - } - None => bail!("no {} target named `{}`", desc, name), - } - } - }; - debug!("found {} `{}`", desc, name); - targets.push(BuildProposal { - target: t, - profile, - required: true, - }); + .map(|s| format!("`{}`", s)) + .collect(); + bail!( + "target `{}` requires the features: {}\n\ + Consider enabling them by passing e.g. `--features=\"{}\"`", + unit.target.name(), + quoted_required_features.join(", "), + required_features.join(" ") + ); } - Ok(targets) + // else, silently skip target. } } + Ok(units) } -/// Collect the targets that are libraries or have all required features available. -fn filter_compatible_targets<'a>( - mut proposals: Vec>, - features: &HashSet, -) -> CargoResult> { - let mut compatible = Vec::with_capacity(proposals.len()); - for proposal in proposals.drain(..) { - let unavailable_features = match proposal.target.required_features() { - Some(rf) => rf.iter().filter(|f| !features.contains(*f)).collect(), - None => Vec::new(), - }; - if proposal.target.is_lib() || unavailable_features.is_empty() { - compatible.push((proposal.target, proposal.profile)); - } else if proposal.required { - let required_features = proposal.target.required_features().unwrap(); - let quoted_required_features: Vec = required_features +fn resolve_all_features( + resolve_with_overrides: &Resolve, + package_id: &PackageId, +) -> HashSet { + let mut features = resolve_with_overrides.features(package_id).clone(); + + // Include features enabled for use by dependencies so targets can also use them with the + // required-features field when deciding whether to be built or skipped. + for (dep, _) in resolve_with_overrides.deps(package_id) { + for feature in resolve_with_overrides.features(dep) { + features.insert(dep.name().to_string() + "/" + feature); + } + } + + features +} + +/// Given a list of all targets for a package, filters out only the targets +/// that are automatically included when the user doesn't specify any targets. +fn generate_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> { + match mode { + CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(), + CompileMode::Test => targets.iter().filter(|t| t.tested()).collect(), + CompileMode::Build | CompileMode::Check { .. } => targets + .iter() + .filter(|t| t.is_bin() || t.is_lib()) + .collect(), + CompileMode::Doc { .. } => { + // `doc` does lib and bins (bin with same name as lib is skipped). + targets .iter() - .map(|s| format!("`{}`", s)) - .collect(); - bail!( - "target `{}` requires the features: {}\n\ - Consider enabling them by passing e.g. `--features=\"{}\"`", - proposal.target.name(), - quoted_required_features.join(", "), - required_features.join(" ") - ); + .filter(|t| { + t.documented() + && (!t.is_bin() + || !targets.iter().any(|l| l.is_lib() && l.name() == t.name())) + }) + .collect() } + CompileMode::Doctest => { + // `test --doc`` + targets + .iter() + .find(|t| t.is_lib() && t.doctested()) + .into_iter() + .collect() + } + CompileMode::RunCustomBuild => panic!("Invalid mode"), } - Ok(compatible) } -/// Given the configuration for a build, this function will generate all -/// target/profile combinations needed to be built. -fn generate_targets<'a>( +/// Returns a list of targets based on command-line target selection flags. +/// The return value is a list of `(Target, bool)` pairs. The `bool` value +/// indicates whether or not all required features *must* be present. +fn list_rule_targets<'a>( pkg: &'a Package, - profiles: &'a Profiles, - mode: CompileMode, - filter: &CompileFilter, - features: &HashSet, - release: bool, -) -> CargoResult> { - let build = if release { - &profiles.release - } else { - &profiles.dev - }; - let test = if release { - &profiles.bench - } else { - &profiles.test - }; - let profile = match mode { - CompileMode::Test => test, - CompileMode::Bench => &profiles.bench, - CompileMode::Build => build, - CompileMode::Check { test: false } => &profiles.check, - CompileMode::Check { test: true } => &profiles.check_test, - CompileMode::Doc { .. } => &profiles.doc, - CompileMode::Doctest => &profiles.doctest, - }; - - let test_profile = if profile.check { - &profiles.check_test - } else if mode == CompileMode::Build { - test - } else { - profile - }; - - let bench_profile = if profile.check { - &profiles.check_test - } else if mode == CompileMode::Build { - &profiles.bench - } else { - profile - }; + rule: &FilterRule, + target_desc: &'static str, + is_expected_kind: fn(&Target) -> bool, +) -> CargoResult> { + match *rule { + FilterRule::All => Ok(pkg.targets() + .iter() + .filter(|t| is_expected_kind(t)) + .map(|t| (t, false)) + .collect()), + FilterRule::Just(ref names) => names + .iter() + .map(|name| find_target(pkg, name, target_desc, is_expected_kind)) + .collect(), + } +} - let targets = match *filter { - CompileFilter::Default { - required_features_filterable, - } => { - let deps = if release { - &profiles.bench_deps - } else { - &profiles.test_deps - }; - generate_default_targets( - mode, - pkg.targets(), - profile, - deps, - required_features_filterable, - ) - } - CompileFilter::Only { - all_targets, - lib, - ref bins, - ref examples, - ref tests, - ref benches, - } => { - let mut targets = Vec::new(); - - if lib { - if let Some(t) = pkg.targets().iter().find(|t| t.is_lib()) { - targets.push(BuildProposal { - target: t, - profile, - required: true, - }); - } else if !all_targets { - bail!("no library targets found") - } +/// Find the target for a specifically named target. +fn find_target<'a>( + pkg: &'a Package, + target_name: &str, + target_desc: &'static str, + is_expected_kind: fn(&Target) -> bool, +) -> CargoResult<(&'a Target, bool)> { + match pkg.targets() + .iter() + .find(|t| t.name() == target_name && is_expected_kind(t)) + { + // When a target is specified by name, required features *must* be + // available. + Some(t) => Ok((t, true)), + None => { + let suggestion = pkg.targets() + .iter() + .filter(|t| is_expected_kind(t)) + .map(|t| (lev_distance(target_name, t.name()), t)) + .filter(|&(d, _)| d < 4) + .min_by_key(|t| t.0) + .map(|t| t.1); + match suggestion { + Some(s) => bail!( + "no {} target named `{}`\n\nDid you mean `{}`?", + target_desc, + target_name, + s.name() + ), + None => bail!("no {} target named `{}`", target_desc, target_name), } - targets.append(&mut propose_indicated_targets( - pkg, - bins, - "bin", - Target::is_bin, - profile, - )?); - targets.append(&mut propose_indicated_targets( - pkg, - examples, - "example", - Target::is_example, - profile, - )?); - // If --tests was specified, add all targets that would be - // generated by `cargo test`. - let test_filter = match *tests { - FilterRule::All => Target::tested, - FilterRule::Just(_) => Target::is_test, - }; - targets.append(&mut propose_indicated_targets( - pkg, - tests, - "test", - test_filter, - test_profile, - )?); - // If --benches was specified, add all targets that would be - // generated by `cargo bench`. - let bench_filter = match *benches { - FilterRule::All => Target::benched, - FilterRule::Just(_) => Target::is_bench, - }; - targets.append(&mut propose_indicated_targets( - pkg, - benches, - "bench", - bench_filter, - bench_profile, - )?); - targets } - }; - - filter_compatible_targets(targets, features) + } } diff --git a/src/cargo/util/machine_message.rs b/src/cargo/util/machine_message.rs index d2225eacd0b..3104d4b6060 100644 --- a/src/cargo/util/machine_message.rs +++ b/src/cargo/util/machine_message.rs @@ -1,7 +1,7 @@ use serde::ser; use serde_json::{self, Value}; -use core::{PackageId, Profile, Target}; +use core::{PackageId, Target}; pub trait Message: ser::Serialize { fn reason(&self) -> &str; @@ -30,7 +30,7 @@ impl<'a> Message for FromCompiler<'a> { pub struct Artifact<'a> { pub package_id: &'a PackageId, pub target: &'a Target, - pub profile: &'a Profile, + pub profile: ArtifactProfile, pub features: Vec, pub filenames: Vec, pub fresh: bool, @@ -42,6 +42,18 @@ impl<'a> Message for Artifact<'a> { } } +/// This is different from the regular `Profile` to maintain backwards +/// compatibility (in particular, `test` is no longer in `Profile`, but we +/// still want it to be included here). +#[derive(Serialize)] +pub struct ArtifactProfile { + pub opt_level: &'static str, + pub debuginfo: Option, + pub debug_assertions: bool, + pub overflow_checks: bool, + pub test: bool, +} + #[derive(Serialize)] pub struct BuildScript<'a> { pub package_id: &'a PackageId, diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 635e7749396..34aad6e05c6 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -12,11 +12,12 @@ use serde_ignored; use toml; use url::Url; -use core::{GitReference, PackageIdSpec, Profiles, SourceId, WorkspaceConfig, WorkspaceRootConfig}; +use core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig}; use core::{Dependency, Manifest, PackageId, Summary, Target}; use core::{Edition, EitherManifest, Feature, Features, VirtualManifest}; use core::dependency::{Kind, Platform}; -use core::manifest::{LibKind, Lto, ManifestMetadata, Profile}; +use core::manifest::{LibKind, ManifestMetadata}; +use core::profiles::Profiles; use sources::CRATES_IO; use util::paths; use util::{self, Config, ToUrl}; @@ -243,8 +244,29 @@ pub struct TomlProfiles { release: Option, } +impl TomlProfiles { + fn validate(&self, features: &Features) -> CargoResult<()> { + if let Some(ref test) = self.test { + test.validate("test", features)?; + } + if let Some(ref doc) = self.doc { + doc.validate("doc", features)?; + } + if let Some(ref bench) = self.bench { + bench.validate("bench", features)?; + } + if let Some(ref dev) = self.dev { + dev.validate("dev", features)?; + } + if let Some(ref release) = self.release { + release.validate("release", features)?; + } + Ok(()) + } +} + #[derive(Clone, Debug)] -pub struct TomlOptLevel(String); +pub struct TomlOptLevel(pub String); impl<'de> de::Deserialize<'de> for TomlOptLevel { fn deserialize(d: D) -> Result @@ -347,20 +369,65 @@ impl<'de> de::Deserialize<'de> for U32OrBool { } #[derive(Deserialize, Serialize, Clone, Debug, Default)] +#[serde(rename_all = "kebab-case")] pub struct TomlProfile { - #[serde(rename = "opt-level")] - opt_level: Option, - lto: Option, - #[serde(rename = "codegen-units")] - codegen_units: Option, - debug: Option, - #[serde(rename = "debug-assertions")] - debug_assertions: Option, - rpath: Option, - panic: Option, - #[serde(rename = "overflow-checks")] - overflow_checks: Option, - incremental: Option, + pub opt_level: Option, + pub lto: Option, + pub codegen_units: Option, + pub debug: Option, + pub debug_assertions: Option, + pub rpath: Option, + pub panic: Option, + pub overflow_checks: Option, + pub incremental: Option, + pub overrides: Option>, + #[serde(rename = "build_override")] + pub build_override: Option>, +} + +impl TomlProfile { + fn validate(&self, name: &str, features: &Features) -> CargoResult<()> { + if let Some(ref profile) = self.build_override { + features.require(Feature::profile_overrides())?; + profile.validate_override()?; + } + if let Some(ref override_map) = self.overrides { + features.require(Feature::profile_overrides())?; + for profile in override_map.values() { + profile.validate_override()?; + } + } + + match name { + "dev" | "release" => {} + _ => { + if self.overrides.is_some() || self.build_override.is_some() { + bail!( + "Profile overrides may only be specified for `dev` + or `release` profile, not {}.", + name + ); + } + } + } + Ok(()) + } + + fn validate_override(&self) -> CargoResult<()> { + if self.overrides.is_some() || self.build_override.is_some() { + bail!("Profile overrides cannot be nested."); + } + if self.panic.is_some() { + bail!("`panic` may not be specified in a build override.") + } + if self.lto.is_some() { + bail!("`lto` may not be specified in a build override.") + } + if self.rpath.is_some() { + bail!("`rpath` may not be specified in a build override.") + } + Ok(()) + } } #[derive(Clone, Debug, Serialize)] @@ -794,6 +861,9 @@ impl TomlManifest { `[workspace]`, only one can be specified" ), }; + if let Some(ref profiles) = me.profile { + profiles.validate(&features)?; + } let profiles = build_profiles(&me.profile); let publish = match project.publish { Some(VecStringOrBool::VecString(ref vecstring)) => { @@ -1004,6 +1074,10 @@ impl TomlManifest { } } } + + pub fn has_profiles(&self) -> bool { + self.profile.is_some() + } } /// Will check a list of build targets, and make sure the target names are unique within a vector. @@ -1279,98 +1353,11 @@ impl fmt::Debug for PathValue { fn build_profiles(profiles: &Option) -> Profiles { let profiles = profiles.as_ref(); - let mut profiles = Profiles { - release: merge( - Profile::default_release(), - profiles.and_then(|p| p.release.as_ref()), - ), - dev: merge( - Profile::default_dev(), - profiles.and_then(|p| p.dev.as_ref()), - ), - test: merge( - Profile::default_test(), - profiles.and_then(|p| p.test.as_ref()), - ), - test_deps: merge( - Profile::default_dev(), - profiles.and_then(|p| p.dev.as_ref()), - ), - bench: merge( - Profile::default_bench(), - profiles.and_then(|p| p.bench.as_ref()), - ), - bench_deps: merge( - Profile::default_release(), - profiles.and_then(|p| p.release.as_ref()), - ), - doc: merge( - Profile::default_doc(), - profiles.and_then(|p| p.doc.as_ref()), - ), - custom_build: Profile::default_custom_build(), - check: merge( - Profile::default_check(), - profiles.and_then(|p| p.dev.as_ref()), - ), - check_test: merge( - Profile::default_check_test(), - profiles.and_then(|p| p.dev.as_ref()), - ), - doctest: Profile::default_doctest(), - }; - // The test/bench targets cannot have panic=abort because they'll all get - // compiled with --test which requires the unwind runtime currently - profiles.test.panic = None; - profiles.bench.panic = None; - profiles.test_deps.panic = None; - profiles.bench_deps.panic = None; - return profiles; - - fn merge(profile: Profile, toml: Option<&TomlProfile>) -> Profile { - let &TomlProfile { - ref opt_level, - ref lto, - codegen_units, - ref debug, - debug_assertions, - rpath, - ref panic, - ref overflow_checks, - ref incremental, - } = match toml { - Some(toml) => toml, - None => return profile, - }; - let debug = match *debug { - Some(U32OrBool::U32(debug)) => Some(Some(debug)), - Some(U32OrBool::Bool(true)) => Some(Some(2)), - Some(U32OrBool::Bool(false)) => Some(None), - None => None, - }; - Profile { - opt_level: opt_level - .clone() - .unwrap_or(TomlOptLevel(profile.opt_level)) - .0, - lto: match *lto { - Some(StringOrBool::Bool(b)) => Lto::Bool(b), - Some(StringOrBool::String(ref n)) => Lto::Named(n.clone()), - None => profile.lto, - }, - codegen_units, - rustc_args: None, - rustdoc_args: None, - debuginfo: debug.unwrap_or(profile.debuginfo), - debug_assertions: debug_assertions.unwrap_or(profile.debug_assertions), - overflow_checks: overflow_checks.unwrap_or(profile.overflow_checks), - rpath: rpath.unwrap_or(profile.rpath), - test: profile.test, - doc: profile.doc, - run_custom_build: profile.run_custom_build, - check: profile.check, - panic: panic.clone().or(profile.panic), - incremental: incremental.unwrap_or(profile.incremental), - } - } + Profiles::new( + profiles.and_then(|p| p.dev.clone()), + profiles.and_then(|p| p.release.clone()), + profiles.and_then(|p| p.test.clone()), + profiles.and_then(|p| p.bench.clone()), + profiles.and_then(|p| p.doc.clone()), + ) } diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 1a305ba1fd9..bd8ccbdada5 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -192,3 +192,38 @@ cargo-features = ["edition"] ... rust = "2018" ``` + + +### Profile Overrides +* Tracking Issue: [rust-lang/rust#48683](https://github.com/rust-lang/rust/issues/48683) +* RFC: [#2282](https://github.com/rust-lang/rfcs/blob/master/text/2282-profile-dependencies.md) + +Profiles can be overridden for specific packages and custom build scripts. +The general format looks like this: + +```toml +cargo-features = ["profile-overrides"] + +[package] +... + +[profile.dev] +opt-level = 0 +debug = true + +# the `image` crate will be compiled with -Copt-level=3 +[profile.dev.overrides.image] +opt-level = 3 + +# All dependencies (but not this crate itself) will be compiled +# with -Copt-level=2 . This includes build dependencies. +[profile.dev.overrides."*"] +opt-level = 2 + +# Build scripts and their dependencies will be compiled with -Copt-level=3 +# By default, build scripts use the same rules as the rest of the profile +[profile.dev.build_override] +opt-level = 3 +``` + +Overrides can only be specified for dev and release profiles. diff --git a/tests/testsuite/check.rs b/tests/testsuite/check.rs index 9c7097075f7..8e3667c2a88 100644 --- a/tests/testsuite/check.rs +++ b/tests/testsuite/check.rs @@ -747,7 +747,7 @@ fn check_filters() { .with_stderr_contains("[..] --crate-name foo src[/]lib.rs [..] --test [..]") .with_stderr_contains("[..] --crate-name foo src[/]lib.rs --crate-type lib [..]") .with_stderr_contains("[..] --crate-name foo src[/]main.rs [..] --test [..]") - .with_stderr_contains("[..] --crate-name foo src[/]main.rs --crate-type bin [..]") + // .with_stderr_contains("[..] --crate-name foo src[/]main.rs --crate-type bin [..]") .with_stderr_contains("[..]unused_unit_lib[..]") .with_stderr_contains("[..]unused_unit_bin[..]") .with_stderr_contains("[..]unused_normal_lib[..]") @@ -757,6 +757,8 @@ fn check_filters() { .with_stderr_contains("[..]unused_unit_ex1[..]") .with_stderr_does_not_contain("[..]unused_normal_b1[..]") .with_stderr_does_not_contain("[..]unused_unit_b1[..]"), + // with_stderr_does_not_contain --crate-type lib + // with_stderr_does_not_contain --crate-type bin ); p.root().join("target").rm_rf(); assert_that( @@ -764,7 +766,7 @@ fn check_filters() { execs() .with_status(0) .with_stderr_contains("[..]unused_normal_lib[..]") - .with_stderr_contains("[..]unused_normal_bin[..]") + // .with_stderr_contains("[..]unused_normal_bin[..]") .with_stderr_contains("[..]unused_unit_t1[..]") .with_stderr_does_not_contain("[..]unused_unit_lib[..]") .with_stderr_does_not_contain("[..]unused_unit_bin[..]") diff --git a/tests/testsuite/doc.rs b/tests/testsuite/doc.rs index 82638b0408c..ac9d269d3b1 100644 --- a/tests/testsuite/doc.rs +++ b/tests/testsuite/doc.rs @@ -8,6 +8,7 @@ use cargotest::support::{execs, project, path2url}; use cargotest::support::registry::Package; use hamcrest::{assert_that, existing_dir, existing_file, is_not}; use cargo::util::ProcessError; +use glob::glob; #[test] fn simple() { @@ -163,6 +164,20 @@ fn doc_deps() { assert_that(&p.root().join("target/doc/foo/index.html"), existing_file()); assert_that(&p.root().join("target/doc/bar/index.html"), existing_file()); + // Verify that it only emits rmeta for the dependency. + assert_eq!( + glob(&p.root().join("target/debug/**/*.rlib").to_str().unwrap()) + .unwrap() + .count(), + 0 + ); + assert_eq!( + glob(&p.root().join("target/debug/deps/libbar-*.rmeta").to_str().unwrap()) + .unwrap() + .count(), + 1 + ); + assert_that( p.cargo("doc") .env("RUST_LOG", "cargo::ops::cargo_rustc::fingerprint"), diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs index 999de518cee..8b644055a08 100644 --- a/tests/testsuite/profiles.rs +++ b/tests/testsuite/profiles.rs @@ -2,6 +2,7 @@ use std::env; use cargotest::is_nightly; use cargotest::support::{execs, project}; +use cargotest::ChannelChanger; use hamcrest::assert_that; #[test] @@ -340,3 +341,115 @@ fn profile_in_virtual_manifest_works() { ), ); } + +#[test] +fn dep_override_gated() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.dev.build_override] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs().with_status(101).with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + feature `profile-overrides` is required + +consider adding `cargo-features = [\"profile-overrides\"]` to the manifest +", + ), + ); + + let p = project("foo") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.dev.overrides."*"] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs().with_status(101).with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + feature `profile-overrides` is required + +consider adding `cargo-features = [\"profile-overrides\"]` to the manifest +", + ), + ); +} + +#[test] +fn dep_override_basic() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-overrides"] + + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = {path = "bar"} + + [profile.dev.overrides.bar] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build -v").masquerade_as_nightly_cargo(), + execs().with_status(0).with_stderr( +"[COMPILING] bar v0.0.1 ([..]) +[RUNNING] `rustc --crate-name bar [..] -C opt-level=3 [..]` +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + // TODO: does_not_contain does not support patterns! + // .with_stderr_does_not_contain("\ + // `rustc --crate-name bar[..]-C opt-level=3"), + ); + +} diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index a1953003fde..19afe54cc8c 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -1649,6 +1649,7 @@ fn test_run_implicit_test_target() { ) .build(); + // TODO FIXME: This needs to better verify that examples are not built. assert_that( prj.cargo("test").arg("--tests"), execs() @@ -1658,8 +1659,7 @@ fn test_run_implicit_test_target() { [COMPILING] foo v0.0.1 ({dir}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] target[/]debug[/]deps[/]mybin-[..][EXE] -[RUNNING] target[/]debug[/]deps[/]mytest-[..][EXE] -[RUNNING] target[/]debug[/]examples[/]myexm-[..][EXE]", +[RUNNING] target[/]debug[/]deps[/]mytest-[..][EXE]", dir = prj.url() )) .with_stdout_contains("test test_in_test ... ok"), @@ -1742,13 +1742,13 @@ fn test_run_implicit_example_target() { ) .build(); + // TODO FIXME - verify example does NOT get run. assert_that( prj.cargo("test").arg("--examples"), execs().with_status(0).with_stderr(format!( "\ [COMPILING] foo v0.0.1 ({dir}) -[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] -[RUNNING] target[/]debug[/]examples[/]myexm-[..][EXE]", +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", dir = prj.url() )), ); From 62d6f0d023359b8472b52ca4bec5029ab17667cb Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 18 Apr 2018 07:03:17 -0700 Subject: [PATCH 02/23] Minor cleanup. --- src/cargo/util/toml/mod.rs | 6 +++--- tests/testsuite/profiles.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 34aad6e05c6..c8ae4839dba 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -418,13 +418,13 @@ impl TomlProfile { bail!("Profile overrides cannot be nested."); } if self.panic.is_some() { - bail!("`panic` may not be specified in a build override.") + bail!("`panic` may not be specified in a profile override.") } if self.lto.is_some() { - bail!("`lto` may not be specified in a build override.") + bail!("`lto` may not be specified in a profile override.") } if self.rpath.is_some() { - bail!("`rpath` may not be specified in a build override.") + bail!("`rpath` may not be specified in a profile override.") } Ok(()) } diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs index 8b644055a08..978efd79ce6 100644 --- a/tests/testsuite/profiles.rs +++ b/tests/testsuite/profiles.rs @@ -343,7 +343,7 @@ fn profile_in_virtual_manifest_works() { } #[test] -fn dep_override_gated() { +fn profile_override_gated() { let p = project("foo") .file( "Cargo.toml", @@ -406,7 +406,7 @@ consider adding `cargo-features = [\"profile-overrides\"]` to the manifest } #[test] -fn dep_override_basic() { +fn profile_override_basic() { let p = project("foo") .file( "Cargo.toml", From 73660740c8447dd3f8a65f8b68333027b4b6ffc9 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 18 Apr 2018 11:41:58 -0700 Subject: [PATCH 03/23] Move `Profile` back into `Unit` as a copy. The de-duplication of Units just doesn't work without it. --- .../compiler/context/compilation_files.rs | 2 +- src/cargo/core/compiler/context/mod.rs | 36 ++--- .../compiler/context/unit_dependencies.rs | 132 ++++++++++-------- src/cargo/core/compiler/custom_build.rs | 5 +- src/cargo/core/compiler/fingerprint.rs | 10 +- src/cargo/core/compiler/job_queue.rs | 12 +- src/cargo/core/compiler/mod.rs | 7 +- src/cargo/core/profiles.rs | 67 +-------- src/cargo/ops/cargo_clean.rs | 9 +- src/cargo/ops/cargo_compile.rs | 34 ++--- 10 files changed, 128 insertions(+), 186 deletions(-) diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index f8b0ca5c952..e44414cb336 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -425,7 +425,7 @@ fn compute_metadata<'a, 'cfg>( // Throw in the profile we're compiling with. This helps caching // panic=abort and panic=unwind artifacts, additionally with various // settings like debuginfo and whatnot. - cx.unit_profile(unit).hash(&mut hasher); + unit.profile.hash(&mut hasher); unit.mode.hash(&mut hasher); // Artifacts compiled for the host should have a different metadata diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index 02380498908..284515814e4 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -9,7 +9,7 @@ use jobserver::Client; use core::{Package, PackageId, PackageSet, Resolve, Target}; use core::{Dependency, Workspace}; -use core::profiles::{Profile, ProfileFor, Profiles}; +use core::profiles::{Profile, Profiles}; use ops::CompileMode; use util::{internal, profile, Cfg, CfgExpr, Config}; use util::errors::{CargoResult, CargoResultExt}; @@ -55,9 +55,9 @@ pub struct Unit<'a> { /// to be confused with *target-triple* (or *target architecture* ...), the target arch for a /// build. pub target: &'a Target, - /// This indicates the purpose of the target for profile selection. See - /// `ProfileFor` for more details. - pub profile_for: ProfileFor, + /// The profile contains information about *how* the build should be run, including debug + /// level, etc. + pub profile: Profile, /// Whether this compilation unit is for the host or target architecture. /// /// For example, when @@ -103,7 +103,6 @@ pub struct Context<'a, 'cfg: 'a> { incremental_env: Option, unit_dependencies: HashMap, Vec>>, - unit_profiles: HashMap, Profile>, files: Option>, } @@ -166,7 +165,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> { build_script_overridden: HashSet::new(), unit_dependencies: HashMap::new(), - unit_profiles: HashMap::new(), files: None, extra_compiler_args, }; @@ -328,15 +326,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> { let deps = build_unit_dependencies(units, self)?; self.unit_dependencies = deps; - self.unit_profiles = self.profiles.build_unit_profiles(units, self); - let files = CompilationFiles::new( - units, - host_layout, - target_layout, - export_dir, - self.ws, - self, - ); + let files = + CompilationFiles::new(units, host_layout, target_layout, export_dir, self.ws, self); self.files = Some(files); Ok(()) } @@ -490,11 +481,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { self.build_config.jobs } - pub fn incremental_args( - &self, - unit: &Unit, - profile_incremental: bool, - ) -> CargoResult> { + pub fn incremental_args(&self, unit: &Unit) -> CargoResult> { // There's a number of ways to configure incremental compilation right // now. In order of descending priority (first is highest priority) we // have: @@ -517,7 +504,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { // have it enabled by default while release profiles have it disabled // by default. let global_cfg = self.config.get_bool("build.incremental")?.map(|c| c.val); - let incremental = match (self.incremental_env, global_cfg, profile_incremental) { + let incremental = match (self.incremental_env, global_cfg, unit.profile.incremental) { (Some(v), _, _) => v, (None, Some(false), _) => false, (None, _, other) => other, @@ -571,13 +558,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Kind::Target => &self.target_info, } } - - /// Returns the profile for a given unit. - /// This should not be called until profiles are computed in - /// `prepare_units`. - pub fn unit_profile(&self, unit: &Unit<'a>) -> &Profile { - &self.unit_profiles[unit] - } } /// Acquire extra flags to pass to the compiler from various locations. diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs index 7460f13cace..7f9eeabc3b3 100644 --- a/src/cargo/core/compiler/context/unit_dependencies.rs +++ b/src/cargo/core/compiler/context/unit_dependencies.rs @@ -18,7 +18,7 @@ use super::{Context, Kind, Unit}; use core::dependency::Kind as DepKind; use core::profiles::ProfileFor; -use core::Target; +use core::{Package, Target}; use ops::CompileMode; use std::collections::HashMap; use CargoResult; @@ -29,7 +29,13 @@ pub fn build_unit_dependencies<'a, 'cfg>( ) -> CargoResult, Vec>>> { let mut deps = HashMap::new(); for unit in roots.iter() { - deps_of(unit, cx, &mut deps, unit.profile_for)?; + // Dependencies of tests should not have `panic` set. + let profile_for = if unit.mode.is_any_test() { + ProfileFor::TestDependency + } else { + ProfileFor::Any + }; + deps_of(unit, cx, &mut deps, profile_for)?; } Ok(deps) @@ -108,16 +114,8 @@ fn compute_deps<'a, 'b, 'cfg>( }).filter_map(|(id, _)| match cx.get_package(id) { Ok(pkg) => pkg.targets().iter().find(|t| t.is_lib()).map(|t| { let mode = check_or_build_mode(&unit.mode, t); - Ok(( - Unit { - pkg, - target: t, - profile_for, - kind: unit.kind.for_target(t), - mode, - }, - profile_for, - )) + let unit = new_unit(cx, pkg, t, profile_for, unit.kind.for_target(t), mode); + Ok((unit, profile_for)) }), Err(e) => Some(Err(e)), }) @@ -138,7 +136,7 @@ fn compute_deps<'a, 'b, 'cfg>( if unit.target.is_lib() && unit.mode != CompileMode::Doctest { return Ok(ret); } - ret.extend(maybe_lib(unit, profile_for)); + ret.extend(maybe_lib(cx, unit, profile_for)); Ok(ret) } @@ -167,7 +165,7 @@ fn compute_deps_custom_build<'a, 'cfg>( target: not_custom_build, // The profile here isn't critical. We are just using this temp unit // for fetching dependencies that might have `links`. - profile_for: ProfileFor::Any, + profile: unit.profile, kind: unit.kind, mode: CompileMode::Build, }; @@ -180,13 +178,16 @@ fn compute_deps_custom_build<'a, 'cfg>( dep_build_script(unit) }) .chain(Some(( - Unit { - pkg: unit.pkg, - target: unit.target, - profile_for: ProfileFor::CustomBuild, - kind: Kind::Host, // build scripts always compiled for the host - mode: CompileMode::Build, - }, + new_unit( + cx, + unit.pkg, + unit.target, + ProfileFor::CustomBuild, + Kind::Host, // build scripts always compiled for the host + CompileMode::Build, + ), + // All dependencies of this unit should use profiles for custom + // builds. ProfileFor::CustomBuild, ))) .collect()) @@ -220,27 +221,25 @@ fn compute_deps_doc<'a, 'cfg>( // rustdoc only needs rmeta files for regular dependencies. // However, for plugins/proc-macros, deps should be built like normal. let mode = check_or_build_mode(&unit.mode, lib); - ret.push(( - Unit { - pkg: dep, - target: lib, - profile_for: ProfileFor::Any, - kind: unit.kind.for_target(lib), - mode, - }, + let unit = new_unit( + cx, + dep, + lib, ProfileFor::Any, - )); + unit.kind.for_target(lib), + mode, + ); + ret.push((unit, ProfileFor::Any)); if let CompileMode::Doc { deps: true } = unit.mode { - ret.push(( - Unit { - pkg: dep, - target: lib, - profile_for: ProfileFor::Any, - kind: unit.kind.for_target(lib), - mode: unit.mode, - }, + let unit = new_unit( + cx, + dep, + lib, ProfileFor::Any, - )); + unit.kind.for_target(lib), + unit.mode, + ); + ret.push((unit, ProfileFor::Any)); } } @@ -249,24 +248,20 @@ fn compute_deps_doc<'a, 'cfg>( // If we document a binary, we need the library available if unit.target.is_bin() { - ret.extend(maybe_lib(unit, ProfileFor::Any)); + ret.extend(maybe_lib(cx, unit, ProfileFor::Any)); } Ok(ret) } -fn maybe_lib<'a>(unit: &Unit<'a>, profile_for: ProfileFor) -> Option<(Unit<'a>, ProfileFor)> { +fn maybe_lib<'a>( + cx: &Context, + unit: &Unit<'a>, + profile_for: ProfileFor, +) -> Option<(Unit<'a>, ProfileFor)> { let mode = check_or_build_mode(&unit.mode, unit.target); unit.pkg.targets().iter().find(|t| t.linkable()).map(|t| { - ( - Unit { - pkg: unit.pkg, - target: t, - profile_for, - kind: unit.kind.for_target(t), - mode, - }, - profile_for, - ) + let unit = new_unit(cx, unit.pkg, t, profile_for, unit.kind.for_target(t), mode); + (unit, profile_for) }) } @@ -283,16 +278,15 @@ fn dep_build_script<'a>(unit: &Unit<'a>) -> Option<(Unit<'a>, ProfileFor)> { .iter() .find(|t| t.is_custom_build()) .map(|t| { + // The profile stored in the Unit is the profile for the thing + // the custom build script is running for. + // TODO: Fix this for different profiles that don't affect the + // build.rs environment variables. ( Unit { pkg: unit.pkg, target: t, - // The profile for *running* the build script will actually be the - // target the build script is running for (so that the environment - // variables get set correctly). This is overridden in - // `Profiles::build_unit_profiles`, so the exact value here isn't - // critical. - profile_for: ProfileFor::CustomBuild, + profile: unit.profile, kind: unit.kind, mode: CompileMode::RunCustomBuild, }, @@ -318,3 +312,27 @@ fn check_or_build_mode(mode: &CompileMode, target: &Target) -> CompileMode { _ => CompileMode::Build, } } + +fn new_unit<'a>( + cx: &Context, + pkg: &'a Package, + target: &'a Target, + profile_for: ProfileFor, + kind: Kind, + mode: CompileMode, +) -> Unit<'a> { + let profile = cx.profiles.get_profile( + &pkg.name(), + cx.ws.is_member(pkg), + profile_for, + mode, + cx.build_config.release, + ); + Unit { + pkg, + target, + profile, + kind, + mode, + } +} diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index 3b00c478c8c..96638bf0f75 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -120,7 +120,6 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes // package's library profile. let to_exec = to_exec.into_os_string(); let mut cmd = cx.compilation.host_process(to_exec, unit.pkg)?; - let profile = cx.unit_profile(unit).clone(); cmd.env("OUT_DIR", &build_output) .env("CARGO_MANIFEST_DIR", unit.pkg.root()) .env("NUM_JOBS", &cx.jobs().to_string()) @@ -131,8 +130,8 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes Kind::Target => cx.build_config.target_triple(), }, ) - .env("DEBUG", &profile.debuginfo.is_some().to_string()) - .env("OPT_LEVEL", &profile.opt_level.to_string()) + .env("DEBUG", &unit.profile.debuginfo.is_some().to_string()) + .env("OPT_LEVEL", &unit.profile.opt_level.to_string()) .env( "PROFILE", if cx.build_config.release { diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 100912ceb3d..e00b1324c41 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -454,18 +454,10 @@ fn calculate<'a, 'cfg>( } else { cx.rustflags_args(unit)? }; - let profile_hash = { - let profile = cx.unit_profile(unit); - util::hash_u64(&( - profile, - unit.mode, - cx.incremental_args(unit, profile.incremental)?, - )) - }; let fingerprint = Arc::new(Fingerprint { rustc: util::hash_u64(&cx.build_config.rustc.verbose_version), target: util::hash_u64(&unit.target), - profile: profile_hash, + profile: util::hash_u64(&(&unit.profile, unit.mode, cx.incremental_args(unit)?)), // Note that .0 is hashed here, not .1 which is the cwd. That doesn't // actually affect the output artifact so there's no need to hash it. path: util::hash_u64(&super::path_args(cx, unit).0), diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs index 4fcaf4ea5ab..b6202a90541 100644 --- a/src/cargo/core/compiler/job_queue.rs +++ b/src/cargo/core/compiler/job_queue.rs @@ -9,7 +9,7 @@ use crossbeam::{self, Scope}; use jobserver::{Acquired, HelperThread}; use core::{PackageId, Target}; -use core::profiles::ProfileFor; +use core::profiles::Profile; use ops::CompileMode; use util::{Config, DependencyQueue, Dirty, Fresh, Freshness}; use util::{internal, profile, CargoResult, CargoResultExt, ProcessBuilder}; @@ -48,7 +48,7 @@ struct PendingBuild { struct Key<'a> { pkg: &'a PackageId, target: &'a Target, - profile_for: ProfileFor, + profile: Profile, kind: Kind, mode: CompileMode, } @@ -410,7 +410,7 @@ impl<'a> Key<'a> { Key { pkg: unit.pkg.package_id(), target: unit.target, - profile_for: unit.profile_for, + profile: unit.profile, kind: unit.kind, mode: unit.mode, } @@ -420,7 +420,7 @@ impl<'a> Key<'a> { let unit = Unit { pkg: cx.get_package(self.pkg)?, target: self.target, - profile_for: self.profile_for, + profile: self.profile, kind: self.kind, mode: self.mode, }; @@ -444,8 +444,8 @@ impl<'a> fmt::Debug for Key<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "{} => {}/{:?} => {:?}", - self.pkg, self.target, self.mode, self.kind + "{} => {}/{} => {:?}", + self.pkg, self.target, self.profile, self.kind ) } } diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 05c31e96acb..f0605aa0508 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -556,7 +556,7 @@ fn link_targets<'a, 'cfg>( let export_dir = cx.files().export_dir(unit); let package_id = unit.pkg.package_id().clone(); let target = unit.target.clone(); - let profile = cx.unit_profile(unit).clone(); + let profile = unit.profile; let unit_mode = unit.mode; let features = cx.resolve .features_sorted(&package_id) @@ -850,9 +850,8 @@ fn build_base_args<'a, 'cfg>( overflow_checks, rpath, ref panic, - incremental, .. - } = *cx.unit_profile(unit); + } = unit.profile; let test = unit.mode.is_any_test(); cmd.arg("--crate-name").arg(&unit.target.crate_name()); @@ -1022,7 +1021,7 @@ fn build_base_args<'a, 'cfg>( "linker=", cx.linker(unit.kind).map(|s| s.as_ref()), ); - cmd.args(&cx.incremental_args(unit, incremental)?); + cmd.args(&cx.incremental_args(unit)?); Ok(()) } diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index 615b0ac5486..e7aa0c88349 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -1,9 +1,6 @@ -use std::collections::HashMap; use std::{cmp, fmt, hash}; use ops::CompileMode; use util::toml::{StringOrBool, TomlProfile, U32OrBool}; -use core::compiler::Context; -use core::compiler::Unit; use core::interning::InternedString; /// Collection of all user profiles. @@ -51,7 +48,7 @@ impl Profiles { /// Retrieve the profile for a target. /// `is_member` is whether or not this package is a member of the /// workspace. - fn get_profile( + pub fn get_profile( &self, pkg_name: &str, is_member: bool, @@ -105,58 +102,6 @@ impl Profiles { self.dev.profile_for("", true, ProfileFor::Any) } } - - /// Build a mapping from Unit -> Profile for all the given units and all - /// of their dependencies. - pub fn build_unit_profiles<'a, 'cfg>( - &self, - units: &[Unit<'a>], - cx: &Context<'a, 'cfg>, - ) -> HashMap, Profile> { - let mut result = HashMap::new(); - for unit in units.iter() { - self.build_unit_profiles_rec(unit, None, cx, &mut result); - } - result - } - - fn build_unit_profiles_rec<'a, 'cfg>( - &self, - unit: &Unit<'a>, - parent: Option<&Unit<'a>>, - cx: &Context<'a, 'cfg>, - map: &mut HashMap, Profile>, - ) { - if !map.contains_key(unit) { - let for_unit = if unit.mode.is_run_custom_build() { - // The profile for *running* a custom build script is the - // target the script is running for. This allows - // `custom_build::build_work` to set the correct environment - // settings. - // - // In the case of `cargo clean -p`, it creates artificial - // units to compute filenames, without a dependency hierarchy, - // so we don't have a parent here. That should be OK, it - // only affects the environment variables used to *run* - // `build.rs`. - parent.unwrap_or(unit) - } else { - unit - }; - let profile = self.get_profile( - &for_unit.pkg.name(), - cx.ws.is_member(for_unit.pkg), - for_unit.profile_for, - for_unit.mode, - cx.build_config.release, - ); - map.insert(*unit, profile); - let deps = cx.dep_targets(unit); - for dep in &deps { - self.build_unit_profiles_rec(dep, Some(unit), cx, map); - } - } - } } /// An object used for handling the profile override hierarchy. @@ -176,7 +121,7 @@ struct ProfileMaker { impl ProfileMaker { fn profile_for(&self, pkg_name: &str, is_member: bool, profile_for: ProfileFor) -> Profile { - let mut profile = self.default.clone(); + let mut profile = self.default; if let Some(ref toml) = self.toml { merge_profile(&mut profile, toml); if profile_for == ProfileFor::CustomBuild { @@ -205,7 +150,7 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) { } match toml.lto { Some(StringOrBool::Bool(b)) => profile.lto = Lto::Bool(b), - Some(StringOrBool::String(ref n)) => profile.lto = Lto::Named(n.clone()), + Some(StringOrBool::String(ref n)) => profile.lto = Lto::Named(InternedString::new(n)), None => {} } if toml.codegen_units.is_some() { @@ -236,7 +181,7 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) { /// Profile settings used to determine which compiler flags to use for a /// target. -#[derive(Debug, Clone, Eq)] +#[derive(Debug, Clone, Copy, Eq)] pub struct Profile { pub name: &'static str, pub opt_level: InternedString, @@ -361,13 +306,13 @@ impl Profile { } /// The link-time-optimization setting. -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum Lto { /// False = no LTO /// True = "Fat" LTO Bool(bool), /// Named LTO settings like "thin". - Named(String), + Named(InternedString), } /// A flag used in `Unit` to indicate the purpose for the target. diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index 64eed368292..d3692d5cde4 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -50,10 +50,17 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { for kind in [Kind::Host, Kind::Target].iter() { for mode in CompileMode::all_modes() { for profile_for in ProfileFor::all_values() { + let profile = profiles.get_profile( + &pkg.name(), + ws.is_member(pkg), + profile_for, + mode, + opts.release, + ); units.push(Unit { pkg, target, - profile_for, + profile, kind: *kind, mode, }); diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 9850101b105..cca95a7797e 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -28,7 +28,7 @@ use std::sync::Arc; use core::compiler::{BuildConfig, Compilation, Context, DefaultExecutor, Executor}; use core::compiler::{Kind, Unit}; -use core::profiles::ProfileFor; +use core::profiles::{ProfileFor, Profiles}; use core::resolver::{Method, Resolve}; use core::{Package, Source, Target}; use core::{PackageId, PackageIdSpec, TargetKind, Workspace}; @@ -383,11 +383,14 @@ pub fn compile_ws<'a>( let mut extra_compiler_args = HashMap::new(); let units = generate_targets( + ws, + profiles, &to_builds, filter, default_arch_kind, mode, &resolve_with_overrides, + release, )?; if let Some(args) = extra_args { @@ -552,30 +555,28 @@ impl CompileFilter { /// 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: Kind, mode: CompileMode, resolve: &Resolve, + release: bool, ) -> CargoResult>> { let mut units = Vec::new(); // Helper for creating a Unit struct. let new_unit = |pkg: &'a Package, target: &'a Target, mode: CompileMode, profile_for: ProfileFor| { - let actual_profile_for = if profile_for != ProfileFor::Any { - profile_for - } else if mode.is_any_test() { - // Force dependencies of this unit to not set `panic`. - ProfileFor::TestDependency - } else { - profile_for - }; - let actual_mode = match mode { + let mode = match mode { CompileMode::Test => { if target.is_example() { // Examples are included as regular binaries to verify // that they compile. + // TODO: Broken - Dependencies of examples can be + // built with `panic`, which will cause `rustdoc` to + // fail with linking multiple binaries. CompileMode::Build } else { CompileMode::Test @@ -593,12 +594,14 @@ fn generate_targets<'a>( } else { default_arch_kind }; + let profile = + profiles.get_profile(&pkg.name(), ws.is_member(pkg), profile_for, mode, release); Unit { pkg, target, - profile_for: actual_profile_for, + profile, kind, - mode: actual_mode, + mode, } }; @@ -628,11 +631,10 @@ fn generate_targets<'a>( proposals.extend(default_units); if mode == CompileMode::Test { // Include the lib as it will be required for doctests. + // TODO: Broken - Dependencies won't have ProfileFor::TestDependency. if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) { - proposals.push(( - new_unit(pkg, t, CompileMode::Build, ProfileFor::TestDependency), - false, - )); + proposals + .push((new_unit(pkg, t, CompileMode::Build, ProfileFor::Any), false)); } } } From 138eb33e980df24042037b901c60b2f07c256f46 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 18 Apr 2018 17:21:03 -0700 Subject: [PATCH 04/23] Fix a variety of profile bugs. --- .../compiler/context/unit_dependencies.rs | 24 +++++--- src/cargo/core/compiler/mod.rs | 3 + src/cargo/ops/cargo_compile.rs | 60 +++++++++++++------ 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs index 7f9eeabc3b3..0e1c9c07802 100644 --- a/src/cargo/core/compiler/context/unit_dependencies.rs +++ b/src/cargo/core/compiler/context/unit_dependencies.rs @@ -29,8 +29,13 @@ pub fn build_unit_dependencies<'a, 'cfg>( ) -> CargoResult, Vec>>> { let mut deps = HashMap::new(); for unit in roots.iter() { - // Dependencies of tests should not have `panic` set. - let profile_for = if unit.mode.is_any_test() { + // Dependencies of tests/benches should not have `panic` set. + // We check the global test mode to see if we are running in `cargo + // test` in which case we ensure all dependencies have `panic` + // cleared, and avoid building the lib thrice (once with `panic`, once + // without, once for --test). In particular, the lib included for + // doctests and examples are `Build` mode here. + let profile_for = if unit.mode.is_any_test() || cx.build_config.test { ProfileFor::TestDependency } else { ProfileFor::Any @@ -163,13 +168,13 @@ fn compute_deps_custom_build<'a, 'cfg>( let tmp = Unit { pkg: unit.pkg, target: not_custom_build, - // The profile here isn't critical. We are just using this temp unit - // for fetching dependencies that might have `links`. profile: unit.profile, kind: unit.kind, mode: CompileMode::Build, }; - let deps = deps_of(&tmp, cx, deps, ProfileFor::CustomBuild)?; + // TODO: ProfileFor may need to be TestDependency if the random target we + // picked is a test and `panic` is set. Need it investigate. + let deps = deps_of(&tmp, cx, deps, ProfileFor::Any)?; Ok(deps.iter() .filter_map(|unit| { if !unit.target.linkable() || unit.pkg.manifest().links().is_none() { @@ -221,7 +226,7 @@ fn compute_deps_doc<'a, 'cfg>( // rustdoc only needs rmeta files for regular dependencies. // However, for plugins/proc-macros, deps should be built like normal. let mode = check_or_build_mode(&unit.mode, lib); - let unit = new_unit( + let lib_unit = new_unit( cx, dep, lib, @@ -229,9 +234,10 @@ fn compute_deps_doc<'a, 'cfg>( unit.kind.for_target(lib), mode, ); - ret.push((unit, ProfileFor::Any)); + ret.push((lib_unit, ProfileFor::Any)); if let CompileMode::Doc { deps: true } = unit.mode { - let unit = new_unit( + // Document this lib as well. + let doc_unit = new_unit( cx, dep, lib, @@ -239,7 +245,7 @@ fn compute_deps_doc<'a, 'cfg>( unit.kind.for_target(lib), unit.mode, ); - ret.push((unit, ProfileFor::Any)); + ret.push((doc_unit, ProfileFor::Any)); } } diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index f0605aa0508..f458864ddee 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -60,6 +60,8 @@ pub struct BuildConfig { pub jobs: u32, /// Whether we are building for release pub release: bool, + /// Whether we are running tests + pub test: bool, /// Whether to print std output in json format (for machine reading) pub json_messages: bool, } @@ -128,6 +130,7 @@ impl BuildConfig { host: host_config, target: target_config, release: false, + test: false, json_messages: false, }) } diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index cca95a7797e..3c257aa51f8 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -338,6 +338,7 @@ pub fn compile_ws<'a>( .into_path_unlocked(); let mut build_config = BuildConfig::new(config, jobs, &target, Some(rustc_info_cache))?; build_config.release = release; + build_config.test = mode == CompileMode::Test; build_config.json_messages = message_format == MessageFormat::Json; let default_arch_kind = if build_config.requested_target.is_some() { Kind::Target @@ -568,15 +569,34 @@ fn generate_targets<'a>( // Helper for creating a Unit struct. let new_unit = - |pkg: &'a Package, target: &'a Target, mode: CompileMode, profile_for: ProfileFor| { - let mode = match mode { + |pkg: &'a Package, target: &'a Target, target_mode: CompileMode| { + let profile_for = if mode == CompileMode::Test { + // NOTE: The ProfileFor here is subtle. If you have a profile + // with `panic` set, the `panic` flag is cleared for tests and + // their dependencies. If we left this as an "Any" profile, + // then the lib would get compiled three times (once with + // panic, once without, and once with --test). + // + // This would cause a problem for Doc tests, which would fail + // because `rustdoc` would attempt to link with both libraries + // at the same time. Also, it's probably not important (or + // even desirable?) for rustdoc to link with a lib with + // `panic` set. + // + // As a consequence, Examples and Binaries get compiled + // without `panic` set. This probably isn't a bad deal. + // + // Forcing the lib to be compiled three times during `cargo + // test` is probably also not desirable. + ProfileFor::TestDependency + } else { + ProfileFor::Any + }; + let target_mode = match target_mode { CompileMode::Test => { if target.is_example() { // Examples are included as regular binaries to verify // that they compile. - // TODO: Broken - Dependencies of examples can be - // built with `panic`, which will cause `rustdoc` to - // fail with linking multiple binaries. CompileMode::Build } else { CompileMode::Test @@ -587,21 +607,22 @@ fn generate_targets<'a>( TargetKind::Bench => CompileMode::Bench, _ => CompileMode::Build, }, - _ => mode, + _ => target_mode, }; + // Plugins or proc-macro should be built for the host. let kind = if target.for_host() { Kind::Host } else { default_arch_kind }; let profile = - profiles.get_profile(&pkg.name(), ws.is_member(pkg), profile_for, mode, release); + profiles.get_profile(&pkg.name(), ws.is_member(pkg), profile_for, target_mode, release); Unit { pkg, target, profile, kind, - mode, + mode: target_mode, } }; @@ -623,7 +644,7 @@ fn generate_targets<'a>( .iter() .map(|t| { ( - new_unit(pkg, t, mode, ProfileFor::Any), + new_unit(pkg, t, mode), !required_features_filterable, ) }) @@ -631,10 +652,11 @@ fn generate_targets<'a>( proposals.extend(default_units); if mode == CompileMode::Test { // Include the lib as it will be required for doctests. - // TODO: Broken - Dependencies won't have ProfileFor::TestDependency. if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) { - proposals - .push((new_unit(pkg, t, CompileMode::Build, ProfileFor::Any), false)); + proposals.push(( + new_unit(pkg, t, CompileMode::Build), + false, + )); } } } @@ -648,7 +670,7 @@ fn generate_targets<'a>( } => { if lib { if let Some(target) = pkg.targets().iter().find(|t| t.is_lib()) { - proposals.push((new_unit(pkg, target, mode, ProfileFor::Any), false)); + proposals.push((new_unit(pkg, target, mode), false)); } else if !all_targets { bail!("no library targets found") } @@ -679,26 +701,26 @@ fn generate_targets<'a>( proposals.extend( list_rule_targets(pkg, bins, "bin", Target::is_bin)? .into_iter() - .map(|(t, required)| (new_unit(pkg, t, mode, ProfileFor::Any), required)) + .map(|(t, required)| (new_unit(pkg, t, mode), required)) .chain( list_rule_targets(pkg, examples, "example", Target::is_example)? .into_iter() .map(|(t, required)| { - (new_unit(pkg, t, mode, ProfileFor::Any), required) + (new_unit(pkg, t, mode), required) }), ) .chain( list_rule_targets(pkg, tests, "test", test_filter)? .into_iter() .map(|(t, required)| { - (new_unit(pkg, t, test_mode, ProfileFor::Any), required) + (new_unit(pkg, t, test_mode), required) }), ) .chain( list_rule_targets(pkg, benches, "bench", bench_filter)? .into_iter() .map(|(t, required)| { - (new_unit(pkg, t, bench_mode, ProfileFor::Any), required) + (new_unit(pkg, t, bench_mode), required) }), ) .collect::>(), @@ -706,7 +728,7 @@ fn generate_targets<'a>( } } - // If any integration tests/benches are being tested, make sure that + // If any integration tests/benches are being run, make sure that // binaries are built as well. if !mode.is_check() && proposals.iter().any(|&(ref unit, _)| { unit.mode.is_any_test() && (unit.target.is_test() || unit.target.is_bench()) @@ -715,7 +737,7 @@ fn generate_targets<'a>( pkg.targets() .iter() .filter(|t| t.is_bin()) - .map(|t| (new_unit(pkg, t, CompileMode::Build, ProfileFor::Any), false)), + .map(|t| (new_unit(pkg, t, CompileMode::Build), false)), ); } From 7051b630c82994712db31d314287c39793351740 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 18 Apr 2018 19:17:38 -0700 Subject: [PATCH 05/23] Update for some review comments. --- src/cargo/core/compiler/context/mod.rs | 13 +++++++------ src/cargo/core/compiler/fingerprint.rs | 2 +- src/cargo/core/compiler/mod.rs | 12 ++++++++---- src/cargo/core/profiles.rs | 13 ++++++------- src/cargo/ops/cargo_clean.rs | 9 ++++----- src/cargo/ops/cargo_compile.rs | 13 +++++++------ src/cargo/util/toml/mod.rs | 1 - src/doc/src/reference/unstable.md | 6 +++--- tests/testsuite/profiles.rs | 2 +- 9 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index 284515814e4..f231c13084a 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -92,11 +92,12 @@ pub struct Context<'a, 'cfg: 'a> { pub used_in_plugin: HashSet>, pub jobserver: Client, pub profiles: &'a Profiles, - /// This is a workaround to carry the extra compiler args given on the - /// command-line for `cargo rustc` and `cargo rustdoc`. These commands - /// only support one target, but we don't want the args passed to any - /// dependencies. - pub extra_compiler_args: HashMap, Vec>, + /// This is a workaround to carry the extra compiler args for either + /// `rustc` or `rustdoc` given on the command-line for the commands `cargo + /// rustc` and `cargo rustdoc`. These commands only support one target, + /// but we don't want the args passed to any dependencies, so we include + /// the `Unit` corresponding to the top-level target. + pub extra_compiler_args: Option<(Unit<'a>, Vec)>, target_info: TargetInfo, host_info: TargetInfo, @@ -114,7 +115,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { config: &'cfg Config, build_config: BuildConfig, profiles: &'a Profiles, - extra_compiler_args: HashMap, Vec>, + extra_compiler_args: Option<(Unit<'a>, Vec)>, ) -> CargoResult> { let incremental_env = match env::var("CARGO_INCREMENTAL") { Ok(v) => Some(v == "1"), diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index e00b1324c41..35a7ca73169 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -102,7 +102,7 @@ pub fn prepare_target<'a, 'cfg>( } } - let allow_failure = cx.extra_compiler_args.get(unit).is_some(); + let allow_failure = cx.extra_compiler_args.is_some(); let target_root = cx.files().target_root().to_path_buf(); let write_fingerprint = Work::new(move |_| { match fingerprint.update_local(&target_root) { diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index f458864ddee..e4a6617091c 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -777,8 +777,10 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult rustdoc.arg(format!("--edition={}", &manifest.edition())); } - if let Some(args) = cx.extra_compiler_args.get(unit) { - rustdoc.args(args); + if let Some((ref args_unit, ref args)) = cx.extra_compiler_args { + if args_unit == unit { + rustdoc.args(args); + } } build_deps_args(&mut rustdoc, cx, unit)?; @@ -942,8 +944,10 @@ fn build_base_args<'a, 'cfg>( cmd.arg("-C").arg(format!("debuginfo={}", debuginfo)); } - if let Some(args) = cx.extra_compiler_args.get(unit) { - cmd.args(args); + if let Some((ref args_unit, ref args)) = cx.extra_compiler_args { + if args_unit == unit { + cmd.args(args); + } } // -C overflow-checks is implied by the setting of -C debug-assertions, diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index e7aa0c88349..c76be468c27 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -72,8 +72,6 @@ impl Profiles { // `build_unit_profiles` normally ensures that it selects the // ancestor's profile. However `cargo clean -p` can hit this // path. - // TODO: I think `cargo clean -p xxx` is not cleaning out - // the "OUT_DIR" directory. This is not a new bug. if release { &self.release } else { @@ -109,7 +107,7 @@ impl Profiles { /// The precedence of profiles are (first one wins): /// - [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 +/// - [profile.dev.build-override] - This can only apply to `build.rs` scripts /// and their dependencies. /// - [profile.dev] /// - Default (hard-coded) values. @@ -321,7 +319,7 @@ pub enum ProfileFor { /// A general-purpose target. Any, /// A target for `build.rs` or any of its dependencies. This enables - /// `build_override` profiles for these targets. + /// `build-override` profiles for these targets. CustomBuild, /// A target that is a dependency of a test or benchmark. Currently this /// enforces that the `panic` setting is not set. @@ -329,11 +327,12 @@ pub enum ProfileFor { } impl ProfileFor { - pub fn all_values() -> Vec { - vec![ + pub fn all_values() -> &'static [ProfileFor] { + static ALL: [ProfileFor; 3] = [ ProfileFor::Any, ProfileFor::CustomBuild, ProfileFor::TestDependency, - ] + ]; + &ALL } } diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index d3692d5cde4..21e195212e8 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -1,6 +1,5 @@ use std::fs; use std::path::Path; -use std::collections::HashMap; use core::Workspace; use core::compiler::{BuildConfig, Context, Kind, Unit}; @@ -53,8 +52,8 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { let profile = profiles.get_profile( &pkg.name(), ws.is_member(pkg), - profile_for, - mode, + *profile_for, + *mode, opts.release, ); units.push(Unit { @@ -62,7 +61,7 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { target, profile, kind: *kind, - mode, + mode: *mode, }); } } @@ -79,7 +78,7 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { opts.config, build_config, profiles, - HashMap::new(), + None, )?; cx.prepare_units(None, &units)?; diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 3c257aa51f8..6025b293253 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -22,7 +22,7 @@ //! previously compiled dependency //! -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -162,8 +162,8 @@ impl CompileMode { /// List of all modes (currently used by `cargo clean -p` for computing /// all possible outputs). - pub fn all_modes() -> Vec { - vec![ + pub fn all_modes() -> &'static [CompileMode] { + static ALL: [CompileMode; 9] = [ CompileMode::Test, CompileMode::Build, CompileMode::Check { test: true }, @@ -173,7 +173,8 @@ impl CompileMode { CompileMode::Doc { deps: false }, CompileMode::Doctest, CompileMode::RunCustomBuild, - ] + ]; + &ALL } } @@ -381,7 +382,7 @@ pub fn compile_ws<'a>( } let profiles = ws.profiles(); - let mut extra_compiler_args = HashMap::new(); + let mut extra_compiler_args = None; let units = generate_targets( ws, @@ -403,7 +404,7 @@ pub fn compile_ws<'a>( extra_args_name ); } - extra_compiler_args.insert(units[0], args); + extra_compiler_args = Some((units[0], args)); } let mut ret = { diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index c8ae4839dba..f101b952fc7 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -381,7 +381,6 @@ pub struct TomlProfile { pub overflow_checks: Option, pub incremental: Option, pub overrides: Option>, - #[serde(rename = "build_override")] pub build_override: Option>, } diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index bd8ccbdada5..483e5e2537e 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -215,14 +215,14 @@ debug = true [profile.dev.overrides.image] opt-level = 3 -# All dependencies (but not this crate itself) will be compiled -# with -Copt-level=2 . This includes build dependencies. +# All dependencies (but not this crate itself or any workspace member) +# will be compiled with -Copt-level=2 . This includes build dependencies. [profile.dev.overrides."*"] opt-level = 2 # Build scripts and their dependencies will be compiled with -Copt-level=3 # By default, build scripts use the same rules as the rest of the profile -[profile.dev.build_override] +[profile.dev.build-override] opt-level = 3 ``` diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs index 978efd79ce6..528806856bd 100644 --- a/tests/testsuite/profiles.rs +++ b/tests/testsuite/profiles.rs @@ -353,7 +353,7 @@ fn profile_override_gated() { version = "0.0.1" authors = [] - [profile.dev.build_override] + [profile.dev.build-override] opt-level = 3 "#, ) From 768f5739557074fa493dda01b8c3ca30fad610b4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 18 Apr 2018 19:54:01 -0700 Subject: [PATCH 06/23] Add extra rustc/rustdoc args to the fingerprint and metadata. --- src/cargo/core/compiler/context/compilation_files.rs | 3 +++ src/cargo/core/compiler/context/mod.rs | 11 ++++++++++- src/cargo/core/compiler/fingerprint.rs | 10 ++++++++-- src/cargo/core/compiler/mod.rs | 12 ++++-------- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index e44414cb336..358d320e6ca 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -427,6 +427,9 @@ fn compute_metadata<'a, 'cfg>( // settings like debuginfo and whatnot. unit.profile.hash(&mut hasher); unit.mode.hash(&mut hasher); + if let Some(ref args) = cx.extra_args_for(unit) { + args.hash(&mut hasher); + } // Artifacts compiled for the host should have a different metadata // piece than those compiled for the target, so make sure we throw in diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index f231c13084a..f0ef400cd2d 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -97,7 +97,7 @@ pub struct Context<'a, 'cfg: 'a> { /// rustc` and `cargo rustdoc`. These commands only support one target, /// but we don't want the args passed to any dependencies, so we include /// the `Unit` corresponding to the top-level target. - pub extra_compiler_args: Option<(Unit<'a>, Vec)>, + extra_compiler_args: Option<(Unit<'a>, Vec)>, target_info: TargetInfo, host_info: TargetInfo, @@ -559,6 +559,15 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Kind::Target => &self.target_info, } } + + pub fn extra_args_for(&self, unit: &Unit<'a>) -> Option<&Vec> { + if let Some((ref args_unit, ref args)) = self.extra_compiler_args { + if args_unit == unit { + return Some(args); + } + } + None + } } /// Acquire extra flags to pass to the compiler from various locations. diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 35a7ca73169..e41f546f4ea 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -102,7 +102,7 @@ pub fn prepare_target<'a, 'cfg>( } } - let allow_failure = cx.extra_compiler_args.is_some(); + let allow_failure = cx.extra_args_for(unit).is_some(); let target_root = cx.files().target_root().to_path_buf(); let write_fingerprint = Work::new(move |_| { match fingerprint.update_local(&target_root) { @@ -454,10 +454,16 @@ fn calculate<'a, 'cfg>( } else { cx.rustflags_args(unit)? }; + let profile_hash = util::hash_u64(&( + &unit.profile, + unit.mode, + cx.extra_args_for(unit), + cx.incremental_args(unit)?, + )); let fingerprint = Arc::new(Fingerprint { rustc: util::hash_u64(&cx.build_config.rustc.verbose_version), target: util::hash_u64(&unit.target), - profile: util::hash_u64(&(&unit.profile, unit.mode, cx.incremental_args(unit)?)), + profile: profile_hash, // Note that .0 is hashed here, not .1 which is the cwd. That doesn't // actually affect the output artifact so there's no need to hash it. path: util::hash_u64(&super::path_args(cx, unit).0), diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index e4a6617091c..ad9ea2d0d5f 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -777,10 +777,8 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult rustdoc.arg(format!("--edition={}", &manifest.edition())); } - if let Some((ref args_unit, ref args)) = cx.extra_compiler_args { - if args_unit == unit { - rustdoc.args(args); - } + if let Some(ref args) = cx.extra_args_for(unit) { + rustdoc.args(args); } build_deps_args(&mut rustdoc, cx, unit)?; @@ -944,10 +942,8 @@ fn build_base_args<'a, 'cfg>( cmd.arg("-C").arg(format!("debuginfo={}", debuginfo)); } - if let Some((ref args_unit, ref args)) = cx.extra_compiler_args { - if args_unit == unit { - cmd.args(args); - } + if let Some(ref args) = cx.extra_args_for(unit) { + cmd.args(args); } // -C overflow-checks is implied by the setting of -C debug-assertions, From 0d8ebe9109b260736591e23fccf48e717714e1c3 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 20 Apr 2018 13:20:38 -0700 Subject: [PATCH 07/23] Avoid running `build.rs` too often. --- .../compiler/context/unit_dependencies.rs | 12 ++++------ src/cargo/core/compiler/custom_build.rs | 3 +++ src/cargo/core/profiles.rs | 12 ++++++++++ src/cargo/ops/cargo_clean.rs | 24 +++++++++++++------ 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs index 0e1c9c07802..b329de85aa9 100644 --- a/src/cargo/core/compiler/context/unit_dependencies.rs +++ b/src/cargo/core/compiler/context/unit_dependencies.rs @@ -132,7 +132,7 @@ fn compute_deps<'a, 'b, 'cfg>( if unit.target.is_custom_build() { return Ok(ret); } - ret.extend(dep_build_script(unit)); + ret.extend(dep_build_script(cx, unit)); // If this target is a binary, test, example, etc, then it depends on // the library of the same package. The call to `resolve.deps` above @@ -180,7 +180,7 @@ fn compute_deps_custom_build<'a, 'cfg>( if !unit.target.linkable() || unit.pkg.manifest().links().is_none() { return None; } - dep_build_script(unit) + dep_build_script(cx, unit) }) .chain(Some(( new_unit( @@ -250,7 +250,7 @@ fn compute_deps_doc<'a, 'cfg>( } // Be sure to build/run the build script for documented libraries as - ret.extend(dep_build_script(unit)); + ret.extend(dep_build_script(cx, unit)); // If we document a binary, we need the library available if unit.target.is_bin() { @@ -278,7 +278,7 @@ fn maybe_lib<'a>( /// script itself doesn't have any dependencies, so even in that case a unit /// of work is still returned. `None` is only returned if the package has no /// build script. -fn dep_build_script<'a>(unit: &Unit<'a>) -> Option<(Unit<'a>, ProfileFor)> { +fn dep_build_script<'a>(cx: &Context, unit: &Unit<'a>) -> Option<(Unit<'a>, ProfileFor)> { unit.pkg .targets() .iter() @@ -286,13 +286,11 @@ fn dep_build_script<'a>(unit: &Unit<'a>) -> Option<(Unit<'a>, ProfileFor)> { .map(|t| { // The profile stored in the Unit is the profile for the thing // the custom build script is running for. - // TODO: Fix this for different profiles that don't affect the - // build.rs environment variables. ( Unit { pkg: unit.pkg, target: t, - profile: unit.profile, + profile: cx.profiles.get_profile_run_custom_build(&unit.profile), kind: unit.kind, mode: CompileMode::RunCustomBuild, }, diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index 96638bf0f75..02fd48de031 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -118,6 +118,9 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes // environment variables. Note that the profile-related environment // variables are not set with this the build script's profile but rather the // package's library profile. + // NOTE: If you add any profile flags, be sure to update + // `Profiles::get_profile_run_custom_build` so that those flags get + // carried over. let to_exec = to_exec.into_os_string(); let mut cmd = cx.compilation.host_process(to_exec, unit.pkg)?; cmd.env("OUT_DIR", &build_output) diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index c76be468c27..ac3651fe0bf 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -90,6 +90,18 @@ impl Profiles { profile } + /// The profile for *running* a `build.rs` script is only used for setting + /// a few environment variables. To ensure proper de-duplication of the + /// running `Unit`, this uses a stripped-down profile (so that unrelated + /// profile flags don't cause `build.rs` to needlessly run multiple + /// times). + pub fn get_profile_run_custom_build(&self, for_unit_profile: &Profile) -> Profile { + let mut result = Profile::default(); + result.debuginfo = for_unit_profile.debuginfo; + result.opt_level = for_unit_profile.opt_level; + result + } + /// This returns a generic base profile. This is currently used for the /// `[Finished]` line. It is not entirely accurate, since it doesn't /// select for the package that was actually built. diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index 21e195212e8..3c0321c42ff 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -49,13 +49,23 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { for kind in [Kind::Host, Kind::Target].iter() { for mode in CompileMode::all_modes() { for profile_for in ProfileFor::all_values() { - let profile = profiles.get_profile( - &pkg.name(), - ws.is_member(pkg), - *profile_for, - *mode, - opts.release, - ); + let profile = if mode.is_run_custom_build() { + profiles.get_profile_run_custom_build(&profiles.get_profile( + &pkg.name(), + ws.is_member(pkg), + *profile_for, + CompileMode::Build, + opts.release, + )) + } else { + profiles.get_profile( + &pkg.name(), + ws.is_member(pkg), + *profile_for, + *mode, + opts.release, + ) + }; units.push(Unit { pkg, target, From 7dc9e071c77965d8d9326a311123d6c1c9dbfbcf Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 20 Apr 2018 19:43:57 -0700 Subject: [PATCH 08/23] Minor tweaks on how examples are handled with tests, and some panic propagation. --- src/cargo/core/compiler/fingerprint.rs | 2 +- src/cargo/core/manifest.rs | 1 + src/cargo/ops/cargo_compile.rs | 15 ++++++++------- tests/testsuite/check.rs | 7 +++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index e41f546f4ea..b71b47d859c 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -764,7 +764,7 @@ fn filename<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> String { TargetKind::Bench => "bench", TargetKind::CustomBuild => "build-script", }; - let flavor = if unit.mode.is_any_test() && !unit.mode.is_check() { + let flavor = if unit.mode.is_any_test() { "test-" } else if unit.mode.is_doc() { "doc-" diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index da7269c5faa..e8c11180823 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -472,6 +472,7 @@ impl Target { kind, name: name.to_string(), required_features, + tested: false, benched: false, ..Target::with_path(src_path) } diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 6025b293253..6ff4cc01a8d 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -339,7 +339,7 @@ pub fn compile_ws<'a>( .into_path_unlocked(); let mut build_config = BuildConfig::new(config, jobs, &target, Some(rustc_info_cache))?; build_config.release = release; - build_config.test = mode == CompileMode::Test; + build_config.test = mode == CompileMode::Test || mode == CompileMode::Bench; build_config.json_messages = message_format == MessageFormat::Json; let default_arch_kind = if build_config.requested_target.is_some() { Kind::Target @@ -571,12 +571,13 @@ fn generate_targets<'a>( // Helper for creating a Unit struct. let new_unit = |pkg: &'a Package, target: &'a Target, target_mode: CompileMode| { - let profile_for = if mode == CompileMode::Test { + let profile_for = if mode.is_any_test() { // NOTE: The ProfileFor here is subtle. If you have a profile - // with `panic` set, the `panic` flag is cleared for tests and - // their dependencies. If we left this as an "Any" profile, - // then the lib would get compiled three times (once with - // panic, once without, and once with --test). + // with `panic` set, the `panic` flag is cleared for + // tests/benchmarks and their dependencies. If we left this + // as an "Any" profile, then the lib would get compiled three + // times (once with panic, once without, and once with + // --test). // // This would cause a problem for Doc tests, which would fail // because `rustdoc` would attempt to link with both libraries @@ -793,7 +794,7 @@ fn resolve_all_features( fn generate_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> { match mode { CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(), - CompileMode::Test => targets.iter().filter(|t| t.tested()).collect(), + CompileMode::Test => targets.iter().filter(|t| t.tested() || t.is_example()).collect(), CompileMode::Build | CompileMode::Check { .. } => targets .iter() .filter(|t| t.is_bin() || t.is_lib()) diff --git a/tests/testsuite/check.rs b/tests/testsuite/check.rs index 8e3667c2a88..d9ecfb5c8e9 100644 --- a/tests/testsuite/check.rs +++ b/tests/testsuite/check.rs @@ -747,14 +747,13 @@ fn check_filters() { .with_stderr_contains("[..] --crate-name foo src[/]lib.rs [..] --test [..]") .with_stderr_contains("[..] --crate-name foo src[/]lib.rs --crate-type lib [..]") .with_stderr_contains("[..] --crate-name foo src[/]main.rs [..] --test [..]") - // .with_stderr_contains("[..] --crate-name foo src[/]main.rs --crate-type bin [..]") .with_stderr_contains("[..]unused_unit_lib[..]") .with_stderr_contains("[..]unused_unit_bin[..]") .with_stderr_contains("[..]unused_normal_lib[..]") .with_stderr_contains("[..]unused_normal_bin[..]") .with_stderr_contains("[..]unused_unit_t1[..]") - .with_stderr_contains("[..]unused_normal_ex1[..]") - .with_stderr_contains("[..]unused_unit_ex1[..]") + .with_stderr_does_not_contain("[..]unused_normal_ex1[..]") + .with_stderr_does_not_contain("[..]unused_unit_ex1[..]") .with_stderr_does_not_contain("[..]unused_normal_b1[..]") .with_stderr_does_not_contain("[..]unused_unit_b1[..]"), // with_stderr_does_not_contain --crate-type lib @@ -789,7 +788,7 @@ fn check_filters() { .with_stderr_contains("[..]unused_unit_t1[..]") .with_stderr_contains("[..]unused_unit_lib[..]") .with_stderr_contains("[..]unused_unit_bin[..]") - .with_stderr_contains("[..]unused_unit_ex1[..]"), + .with_stderr_does_not_contain("[..]unused_unit_ex1[..]"), ); } From a0a880c36e18854b8eb3f65715ada2de04924372 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 20 Apr 2018 22:19:16 -0700 Subject: [PATCH 09/23] Add warnings for unknown profile overrides. --- src/cargo/core/profiles.rs | 47 +++++++++++++++++++++++++++++- src/cargo/core/workspace.rs | 2 -- src/cargo/ops/cargo_compile.rs | 6 ++++ tests/testsuite/profiles.rs | 53 ++++++++++++++++++++++++++-------- 4 files changed, 93 insertions(+), 15 deletions(-) diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index ac3651fe0bf..71ee27102fe 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -1,7 +1,12 @@ use std::{cmp, fmt, hash}; +use std::collections::HashSet; + +use core::Shell; +use core::interning::InternedString; use ops::CompileMode; +use util::CargoResult; +use util::lev_distance::lev_distance; use util::toml::{StringOrBool, TomlProfile, U32OrBool}; -use core::interning::InternedString; /// Collection of all user profiles. #[derive(Clone, Debug)] @@ -112,6 +117,20 @@ impl Profiles { self.dev.profile_for("", true, ProfileFor::Any) } } + + /// Used to check for overrides for non-existing packages. + pub fn validate_packages( + &self, + shell: &mut Shell, + packages: &HashSet<&str>, + ) -> CargoResult<()> { + self.dev.validate_packages(shell, packages)?; + self.release.validate_packages(shell, packages)?; + self.test.validate_packages(shell, packages)?; + self.bench.validate_packages(shell, packages)?; + self.doc.validate_packages(shell, packages)?; + Ok(()) + } } /// An object used for handling the profile override hierarchy. @@ -152,6 +171,32 @@ impl ProfileMaker { } profile } + + fn validate_packages(&self, shell: &mut Shell, packages: &HashSet<&str>) -> CargoResult<()> { + if let Some(ref toml) = self.toml { + if let Some(ref overrides) = toml.overrides { + for key in overrides.keys().filter(|k| k.as_str() != "*") { + if !packages.contains(key.as_str()) { + let suggestion = packages + .iter() + .map(|p| (lev_distance(key, p), p)) + .filter(|&(d, _)| d < 4) + .min_by_key(|p| p.0) + .map(|p| p.1); + match suggestion { + Some(p) => shell.warn(format!( + "package `{}` for profile override not found\n\nDid you mean `{}`?", + key, p + ))?, + None => shell + .warn(format!("package `{}` for profile override not found", key))?, + }; + } + } + } + } + Ok(()) + } } fn merge_profile(profile: &mut Profile, toml: &TomlProfile) { diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index 0de6ef40ab8..8f339045407 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -310,8 +310,6 @@ impl<'cfg> Workspace<'cfg> { /// Returns true if the package is a member of the workspace. pub fn is_member(&self, pkg: &Package) -> bool { - // TODO: Implement this in a better way. - // Maybe make it part of Package? self.members().any(|p| p == pkg) } diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 6ff4cc01a8d..dec8cb196d5 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -382,6 +382,12 @@ pub fn compile_ws<'a>( } let profiles = ws.profiles(); + let package_names = packages + .package_ids() + .map(|pid| pid.name().as_str()) + .collect::>(); + profiles.validate_packages(&mut config.shell(), &package_names)?; + let mut extra_compiler_args = None; let units = generate_targets( diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs index 528806856bd..4e526a93274 100644 --- a/tests/testsuite/profiles.rs +++ b/tests/testsuite/profiles.rs @@ -1,7 +1,7 @@ use std::env; use cargotest::is_nightly; -use cargotest::support::{execs, project}; +use cargotest::support::{basic_lib_manifest, execs, project}; use cargotest::ChannelChanger; use hamcrest::assert_that; @@ -426,24 +426,16 @@ fn profile_override_basic() { "#, ) .file("src/lib.rs", "") - .file( - "bar/Cargo.toml", - r#" - [package] - name = "bar" - version = "0.0.1" - authors = [] - "#, - ) + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); assert_that( p.cargo("build -v").masquerade_as_nightly_cargo(), execs().with_status(0).with_stderr( -"[COMPILING] bar v0.0.1 ([..]) +"[COMPILING] bar [..] [RUNNING] `rustc --crate-name bar [..] -C opt-level=3 [..]` -[COMPILING] foo v0.0.1 ([..]) +[COMPILING] foo [..] [RUNNING] `rustc --crate-name foo [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", ) @@ -451,5 +443,42 @@ fn profile_override_basic() { // .with_stderr_does_not_contain("\ // `rustc --crate-name bar[..]-C opt-level=3"), ); +} + +#[test] +fn profile_override_bad_name() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-overrides"] + + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = {path = "bar"} + + [profile.dev.overrides.bart] + opt-level = 3 + + [profile.dev.overrides.no-suggestion] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs().with_status(0).with_stderr_contains("\ +[WARNING] package `bart` for profile override not found +Did you mean `bar`? +[WARNING] package `no-suggestion` for profile override not found +[COMPILING] [..] +")); } From 36b876902552cce138dbdd97e271c02f77703df6 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 21 Apr 2018 09:40:00 -0700 Subject: [PATCH 10/23] Add warning if `panic` is set in test or bench profile. --- src/cargo/util/toml/mod.rs | 30 ++++++++++++++++++++++-------- tests/testsuite/profiles.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index f101b952fc7..acd4b0af208 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -245,21 +245,21 @@ pub struct TomlProfiles { } impl TomlProfiles { - fn validate(&self, features: &Features) -> CargoResult<()> { + fn validate(&self, features: &Features, warnings: &mut Vec) -> CargoResult<()> { if let Some(ref test) = self.test { - test.validate("test", features)?; + test.validate("test", features, warnings)?; } if let Some(ref doc) = self.doc { - doc.validate("doc", features)?; + doc.validate("doc", features, warnings)?; } if let Some(ref bench) = self.bench { - bench.validate("bench", features)?; + bench.validate("bench", features, warnings)?; } if let Some(ref dev) = self.dev { - dev.validate("dev", features)?; + dev.validate("dev", features, warnings)?; } if let Some(ref release) = self.release { - release.validate("release", features)?; + release.validate("release", features, warnings)?; } Ok(()) } @@ -385,7 +385,12 @@ pub struct TomlProfile { } impl TomlProfile { - fn validate(&self, name: &str, features: &Features) -> CargoResult<()> { + fn validate( + &self, + name: &str, + features: &Features, + warnings: &mut Vec, + ) -> CargoResult<()> { if let Some(ref profile) = self.build_override { features.require(Feature::profile_overrides())?; profile.validate_override()?; @@ -409,6 +414,15 @@ impl TomlProfile { } } } + + match name { + "test" | "bench" => { + if self.panic.is_some() { + warnings.push(format!("`panic` setting is ignored for `{}` profile", name)) + } + } + _ => {} + } Ok(()) } @@ -861,7 +875,7 @@ impl TomlManifest { ), }; if let Some(ref profiles) = me.profile { - profiles.validate(&features)?; + profiles.validate(&features, &mut warnings)?; } let profiles = build_profiles(&me.profile); let publish = match project.publish { diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs index 4e526a93274..be5c6b615b2 100644 --- a/tests/testsuite/profiles.rs +++ b/tests/testsuite/profiles.rs @@ -482,3 +482,31 @@ Did you mean `bar`? [COMPILING] [..] ")); } + +#[test] +fn profile_panic_test_bench() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [profile.test] + panic = "abort" + + [profile.bench] + panic = "abort" + "#, + ) + .file("src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build"), + execs().with_status(0).with_stderr_contains("\ +[WARNING] `panic` setting is ignored for `test` profile +[WARNING] `panic` setting is ignored for `bench` profile +")); +} From ba537d73b9477d08677b5547cbd7368a86d60e40 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 21 Apr 2018 09:50:55 -0700 Subject: [PATCH 11/23] Add test for profile override on non-dev/release. --- src/cargo/util/toml/mod.rs | 4 +-- tests/testsuite/profiles.rs | 49 ++++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index acd4b0af208..76f54de916f 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -407,8 +407,8 @@ impl TomlProfile { _ => { if self.overrides.is_some() || self.build_override.is_some() { bail!( - "Profile overrides may only be specified for `dev` - or `release` profile, not {}.", + "Profile overrides may only be specified for \ + `dev` or `release` profile, not `{}`.", name ); } diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs index be5c6b615b2..9452c141789 100644 --- a/tests/testsuite/profiles.rs +++ b/tests/testsuite/profiles.rs @@ -474,13 +474,16 @@ fn profile_override_bad_name() { assert_that( p.cargo("build").masquerade_as_nightly_cargo(), - execs().with_status(0).with_stderr_contains("\ + execs().with_status(0).with_stderr_contains( + "\ [WARNING] package `bart` for profile override not found Did you mean `bar`? [WARNING] package `no-suggestion` for profile override not found [COMPILING] [..] -")); +", + ), + ); } #[test] @@ -505,8 +508,46 @@ fn profile_panic_test_bench() { assert_that( p.cargo("build"), - execs().with_status(0).with_stderr_contains("\ + execs().with_status(0).with_stderr_contains( + "\ [WARNING] `panic` setting is ignored for `test` profile [WARNING] `panic` setting is ignored for `bench` profile -")); +", + ), + ); +} + +#[test] +fn profile_override_dev_release_only() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-overrides"] + + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = {path = "bar"} + + [profile.test.overrides.bar] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs().with_status(101).with_stderr_contains( + "\ +Caused by: + Profile overrides may only be specified for `dev` or `release` profile, not `test`. +", + ), + ); } From c667fc231dd789852ad583a1ea5d404488827c9a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 21 Apr 2018 10:03:23 -0700 Subject: [PATCH 12/23] Add more profile override validation tests. --- tests/testsuite/profiles.rs | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs index 9452c141789..c53354075aa 100644 --- a/tests/testsuite/profiles.rs +++ b/tests/testsuite/profiles.rs @@ -551,3 +551,55 @@ Caused by: ), ); } + +#[test] +fn profile_override_bad_settings() { + let bad_values = [ + ( + "panic = \"abort\"", + "`panic` may not be specified in a profile override.", + ), + ( + "lto = true", + "`lto` may not be specified in a profile override.", + ), + ( + "rpath = true", + "`rpath` may not be specified in a profile override.", + ), + ("overrides = {}", "Profile overrides cannot be nested."), + ]; + for &(ref snippet, ref expected) in bad_values.iter() { + let p = project("foo") + .file( + "Cargo.toml", + &format!( + r#" + cargo-features = ["profile-overrides"] + + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = {{path = "bar"}} + + [profile.dev.overrides.bar] + {} + "#, + snippet + ), + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs() + .with_status(101) + .with_stderr_contains(format!("Caused by:\n {}", expected)), + ); + } +} From ec7be849e3cc7d206a2f511bf1b939f5530b9351 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 21 Apr 2018 10:41:05 -0700 Subject: [PATCH 13/23] Some test cleanup for profiles. --- tests/testsuite/check.rs | 7 +++---- tests/testsuite/freshness.rs | 3 --- tests/testsuite/profiles.rs | 14 +++++++------- tests/testsuite/test.rs | 12 ++++-------- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/tests/testsuite/check.rs b/tests/testsuite/check.rs index d9ecfb5c8e9..f4e49e711d4 100644 --- a/tests/testsuite/check.rs +++ b/tests/testsuite/check.rs @@ -755,9 +755,8 @@ fn check_filters() { .with_stderr_does_not_contain("[..]unused_normal_ex1[..]") .with_stderr_does_not_contain("[..]unused_unit_ex1[..]") .with_stderr_does_not_contain("[..]unused_normal_b1[..]") - .with_stderr_does_not_contain("[..]unused_unit_b1[..]"), - // with_stderr_does_not_contain --crate-type lib - // with_stderr_does_not_contain --crate-type bin + .with_stderr_does_not_contain("[..]unused_unit_b1[..]") + .with_stderr_does_not_contain("[..]--crate-type bin[..]"), ); p.root().join("target").rm_rf(); assert_that( @@ -765,9 +764,9 @@ fn check_filters() { execs() .with_status(0) .with_stderr_contains("[..]unused_normal_lib[..]") - // .with_stderr_contains("[..]unused_normal_bin[..]") .with_stderr_contains("[..]unused_unit_t1[..]") .with_stderr_does_not_contain("[..]unused_unit_lib[..]") + .with_stderr_does_not_contain("[..]unused_normal_bin[..]") .with_stderr_does_not_contain("[..]unused_unit_bin[..]") .with_stderr_does_not_contain("[..]unused_normal_ex1[..]") .with_stderr_does_not_contain("[..]unused_normal_b1[..]") diff --git a/tests/testsuite/freshness.rs b/tests/testsuite/freshness.rs index 3b94f5f05d6..9ff31d66afe 100644 --- a/tests/testsuite/freshness.rs +++ b/tests/testsuite/freshness.rs @@ -268,9 +268,6 @@ fn changing_profiles_caches_targets() { [profile.dev] panic = "abort" - - [profile.test] - panic = "unwind" "#, ) .file("src/lib.rs", "") diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs index c53354075aa..00f1340ee1a 100644 --- a/tests/testsuite/profiles.rs +++ b/tests/testsuite/profiles.rs @@ -421,6 +421,9 @@ fn profile_override_basic() { [dependencies] bar = {path = "bar"} + [profile.dev] + opt-level = 1 + [profile.dev.overrides.bar] opt-level = 3 "#, @@ -433,15 +436,12 @@ fn profile_override_basic() { assert_that( p.cargo("build -v").masquerade_as_nightly_cargo(), execs().with_status(0).with_stderr( -"[COMPILING] bar [..] + "[COMPILING] bar [..] [RUNNING] `rustc --crate-name bar [..] -C opt-level=3 [..]` [COMPILING] foo [..] -[RUNNING] `rustc --crate-name foo [..]` -[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", - ) - // TODO: does_not_contain does not support patterns! - // .with_stderr_does_not_contain("\ - // `rustc --crate-name bar[..]-C opt-level=3"), +[RUNNING] `rustc --crate-name foo [..] -C opt-level=1 [..]` +[FINISHED] dev [optimized + debuginfo] target(s) in [..]", + ), ); } diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index 19afe54cc8c..925aa3b55aa 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -1644,12 +1644,10 @@ fn test_run_implicit_test_target() { .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") .file( "examples/myexm.rs", - "#[test] fn test_in_exm() { } - fn main() { panic!(\"Don't execute me!\"); }", + "fn main() { compile_error!(\"Don't build me!\"); }", ) .build(); - // TODO FIXME: This needs to better verify that examples are not built. assert_that( prj.cargo("test").arg("--tests"), execs() @@ -1691,8 +1689,7 @@ fn test_run_implicit_bench_target() { .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") .file( "examples/myexm.rs", - "#[test] fn test_in_exm() { } - fn main() { panic!(\"Don't execute me!\"); }", + "fn main() { compile_error!(\"Don't build me!\"); }", ) .build(); @@ -1737,12 +1734,11 @@ fn test_run_implicit_example_target() { .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") .file( "examples/myexm.rs", - "#[test] fn test_in_exm() { } - fn main() { panic!(\"Don't execute me!\"); }", + r#"#[test] fn test_in_exm() { panic!("Don't even test me."); } + fn main() { panic!("Don't execute me!"); }"#, ) .build(); - // TODO FIXME - verify example does NOT get run. assert_that( prj.cargo("test").arg("--examples"), execs().with_status(0).with_stderr(format!( From accc9b28677049c8dc7596782752102ff4d5cd98 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 21 Apr 2018 11:18:05 -0700 Subject: [PATCH 14/23] Remove last TODO comments. --- src/cargo/core/compiler/context/unit_dependencies.rs | 2 -- src/cargo/core/compiler/job_queue.rs | 8 +++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs index b329de85aa9..75dbd24f1e6 100644 --- a/src/cargo/core/compiler/context/unit_dependencies.rs +++ b/src/cargo/core/compiler/context/unit_dependencies.rs @@ -172,8 +172,6 @@ fn compute_deps_custom_build<'a, 'cfg>( kind: unit.kind, mode: CompileMode::Build, }; - // TODO: ProfileFor may need to be TestDependency if the random target we - // picked is a test and `panic` is set. Need it investigate. let deps = deps_of(&tmp, cx, deps, ProfileFor::Any)?; Ok(deps.iter() .filter_map(|unit| { diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs index b6202a90541..a713a6285ca 100644 --- a/src/cargo/core/compiler/job_queue.rs +++ b/src/cargo/core/compiler/job_queue.rs @@ -256,7 +256,13 @@ impl<'a> JobQueue<'a> { } let build_type = if self.is_release { "release" } else { "dev" }; - // TODO FIXME: We don't know which pkg to display this for! + // 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 + // different profiles. To be accurate, it would need to collect a + // 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.profiles.base_profile(self.is_release); let mut opt_type = String::from(if profile.opt_level.as_str() == "0" { "unoptimized" From a4947c2b47fd38857c7332d333fda7bcc405de0c Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 21 Apr 2018 12:16:03 -0700 Subject: [PATCH 15/23] rustfmt --- .../compiler/context/compilation_files.rs | 2 +- src/cargo/core/compiler/context/mod.rs | 10 +- src/cargo/core/compiler/custom_build.rs | 6 +- src/cargo/core/compiler/fingerprint.rs | 15 +- src/cargo/core/compiler/job_queue.rs | 10 +- src/cargo/core/compiler/mod.rs | 10 +- src/cargo/core/compiler/output_depinfo.rs | 4 +- src/cargo/core/features.rs | 8 +- src/cargo/core/manifest.rs | 10 +- src/cargo/core/mod.rs | 20 +-- src/cargo/core/profiles.rs | 6 +- src/cargo/core/workspace.rs | 4 +- src/cargo/ops/cargo_clean.rs | 6 +- src/cargo/ops/cargo_compile.rs | 143 +++++++++--------- src/cargo/util/toml/mod.rs | 10 +- 15 files changed, 127 insertions(+), 137 deletions(-) diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index 358d320e6ca..9fcacb33614 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -7,8 +7,8 @@ use std::sync::Arc; use lazycell::LazyCell; -use core::{TargetKind, Workspace}; use super::{Context, FileFlavor, Kind, Layout, Unit}; +use core::{TargetKind, Workspace}; use util::{self, CargoResult}; #[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index f0ef400cd2d..8326e16d4a4 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -7,27 +7,27 @@ use std::sync::Arc; use jobserver::Client; -use core::{Package, PackageId, PackageSet, Resolve, Target}; -use core::{Dependency, Workspace}; use core::profiles::{Profile, Profiles}; +use core::{Dependency, Workspace}; +use core::{Package, PackageId, PackageSet, Resolve, Target}; use ops::CompileMode; -use util::{internal, profile, Cfg, CfgExpr, Config}; use util::errors::{CargoResult, CargoResultExt}; +use util::{internal, profile, Cfg, CfgExpr, Config}; -use super::TargetConfig; use super::custom_build::{self, BuildDeps, BuildScripts, BuildState}; use super::fingerprint::Fingerprint; use super::job_queue::JobQueue; use super::layout::Layout; use super::links::Links; +use super::TargetConfig; use super::{BuildConfig, Compilation, Executor, Kind}; mod unit_dependencies; use self::unit_dependencies::build_unit_dependencies; mod compilation_files; -use self::compilation_files::{CompilationFiles, OutputFile}; pub use self::compilation_files::Metadata; +use self::compilation_files::{CompilationFiles, OutputFile}; mod target_info; pub use self::target_info::{FileFlavor, TargetInfo}; diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index 02fd48de031..ff28c0e065a 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -1,15 +1,15 @@ -use std::collections::{BTreeSet, HashSet}; use std::collections::hash_map::{Entry, HashMap}; +use std::collections::{BTreeSet, HashSet}; use std::fs; use std::path::{Path, PathBuf}; use std::str; use std::sync::{Arc, Mutex}; use core::PackageId; -use util::{Cfg, Freshness}; use util::errors::{CargoResult, CargoResultExt}; -use util::{self, internal, paths, profile}; use util::machine_message; +use util::{self, internal, paths, profile}; +use util::{Cfg, Freshness}; use super::job::Work; use super::{fingerprint, Context, Kind, Unit}; diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index b71b47d859c..9020c5cf2a8 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -5,19 +5,19 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use filetime::FileTime; -use serde::ser::{self, Serialize}; use serde::de::{self, Deserialize}; +use serde::ser::{self, Serialize}; use serde_json; use core::{Edition, Package, TargetKind}; use util; -use util::{internal, profile, Dirty, Fresh, Freshness}; use util::errors::{CargoResult, CargoResultExt}; use util::paths; +use util::{internal, profile, Dirty, Fresh, Freshness}; -use super::job::Work; use super::context::{Context, FileFlavor, Unit}; use super::custom_build::BuildDeps; +use super::job::Work; /// A tuple result of the `prepare_foo` functions in this module. /// @@ -353,14 +353,7 @@ impl hash::Hash for Fingerprint { .. } = *self; ( - rustc, - features, - target, - path, - profile, - local, - edition, - rustflags, + rustc, features, target, path, profile, local, edition, rustflags, ).hash(h); h.write_usize(deps.len()); diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs index a713a6285ca..026ac0962ba 100644 --- a/src/cargo/core/compiler/job_queue.rs +++ b/src/cargo/core/compiler/job_queue.rs @@ -1,5 +1,5 @@ -use std::collections::HashSet; use std::collections::hash_map::HashMap; +use std::collections::HashSet; use std::fmt; use std::io; use std::mem; @@ -8,15 +8,15 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use crossbeam::{self, Scope}; use jobserver::{Acquired, HelperThread}; -use core::{PackageId, Target}; use core::profiles::Profile; +use core::{PackageId, Target}; +use handle_error; use ops::CompileMode; -use util::{Config, DependencyQueue, Dirty, Fresh, Freshness}; use util::{internal, profile, CargoResult, CargoResultExt, ProcessBuilder}; -use handle_error; +use util::{Config, DependencyQueue, Dirty, Fresh, Freshness}; -use super::{Context, Kind, Unit}; use super::job::Job; +use super::{Context, Kind, Unit}; /// A management structure of the entire dependency graph to compile. /// diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index ad9ea2d0d5f..c0a948cc911 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -9,14 +9,14 @@ use std::sync::Arc; use same_file::is_same_file; use serde_json; -use core::{Feature, PackageId, Target}; use core::profiles::{Lto, Profile}; use core::shell::ColorChoice; +use core::{Feature, PackageId, Target}; use ops::CompileMode; +use util::errors::{CargoResult, CargoResultExt, Internal}; +use util::paths; use util::{self, machine_message, Config, Freshness, ProcessBuilder, Rustc}; use util::{internal, join_paths, profile}; -use util::paths; -use util::errors::{CargoResult, CargoResultExt, Internal}; use self::job::{Job, Work}; use self::job_queue::JobQueue; @@ -631,10 +631,10 @@ fn hardlink_or_copy(src: &Path, dst: &Path) -> CargoResult<()> { } let link_result = if src.is_dir() { - #[cfg(unix)] - use std::os::unix::fs::symlink; #[cfg(target_os = "redox")] use std::os::redox::fs::symlink; + #[cfg(unix)] + use std::os::unix::fs::symlink; #[cfg(windows)] use std::os::windows::fs::symlink_dir as symlink; diff --git a/src/cargo/core/compiler/output_depinfo.rs b/src/cargo/core/compiler/output_depinfo.rs index 242222ef31d..a0a006a5d0a 100644 --- a/src/cargo/core/compiler/output_depinfo.rs +++ b/src/cargo/core/compiler/output_depinfo.rs @@ -1,11 +1,11 @@ use std::collections::{BTreeSet, HashSet}; -use std::io::{BufWriter, Write}; use std::fs::File; +use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; use super::{fingerprint, Context, Unit}; -use util::{internal, CargoResult}; use util::paths; +use util::{internal, CargoResult}; fn render_filename>(path: P, basedir: Option<&str>) -> CargoResult { let path = path.as_ref(); diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 406c969aa82..b38c0950e9e 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -133,8 +133,12 @@ macro_rules! features { } macro_rules! stab { - (stable) => (Status::Stable); - (unstable) => (Status::Unstable); + (stable) => { + Status::Stable + }; + (unstable) => { + Status::Unstable + }; } /// A listing of all features in Cargo diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index e8c11180823..5e42103862c 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -1,21 +1,21 @@ use std::collections::{BTreeMap, HashMap}; use std::fmt; +use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; use std::rc::Rc; -use std::hash::{Hash, Hasher}; use semver::Version; use serde::ser; use toml; use url::Url; -use core::{Dependency, PackageId, PackageIdSpec, SourceId, Summary}; +use core::interning::InternedString; use core::profiles::Profiles; +use core::{Dependency, PackageId, PackageIdSpec, SourceId, Summary}; use core::{Edition, Feature, Features, WorkspaceConfig}; -use core::interning::InternedString; -use util::Config; -use util::toml::TomlManifest; use util::errors::*; +use util::toml::TomlManifest; +use util::Config; pub enum EitherManifest { Real(Manifest), diff --git a/src/cargo/core/mod.rs b/src/cargo/core/mod.rs index 347772bb810..67578c8223c 100644 --- a/src/cargo/core/mod.rs +++ b/src/cargo/core/mod.rs @@ -12,18 +12,18 @@ pub use self::source::{GitReference, Source, SourceId, SourceMap}; pub use self::summary::{FeatureMap, FeatureValue, Summary}; pub use self::workspace::{Members, Workspace, WorkspaceConfig, WorkspaceRootConfig}; -pub mod source; -pub mod package; -pub mod package_id; +pub mod compiler; pub mod dependency; +mod features; +mod interning; pub mod manifest; +pub mod package; +pub mod package_id; +mod package_id_spec; +pub mod profiles; +pub mod registry; pub mod resolver; -pub mod summary; pub mod shell; -pub mod registry; -pub mod compiler; -pub mod profiles; -mod interning; -mod package_id_spec; +pub mod source; +pub mod summary; mod workspace; -mod features; diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index 71ee27102fe..70d6a1fe5e5 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -1,12 +1,12 @@ -use std::{cmp, fmt, hash}; use std::collections::HashSet; +use std::{cmp, fmt, hash}; -use core::Shell; use core::interning::InternedString; +use core::Shell; use ops::CompileMode; -use util::CargoResult; use util::lev_distance::lev_distance; use util::toml::{StringOrBool, TomlProfile, U32OrBool}; +use util::CargoResult; /// Collection of all user profiles. #[derive(Clone, Debug)] diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index 8f339045407..cd5bc220a3c 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -1,16 +1,16 @@ use std::cell::RefCell; -use std::collections::BTreeMap; use std::collections::hash_map::{Entry, HashMap}; +use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::slice; use glob::glob; use url::Url; +use core::profiles::Profiles; use core::registry::PackageRegistry; use core::{Dependency, PackageIdSpec}; use core::{EitherManifest, Package, SourceId, VirtualManifest}; -use core::profiles::Profiles; use ops; use sources::PathSource; use util::errors::{CargoResult, CargoResultExt}; diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index 3c0321c42ff..72b1966990b 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -1,13 +1,13 @@ use std::fs; use std::path::Path; -use core::Workspace; use core::compiler::{BuildConfig, Context, Kind, Unit}; use core::profiles::ProfileFor; -use util::Config; +use core::Workspace; +use ops::{self, CompileMode}; use util::errors::{CargoResult, CargoResultExt}; use util::paths; -use ops::{self, CompileMode}; +use util::Config; pub struct CleanOptions<'a> { pub config: &'a Config, diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index dec8cb196d5..e0c4e0b1e40 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -575,64 +575,68 @@ fn generate_targets<'a>( let mut units = Vec::new(); // Helper for creating a Unit struct. - let new_unit = - |pkg: &'a Package, target: &'a Target, target_mode: CompileMode| { - let profile_for = if mode.is_any_test() { - // NOTE: The ProfileFor here is subtle. If you have a profile - // with `panic` set, the `panic` flag is cleared for - // tests/benchmarks and their dependencies. If we left this - // as an "Any" profile, then the lib would get compiled three - // times (once with panic, once without, and once with - // --test). - // - // This would cause a problem for Doc tests, which would fail - // because `rustdoc` would attempt to link with both libraries - // at the same time. Also, it's probably not important (or - // even desirable?) for rustdoc to link with a lib with - // `panic` set. - // - // As a consequence, Examples and Binaries get compiled - // without `panic` set. This probably isn't a bad deal. - // - // Forcing the lib to be compiled three times during `cargo - // test` is probably also not desirable. - ProfileFor::TestDependency - } else { - ProfileFor::Any - }; - let target_mode = match target_mode { - CompileMode::Test => { - if target.is_example() { - // Examples are included as regular binaries to verify - // that they compile. - CompileMode::Build - } else { - CompileMode::Test - } + let new_unit = |pkg: &'a Package, target: &'a Target, target_mode: CompileMode| { + let profile_for = if mode.is_any_test() { + // NOTE: The ProfileFor here is subtle. If you have a profile + // with `panic` set, the `panic` flag is cleared for + // tests/benchmarks and their dependencies. If we left this + // as an "Any" profile, then the lib would get compiled three + // times (once with panic, once without, and once with + // --test). + // + // This would cause a problem for Doc tests, which would fail + // because `rustdoc` would attempt to link with both libraries + // at the same time. Also, it's probably not important (or + // even desirable?) for rustdoc to link with a lib with + // `panic` set. + // + // As a consequence, Examples and Binaries get compiled + // without `panic` set. This probably isn't a bad deal. + // + // Forcing the lib to be compiled three times during `cargo + // test` is probably also not desirable. + ProfileFor::TestDependency + } else { + ProfileFor::Any + }; + let target_mode = match target_mode { + CompileMode::Test => { + if target.is_example() { + // Examples are included as regular binaries to verify + // that they compile. + CompileMode::Build + } else { + CompileMode::Test } - CompileMode::Build => match *target.kind() { - TargetKind::Test => CompileMode::Test, - TargetKind::Bench => CompileMode::Bench, - _ => CompileMode::Build, - }, - _ => target_mode, - }; - // Plugins or proc-macro should be built for the host. - let kind = if target.for_host() { - Kind::Host - } else { - default_arch_kind - }; - let profile = - profiles.get_profile(&pkg.name(), ws.is_member(pkg), profile_for, target_mode, release); - Unit { - pkg, - target, - profile, - kind, - mode: target_mode, } + CompileMode::Build => match *target.kind() { + TargetKind::Test => CompileMode::Test, + TargetKind::Bench => CompileMode::Bench, + _ => CompileMode::Build, + }, + _ => target_mode, }; + // Plugins or proc-macro should be built for the host. + let kind = if target.for_host() { + Kind::Host + } else { + default_arch_kind + }; + let profile = profiles.get_profile( + &pkg.name(), + ws.is_member(pkg), + profile_for, + target_mode, + release, + ); + Unit { + pkg, + target, + profile, + kind, + mode: target_mode, + } + }; for pkg in packages { let features = resolve_all_features(resolve, pkg.package_id()); @@ -650,21 +654,13 @@ fn generate_targets<'a>( } => { let default_units = generate_default_targets(pkg.targets(), mode) .iter() - .map(|t| { - ( - new_unit(pkg, t, mode), - !required_features_filterable, - ) - }) + .map(|t| (new_unit(pkg, t, mode), !required_features_filterable)) .collect::>(); proposals.extend(default_units); if mode == CompileMode::Test { // Include the lib as it will be required for doctests. if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) { - proposals.push(( - new_unit(pkg, t, CompileMode::Build), - false, - )); + proposals.push((new_unit(pkg, t, CompileMode::Build), false)); } } } @@ -713,23 +709,17 @@ fn generate_targets<'a>( .chain( list_rule_targets(pkg, examples, "example", Target::is_example)? .into_iter() - .map(|(t, required)| { - (new_unit(pkg, t, mode), required) - }), + .map(|(t, required)| (new_unit(pkg, t, mode), required)), ) .chain( list_rule_targets(pkg, tests, "test", test_filter)? .into_iter() - .map(|(t, required)| { - (new_unit(pkg, t, test_mode), required) - }), + .map(|(t, required)| (new_unit(pkg, t, test_mode), required)), ) .chain( list_rule_targets(pkg, benches, "bench", bench_filter)? .into_iter() - .map(|(t, required)| { - (new_unit(pkg, t, bench_mode), required) - }), + .map(|(t, required)| (new_unit(pkg, t, bench_mode), required)), ) .collect::>(), ); @@ -800,7 +790,10 @@ fn resolve_all_features( fn generate_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> { match mode { CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(), - CompileMode::Test => targets.iter().filter(|t| t.tested() || t.is_example()).collect(), + CompileMode::Test => targets + .iter() + .filter(|t| t.tested() || t.is_example()) + .collect(), CompileMode::Build | CompileMode::Check { .. } => targets .iter() .filter(|t| t.is_bin() || t.is_lib()) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 76f54de916f..28cebea35d3 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -6,22 +6,22 @@ use std::rc::Rc; use std::str; use semver::{self, VersionReq}; -use serde::ser; use serde::de::{self, Deserialize}; +use serde::ser; use serde_ignored; use toml; use url::Url; -use core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig}; -use core::{Dependency, Manifest, PackageId, Summary, Target}; -use core::{Edition, EitherManifest, Feature, Features, VirtualManifest}; use core::dependency::{Kind, Platform}; use core::manifest::{LibKind, ManifestMetadata}; use core::profiles::Profiles; +use core::{Dependency, Manifest, PackageId, Summary, Target}; +use core::{Edition, EitherManifest, Feature, Features, VirtualManifest}; +use core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig}; use sources::CRATES_IO; +use util::errors::{CargoError, CargoResult, CargoResultExt}; use util::paths; use util::{self, Config, ToUrl}; -use util::errors::{CargoError, CargoResult, CargoResultExt}; mod targets; use self::targets::targets; From 9ca36de444706322c80af7d29f4700ae0b4f63e5 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 21 Apr 2018 13:47:01 -0700 Subject: [PATCH 16/23] Move profile override tests to a dedicated file. --- tests/testsuite/main.rs | 1 + tests/testsuite/profile_overrides.rs | 234 +++++++++++++++++++++++++++ tests/testsuite/profiles.rs | 234 +-------------------------- 3 files changed, 236 insertions(+), 233 deletions(-) create mode 100644 tests/testsuite/profile_overrides.rs diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index e1d3181cd08..51b8ae58468 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -70,6 +70,7 @@ mod path; mod plugins; mod proc_macro; mod profiles; +mod profile_overrides; mod publish; mod read_manifest; mod registry; diff --git a/tests/testsuite/profile_overrides.rs b/tests/testsuite/profile_overrides.rs new file mode 100644 index 00000000000..458e9e057ee --- /dev/null +++ b/tests/testsuite/profile_overrides.rs @@ -0,0 +1,234 @@ +use cargotest::support::{basic_lib_manifest, execs, project}; +use cargotest::ChannelChanger; +use hamcrest::assert_that; + +#[test] +fn profile_override_gated() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.dev.build-override] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs().with_status(101).with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + feature `profile-overrides` is required + +consider adding `cargo-features = [\"profile-overrides\"]` to the manifest +", + ), + ); + + let p = project("foo") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.dev.overrides."*"] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs().with_status(101).with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + feature `profile-overrides` is required + +consider adding `cargo-features = [\"profile-overrides\"]` to the manifest +", + ), + ); +} + +#[test] +fn profile_override_basic() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-overrides"] + + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [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(); + + assert_that( + p.cargo("build -v").masquerade_as_nightly_cargo(), + execs().with_status(0).with_stderr( + "[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar [..] -C opt-level=3 [..]` +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..] -C opt-level=1 [..]` +[FINISHED] dev [optimized + debuginfo] target(s) in [..]", + ), + ); +} + +#[test] +fn profile_override_bad_name() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-overrides"] + + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = {path = "bar"} + + [profile.dev.overrides.bart] + opt-level = 3 + + [profile.dev.overrides.no-suggestion] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs().with_status(0).with_stderr_contains( + "\ +[WARNING] package `bart` for profile override not found + +Did you mean `bar`? +[WARNING] package `no-suggestion` for profile override not found +[COMPILING] [..] +", + ), + ); +} + +#[test] +fn profile_override_dev_release_only() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-overrides"] + + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = {path = "bar"} + + [profile.test.overrides.bar] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs().with_status(101).with_stderr_contains( + "\ +Caused by: + Profile overrides may only be specified for `dev` or `release` profile, not `test`. +", + ), + ); +} + +#[test] +fn profile_override_bad_settings() { + let bad_values = [ + ( + "panic = \"abort\"", + "`panic` may not be specified in a profile override.", + ), + ( + "lto = true", + "`lto` may not be specified in a profile override.", + ), + ( + "rpath = true", + "`rpath` may not be specified in a profile override.", + ), + ("overrides = {}", "Profile overrides cannot be nested."), + ]; + for &(ref snippet, ref expected) in bad_values.iter() { + let p = project("foo") + .file( + "Cargo.toml", + &format!( + r#" + cargo-features = ["profile-overrides"] + + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = {{path = "bar"}} + + [profile.dev.overrides.bar] + {} + "#, + snippet + ), + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + assert_that( + p.cargo("build").masquerade_as_nightly_cargo(), + execs() + .with_status(101) + .with_stderr_contains(format!("Caused by:\n {}", expected)), + ); + } +} diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs index 00f1340ee1a..8d1478ced24 100644 --- a/tests/testsuite/profiles.rs +++ b/tests/testsuite/profiles.rs @@ -1,8 +1,7 @@ use std::env; use cargotest::is_nightly; -use cargotest::support::{basic_lib_manifest, execs, project}; -use cargotest::ChannelChanger; +use cargotest::support::{execs, project}; use hamcrest::assert_that; #[test] @@ -342,150 +341,6 @@ fn profile_in_virtual_manifest_works() { ); } -#[test] -fn profile_override_gated() { - let p = project("foo") - .file( - "Cargo.toml", - r#" - [package] - name = "foo" - version = "0.0.1" - authors = [] - - [profile.dev.build-override] - opt-level = 3 - "#, - ) - .file("src/lib.rs", "") - .build(); - - assert_that( - p.cargo("build").masquerade_as_nightly_cargo(), - execs().with_status(101).with_stderr( - "\ -error: failed to parse manifest at `[..]` - -Caused by: - feature `profile-overrides` is required - -consider adding `cargo-features = [\"profile-overrides\"]` to the manifest -", - ), - ); - - let p = project("foo") - .file( - "Cargo.toml", - r#" - [package] - name = "foo" - version = "0.0.1" - authors = [] - - [profile.dev.overrides."*"] - opt-level = 3 - "#, - ) - .file("src/lib.rs", "") - .build(); - - assert_that( - p.cargo("build").masquerade_as_nightly_cargo(), - execs().with_status(101).with_stderr( - "\ -error: failed to parse manifest at `[..]` - -Caused by: - feature `profile-overrides` is required - -consider adding `cargo-features = [\"profile-overrides\"]` to the manifest -", - ), - ); -} - -#[test] -fn profile_override_basic() { - let p = project("foo") - .file( - "Cargo.toml", - r#" - cargo-features = ["profile-overrides"] - - [package] - name = "foo" - version = "0.0.1" - authors = [] - - [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(); - - assert_that( - p.cargo("build -v").masquerade_as_nightly_cargo(), - execs().with_status(0).with_stderr( - "[COMPILING] bar [..] -[RUNNING] `rustc --crate-name bar [..] -C opt-level=3 [..]` -[COMPILING] foo [..] -[RUNNING] `rustc --crate-name foo [..] -C opt-level=1 [..]` -[FINISHED] dev [optimized + debuginfo] target(s) in [..]", - ), - ); -} - -#[test] -fn profile_override_bad_name() { - let p = project("foo") - .file( - "Cargo.toml", - r#" - cargo-features = ["profile-overrides"] - - [package] - name = "foo" - version = "0.0.1" - - [dependencies] - bar = {path = "bar"} - - [profile.dev.overrides.bart] - opt-level = 3 - - [profile.dev.overrides.no-suggestion] - opt-level = 3 - "#, - ) - .file("src/lib.rs", "") - .file("bar/Cargo.toml", &basic_lib_manifest("bar")) - .file("bar/src/lib.rs", "") - .build(); - - assert_that( - p.cargo("build").masquerade_as_nightly_cargo(), - execs().with_status(0).with_stderr_contains( - "\ -[WARNING] package `bart` for profile override not found - -Did you mean `bar`? -[WARNING] package `no-suggestion` for profile override not found -[COMPILING] [..] -", - ), - ); -} - #[test] fn profile_panic_test_bench() { let p = project("foo") @@ -516,90 +371,3 @@ fn profile_panic_test_bench() { ), ); } - -#[test] -fn profile_override_dev_release_only() { - let p = project("foo") - .file( - "Cargo.toml", - r#" - cargo-features = ["profile-overrides"] - - [package] - name = "foo" - version = "0.0.1" - - [dependencies] - bar = {path = "bar"} - - [profile.test.overrides.bar] - opt-level = 3 - "#, - ) - .file("src/lib.rs", "") - .file("bar/Cargo.toml", &basic_lib_manifest("bar")) - .file("bar/src/lib.rs", "") - .build(); - - assert_that( - p.cargo("build").masquerade_as_nightly_cargo(), - execs().with_status(101).with_stderr_contains( - "\ -Caused by: - Profile overrides may only be specified for `dev` or `release` profile, not `test`. -", - ), - ); -} - -#[test] -fn profile_override_bad_settings() { - let bad_values = [ - ( - "panic = \"abort\"", - "`panic` may not be specified in a profile override.", - ), - ( - "lto = true", - "`lto` may not be specified in a profile override.", - ), - ( - "rpath = true", - "`rpath` may not be specified in a profile override.", - ), - ("overrides = {}", "Profile overrides cannot be nested."), - ]; - for &(ref snippet, ref expected) in bad_values.iter() { - let p = project("foo") - .file( - "Cargo.toml", - &format!( - r#" - cargo-features = ["profile-overrides"] - - [package] - name = "foo" - version = "0.0.1" - - [dependencies] - bar = {{path = "bar"}} - - [profile.dev.overrides.bar] - {} - "#, - snippet - ), - ) - .file("src/lib.rs", "") - .file("bar/Cargo.toml", &basic_lib_manifest("bar")) - .file("bar/src/lib.rs", "") - .build(); - - assert_that( - p.cargo("build").masquerade_as_nightly_cargo(), - execs() - .with_status(101) - .with_stderr_contains(format!("Caused by:\n {}", expected)), - ); - } -} From 10a6da6298c64d58d3f539101fc992ae06911585 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 21 Apr 2018 13:57:54 -0700 Subject: [PATCH 17/23] Add thorough tests for target/profile selection. --- tests/testsuite/cargotest/support/mod.rs | 43 ++ tests/testsuite/main.rs | 1 + tests/testsuite/profile_targets.rs | 710 +++++++++++++++++++++++ 3 files changed, 754 insertions(+) create mode 100644 tests/testsuite/profile_targets.rs diff --git a/tests/testsuite/cargotest/support/mod.rs b/tests/testsuite/cargotest/support/mod.rs index 115f5be47b6..f51ea4384aa 100644 --- a/tests/testsuite/cargotest/support/mod.rs +++ b/tests/testsuite/cargotest/support/mod.rs @@ -380,6 +380,7 @@ pub struct Execs { expect_stdout_contains_n: Vec<(String, usize)>, expect_stdout_not_contains: Vec, expect_stderr_not_contains: Vec, + expect_stderr_unordered: Vec, expect_neither_contains: Vec, expect_json: Option>, stream_output: bool, @@ -436,6 +437,11 @@ impl Execs { self } + pub fn with_stderr_unordered(mut self, expected: S) -> Execs { + self.expect_stderr_unordered.push(expected.to_string()); + self + } + pub fn with_json(mut self, expected: &str) -> Execs { self.expect_json = Some( expected @@ -526,6 +532,15 @@ impl Execs { MatchKind::NotPresent, )?; } + for expect in self.expect_stderr_unordered.iter() { + self.match_std( + Some(expect), + &actual.stderr, + "stderr", + &actual.stdout, + MatchKind::Unordered, + )?; + } for expect in self.expect_neither_contains.iter() { self.match_std( Some(expect), @@ -709,6 +724,32 @@ impl Execs { Ok(()) } } + MatchKind::Unordered => { + let mut a = actual.lines().collect::>(); + let e = out.lines(); + + for e_line in e { + match a.iter().position(|a_line| lines_match(e_line, a_line)) { + Some(index) => a.remove(index), + None => { + return Err(format!( + "Did not find expected line:\n\ + {}\n\ + Remaining available output:\n\ + {}\n", + e_line, + a.join("\n") + )) + } + }; + } + if a.len() > 0 { + Err(format!("Output included extra lines:\n\ + {}\n", a.join("\n"))) + } else { + Ok(()) + } + } } } @@ -765,6 +806,7 @@ enum MatchKind { Partial, PartialN(usize), NotPresent, + Unordered, } pub fn lines_match(expected: &str, mut actual: &str) -> bool { @@ -943,6 +985,7 @@ pub fn execs() -> Execs { expect_stdout_contains_n: Vec::new(), expect_stdout_not_contains: Vec::new(), expect_stderr_not_contains: Vec::new(), + expect_stderr_unordered: Vec::new(), expect_neither_contains: Vec::new(), expect_json: None, stream_output: false, diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index 51b8ae58468..413f0463d3f 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -71,6 +71,7 @@ mod plugins; mod proc_macro; mod profiles; mod profile_overrides; +mod profile_targets; mod publish; mod read_manifest; mod registry; diff --git a/tests/testsuite/profile_targets.rs b/tests/testsuite/profile_targets.rs new file mode 100644 index 00000000000..0340e35481d --- /dev/null +++ b/tests/testsuite/profile_targets.rs @@ -0,0 +1,710 @@ +use cargotest::support::{execs, project, Project}; +use hamcrest::assert_that; + +// These tests try to exercise exactly which profiles are selected for every +// target. + +fn all_target_project() -> Project { + // This abuses the `codegen-units` setting so that we can verify exactly + // which profile is used for each compiler invocation. + project("foo") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { path = "bar" } + + [build-dependencies] + bdep = { path = "bdep" } + + [profile.dev] + codegen-units = 1 + panic = "abort" + [profile.release] + codegen-units = 2 + panic = "abort" + [profile.test] + codegen-units = 3 + [profile.bench] + codegen-units = 4 + [profile.doc] + codegen-units = 5 + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file("src/main.rs", r#" + extern crate foo; + fn main() {} + "#) + .file("examples/ex1.rs", r#" + extern crate foo; + fn main() {} + "#) + .file("tests/test1.rs", "extern crate foo;") + .file("benches/bench1.rs", "extern crate foo;") + .file("build.rs", r#" + extern crate bdep; + fn main() { + eprintln!("foo custom build PROFILE={} DEBUG={} OPT_LEVEL={}", + std::env::var("PROFILE").unwrap(), + std::env::var("DEBUG").unwrap(), + std::env::var("OPT_LEVEL").unwrap(), + ); + } + "#) + + // bar package + .file("bar/Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.1" + "#) + .file("bar/src/lib.rs", "") + + // bdep package + .file("bdep/Cargo.toml", r#" + [package] + name = "bdep" + version = "0.0.1" + + [dependencies] + bar = { path = "../bar" } + "#) + .file("bdep/src/lib.rs", "extern crate bar;") + .build() +} + +#[test] +fn profile_selection_build() { + let p = all_target_project(); + + // Build default targets. + // NOTES: + // - bdep `panic` is not set because it thinks `build.rs` is a plugin. + // - bar `panic` is not set because it is shared with `bdep`. + // - build_script_build is built without panic because it thinks `build.rs` is a plugin. + assert_that(p.cargo("build -vv"), execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] bdep [..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..][/]target[/]debug[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,link -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +")); + assert_that( + p.cargo("build -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +", + ), + ); +} + +#[test] +fn profile_selection_build_release() { + let p = all_target_project(); + + // Build default targets, release. + assert_that(p.cargo("build --release -vv"), + execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[COMPILING] bdep [..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `[..][/]target[/]release[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C panic=abort -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C panic=abort -C codegen-units=2 [..] +[FINISHED] release [optimized] [..] +")); + assert_that( + p.cargo("build --release -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] release [optimized] [..] +", + ), + ); +} + +#[test] +fn profile_selection_build_all_targets() { + let p = all_target_project(); + // Build all explicit targets. + // NOTES + // - bdep `panic` is not set because it thinks `build.rs` is a plugin. + // - bar compiled twice. It tries with and without panic, but the "is a + // plugin" logic is forcing it to be cleared. + // - build_script_build is built without panic because it thinks + // `build.rs` is a plugin. + // - build_script_build is being run two times. Once for the `dev` and + // `test` targets, once for the `bench` targets. + // TODO: "PROFILE" says debug both times, though! + // - Benchmark dependencies are compiled in `dev` mode, which may be + // surprising. See https://github.com/rust-lang/cargo/issues/4929. + // + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib dev* For bdep and foo + // bar lib dev-panic For tests/benches + // bdep lib dev* For foo build.rs + // foo custom dev* + // + // `*` = wants panic, but it is cleared when args are built. + // + // - foo target list is: + // Target Profile Mode + // ------ ------- ---- + // lib dev+panic build (a normal lib target) + // lib dev-panic build (used by tests/benches) + // lib test test + // lib bench bench + // test test test + // bench bench bench + // bin test test + // bin bench bench + // bin dev build + // example dev build + assert_that(p.cargo("build --all-targets -vv"), + execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] bdep [..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..][/]target[/]debug[/]build[/]foo-[..][/]build-script-build` +[RUNNING] `[..][/]target[/]debug[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=debug DEBUG=false OPT_LEVEL=3 +foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C panic=abort -C codegen-units=1 -C debuginfo=2 [..]` +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,link -C codegen-units=3 -C debuginfo=2 --test [..]` +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..]` +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link -C codegen-units=3 -C debuginfo=2 --test [..]` +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,link -C codegen-units=3 -C debuginfo=2 --test [..]` +[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,link -C panic=abort -C codegen-units=1 -C debuginfo=2 [..]` +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,link -C panic=abort -C codegen-units=1 -C debuginfo=2 [..]` +[FINISHED] dev [unoptimized + debuginfo] [..] +")); + assert_that( + p.cargo("build -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +", + ), + ); +} + +#[test] +fn profile_selection_build_all_targets_release() { + let p = all_target_project(); + // Build all explicit targets, release. + // NOTES + // - bdep `panic` is not set because it thinks `build.rs` is a plugin. + // - bar compiled twice. It tries with and without panic, but the "is a + // plugin" logic is forcing it to be cleared. + // - build_script_build is built without panic because it thinks + // `build.rs` is a plugin. + // - build_script_build is being run two times. Once for the `dev` and + // `test` targets, once for the `bench` targets. + // TODO: "PROFILE" says debug both times, though! + // + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib release* For bdep and foo + // bar lib release-panic For tests/benches + // bdep lib release* For foo build.rs + // foo custom release* + // + // `*` = wants panic, but it is cleared when args are built. + // + // + // - foo target list is: + // Target Profile Mode + // ------ ------- ---- + // lib release+panic build (a normal lib target) + // lib release-panic build (used by tests/benches) + // lib bench test + // lib bench bench + // test bench test + // bench bench bench + // bin bench test + // bin bench bench + // bin release build + // example release build + assert_that(p.cargo("build --all-targets --release -vv"), + execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[COMPILING] bdep [..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `[..][/]target[/]release[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C panic=abort -C codegen-units=2 [..]` +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..]` +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` +[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C panic=abort -C codegen-units=2 [..]` +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C panic=abort -C codegen-units=2 [..]` +[FINISHED] release [optimized] [..] +")); + assert_that( + p.cargo("build --all-targets --release -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] release [optimized] [..] +", + ), + ); +} + +#[test] +fn profile_selection_test() { + let p = all_target_project(); + // Test default. + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib dev* For bdep and foo + // bar lib dev-panic For tests/benches + // bdep lib dev* For foo build.rs + // foo custom dev* + // + // `*` = wants panic, but it is cleared when args are built. + // + // - foo target list is: + // Target Profile Mode + // ------ ------- ---- + // lib dev-panic build + // lib test test + // test test test + // example dev-panic build + // bin test test + // bin dev-panic build + // + assert_that(p.cargo("test -vv"), execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] bdep [..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..][/]target[/]debug[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,link -C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,link -C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link -C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +[RUNNING] `[..]foo[..]` +[RUNNING] `[..]foo[..]` +[RUNNING] `[..]test1[..]` +[DOCTEST] foo +[RUNNING] `rustdoc --test [..] +")); + assert_that( + p.cargo("test -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +[RUNNING] `[..]foo[..]` +[RUNNING] `[..]foo[..]` +[RUNNING] `[..]test1[..]` +[DOCTEST] foo +[RUNNING] `rustdoc --test [..] +", + ), + ); +} + +#[test] +fn profile_selection_test_release() { + let p = all_target_project(); + // Test default release. + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib release* For bdep and foo + // bar lib release-panic For tests/benches + // bdep lib release* For foo build.rs + // foo custom release* + // + // `*` = wants panic, but it is cleared when args are built. + // + // - foo target list is: + // Target Profile Mode + // ------ ------- ---- + // lib release-panic build + // lib bench test + // test bench test + // example release-panic build + // bin bench test + // bin release-panic build + // + assert_that(p.cargo("test --release -vv"), execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[COMPILING] bdep [..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `[..][/]target[/]release[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..] +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..] +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[FINISHED] release [optimized] [..] +[RUNNING] `[..]foo[..]` +[RUNNING] `[..]foo[..]` +[RUNNING] `[..]test1[..]` +[DOCTEST] foo +[RUNNING] `rustdoc --test [..]` +")); + assert_that( + p.cargo("test --release -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] release [optimized] [..] +[RUNNING] `[..]foo[..]` +[RUNNING] `[..]foo[..]` +[RUNNING] `[..]test1[..]` +[DOCTEST] foo +[RUNNING] `rustdoc --test [..] +", + ), + ); +} + +#[test] +fn profile_selection_bench() { + let p = all_target_project(); + + // Bench default. + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib release* For bdep and foo + // bar lib release-panic For tests/benches + // bdep lib release* For foo build.rs + // foo custom release* + // + // `*` = wants panic, but it is cleared when args are built. + // + // - foo target list is: + // Target Profile Mode + // ------ ------- ---- + // lib release-panic build + // lib bench bench + // bench bench bench + // bin bench bench + // bin release-panic build + // + assert_that(p.cargo("bench -vv"), execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[COMPILING] bdep [..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `[..]target[/]release[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..] +[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[FINISHED] release [optimized] [..] +[RUNNING] `[..]foo[..] --bench` +[RUNNING] `[..]foo[..] --bench` +[RUNNING] `[..]bench1[..] --bench` +")); + assert_that( + p.cargo("bench -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] release [optimized] [..] +[RUNNING] `[..]foo[..] --bench` +[RUNNING] `[..]foo[..] --bench` +[RUNNING] `[..]bench1[..] --bench` +", + ), + ); +} + +#[test] +fn profile_selection_check_all_targets() { + let p = all_target_project(); + // check + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Action Reason + // --- ------ ------- ------ ------ + // bar lib dev* link For bdep + // bar lib dev-panic metadata For tests/benches + // bar lib dev metadata For lib/bins + // bdep lib dev* link For foo build.rs + // foo custom dev* link For build.rs + // + // `*` = wants panic, but it is cleared when args are built. + // + // - foo target list is: + // Target Profile Mode + // ------ ------- ---- + // lib dev check + // lib dev-panic check (for tests/benches) + // lib dev-panic check-test (checking lib as a unittest) + // example dev check + // test dev-panic check-test + // bench dev-panic check-test + // bin dev check + // bin dev-panic check-test (checking bin as a unittest) + // + assert_that(p.cargo("check --all-targets -vv"), execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] bdep[..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..]target[/]debug[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +")); + // Check re-run re-checks bins (and tests/benches) because rustc does not + // emit rmeta files for bins. See + // https://github.com/rust-lang/cargo/issues/3624. + assert_that( + p.cargo("check --all-targets -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[CHECKING] foo [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +", + ), + ); +} + +#[test] +fn profile_selection_check_all_targets_release() { + let p = all_target_project(); + // check --release + // https://github.com/rust-lang/cargo/issues/5218 + // This is a pretty straightforward variant of + // `profile_selection_check_all_targets` that uses `release` instead of + // `dev` for all targets. + assert_that(p.cargo("check --all-targets --release -vv"), execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C opt-level=3 -C panic=abort -C codegen-units=2 [..] +[COMPILING] bdep[..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `[..]target[/]release[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C opt-level=3 -C panic=abort -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] +[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,metadata -C opt-level=3 -C panic=abort -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,metadata -C opt-level=3 -C panic=abort -C codegen-units=2 [..] +[FINISHED] release [optimized] [..] +")); + // Check re-run re-checks bins (and tests/benches) because rustc does not + // emit rmeta files for bins. See + // https://github.com/rust-lang/cargo/issues/3624. + assert_that( + p.cargo("check --all-targets --release -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[CHECKING] foo [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] +[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,metadata -C opt-level=3 -C panic=abort -C codegen-units=2 [..] +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,metadata -C opt-level=3 -C panic=abort -C codegen-units=2 [..] +[FINISHED] release [optimized] [..] +", + ), + ); +} + +#[test] +fn profile_selection_check_all_targets_test() { + let p = all_target_project(); + // check --profile=test + // NOTES: + // - This doesn't actually use the "test" profile. Everything uses dev. + // It probably should use "test"??? Probably doesn't really matter. + // - Dependency profiles: + // Pkg Target Profile Action Reason + // --- ------ ------- ------ ------ + // bar lib dev* link For bdep + // bar lib dev-panic metadata For tests/benches + // bdep lib dev* link For foo build.rs + // foo custom dev* link For build.rs + // + // `*` = wants panic, but it is cleared when args are built. + // + // - foo target list is: + // Target Profile Mode + // ------ ------- ---- + // lib dev-panic check-test (for tests/benches) + // lib dev-panic check-test (checking lib as a unittest) + // example dev-panic check-test + // test dev-panic check-test + // bench dev-panic check-test + // bin dev-panic check-test + // + assert_that(p.cargo("check --all-targets --profile=test -vv"), execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] bdep[..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..]target[/]debug[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +")); + // Check re-run re-checks bins (and tests/benches) because rustc does not + // emit rmeta files for bins. See + // https://github.com/rust-lang/cargo/issues/3624. + assert_that( + p.cargo("check --all-targets --profile=test -vv"), + execs().with_status(0).with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[CHECKING] foo [..] +[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +", + ), + ); +} + +#[test] +fn profile_selection_doc() { + let p = all_target_project(); + // doc + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Action Reason + // --- ------ ------- ------ ------ + // bar lib dev* link For bdep + // bar lib dev metadata For rustdoc + // bdep lib dev* link For foo build.rs + // foo custom dev* link For build.rs + // + // `*` = wants panic, but it is cleared when args are built. + assert_that(p.cargo("doc -vv"), execs().with_status(0).with_stderr_unordered("\ +[COMPILING] bar [..] +[DOCUMENTING] bar [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `rustdoc --crate-name bar bar[/]src[/]lib.rs [..] +[RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type lib --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] bdep [..] +[RUNNING] `rustc --crate-name bdep bdep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin --emit=dep-info,link -C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..]target[/]debug[/]build[/]foo-[..][/]build-script-build` +foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[DOCUMENTING] foo [..] +[RUNNING] `rustdoc --crate-name foo src[/]lib.rs [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +")); +} From b9181ef3b5e9b15c72bacf536a6f9310404305b8 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 21 Apr 2018 17:21:42 -0700 Subject: [PATCH 18/23] Add some more tests. --- tests/testsuite/cargotest/support/mod.rs | 34 ++++--- tests/testsuite/profile_overrides.rs | 111 +++++++++++++++++++++++ tests/testsuite/rustc.rs | 56 +++++++++++- tests/testsuite/test.rs | 75 ++++++++++++++- 4 files changed, 259 insertions(+), 17 deletions(-) diff --git a/tests/testsuite/cargotest/support/mod.rs b/tests/testsuite/cargotest/support/mod.rs index f51ea4384aa..4ae3aeabc51 100644 --- a/tests/testsuite/cargotest/support/mod.rs +++ b/tests/testsuite/cargotest/support/mod.rs @@ -9,26 +9,28 @@ use std::process::Output; use std::str; use std::usize; -use serde_json::{self, Value}; -use url::Url; -use hamcrest as ham; use cargo::util::ProcessBuilder; use cargo::util::ProcessError; +use hamcrest as ham; +use serde_json::{self, Value}; +use url::Url; use cargotest::support::paths::CargoPathExt; macro_rules! t { - ($e:expr) => (match $e { - Ok(e) => e, - Err(e) => panic!("{} failed with {}", stringify!($e), e), - }) + ($e:expr) => { + match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with {}", stringify!($e), e), + } + }; } -pub mod paths; -pub mod git; -pub mod registry; pub mod cross_compile; +pub mod git; +pub mod paths; pub mod publish; +pub mod registry; /* * @@ -588,7 +590,10 @@ impl Execs { if let Some(ref objects) = self.expect_json { let stdout = str::from_utf8(&actual.stdout) .map_err(|_| "stdout was not utf8 encoded".to_owned())?; - let lines = stdout.lines().collect::>(); + let lines = stdout + .lines() + .filter(|line| line.starts_with("{")) + .collect::>(); if lines.len() != objects.len() { return Err(format!( "expected {} json lines, got {}, stdout:\n{}", @@ -744,8 +749,11 @@ impl Execs { }; } if a.len() > 0 { - Err(format!("Output included extra lines:\n\ - {}\n", a.join("\n"))) + Err(format!( + "Output included extra lines:\n\ + {}\n", + a.join("\n") + )) } else { Ok(()) } diff --git a/tests/testsuite/profile_overrides.rs b/tests/testsuite/profile_overrides.rs index 458e9e057ee..59a61616dd4 100644 --- a/tests/testsuite/profile_overrides.rs +++ b/tests/testsuite/profile_overrides.rs @@ -232,3 +232,114 @@ fn profile_override_bad_settings() { ); } } + +#[test] +fn profile_override_hierarchy() { + // Test that the precedence rules are correct for different types. + let p = project("foo") + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-overrides"] + + [workspace] + members = ["m1", "m2", "m3"] + + [profile.dev] + codegen-units = 1 + + [profile.dev.overrides.m2] + codegen-units = 2 + + [profile.dev.overrides."*"] + codegen-units = 3 + + [profile.dev.build-override] + codegen-units = 4 + "#) + + // m1 + .file("m1/Cargo.toml", + r#" + [package] + name = "m1" + version = "0.0.1" + + [dependencies] + m2 = { path = "../m2" } + dep = { path = "../../dep" } + "#) + .file("m1/src/lib.rs", + r#" + extern crate m2; + extern crate dep; + "#) + .file("m1/build.rs", + r#"fn main() {}"#) + + // m2 + .file("m2/Cargo.toml", + r#" + [package] + name = "m2" + version = "0.0.1" + + [dependencies] + m3 = { path = "../m3" } + + [build-dependencies] + m3 = { path = "../m3" } + dep = { path = "../../dep" } + "#) + .file("m2/src/lib.rs", + r#" + extern crate m3; + "#) + .file("m2/build.rs", + r#" + extern crate m3; + extern crate dep; + fn main() {} + "#) + + // m3 + .file("m3/Cargo.toml", &basic_lib_manifest("m3")) + .file("m3/src/lib.rs", "") + .build(); + + // dep (outside of workspace) + let _dep = project("dep") + .file("Cargo.toml", &basic_lib_manifest("dep")) + .file("src/lib.rs", "") + .build(); + + // Profiles should be: + // m3: 4 (as build.rs dependency) + // m3: 1 (as [profile.dev] as workspace member) + // dep: 3 (as [profile.dev.overrides."*"] as non-workspace member) + // m1 build.rs: 4 (as [profile.dev.build-override]) + // m2 build.rs: 2 (as [profile.dev.overrides.m2]) + // m2: 2 (as [profile.dev.overrides.m2]) + // m1: 1 (as [profile.dev]) + + assert_that( + p.cargo("build -v").masquerade_as_nightly_cargo(), + execs().with_status(0).with_stderr_unordered("\ +[COMPILING] m3 [..] +[COMPILING] dep [..] +[RUNNING] `rustc --crate-name m3 m3[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=4 [..] +[RUNNING] `rustc --crate-name dep [..]dep[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=3 [..] +[RUNNING] `rustc --crate-name m3 m3[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 [..] +[RUNNING] `rustc --crate-name build_script_build m1[/]build.rs --crate-type bin --emit=dep-info,link -C codegen-units=4 [..] +[COMPILING] m2 [..] +[RUNNING] `rustc --crate-name build_script_build m2[/]build.rs --crate-type bin --emit=dep-info,link -C codegen-units=2 [..] +[RUNNING] `[..][/]m1-[..][/]build-script-build` +[RUNNING] `[..][/]m2-[..][/]build-script-build` +[RUNNING] `rustc --crate-name m2 m2[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=2 [..] +[COMPILING] m1 [..] +[RUNNING] `rustc --crate-name m1 m1[/]src[/]lib.rs --crate-type lib --emit=dep-info,link -C codegen-units=1 [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +", + ), + ); +} diff --git a/tests/testsuite/rustc.rs b/tests/testsuite/rustc.rs index dd7d51b50a2..95691b8dfb1 100644 --- a/tests/testsuite/rustc.rs +++ b/tests/testsuite/rustc.rs @@ -1,4 +1,4 @@ -use cargotest::support::{execs, project}; +use cargotest::support::{basic_lib_manifest, execs, project}; use hamcrest::assert_that; const CARGO_RUSTC_ERROR: &'static str = @@ -595,3 +595,57 @@ fn rustc_with_other_profile() { execs().with_status(0), ); } + +#[test] +fn rustc_fingerprint() { + // Verify that the fingerprint includes the rustc args. + let p = project("foo") + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .build(); + + assert_that( + p.cargo("rustc -v -- -C debug-assertions"), + execs().with_status(0).with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc [..]-C debug-assertions [..] +[FINISHED] [..] +", + ), + ); + + assert_that( + p.cargo("rustc -v -- -C debug-assertions"), + execs().with_status(0).with_stderr( + "\ +[FRESH] foo [..] +[FINISHED] [..] +", + ), + ); + + assert_that( + p.cargo("rustc -v"), + execs() + .with_status(0) + .with_stderr_does_not_contain("-C debug-assertions") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ), + ); + + assert_that( + p.cargo("rustc -v"), + execs().with_status(0).with_stderr( + "\ +[FRESH] foo [..] +[FINISHED] [..] +", + ), + ); +} diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index 925aa3b55aa..315e886c506 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -3,12 +3,12 @@ use std::io::prelude::*; use std::str; use cargo; -use cargotest::{is_nightly, rustc_host, sleep_ms}; -use cargotest::support::{basic_bin_manifest, basic_lib_manifest, cargo_exe, execs, project}; +use cargo::util::process; use cargotest::support::paths::CargoPathExt; use cargotest::support::registry::Package; +use cargotest::support::{basic_bin_manifest, basic_lib_manifest, cargo_exe, execs, project}; +use cargotest::{is_nightly, rustc_host, sleep_ms}; use hamcrest::{assert_that, existing_file, is_not}; -use cargo::util::process; #[test] fn cargo_test_simple() { @@ -3975,3 +3975,72 @@ fn test_hint_workspace() { .with_status(101), ); } + +#[test] +fn json_artifact_includes_test_flag() { + // Verify that the JSON artifact output includes `test` flag. + let p = project("foo") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.test] + opt-level = 1 + "#, + ) + .file("src/lib.rs", "") + .build(); + + assert_that( + p.cargo("test -v --message-format=json"), + execs().with_status(0).with_json( + r#" + { + "reason":"compiler-artifact", + "profile": { + "debug_assertions": true, + "debuginfo": 2, + "opt_level": "0", + "overflow_checks": true, + "test": false + }, + "features": [], + "package_id":"foo 0.0.1 ([..])", + "target":{ + "kind":["lib"], + "crate_types":["lib"], + "name":"foo", + "src_path":"[..]lib.rs" + }, + "filenames":["[..].rlib"], + "fresh": false + } + + { + "reason":"compiler-artifact", + "profile": { + "debug_assertions": true, + "debuginfo": 2, + "opt_level": "1", + "overflow_checks": true, + "test": true + }, + "features": [], + "package_id":"foo 0.0.1 ([..])", + "target":{ + "kind":["lib"], + "crate_types":["lib"], + "name":"foo", + "src_path":"[..]lib.rs" + }, + "filenames":["[..][/]foo-[..]"], + "fresh": false + } +"#, + ), + ); +} From 025e23b83e6225910eda740211233731b20e6048 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 22 Apr 2018 09:05:18 -0700 Subject: [PATCH 19/23] Update tests now that `cargo check` does not re-check bins. --- tests/testsuite/profile_targets.rs | 55 ++++++++++++++---------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/tests/testsuite/profile_targets.rs b/tests/testsuite/profile_targets.rs index 0340e35481d..91450742e62 100644 --- a/tests/testsuite/profile_targets.rs +++ b/tests/testsuite/profile_targets.rs @@ -1,3 +1,4 @@ +use cargotest::is_nightly; use cargotest::support::{execs, project, Project}; use hamcrest::assert_that; @@ -491,6 +492,11 @@ foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 #[test] fn profile_selection_check_all_targets() { + if !is_nightly() { + // This can be removed once 1.27 is stable, see below. + return; + } + let p = all_target_project(); // check // NOTES: @@ -538,22 +544,18 @@ foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] [FINISHED] dev [unoptimized + debuginfo] [..] ")); - // Check re-run re-checks bins (and tests/benches) because rustc does not - // emit rmeta files for bins. See - // https://github.com/rust-lang/cargo/issues/3624. + // Starting with Rust 1.27, rustc emits `rmeta` files for bins, so + // everything should be completely fresh. Previously, bins were being + // rechecked. + // See https://github.com/rust-lang/rust/pull/49289 and + // https://github.com/rust-lang/cargo/issues/3624 assert_that( p.cargo("check --all-targets -vv"), execs().with_status(0).with_stderr_unordered( "\ [FRESH] bar [..] [FRESH] bdep [..] -[CHECKING] foo [..] -[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] -[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,metadata -C panic=abort -C codegen-units=1 -C debuginfo=2 [..] +[FRESH] foo [..] [FINISHED] dev [unoptimized + debuginfo] [..] ", ), @@ -562,6 +564,11 @@ foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 #[test] fn profile_selection_check_all_targets_release() { + if !is_nightly() { + // See note in profile_selection_check_all_targets. + return; + } + let p = all_target_project(); // check --release // https://github.com/rust-lang/cargo/issues/5218 @@ -589,22 +596,14 @@ foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,metadata -C opt-level=3 -C panic=abort -C codegen-units=2 [..] [FINISHED] release [optimized] [..] ")); - // Check re-run re-checks bins (and tests/benches) because rustc does not - // emit rmeta files for bins. See - // https://github.com/rust-lang/cargo/issues/3624. + assert_that( p.cargo("check --all-targets --release -vv"), execs().with_status(0).with_stderr_unordered( "\ [FRESH] bar [..] [FRESH] bdep [..] -[CHECKING] foo [..] -[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] -[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] -[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] -[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,metadata -C opt-level=3 -C codegen-units=2 --test [..] -[RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin --emit=dep-info,metadata -C opt-level=3 -C panic=abort -C codegen-units=2 [..] -[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --crate-type bin --emit=dep-info,metadata -C opt-level=3 -C panic=abort -C codegen-units=2 [..] +[FRESH] foo [..] [FINISHED] release [optimized] [..] ", ), @@ -613,6 +612,11 @@ foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 #[test] fn profile_selection_check_all_targets_test() { + if !is_nightly() { + // See note in profile_selection_check_all_targets. + return; + } + let p = all_target_project(); // check --profile=test // NOTES: @@ -656,21 +660,14 @@ foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] [FINISHED] dev [unoptimized + debuginfo] [..] ")); - // Check re-run re-checks bins (and tests/benches) because rustc does not - // emit rmeta files for bins. See - // https://github.com/rust-lang/cargo/issues/3624. + assert_that( p.cargo("check --all-targets --profile=test -vv"), execs().with_status(0).with_stderr_unordered( "\ [FRESH] bar [..] [FRESH] bdep [..] -[CHECKING] foo [..] -[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `rustc --crate-name ex1 examples[/]ex1.rs --emit=dep-info,metadata -C codegen-units=1 -C debuginfo=2 --test [..] +[FRESH] foo [..] [FINISHED] dev [unoptimized + debuginfo] [..] ", ), From 195520b853049d2b3f52ddcaf449d8c4aa092531 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 22 Apr 2018 13:57:38 -0700 Subject: [PATCH 20/23] Avoid building libs and bins twice in `cargo build --all-targets --release`. This changes it so that `Build` mode is not set in the Unit, since there is no difference between Bench and Test once the profile has been selected. --- src/cargo/ops/cargo_compile.rs | 16 +++++++++++++++- tests/testsuite/profile_targets.rs | 22 +++++++++------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index e0c4e0b1e40..62465978471 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -112,7 +112,10 @@ pub enum CompileMode { /// `test` is true, then it is also compiled with `--test` to check it like /// a test. Check { test: bool }, - /// A target being built for a benchmark. + /// Used to indicate benchmarks should be built. This is not used in + /// `Target` because it is essentially the same as `Test` (indicating + /// `--test` should be passed to rustc) and by using `Test` instead it + /// allows some de-duping of Units to occur. Bench, /// A target that will be documented with `rustdoc`. /// If `deps` is true, then it will also document all dependencies. @@ -629,6 +632,17 @@ fn generate_targets<'a>( target_mode, release, ); + // Once the profile has been selected for benchmarks, we don't need to + // distinguish between benches and tests. Switching the mode allows + // de-duplication of units that are essentially identical. For + // example, `cargo build --all-targets --release` creates the units + // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench) + // and since these are the same, we want them to be de-duped in + // `unit_dependencies`. + let target_mode = match target_mode { + CompileMode::Bench => CompileMode::Test, + _ => target_mode, + }; Unit { pkg, target, diff --git a/tests/testsuite/profile_targets.rs b/tests/testsuite/profile_targets.rs index 91450742e62..7a245fe8a95 100644 --- a/tests/testsuite/profile_targets.rs +++ b/tests/testsuite/profile_targets.rs @@ -178,11 +178,11 @@ fn profile_selection_build_all_targets() { // lib dev+panic build (a normal lib target) // lib dev-panic build (used by tests/benches) // lib test test - // lib bench bench + // lib bench test(bench) // test test test - // bench bench bench + // bench bench test(bench) // bin test test - // bin bench bench + // bin bench test(bench) // bin dev build // example dev build assert_that(p.cargo("build --all-targets -vv"), @@ -253,12 +253,10 @@ fn profile_selection_build_all_targets_release() { // ------ ------- ---- // lib release+panic build (a normal lib target) // lib release-panic build (used by tests/benches) - // lib bench test - // lib bench bench + // lib bench test (bench/test de-duped) // test bench test - // bench bench bench - // bin bench test - // bin bench bench + // bench bench test + // bin bench test (bench/test de-duped) // bin release build // example release build assert_that(p.cargo("build --all-targets --release -vv"), @@ -275,8 +273,6 @@ foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C panic=abort -C codegen-units=2 [..]` [RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib --emit=dep-info,link -C opt-level=3 -C codegen-units=2 [..]` -[RUNNING] `rustc --crate-name foo src[/]lib.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` -[RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` [RUNNING] `rustc --crate-name test1 tests[/]test1.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` [RUNNING] `rustc --crate-name bench1 benches[/]bench1.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` [RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link -C opt-level=3 -C codegen-units=4 --test [..]` @@ -449,9 +445,9 @@ fn profile_selection_bench() { // Target Profile Mode // ------ ------- ---- // lib release-panic build - // lib bench bench - // bench bench bench - // bin bench bench + // lib bench test(bench) + // bench bench test(bench) + // bin bench test(bench) // bin release-panic build // assert_that(p.cargo("bench -vv"), execs().with_status(0).with_stderr_unordered("\ From 902cb9808d185141e6e962146d1ea7564eedb802 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 24 Apr 2018 10:12:53 -0700 Subject: [PATCH 21/23] Fix Unit/Context parameter order. --- .../core/compiler/context/unit_dependencies.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs index 75dbd24f1e6..e5fdcb5232f 100644 --- a/src/cargo/core/compiler/context/unit_dependencies.rs +++ b/src/cargo/core/compiler/context/unit_dependencies.rs @@ -132,7 +132,7 @@ fn compute_deps<'a, 'b, 'cfg>( if unit.target.is_custom_build() { return Ok(ret); } - ret.extend(dep_build_script(cx, unit)); + ret.extend(dep_build_script(unit, cx)); // If this target is a binary, test, example, etc, then it depends on // the library of the same package. The call to `resolve.deps` above @@ -141,7 +141,7 @@ fn compute_deps<'a, 'b, 'cfg>( if unit.target.is_lib() && unit.mode != CompileMode::Doctest { return Ok(ret); } - ret.extend(maybe_lib(cx, unit, profile_for)); + ret.extend(maybe_lib(unit, cx, profile_for)); Ok(ret) } @@ -178,7 +178,7 @@ fn compute_deps_custom_build<'a, 'cfg>( if !unit.target.linkable() || unit.pkg.manifest().links().is_none() { return None; } - dep_build_script(cx, unit) + dep_build_script(unit, cx) }) .chain(Some(( new_unit( @@ -248,18 +248,18 @@ fn compute_deps_doc<'a, 'cfg>( } // Be sure to build/run the build script for documented libraries as - ret.extend(dep_build_script(cx, unit)); + ret.extend(dep_build_script(unit, cx)); // If we document a binary, we need the library available if unit.target.is_bin() { - ret.extend(maybe_lib(cx, unit, ProfileFor::Any)); + ret.extend(maybe_lib(unit, cx, ProfileFor::Any)); } Ok(ret) } fn maybe_lib<'a>( - cx: &Context, unit: &Unit<'a>, + cx: &Context, profile_for: ProfileFor, ) -> Option<(Unit<'a>, ProfileFor)> { let mode = check_or_build_mode(&unit.mode, unit.target); @@ -276,7 +276,7 @@ fn maybe_lib<'a>( /// script itself doesn't have any dependencies, so even in that case a unit /// of work is still returned. `None` is only returned if the package has no /// build script. -fn dep_build_script<'a>(cx: &Context, unit: &Unit<'a>) -> Option<(Unit<'a>, ProfileFor)> { +fn dep_build_script<'a>(unit: &Unit<'a>, cx: &Context) -> Option<(Unit<'a>, ProfileFor)> { unit.pkg .targets() .iter() From 040f9849dce936a16dcba73ba543e41d147af943 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 24 Apr 2018 10:35:20 -0700 Subject: [PATCH 22/23] Comment why `deps_of` doesn't need to keep track of `profile_for` in map. --- src/cargo/core/compiler/context/unit_dependencies.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs index e5fdcb5232f..84ee755b732 100644 --- a/src/cargo/core/compiler/context/unit_dependencies.rs +++ b/src/cargo/core/compiler/context/unit_dependencies.rs @@ -52,6 +52,12 @@ fn deps_of<'a, 'b, 'cfg>( deps: &'b mut HashMap, Vec>>, profile_for: ProfileFor, ) -> CargoResult<&'b [Unit<'a>]> { + // Currently the `deps` map does not include `profile_for`. This should + // be safe for now. `TestDependency` only exists to clear the `panic` + // flag, and you'll never ask for a `unit` with `panic` set as a + // `TestDependency`. `CustomBuild` should also be fine since if the + // requested unit's settings are the same as `Any`, `CustomBuild` can't + // affect anything else in the hierarchy. if !deps.contains_key(unit) { let unit_deps = compute_deps(unit, cx, deps, profile_for)?; let to_insert: Vec<_> = unit_deps.iter().map(|&(unit, _)| unit).collect(); From e82e9c19c5dab66656fd848d53b8c3ca64a933b4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 27 Apr 2018 11:23:57 -0700 Subject: [PATCH 23/23] Minor style update. --- src/cargo/core/package_id_spec.rs | 2 +- src/cargo/core/profiles.rs | 43 +++++++++++++++++-------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/cargo/core/package_id_spec.rs b/src/cargo/core/package_id_spec.rs index 0eef43800b7..798c746cc5b 100644 --- a/src/cargo/core/package_id_spec.rs +++ b/src/cargo/core/package_id_spec.rs @@ -8,7 +8,7 @@ use core::PackageId; use util::{ToSemver, ToUrl}; use util::errors::{CargoResult, CargoResultExt}; -/// Some or all of the data required to indentify a package: +/// Some or all of the data required to identify a package: /// /// 1. the package name (a `String`, required) /// 2. the package version (a `Version`, optional) diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index 70d6a1fe5e5..fbca991d68f 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -173,26 +173,31 @@ impl ProfileMaker { } fn validate_packages(&self, shell: &mut Shell, packages: &HashSet<&str>) -> CargoResult<()> { - if let Some(ref toml) = self.toml { - if let Some(ref overrides) = toml.overrides { - for key in overrides.keys().filter(|k| k.as_str() != "*") { - if !packages.contains(key.as_str()) { - let suggestion = packages - .iter() - .map(|p| (lev_distance(key, p), p)) - .filter(|&(d, _)| d < 4) - .min_by_key(|p| p.0) - .map(|p| p.1); - match suggestion { - Some(p) => shell.warn(format!( - "package `{}` for profile override not found\n\nDid you mean `{}`?", - key, p - ))?, - None => shell - .warn(format!("package `{}` for profile override not found", key))?, - }; + let toml = match self.toml { + Some(ref toml) => toml, + None => return Ok(()), + }; + let overrides = match toml.overrides { + Some(ref overrides) => overrides, + None => return Ok(()), + }; + for key in overrides.keys().filter(|k| k.as_str() != "*") { + if !packages.contains(key.as_str()) { + let suggestion = packages + .iter() + .map(|p| (lev_distance(key, p), p)) + .filter(|&(d, _)| d < 4) + .min_by_key(|p| p.0) + .map(|p| p.1); + match suggestion { + Some(p) => shell.warn(format!( + "package `{}` for profile override not found\n\nDid you mean `{}`?", + key, p + ))?, + None => { + shell.warn(format!("package `{}` for profile override not found", key))? } - } + }; } } Ok(())