diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 414e6a4a8b6..323366b9bce 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -40,9 +40,40 @@ //! we'll be sure to update this documentation! use std::env; +use std::fmt; +use std::str::FromStr; use util::errors::CargoResult; +/// The epoch of the compiler (RFC 2052) +#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq)] +#[derive(Serialize, Deserialize)] +pub enum Epoch { + /// The 2015 epoch + Epoch2015, + /// The 2018 epoch + Epoch2018, +} + +impl fmt::Display for Epoch { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Epoch::Epoch2015 => f.write_str("2015"), + Epoch::Epoch2018 => f.write_str("2018"), + } + } +} +impl FromStr for Epoch { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "2015" => Ok(Epoch::Epoch2015), + "2018" => Ok(Epoch::Epoch2018), + _ => Err(()) + } + } +} + enum Status { Stable, Unstable, @@ -125,6 +156,9 @@ features! { // Downloading packages from alternative registry indexes. [unstable] alternative_registries: bool, + + // Using epochs + [unstable] epoch: bool, } } @@ -201,6 +235,10 @@ impl Features { bail!("{}", msg); } } + + pub fn is_enabled(&self, feature: &Feature) -> bool { + feature.is_enabled(self) + } } /// A parsed representation of all unstable flags that Cargo accepts. diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 5ba9419e467..18eb96b5ff8 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -9,7 +9,7 @@ use serde::ser; use url::Url; use core::{Dependency, PackageId, Summary, SourceId, PackageIdSpec}; -use core::{WorkspaceConfig, Features, Feature}; +use core::{WorkspaceConfig, Epoch, Features, Feature}; use util::Config; use util::toml::TomlManifest; use util::errors::*; @@ -36,6 +36,7 @@ pub struct Manifest { workspace: WorkspaceConfig, original: Rc, features: Features, + epoch: Epoch, im_a_teapot: Option, } @@ -269,6 +270,7 @@ impl Manifest { patch: HashMap>, workspace: WorkspaceConfig, features: Features, + epoch: Epoch, im_a_teapot: Option, original: Rc) -> Manifest { Manifest { @@ -285,6 +287,7 @@ impl Manifest { patch: patch, workspace: workspace, features: features, + epoch: epoch, original: original, im_a_teapot: im_a_teapot, } @@ -356,6 +359,10 @@ impl Manifest { } } } + + pub fn epoch(&self) -> Epoch { + self.epoch + } } impl VirtualManifest { diff --git a/src/cargo/core/mod.rs b/src/cargo/core/mod.rs index 6b4e3906f8e..df1eff0725e 100644 --- a/src/cargo/core/mod.rs +++ b/src/cargo/core/mod.rs @@ -1,5 +1,5 @@ pub use self::dependency::Dependency; -pub use self::features::{Features, Feature, CliUnstable}; +pub use self::features::{Epoch, Features, Feature, CliUnstable}; pub use self::manifest::{EitherManifest, VirtualManifest}; pub use self::manifest::{Manifest, Target, TargetKind, Profile, LibKind, Profiles}; pub use self::package::{Package, PackageSet}; diff --git a/src/cargo/ops/cargo_rustc/fingerprint.rs b/src/cargo/ops/cargo_rustc/fingerprint.rs index 847008eb7a7..3080cee3dda 100644 --- a/src/cargo/ops/cargo_rustc/fingerprint.rs +++ b/src/cargo/ops/cargo_rustc/fingerprint.rs @@ -9,7 +9,7 @@ use serde::ser::{self, Serialize}; use serde::de::{self, Deserialize}; use serde_json; -use core::{Package, TargetKind}; +use core::{Epoch, Package, TargetKind}; use util; use util::{Fresh, Dirty, Freshness, internal, profile}; use util::errors::{CargoResult, CargoResultExt}; @@ -145,6 +145,7 @@ pub struct Fingerprint { #[serde(skip_serializing, skip_deserializing)] memoized_hash: Mutex>, rustflags: Vec, + epoch: Epoch, } fn serialize_deps(deps: &[(String, Arc)], ser: S) @@ -170,6 +171,7 @@ fn deserialize_deps<'de, D>(d: D) -> Result)>, D:: features: String::new(), deps: Vec::new(), memoized_hash: Mutex::new(Some(hash)), + epoch: Epoch::Epoch2015, rustflags: Vec::new(), })) }).collect()) @@ -252,6 +254,9 @@ impl Fingerprint { if self.local.len() != old.local.len() { bail!("local lens changed"); } + if self.epoch != old.epoch { + bail!("epoch changed") + } for (new, old) in self.local.iter().zip(&old.local) { match (new, old) { (&LocalFingerprint::Precalculated(ref a), @@ -315,9 +320,10 @@ impl hash::Hash for Fingerprint { ref deps, ref local, memoized_hash: _, + epoch, ref rustflags, } = *self; - (rustc, features, target, path, profile, local, rustflags).hash(h); + (rustc, features, target, path, profile, local, epoch, rustflags).hash(h); h.write_usize(deps.len()); for &(ref name, ref fingerprint) in deps { @@ -416,6 +422,7 @@ fn calculate<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) deps: deps, local: vec![local], memoized_hash: Mutex::new(None), + epoch: unit.pkg.manifest().epoch(), rustflags: extra_flags, }); cx.fingerprints.insert(*unit, Arc::clone(&fingerprint)); @@ -468,6 +475,7 @@ pub fn prepare_build_cmd<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) deps: Vec::new(), local: local, memoized_hash: Mutex::new(None), + epoch: Epoch::Epoch2015, rustflags: Vec::new(), }; let compare = compare_old_fingerprint(&loc, &fingerprint); diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 7a413194e20..b3294857608 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use same_file::is_same_file; use serde_json; -use core::{Package, PackageId, PackageSet, Target, Resolve}; +use core::{Feature, Package, PackageId, PackageSet, Target, Resolve}; use core::{Profile, Profiles, Workspace}; use core::manifest::Lto; use core::shell::ColorChoice; @@ -804,6 +804,11 @@ fn build_base_args<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, cmd.arg("-C").arg(format!("panic={}", panic)); } } + let manifest = unit.pkg.manifest(); + + if manifest.features().is_enabled(Feature::epoch()) { + cmd.arg(format!("-Zepoch={}", manifest.epoch())); + } // Disable LTO for host builds as prefer_dynamic and it are mutually // exclusive. diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index a088598e9ba..c33aaef93e9 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -14,7 +14,7 @@ use url::Url; use core::{SourceId, Profiles, PackageIdSpec, GitReference, WorkspaceConfig, WorkspaceRootConfig}; use core::{Summary, Manifest, Target, Dependency, PackageId}; -use core::{EitherManifest, VirtualManifest, Features, Feature}; +use core::{EitherManifest, Epoch, VirtualManifest, Features, Feature}; use core::dependency::{Kind, Platform}; use core::manifest::{LibKind, Profile, ManifestMetadata, Lto}; use sources::CRATES_IO; @@ -441,6 +441,7 @@ pub struct TomlProject { license_file: Option, repository: Option, metadata: Option, + rust: Option, } #[derive(Debug, Deserialize, Serialize)] @@ -715,6 +716,19 @@ impl TomlManifest { Some(VecStringOrBool::Bool(false)) => Some(vec![]), None | Some(VecStringOrBool::Bool(true)) => None, }; + + let epoch = if let Some(ref epoch) = project.rust { + features.require(Feature::epoch()).chain_err(|| { + "epoches are unstable" + })?; + if let Ok(epoch) = epoch.parse() { + epoch + } else { + bail!("the `rust` key must be one of: `2015`, `2018`") + } + } else { + Epoch::Epoch2015 + }; let mut manifest = Manifest::new(summary, targets, exclude, @@ -727,6 +741,7 @@ impl TomlManifest { patch, workspace_config, features, + epoch, project.im_a_teapot, Rc::clone(me)); if project.license_file.is_some() && project.license.is_some() { diff --git a/tests/package.rs b/tests/package.rs index d90e76e95a2..f49a56193aa 100644 --- a/tests/package.rs +++ b/tests/package.rs @@ -887,3 +887,120 @@ fn package_two_kinds_of_deps() { assert_that(p.cargo("package").arg("--no-verify"), execs().with_status(0)); } + +#[test] +fn test_epoch() { + let p = project("foo") + .file("Cargo.toml", r#" + cargo-features = ["epoch"] + [package] + name = "foo" + version = "0.0.1" + authors = [] + rust = "2018" + "#) + .file("src/lib.rs", r#" "#) + .build(); + + assert_that(p.cargo("build").arg("-v") + .masquerade_as_nightly_cargo(), + execs() + // -Zepoch is still in flux and we're not passing -Zunstable-options + // from Cargo so it will probably error. Only partially match the output + // until stuff stabilizes + .with_stderr_contains(format!("\ +[COMPILING] foo v0.0.1 ({url}) +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \ + --emit=dep-info,link -Zepoch=2018 -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]debug[/]deps` +", dir = p.root().display(), url = p.url()))); +} + +#[test] +fn test_epoch_missing() { + // no epoch = 2015 + let p = project("foo") + .file("Cargo.toml", r#" + cargo-features = ["epoch"] + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", r#" "#) + .build(); + + assert_that(p.cargo("build").arg("-v") + .masquerade_as_nightly_cargo(), + execs() + // -Zepoch is still in flux and we're not passing -Zunstable-options + // from Cargo so it will probably error. Only partially match the output + // until stuff stabilizes + .with_stderr_contains(format!("\ +[COMPILING] foo v0.0.1 ({url}) +[RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \ + --emit=dep-info,link -Zepoch=2015 -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]debug[/]deps` +", dir = p.root().display(), url = p.url()))); +} + +#[test] +fn test_epoch_malformed() { + let p = project("foo") + .file("Cargo.toml", r#" + cargo-features = ["epoch"] + [package] + name = "foo" + version = "0.0.1" + authors = [] + rust = "chicken" + "#) + .file("src/lib.rs", r#" "#) + .build(); + + assert_that(p.cargo("build").arg("-v") + .masquerade_as_nightly_cargo(), + execs() + .with_status(101) + .with_stderr(format!("\ +error: failed to parse manifest at `[..]` + +Caused by: + the `rust` key must be one of: `2015`, `2018` +"))); +} + + +#[test] +fn test_epoch_nightly() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + rust = "2015" + "#) + .file("src/lib.rs", r#" "#) + .build(); + + assert_that(p.cargo("build").arg("-v") + .masquerade_as_nightly_cargo(), + execs() + .with_status(101) + .with_stderr(format!("\ +error: failed to parse manifest at `[..]` + +Caused by: + epoches are unstable + +Caused by: + feature `epoch` is required + +consider adding `cargo-features = [\"epoch\"]` to the manifest +"))); +} \ No newline at end of file