Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The `testutil` crate currently supports extracting pinned toolchain versions and comparing these to the toolchain version used to run the test. This is used to replace `#[rustversion::*]` attributes. While it adds more code, it also means that, in order to roll pinned toolchain versions, only the root `Cargo.toml` needs to be updated (previously, it was also necessary to update `tests/trybuild.rs` and `zerocopy-derive/tests/trybuild.rs`). This paves the way to automate the rolling of pinned toolchain versions (see #348). TODO: - More comments in `testutil`? - Make `testutil::PinnedVersions` private?
- Loading branch information
Showing
6 changed files
with
182 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Copyright 2023 The Fuchsia Authors. All rights reserved. | ||
# Use of this source code is governed by a BSD-style license that can be | ||
# found in the LICENSE file. | ||
|
||
[package] | ||
name = "testutil" | ||
version = "0.0.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
cargo_metadata = "0.18.0" | ||
rustc_version = "0.4.0" | ||
# Pin to 0.3.0 because more recent versions require a Rust version more recent | ||
# than our MSRV. | ||
time = { version = "=0.3.0", default-features = false, features = ["formatting", "macros", "parsing"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// Copyright 2023 The Fuchsia Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
use cargo_metadata::MetadataCommand; | ||
use rustc_version::{Channel, Version}; | ||
|
||
#[derive(Debug)] | ||
pub struct PinnedVersions { | ||
pub msrv: String, | ||
pub stable: String, | ||
pub nightly: String, | ||
} | ||
|
||
impl PinnedVersions { | ||
/// Attempts to extract pinned toolchain versions based on the current | ||
/// working directory. | ||
/// | ||
/// `extract_from_pwd` expects to be called from a directory which is a | ||
/// child of a Cargo workspace. It extracts the pinned versions from the | ||
/// metadata of the root package. | ||
pub fn extract_from_pwd() -> Result<PinnedVersions, Box<dyn std::error::Error>> { | ||
let meta = MetadataCommand::new().exec()?; | ||
// NOTE(joshlf): In theory `meta.root_package()` should work instead of | ||
// this manual search, but for some reason it breaks when called from | ||
// zerocopy-derive's tests. This works as a workaround, and it's just | ||
// test code, so I didn't bother investigating. | ||
let pkg = meta | ||
.workspace_packages() | ||
.into_iter() | ||
.find(|pkg| pkg.name == "zerocopy") | ||
.ok_or("no `zerocopy` package found; are we in a workspace?")?; | ||
let msrv = pkg | ||
.rust_version | ||
.as_ref() | ||
.ok_or("failed to find msrv: no `rust-version` key present")? | ||
.to_string(); | ||
let extract = |version_name, key| -> Result<String, String> { | ||
let value = pkg.metadata.pointer(&format!("/ci/{key}")).ok_or_else(|| { | ||
format!("failed to find {version_name}: no `metadata.ci.{key}` key present") | ||
})?; | ||
value.as_str().map(str::to_string).ok_or_else(|| format!("failed to find {version_name}: key `metadata.ci.{key}` (contents: {value:?}) failed to parse as JSON string")) | ||
}; | ||
let stable = extract("stable", "pinned-stable")?; | ||
let nightly = extract("nightly", "pinned-nightly")?; | ||
Ok(PinnedVersions { msrv, stable, nightly }) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum ToolchainVersion { | ||
/// The version listed as our MSRV (ie, the `package.rust-version` key in | ||
/// `Cargo.toml`). | ||
PinnedMsrv, | ||
/// The stable version pinned in CI. | ||
PinnedStable, | ||
/// The nightly version pinned in CI | ||
PinnedNightly, | ||
/// A stable version other than the one pinned in CI. | ||
OtherStable, | ||
/// A nightly version other than the one pinned in CI. | ||
OtherNightly, | ||
} | ||
|
||
impl ToolchainVersion { | ||
pub fn extract_from_pwd() -> Result<ToolchainVersion, Box<dyn std::error::Error>> { | ||
let pinned_versions = PinnedVersions::extract_from_pwd()?; | ||
let current = rustc_version::version_meta()?; | ||
|
||
let s = match current.channel { | ||
Channel::Dev | Channel::Beta => { | ||
return Err(format!("unsupported channel: {:?}", current.channel).into()) | ||
} | ||
Channel::Nightly => { | ||
format!( | ||
"nightly-{}", | ||
current.commit_date.as_ref().ok_or("nightly channel missing commit date")? | ||
) | ||
} | ||
Channel::Stable => { | ||
let Version { major, minor, patch, .. } = current.semver; | ||
format!("{major}.{minor}.{patch}") | ||
} | ||
}; | ||
|
||
// Due to a quirk of how Rust nightly versions are encoded and published | ||
// [1], the version as understood by rustup uses a date one day ahead of | ||
// the version as encoded in the `rustc` binary itself. | ||
// `pinned_versions` encodes the former notion of the date (as it is | ||
// meant to be passed as the `+<toolchain>` selector syntax understood | ||
// by rustup), while `current` encodes the latter notion of the date (as | ||
// it is extracted from `rustc`). Without this adjustment, toolchain | ||
// versions that should be considered equal would not be. | ||
// | ||
// [1] https://github.com/rust-lang/rust/issues/51533 | ||
let pinned_nightly_adjusted = { | ||
let desc = time::macros::format_description!("nightly-[year]-[month]-[day]"); | ||
let date = time::Date::parse(&pinned_versions.nightly, &desc).map_err(|_| { | ||
format!("failed to parse nightly version: {}", pinned_versions.nightly) | ||
})?; | ||
let adjusted = date - time::Duration::DAY; | ||
adjusted.format(&desc).unwrap() | ||
}; | ||
|
||
Ok(match s { | ||
s if s == pinned_versions.msrv => ToolchainVersion::PinnedMsrv, | ||
s if s == pinned_versions.stable => ToolchainVersion::PinnedStable, | ||
s if s == pinned_nightly_adjusted => ToolchainVersion::PinnedNightly, | ||
_ if current.channel == Channel::Stable => ToolchainVersion::OtherStable, | ||
_ if current.channel == Channel::Nightly => ToolchainVersion::OtherNightly, | ||
_ => { | ||
return Err(format!( | ||
"current toolchain ({current:?}) doesn't match any known version" | ||
) | ||
.into()) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
// rust-version = "1.61.0" | ||
|
||
// [package.metadata.ci] | ||
// # The versions of the stable and nightly compiler toolchains to use in CI. | ||
// pinned-stable = "1.69.0" | ||
// pinned-nightly = "nightly-2023-05-25" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters