diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f7cf72..63e3ab4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,7 @@ jobs: - 1.31.0 - 1.33.0 - 1.39.0 + - 1.46.0 - stable - beta - nightly diff --git a/Cargo.toml b/Cargo.toml index a633b19..89d2f38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,4 @@ members = ["test_suite"] [lib] proc-macro = true -[build-dependencies] -version_check = "0.9.2" - [dependencies] diff --git a/README.md b/README.md index bd9af37..325b789 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ pub const fn feature() { ## Alternatives -This crate is proc-macro, but is very lightweight, depending only on [`version_check`](https://github.com/SergioBenitez/version_check) to determine the compiler version. +This crate is proc-macro, but is very lightweight, and has no dependencies. You can manually define declarative macros with similar functionality (see [`if_rust_version`](https://github.com/ogoffart/if_rust_version#examples)), or [you can define the same function twice with different cfg](https://github.com/crossbeam-rs/crossbeam/blob/0b6ea5f69fde8768c1cfac0d3601e0b4325d7997/crossbeam-epoch/src/atomic.rs#L340-L372). (Note: the former approach requires more macros to be defined depending on the number of version requirements, the latter approach requires more functions to be maintained manually) diff --git a/build.rs b/build.rs index e7dd22f..758b390 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,9 @@ -use std::{env, fs, path::PathBuf}; +use std::{ + env, fs, + path::{Path, PathBuf}, + process::Command, + str, +}; // The rustc-cfg strings below are *not* public API. Please let us know by // opening a GitHub issue if your build environment requires some way to enable @@ -6,9 +11,10 @@ use std::{env, fs, path::PathBuf}; fn main() { println!("cargo:rustc-cfg=const_fn_has_build_script"); - let version = match Version::new() { - Some(version) => format!("{:#?}\n", version), - None => panic!("unexpected output from `rustc --version`"), + let rustc = env::var_os("RUSTC").map_or_else(|| "rustc".into(), PathBuf::from); + let version = match Version::from_rustc(&rustc) { + Ok(version) => format!("{:#?}\n", version), + Err(e) => panic!("{}", e), }; let out_dir = env::var_os("OUT_DIR").map(PathBuf::from).expect("OUT_DIR not set"); @@ -23,11 +29,50 @@ struct Version { nightly: bool, } +// Based on https://github.com/cuviper/autocfg/blob/1.0.1/src/version.rs +// +// Using our own parser instead of the existing crates to generate better errors. impl Version { - fn new() -> Option { - let (version, channel, _date) = version_check::triple()?; - let (_major, minor, patch) = version.to_mmp(); - let nightly = channel.is_nightly() || channel.is_dev(); + // from the verbose version output + fn from_vv(vv: &str) -> Option { + // Find the release line in the verbose version output. + let release = vv + .lines() + .find(|line| line.starts_with("release: ")) + .map(|line| &line["release: ".len()..])?; + + // Split the version and channel info. + let mut version_channel = release.split('-'); + let version = version_channel.next().unwrap(); + let channel = version_channel.next(); + + // Split the version into semver components. + let mut digits = version.splitn(3, '.'); + let major = digits.next()?; + if major != "1" { + return None; + } + let minor = digits.next()?.parse().ok()?; + let patch = digits.next().unwrap_or("0").parse().ok()?; + + let nightly = channel.map_or(false, |c| c == "dev" || c == "nightly"); Some(Version { minor, patch, nightly }) } + + fn from_rustc(rustc: &Path) -> Result { + let output = + Command::new(rustc).args(&["--version", "--verbose"]).output().map_err(|e| { + format!("failed to run `{} --version --verbose`: {}", rustc.display(), e) + })?; + if !output.status.success() { + return Err("could not execute rustc".to_string()); + } + let output = str::from_utf8(&output.stdout).map_err(|e| { + format!("failed to parse output of `{} --version --verbose`: {}", rustc.display(), e) + })?; + + Self::from_vv(output).ok_or_else(|| { + format!("unexpected output from `{} --version --verbose`: {}", rustc.display(), output) + }) + } } diff --git a/src/lib.rs b/src/lib.rs index 16dff80..af384af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,7 @@ //! //! # Alternatives //! -//! This crate is proc-macro, but is very lightweight, depending only on [`version_check`](https://github.com/SergioBenitez/version_check) to determine the compiler version. +//! This crate is proc-macro, but is very lightweight, and has no dependencies. //! //! You can manually define declarative macros with similar functionality (see [`if_rust_version`](https://github.com/ogoffart/if_rust_version#examples)), or [you can define the same function twice with different cfg](https://github.com/crossbeam-rs/crossbeam/blob/0b6ea5f69fde8768c1cfac0d3601e0b4325d7997/crossbeam-epoch/src/atomic.rs#L340-L372). //! (Note: the former approach requires more macros to be defined depending on the number of version requirements, the latter approach requires more functions to be maintained manually) diff --git a/test_suite/build.rs b/test_suite/build.rs index e486587..4b0aea7 100644 --- a/test_suite/build.rs +++ b/test_suite/build.rs @@ -17,6 +17,9 @@ fn main() { if minor >= 39 || nightly { println!("cargo:rustc-cfg=has_const_vec_new"); } + if minor >= 46 || nightly { + println!("cargo:rustc-cfg=has_const_match"); + } if nightly { println!("cargo:rustc-cfg=const_unstable"); } diff --git a/test_suite/tests/test.rs b/test_suite/tests/test.rs index 1a3ac10..5976c97 100644 --- a/test_suite/tests/test.rs +++ b/test_suite/tests/test.rs @@ -4,31 +4,15 @@ pub mod version { use const_fn::const_fn; - #[test] - fn test_variables() { - assert!(const_min("variables") == "variables"); - assert_eq!(const_let("variables"), "variables"); - assert_eq!(const_vec_new::(), Vec::new()); - assert_eq!(A::const_unstable(const_vec_new::()), A(Vec::new())); - } - - // min_const_fn (rust 1.31+) + // min_const_fn (1.31+) #[const_fn("1.31")] const fn const_min(x: T) -> T { x } + const _CONST_MIN: &str = const_min("min_const_fn"); - const CONST_MIN: &str = const_min("min_const_fn"); - - #[test] - fn test_const_min() { - assert!(CONST_MIN == "min_const_fn"); - assert_eq!(const_let("min_const_fn"), "min_const_fn"); - assert_eq!(const_vec_new::(), Vec::new()); - } - - // const_let (rust 1.33+) + // const_let (1.33+) #[allow(clippy::let_and_return)] #[const_fn("1.33")] @@ -36,34 +20,31 @@ pub mod version { let y = const_min(x); y } - - #[rustversion::since(1.33)] - const CONST_LET: &str = const_let("const_let"); - #[rustversion::since(1.33)] - #[test] - fn test_const_let() { - assert!(CONST_LET == "const_let"); - assert_eq!(const_vec_new::(), Vec::new()); - } + const _CONST_LET: &str = const_let("const_let"); - // const_vec_new (rust 1.39+) + // const_vec_new (1.39+) #[const_fn("1.39")] const fn const_vec_new() -> Vec { Vec::new() } - #[rustversion::since(1.39)] - const CONST_VEC_NEW: Vec = const_vec_new(); + const _: Vec = const_vec_new(); - #[rustversion::since(1.39)] - #[test] - fn test_const_vec_new() { - assert_eq!(CONST_VEC_NEW, Vec::new()); + // const_match, const_loop (1.46+) + + #[const_fn("1.46")] + const fn const_match(x: u8) -> Option { + match x { + 0 => None, + x => Some(x), + } } + #[rustversion::since(1.46)] + const _: Option = const_match(1); - // const_fn (rust nightly) + // const_fn (nightly) #[derive(Debug, Eq, PartialEq)] pub struct A(T); @@ -74,39 +55,31 @@ pub mod version { A(x) } } - #[rustversion::nightly] - pub const CONST_UNSTABLE: A> = A::const_unstable(const_vec_new()); -} - -pub mod cfg { - use const_fn::const_fn; + const _: A> = A::const_unstable(const_vec_new()); #[test] - fn test_variables() { + fn test() { assert!(const_min("variables") == "variables"); assert_eq!(const_let("variables"), "variables"); assert_eq!(const_vec_new::(), Vec::new()); + assert_eq!(const_match(1), Some(1)); assert_eq!(A::const_unstable(const_vec_new::()), A(Vec::new())); } +} + +pub mod cfg { + use const_fn::const_fn; - // min_const_fn (rust 1.31+) + // min_const_fn (1.31+) #[const_fn(cfg(has_min_const_fn))] const fn const_min(x: T) -> T { x } + const _CONST_MIN: &str = const_min("min_const_fn"); - pub const CONST_MIN: &str = const_min("min_const_fn"); - - #[test] - fn test_const_min() { - assert!(CONST_MIN == "min_const_fn"); - assert_eq!(const_let("min_const_fn"), "min_const_fn"); - assert_eq!(const_vec_new::(), Vec::new()); - } - - // const_let (rust 1.33+) + // const_let (1.33+) #[allow(clippy::let_and_return)] #[const_fn(cfg(has_const_let))] @@ -114,34 +87,31 @@ pub mod cfg { let y = const_min(x); y } - #[rustversion::since(1.33)] - const CONST_LET: &str = const_let("const_let"); - - #[rustversion::since(1.33)] - #[test] - fn test_const_let() { - assert!(CONST_LET == "const_let"); - assert_eq!(const_vec_new::(), Vec::new()); - } + const _CONST_LET: &str = const_let("const_let"); - // const_vec_new (rust 1.39+) + // const_vec_new (1.39+) #[const_fn(cfg(has_const_vec_new))] const fn const_vec_new() -> Vec { Vec::new() } - #[rustversion::since(1.39)] - const CONST_VEC_NEW: Vec = const_vec_new(); + const _: Vec = const_vec_new(); - #[rustversion::since(1.39)] - #[test] - fn test_const_vec_new() { - assert_eq!(CONST_VEC_NEW, Vec::new()); + // const_match, const_loop (1.46+) + + #[const_fn(cfg(has_const_match))] + const fn const_match(x: u8) -> Option { + match x { + 0 => None, + x => Some(x), + } } + #[rustversion::since(1.46)] + const _: Option = const_match(1); - // const_fn (rust nightly) + // const_fn (nightly) #[derive(Debug, Eq, PartialEq)] pub struct A(T); @@ -152,7 +122,15 @@ pub mod cfg { A(x) } } - #[rustversion::nightly] - pub const CONST_UNSTABLE: A> = A::const_unstable(const_vec_new()); + const _: A> = A::const_unstable(const_vec_new()); + + #[test] + fn test() { + assert!(const_min("variables") == "variables"); + assert_eq!(const_let("variables"), "variables"); + assert_eq!(const_vec_new::(), Vec::new()); + assert_eq!(const_match(1), Some(1)); + assert_eq!(A::const_unstable(const_vec_new::()), A(Vec::new())); + } }