diff --git a/crates/vm/build.rs b/crates/vm/build.rs index f38ae993568..6c65aa8633b 100644 --- a/crates/vm/build.rs +++ b/crates/vm/build.rs @@ -4,7 +4,13 @@ )] use itertools::Itertools; -use std::{env, io::prelude::*, path::PathBuf, process::Command}; +use std::{ + env, + io::{self, prelude::*}, + path::PathBuf, + process::Command, + time::{SystemTime, UNIX_EPOCH}, +}; fn main() { let frozen_libs = if cfg!(feature = "freeze-stdlib") { @@ -27,6 +33,11 @@ fn main() { println!("cargo:rustc-env=RUSTPYTHON_GIT_BRANCH={}", git_branch()); println!("cargo:rustc-env=RUSTC_VERSION={}", rustc_version()); + let release_level = option_env!("RUSTPYTHON_RELEASE_LEVEL").unwrap_or("alpha"); + println!("cargo:rustc-env=RUSTPYTHON_RELEASE_LEVEL={release_level}"); + let release_serial = option_env!("RUSTPYTHON_RELEASE_SERIAL").unwrap_or("0"); + println!("cargo:rustc-env=RUSTPYTHON_RELEASE_SERIAL={release_serial}"); + println!( "cargo:rustc-env=RUSTPYTHON_TARGET_TRIPLE={}", env::var("TARGET").unwrap() @@ -44,36 +55,50 @@ fn main() { } fn git_hash() -> String { - git(&["rev-parse", "--short", "HEAD"]) + git(&["rev-parse", "--short", "HEAD"]).unwrap_or_else(|_| "0000000".into()) } fn git_timestamp() -> String { - git(&["log", "-1", "--format=%ct"]) + git(&["log", "-1", "--format=%ct"]).unwrap_or_else(|_| { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs() + .to_string() + }) } fn git_tag() -> String { git(&["describe", "--all", "--always", "--dirty"]) + .unwrap_or_else(|_| "heads/unknown-branch".into()) } fn git_branch() -> String { - git(&["name-rev", "--name-only", "HEAD"]) + git(&["name-rev", "--name-only", "HEAD"]).unwrap_or_else(|_| "unknown-branch".into()) } -fn git(args: &[&str]) -> String { +fn git(args: &[&str]) -> io::Result { command("git", args) } fn rustc_version() -> String { let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); - command(rustc, &["-V"]) + command(rustc, &["-V"]).unwrap_or_else(|_| "rustc [unknown]".into()) } -fn command(cmd: impl AsRef, args: &[&str]) -> String { - match Command::new(cmd).args(args).output() { - Ok(output) => match String::from_utf8(output.stdout) { - Ok(s) => s, - Err(err) => format!("(output error: {err})"), - }, - Err(err) => format!("(command error: {err})"), - } +fn command(cmd: impl AsRef, args: &[&str]) -> io::Result { + Command::new(&cmd).args(args).output().and_then(|output| { + // TODO: Switch to exit_ok()? when stable. + if !output.status.success() { + Err(io::Error::other(format!( + "command '{}' exited with status {}", + cmd.as_ref().to_string_lossy(), + output.status + )))? + } + + str::from_utf8(&output.stdout) + .map(|s| s.trim().to_owned()) + .map_err(io::Error::other) + }) } diff --git a/crates/vm/src/stdlib/sys.rs b/crates/vm/src/stdlib/sys.rs index 59972bfc3f3..db978becbb4 100644 --- a/crates/vm/src/stdlib/sys.rs +++ b/crates/vm/src/stdlib/sys.rs @@ -660,7 +660,7 @@ pub mod sys { vm.new_tuple(( ascii!("RustPython"), version::get_git_identifier(), - version::get_git_revision(), + version::GIT_REVISION, )) } @@ -668,14 +668,14 @@ pub mod sys { fn implementation(vm: &VirtualMachine) -> PyRef { const NAME: &str = "rustpython"; - let cache_tag = format!("{NAME}-{}{}", version::MAJOR, version::MINOR); + let cache_tag = format!("{NAME}-{}_{}", version::MAJOR_IMPL, version::MINOR_IMPL); let ctx = &vm.ctx; py_namespace!(vm, { "name" => ctx.new_str(NAME), "cache_tag" => ctx.new_str(cache_tag), "_multiarch" => ctx.new_str(multiarch()), - "version" => version_info(vm), - "hexversion" => ctx.new_int(version::VERSION_HEX), + "version" => PyVersionInfo::from_data(VersionInfoData::IMPLEMENTATION, vm), + "hexversion" => ctx.new_int(version::VERSION_HEX_IMPL), "supports_isolated_interpreters" => ctx.new_bool(false), }) } @@ -1758,6 +1758,14 @@ pub mod sys { releaselevel: version::RELEASELEVEL, serial: version::SERIAL, }; + + pub const IMPLEMENTATION: Self = Self { + major: version::MAJOR_IMPL, + minor: version::MINOR_IMPL, + micro: version::MICRO_IMPL, + releaselevel: version::RELEASELEVEL_IMPL, + serial: version::SERIAL_IMPL, + }; } #[pystruct_sequence(name = "version_info", data = "VersionInfoData", no_attr)] diff --git a/crates/vm/src/version.rs b/crates/vm/src/version.rs index ab20573c50d..29ed3c2aa4a 100644 --- a/crates/vm/src/version.rs +++ b/crates/vm/src/version.rs @@ -11,10 +11,37 @@ pub const MICRO: usize = 0; pub const RELEASELEVEL: &str = "alpha"; pub const RELEASELEVEL_N: usize = 0xA; pub const SERIAL: usize = 0; - pub const VERSION_HEX: usize = (MAJOR << 24) | (MINOR << 16) | (MICRO << 8) | (RELEASELEVEL_N << 4) | SERIAL; +pub const GIT_REVISION: &str = env!("RUSTPYTHON_GIT_HASH"); +const GIT_TAG: &str = env!("RUSTPYTHON_GIT_TAG"); +const GIT_BRANCH: &str = env!("RUSTPYTHON_GIT_BRANCH"); + +// RustPython version +pub const MAJOR_IMPL: usize = match usize::from_str_radix(env!("CARGO_PKG_VERSION_MAJOR"), 10) { + Ok(v) => v, + Err(_) => panic!("Compile with Cargo to get 'CARGO_PKG_VERSION_MAJOR'"), +}; +pub const MINOR_IMPL: usize = match usize::from_str_radix(env!("CARGO_PKG_VERSION_MINOR"), 10) { + Ok(v) => v, + Err(_) => panic!("Compile with Cargo to get 'CARGO_PKG_VERSION_MINOR'"), +}; +pub const MICRO_IMPL: usize = match usize::from_str_radix(env!("CARGO_PKG_VERSION_PATCH"), 10) { + Ok(v) => v, + Err(_) => panic!("Compile with Cargo to get 'CARGO_PKG_VERSION_PATCH'"), +}; +pub const RELEASELEVEL_IMPL: &str = env!("RUSTPYTHON_RELEASE_LEVEL"); +pub const SERIAL_IMPL: usize = match usize::from_str_radix(env!("RUSTPYTHON_RELEASE_SERIAL"), 10) { + Ok(v) => v, + Err(_) => panic!("Compile with Cargo to get 'RUSTPYTHON_RELEASE_SERIAL'"), +}; +pub const VERSION_HEX_IMPL: usize = (MAJOR_IMPL << 24) + | (MINOR_IMPL << 16) + | (MICRO_IMPL << 8) + | (RELEASELEVEL_N << 4) + | SERIAL_IMPL; + #[must_use] pub fn get_version() -> String { // Windows: include MSC v. for compatibility with ctypes.util.find_library @@ -57,59 +84,35 @@ const COMPILER: &str = env!("RUSTC_VERSION"); #[must_use] pub fn get_build_info() -> String { // See: https://reproducible-builds.org/docs/timestamps/ - let git_revision = get_git_revision(); - let separator = if git_revision.is_empty() { "" } else { ":" }; - + let separator = if GIT_REVISION.is_empty() { "" } else { ":" }; let git_identifier = get_git_identifier(); format!( "{id}{sep}{revision}, {date:.20}, {time:.9}", id = if git_identifier.is_empty() { - "default".to_owned() + "default" } else { git_identifier }, sep = separator, - revision = git_revision, + revision = GIT_REVISION, date = get_git_date(), time = get_git_time(), ) } #[must_use] -pub fn get_git_revision() -> String { - option_env!("RUSTPYTHON_GIT_HASH").unwrap_or("").to_owned() -} - -#[must_use] -pub fn get_git_tag() -> String { - option_env!("RUSTPYTHON_GIT_TAG").unwrap_or("").to_owned() -} - -#[must_use] -pub fn get_git_branch() -> String { - option_env!("RUSTPYTHON_GIT_BRANCH") - .unwrap_or("") - .to_owned() -} - -#[must_use] -pub fn get_git_identifier() -> String { - let git_tag = get_git_tag(); - let git_branch = get_git_branch(); - - if git_tag.is_empty() || git_tag == "undefined" { - git_branch +pub const fn get_git_identifier() -> &'static str { + if GIT_TAG.is_empty() || GIT_TAG.eq_ignore_ascii_case("undefined") { + GIT_BRANCH } else { - git_tag + GIT_TAG } } fn get_git_timestamp_datetime() -> DateTime { - let timestamp = option_env!("RUSTPYTHON_GIT_TIMESTAMP") - .unwrap_or("") - .to_owned(); - let timestamp = timestamp.parse::().unwrap_or(0); + let timestamp = option_env!("RUSTPYTHON_GIT_TIMESTAMP").unwrap_or_default(); + let timestamp = timestamp.parse::().unwrap_or_default(); let datetime = UNIX_EPOCH + Duration::from_secs(timestamp);