From 50cc40bc895414bbe21c46a3626c385d2440d6e9 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Sat, 4 Apr 2020 20:39:14 +0200 Subject: [PATCH 1/2] Fix typo in method name --- src/cargo/core/compiler/context/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index 4e31fed0b12..c3a3cb78f66 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -130,7 +130,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { self.prepare_units()?; self.prepare()?; custom_build::build_map(&mut self)?; - self.check_collistions()?; + self.check_collisions()?; for unit in &self.bcx.roots { // Build up a list of pending jobs, each of which represent @@ -398,7 +398,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Ok(inputs.into_iter().collect()) } - fn check_collistions(&self) -> CargoResult<()> { + fn check_collisions(&self) -> CargoResult<()> { let mut output_collisions = HashMap::new(); let describe_collision = |unit: &Unit, other_unit: &Unit, path: &PathBuf| -> String { format!( From c221fec91119cd94a3043cc19566ed61d4f5c13d Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 24 Mar 2020 22:31:28 +0100 Subject: [PATCH 2/2] Implement support for rust-version field in project metadata --- crates/cargo-test-support/Cargo.toml | 1 + crates/cargo-test-support/src/registry.rs | 33 ++- src/bin/cargo/commands/bench.rs | 1 + src/bin/cargo/commands/build.rs | 1 + src/bin/cargo/commands/check.rs | 1 + src/bin/cargo/commands/doc.rs | 1 + src/bin/cargo/commands/fix.rs | 1 + src/bin/cargo/commands/run.rs | 1 + src/bin/cargo/commands/rustc.rs | 1 + src/bin/cargo/commands/rustdoc.rs | 1 + src/bin/cargo/commands/test.rs | 1 + src/cargo/core/features.rs | 14 ++ src/cargo/core/manifest.rs | 7 + src/cargo/core/package.rs | 4 + src/cargo/ops/cargo_compile.rs | 35 +++ src/cargo/ops/cargo_package.rs | 1 + src/cargo/util/command_prelude.rs | 17 ++ src/cargo/util/toml/mod.rs | 50 ++++- src/doc/src/reference/unstable.md | 19 ++ tests/testsuite/main.rs | 1 + tests/testsuite/rust_version.rs | 260 ++++++++++++++++++++++ 21 files changed, 445 insertions(+), 6 deletions(-) create mode 100644 tests/testsuite/rust_version.rs diff --git a/crates/cargo-test-support/Cargo.toml b/crates/cargo-test-support/Cargo.toml index 0340c466765..e05444b094c 100644 --- a/crates/cargo-test-support/Cargo.toml +++ b/crates/cargo-test-support/Cargo.toml @@ -19,4 +19,5 @@ lazy_static = "1.0" remove_dir_all = "0.5" serde_json = "1.0" tar = { version = "0.4.18", default-features = false } +toml = "0.5.7" url = "2.0" diff --git a/crates/cargo-test-support/src/registry.rs b/crates/cargo-test-support/src/registry.rs index 4bb6f2aa43b..e2e3b03024d 100644 --- a/crates/cargo-test-support/src/registry.rs +++ b/crates/cargo-test-support/src/registry.rs @@ -146,6 +146,8 @@ pub struct Package { invalid_json: bool, proc_macro: bool, links: Option, + rust_version: Option, + cargo_features: Vec, } #[derive(Clone)] @@ -247,6 +249,8 @@ impl Package { invalid_json: false, proc_macro: false, links: None, + rust_version: None, + cargo_features: Vec::new(), } } @@ -363,6 +367,12 @@ impl Package { self } + /// Specify a minimal Rust version. + pub fn rust_version(&mut self, rust_version: &str) -> &mut Package { + self.rust_version = Some(rust_version.into()); + self + } + /// Causes the JSON line emitted in the index to be invalid, presumably /// causing Cargo to skip over this version. pub fn invalid_json(&mut self, invalid: bool) -> &mut Package { @@ -375,6 +385,11 @@ impl Package { self } + pub fn cargo_feature(&mut self, feature: &str) -> &mut Package { + self.cargo_features.push(feature.to_owned()); + self + } + /// Creates the package and place it in the registry. /// /// This does not actually use Cargo's publishing system, but instead @@ -502,7 +517,16 @@ impl Package { } fn append_manifest(&self, ar: &mut Builder) { - let mut manifest = format!( + let mut manifest = String::new(); + + if !self.cargo_features.is_empty() { + manifest.push_str(&format!( + "cargo-features = {}\n\n", + toml::to_string(&self.cargo_features).unwrap() + )); + } + + manifest.push_str(&format!( r#" [package] name = "{}" @@ -510,7 +534,12 @@ impl Package { authors = [] "#, self.name, self.vers - ); + )); + + if let Some(version) = &self.rust_version { + manifest.push_str(&format!("rust-version = \"{}\"", version)); + } + for dep in self.deps.iter() { let target = match dep.target { None => String::new(), diff --git a/src/bin/cargo/commands/bench.rs b/src/bin/cargo/commands/bench.rs index 700dca258bf..160ccd9abe8 100644 --- a/src/bin/cargo/commands/bench.rs +++ b/src/bin/cargo/commands/bench.rs @@ -39,6 +39,7 @@ pub fn cli() -> App { .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_manifest_path() + .arg_ignore_rust_version() .arg_message_format() .arg(opt( "no-fail-fast", diff --git a/src/bin/cargo/commands/build.rs b/src/bin/cargo/commands/build.rs index e165b250d69..238d0864808 100644 --- a/src/bin/cargo/commands/build.rs +++ b/src/bin/cargo/commands/build.rs @@ -39,6 +39,7 @@ pub fn cli() -> App { .value_name("PATH"), ) .arg_manifest_path() + .arg_ignore_rust_version() .arg_message_format() .arg_build_plan() .arg_unit_graph() diff --git a/src/bin/cargo/commands/check.rs b/src/bin/cargo/commands/check.rs index 6d26ef2d765..fec00ad8b7e 100644 --- a/src/bin/cargo/commands/check.rs +++ b/src/bin/cargo/commands/check.rs @@ -32,6 +32,7 @@ pub fn cli() -> App { .arg_target_triple("Check for the target triple") .arg_target_dir() .arg_manifest_path() + .arg_ignore_rust_version() .arg_message_format() .arg_unit_graph() .after_help("Run `cargo help check` for more detailed information.\n") diff --git a/src/bin/cargo/commands/doc.rs b/src/bin/cargo/commands/doc.rs index d82dc0ec3f6..7ef405177be 100644 --- a/src/bin/cargo/commands/doc.rs +++ b/src/bin/cargo/commands/doc.rs @@ -30,6 +30,7 @@ pub fn cli() -> App { .arg_target_dir() .arg_manifest_path() .arg_message_format() + .arg_ignore_rust_version() .arg_unit_graph() .after_help("Run `cargo help doc` for more detailed information.\n") } diff --git a/src/bin/cargo/commands/fix.rs b/src/bin/cargo/commands/fix.rs index 0cab3741dea..bbcf6f7d4e5 100644 --- a/src/bin/cargo/commands/fix.rs +++ b/src/bin/cargo/commands/fix.rs @@ -72,6 +72,7 @@ pub fn cli() -> App { .long("allow-staged") .help("Fix code even if the working directory has staged changes"), ) + .arg_ignore_rust_version() .after_help("Run `cargo help fix` for more detailed information.\n") } diff --git a/src/bin/cargo/commands/run.rs b/src/bin/cargo/commands/run.rs index 344d5f19b6c..ed17e952b31 100644 --- a/src/bin/cargo/commands/run.rs +++ b/src/bin/cargo/commands/run.rs @@ -26,6 +26,7 @@ pub fn cli() -> App { .arg_manifest_path() .arg_message_format() .arg_unit_graph() + .arg_ignore_rust_version() .after_help("Run `cargo help run` for more detailed information.\n") } diff --git a/src/bin/cargo/commands/rustc.rs b/src/bin/cargo/commands/rustc.rs index aa8fb6d9d9f..187ff4651f4 100644 --- a/src/bin/cargo/commands/rustc.rs +++ b/src/bin/cargo/commands/rustc.rs @@ -30,6 +30,7 @@ pub fn cli() -> App { .arg_manifest_path() .arg_message_format() .arg_unit_graph() + .arg_ignore_rust_version() .after_help("Run `cargo help rustc` for more detailed information.\n") } diff --git a/src/bin/cargo/commands/rustdoc.rs b/src/bin/cargo/commands/rustdoc.rs index f67f6ba985b..aaa8449f30d 100644 --- a/src/bin/cargo/commands/rustdoc.rs +++ b/src/bin/cargo/commands/rustdoc.rs @@ -34,6 +34,7 @@ pub fn cli() -> App { .arg_manifest_path() .arg_message_format() .arg_unit_graph() + .arg_ignore_rust_version() .after_help("Run `cargo help rustdoc` for more detailed information.\n") } diff --git a/src/bin/cargo/commands/test.rs b/src/bin/cargo/commands/test.rs index 3d4dd5b2f02..04d4dce34fb 100644 --- a/src/bin/cargo/commands/test.rs +++ b/src/bin/cargo/commands/test.rs @@ -53,6 +53,7 @@ pub fn cli() -> App { .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_manifest_path() + .arg_ignore_rust_version() .arg_message_format() .arg_unit_graph() .after_help("Run `cargo help test` for more detailed information.\n") diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 5911fc653b5..45df0d247fe 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -70,6 +70,17 @@ pub enum Edition { Edition2021, } +impl Edition { + pub(crate) fn first_version(&self) -> Option { + use Edition::*; + match self { + Edition2015 => None, + Edition2018 => Some(semver::Version::new(1, 31, 0)), + Edition2021 => Some(semver::Version::new(1, 62, 0)), + } + } +} + impl fmt::Display for Edition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { @@ -218,6 +229,9 @@ features! { // Allow to specify whether binaries should be stripped. [unstable] strip: bool, + + // Specifying a minimal 'rust-version' attribute for crates + [unstable] rust_version: bool, } } diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index f6a7323d25f..4608adcbe3f 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -46,6 +46,7 @@ pub struct Manifest { original: Rc, unstable_features: Features, edition: Edition, + rust_version: Option, im_a_teapot: Option, default_run: Option, metabuild: Option>, @@ -379,6 +380,7 @@ impl Manifest { workspace: WorkspaceConfig, unstable_features: Features, edition: Edition, + rust_version: Option, im_a_teapot: Option, default_run: Option, original: Rc, @@ -401,6 +403,7 @@ impl Manifest { workspace, unstable_features, edition, + rust_version, original, im_a_teapot, default_run, @@ -520,6 +523,10 @@ impl Manifest { self.edition } + pub fn rust_version(&self) -> Option<&str> { + self.rust_version.as_deref() + } + pub fn custom_metadata(&self) -> Option<&toml::Value> { self.custom_metadata.as_ref() } diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index 76bef34df13..6585e29aec7 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -170,6 +170,10 @@ impl Package { pub fn proc_macro(&self) -> bool { self.targets().iter().any(|target| target.proc_macro()) } + /// Gets the package's minimum Rust version. + pub fn rust_version(&self) -> Option<&str> { + self.manifest().rust_version() + } /// Returns `true` if the package uses a custom build script for any target. pub fn has_custom_build(&self) -> bool { diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 8a53c5b9b56..cfe418cfe9b 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -78,6 +78,9 @@ pub struct CompileOptions { /// Whether the `--document-private-items` flags was specified and should /// be forwarded to `rustdoc`. pub rustdoc_document_private_items: bool, + /// Whether the build process should check the minimum Rust version + /// defined in the cargo metadata for a crate. + pub honor_rust_version: bool, } impl<'a> CompileOptions { @@ -95,6 +98,7 @@ impl<'a> CompileOptions { target_rustc_args: None, local_rustdoc_args: None, rustdoc_document_private_items: false, + honor_rust_version: true, }) } } @@ -306,6 +310,7 @@ pub fn create_bcx<'a, 'cfg>( ref target_rustc_args, ref local_rustdoc_args, rustdoc_document_private_items, + honor_rust_version, } = *options; let config = ws.config(); @@ -551,6 +556,36 @@ pub fn create_bcx<'a, 'cfg>( } } + if honor_rust_version { + // Remove any pre-release identifiers for easier comparison + let current_version = &target_data.rustc.version; + let untagged_version = semver::Version::new( + current_version.major, + current_version.minor, + current_version.patch, + ); + + for unit in unit_graph.keys() { + let version = match unit.pkg.rust_version() { + Some(v) => v, + None => continue, + }; + + let req = semver::VersionReq::parse(version).unwrap(); + if req.matches(&untagged_version) { + continue; + } + + anyhow::bail!( + "package `{}` cannot be built because it requires rustc {} or newer, \ + while the currently active rustc version is {}", + unit.pkg, + version, + current_version, + ); + } + } + let bcx = BuildContext::new( ws, pkg_set, diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index 4e19a30b91e..a07f9fe072d 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -701,6 +701,7 @@ fn run_verify(ws: &Workspace<'_>, tar: &FileLock, opts: &PackageOpts<'_>) -> Car target_rustc_args: rustc_args, local_rustdoc_args: None, rustdoc_document_private_items: false, + honor_rust_version: true, }, &exec, )?; diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index a7357dc6d35..27c6e54e74f 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -212,6 +212,16 @@ pub trait AppExt: Sized { fn arg_dry_run(self, dry_run: &'static str) -> Self { self._arg(opt("dry-run", dry_run)) } + + fn arg_ignore_rust_version(self) -> Self { + self._arg( + opt( + "ignore-rust-version", + "Ignore `rust-version` specification in packages", + ) + .hidden(true), // nightly only (`rust-version` feature) + ) + } } impl AppExt for App { @@ -488,8 +498,15 @@ pub trait ArgMatchesExt { target_rustc_args: None, local_rustdoc_args: None, rustdoc_document_private_items: false, + honor_rust_version: !self._is_present("ignore-rust-version"), }; + if !opts.honor_rust_version { + config + .cli_unstable() + .fail_if_stable_opt("--ignore-rust-version", 8072)?; + } + if let Some(ws) = workspace { self.check_optional_opts(ws, &opts)?; } else if self.is_present_with_zero_values("package") { diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 76db59faf3a..44dd7140df6 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -15,6 +15,7 @@ use url::Url; use crate::core::dependency::DepKind; use crate::core::manifest::{ManifestMetadata, TargetSourcePath, Warnings}; +use crate::core::nightly_features_allowed; use crate::core::profiles::Strip; use crate::core::resolver::ResolveBehavior; use crate::core::{Dependency, Manifest, PackageId, Summary, Target}; @@ -800,8 +801,10 @@ impl<'de> de::Deserialize<'de> for VecStringOrBool { /// the field `metadata`, since it is a table and values cannot appear after /// tables. #[derive(Deserialize, Serialize, Clone, Debug)] +#[serde(rename_all = "kebab-case")] pub struct TomlProject { edition: Option, + rust_version: Option, name: InternedString, version: semver::Version, authors: Option>, @@ -811,16 +814,13 @@ pub struct TomlProject { exclude: Option>, include: Option>, publish: Option, - #[serde(rename = "publish-lockfile")] publish_lockfile: Option, workspace: Option, - #[serde(rename = "im-a-teapot")] im_a_teapot: Option, autobins: Option, autoexamples: Option, autotests: Option, autobenches: Option, - #[serde(rename = "default-run")] default_run: Option, // Package metadata. @@ -831,7 +831,6 @@ pub struct TomlProject { keywords: Option>, categories: Option>, license: Option, - #[serde(rename = "license-file")] license_file: Option, repository: Option, metadata: Option, @@ -1049,6 +1048,48 @@ impl TomlManifest { Edition::Edition2015 }; + if let Some(rust_version) = &project.rust_version { + if features.require(Feature::rust_version()).is_err() { + let mut msg = + "`rust-version` is not supported on this version of Cargo and will be ignored" + .to_string(); + if nightly_features_allowed() { + msg.push_str( + "\n\n\ + consider adding `cargo-features = [\"rust-version\"]` to the manifest", + ); + } else { + msg.push_str( + "\n\n\ + this Cargo does not support nightly features, but if you\n\ + switch to nightly channel you can add\n\ + `cargo-features = [\"rust-version\"]` to enable this feature", + ); + } + warnings.push(msg); + } + + let req = match semver::VersionReq::parse(rust_version) { + // Exclude semver operators like `^` and pre-release identifiers + Ok(req) if rust_version.chars().all(|c| c.is_ascii_digit() || c == '.') => req, + _ => bail!("`rust-version` must be a value like \"1.32\""), + }; + + if let Some(first_version) = edition.first_version() { + let unsupported = + semver::Version::new(first_version.major, first_version.minor - 1, 9999); + if req.matches(&unsupported) { + bail!( + "rust-version {} is older than first version ({}) required by \ + the specified edition ({})", + rust_version, + first_version, + edition, + ) + } + } + } + if project.metabuild.is_some() { features.require(Feature::metabuild())?; } @@ -1302,6 +1343,7 @@ impl TomlManifest { workspace_config, features, edition, + project.rust_version.clone(), project.im_a_teapot, project.default_run.clone(), Rc::clone(me), diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index c63d21fba37..73989b97dad 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -1003,3 +1003,22 @@ cargo logout -Z credential-process` [`credentials` file]: config.md#credentials [crates.io]: https://crates.io/ [config file]: config.md + +### rust-version +* RFC: [#2495](https://github.com/rust-lang/rfcs/blob/master/text/2495-min-rust-version.md) +* rustc Tracking Issue: [#65262](https://github.com/rust-lang/rust/issues/65262) + +The `-Z rust-version` flag enables the reading the `rust-version` field in the +Cargo manifest `package` section. This can be used by a package to state a minimal +version of the compiler required to build the package. An error is generated if +the version of rustc is older than the stated `rust-version`. The +`--ignore-rust-version` flag can be used to override the check. + +```toml +cargo-features = ["rust-version"] + +[package] +name = "mypackage" +version = "0.0.1" +rust-version = "1.42" +``` diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index 8af5858b373..4df5679189b 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -103,6 +103,7 @@ mod rename_deps; mod replace; mod required_features; mod run; +mod rust_version; mod rustc; mod rustc_info_cache; mod rustdoc; diff --git a/tests/testsuite/rust_version.rs b/tests/testsuite/rust_version.rs new file mode 100644 index 00000000000..b3d4353bbae --- /dev/null +++ b/tests/testsuite/rust_version.rs @@ -0,0 +1,260 @@ +//! Tests for targets with `rust-version`. + +use cargo_test_support::{project, registry::Package}; + +#[cargo_test] +fn rust_version_gated() { + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + rust-version = "1.17" + "#, + ) + .file("src/lib.rs", "") + .build() + .cargo("build") + .masquerade_as_nightly_cargo() + .with_stderr_contains( + "warning: `rust-version` is not supported on this version of Cargo and will be ignored\ + \n\nconsider adding `cargo-features = [\"rust-version\"]` to the manifest", + ) + .run(); + + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + rust-version = "1.17" + "#, + ) + .file("src/lib.rs", "") + .build() + .cargo("build") + .with_stderr_contains( + "warning: `rust-version` is not supported on this version of Cargo and will be ignored\ + \n\nthis Cargo does not support nightly features, but if you\n\ + switch to nightly channel you can add\n\ + `cargo-features = [\"rust-version\"]` to enable this feature", + ) + .run(); +} + +#[cargo_test] +fn rust_version_satisfied() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["rust-version"] + + [project] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "1.1.1" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").masquerade_as_nightly_cargo().run(); + p.cargo("build --ignore-rust-version -Zunstable-options") + .masquerade_as_nightly_cargo() + .run(); +} + +#[cargo_test] +fn rust_version_bad_caret() { + project() + .file( + "Cargo.toml", + r#" + cargo-features = ["rust-version"] + + [project] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "^1.43" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build() + .cargo("build") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + "error: failed to parse manifest at `[..]`\n\n\ + Caused by:\n `rust-version` must be a value like \"1.32\"", + ) + .run(); +} + +#[cargo_test] +fn rust_version_bad_pre_release() { + project() + .file( + "Cargo.toml", + r#" + cargo-features = ["rust-version"] + + [project] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "1.43-beta.1" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build() + .cargo("build") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + "error: failed to parse manifest at `[..]`\n\n\ + Caused by:\n `rust-version` must be a value like \"1.32\"", + ) + .run(); +} + +#[cargo_test] +fn rust_version_bad_nonsense() { + project() + .file( + "Cargo.toml", + r#" + cargo-features = ["rust-version"] + + [project] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "foodaddle" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build() + .cargo("build") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + "error: failed to parse manifest at `[..]`\n\n\ + Caused by:\n `rust-version` must be a value like \"1.32\"", + ) + .run(); +} + +#[cargo_test] +fn rust_version_too_high() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["rust-version"] + + [project] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "1.9876.0" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + "error: package `foo v0.0.1 ([..])` cannot be built because it requires \ + rustc 1.9876.0 or newer, while the currently active rustc version is [..]", + ) + .run(); + p.cargo("build --ignore-rust-version -Zunstable-options") + .masquerade_as_nightly_cargo() + .run(); +} + +#[cargo_test] +fn rust_version_dependency_fails() { + Package::new("bar", "0.0.1") + .cargo_feature("rust-version") + .rust_version("1.2345.0") + .file("src/lib.rs", "fn other_stuff() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + [dependencies] + bar = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main(){}") + .build(); + + p.cargo("build") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + " Updating `[..]` index\n \ + Downloading crates ...\n \ + Downloaded bar v0.0.1 (registry `[..]`)\n\ + error: package `bar v0.0.1` cannot be built because it requires \ + rustc 1.2345.0 or newer, while the currently active rustc version is [..]", + ) + .run(); + p.cargo("build --ignore-rust-version -Zunstable-options") + .masquerade_as_nightly_cargo() + .run(); +} + +#[cargo_test] +fn rust_version_older_than_edition() { + project() + .file( + "Cargo.toml", + r#" + cargo-features = ["rust-version"] + + [project] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "1.1" + edition = "2018" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build() + .cargo("build") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr_contains(" rust-version 1.1 is older than first version (1.31.0) required by the specified edition (2018)", + ) + .run(); +}