Skip to content

Commit a6f57bb

Browse files
Const eval all of version.rs
`version.rs` essentially consists of constants that can be baked in at compile time. I moved most of `version.rs` to `build.rs`. The constants are passed via rustc's environment then stored in the binary.
1 parent 883ce9d commit a6f57bb

5 files changed

Lines changed: 187 additions & 135 deletions

File tree

crates/vm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ widestring = { workspace = true }
9999
wasm-bindgen = { workspace = true, optional = true }
100100

101101
[build-dependencies]
102+
chrono = { workspace = true }
102103
glob = { workspace = true }
103104
itertools = { workspace = true }
104105

crates/vm/build.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
reason = "build scripts cannot use rustpython-host_env"
44
)]
55

6+
use chrono::{Local, prelude::DateTime};
7+
use core::time::Duration;
68
use itertools::Itertools;
79
use std::{
810
env,
@@ -24,17 +26,27 @@ fn main() {
2426
}
2527
println!("cargo:rerun-if-changed=../../Lib/importlib/_bootstrap.py");
2628

29+
// = 3.14.0alpha
30+
python_version(3, 14, 0, "alpha", 0);
31+
2732
println!("cargo:rustc-env=RUSTPYTHON_GIT_HASH={}", git_hash());
2833
println!(
2934
"cargo:rustc-env=RUSTPYTHON_GIT_TIMESTAMP={}",
3035
git_timestamp()
3136
);
3237
println!("cargo:rustc-env=RUSTPYTHON_GIT_TAG={}", git_tag());
3338
println!("cargo:rustc-env=RUSTPYTHON_GIT_BRANCH={}", git_branch());
39+
println!(
40+
"cargo:rustc-env=RUSTPYTHON_GIT_IDENTIFIER={}",
41+
git_identifier()
42+
);
43+
println!("cargo:rustc-env=RUSTPYTHON_BUILD_INFO={}", get_build_info());
3444
println!("cargo:rustc-env=RUSTC_VERSION={}", rustc_version());
3545

3646
let release_level = option_env!("RUSTPYTHON_RELEASE_LEVEL").unwrap_or("alpha");
3747
println!("cargo:rustc-env=RUSTPYTHON_RELEASE_LEVEL={release_level}");
48+
let release_level_n = release_to_n(release_level);
49+
println!("cargo:rustc-env=RUSTPYTHON_RELEASE_LEVEL_N={release_level_n}");
3850
let release_serial = option_env!("RUSTPYTHON_RELEASE_SERIAL").unwrap_or("0");
3951
println!("cargo:rustc-env=RUSTPYTHON_RELEASE_SERIAL={release_serial}");
4052

@@ -81,11 +93,127 @@ fn git(args: &[&str]) -> io::Result<String> {
8193
command("git", args)
8294
}
8395

96+
#[must_use]
97+
pub fn get_build_info() -> String {
98+
// See: https://reproducible-builds.org/docs/timestamps/
99+
let revision = git_hash();
100+
let separator = if revision.is_empty() { "" } else { ":" };
101+
let identifier = git_identifier();
102+
103+
format!(
104+
"{id}{sep}{revision}, {date:.20}, {time:.9}",
105+
id = if identifier.is_empty() {
106+
"default"
107+
} else {
108+
&identifier
109+
},
110+
sep = separator,
111+
revision = revision,
112+
date = get_git_date(),
113+
time = get_git_time(),
114+
)
115+
}
116+
117+
fn git_identifier() -> String {
118+
let tag = git_tag();
119+
if tag.is_empty() || tag.eq_ignore_ascii_case("undefined") {
120+
git_branch()
121+
} else {
122+
tag
123+
}
124+
}
125+
126+
fn get_git_timestamp_datetime() -> DateTime<Local> {
127+
let timestamp = git_timestamp().parse::<u64>().unwrap_or_default();
128+
let datetime = UNIX_EPOCH + Duration::from_secs(timestamp);
129+
datetime.into()
130+
}
131+
132+
#[must_use]
133+
pub fn get_git_date() -> String {
134+
let datetime = get_git_timestamp_datetime();
135+
136+
datetime.format("%b %e %Y").to_string()
137+
}
138+
139+
#[must_use]
140+
pub fn get_git_time() -> String {
141+
let datetime = get_git_timestamp_datetime();
142+
143+
datetime.format("%H:%M:%S").to_string()
144+
}
145+
146+
#[must_use]
147+
pub fn get_git_datetime() -> String {
148+
let date = get_git_date();
149+
let time = get_git_time();
150+
151+
format!("{date} {time}")
152+
}
153+
84154
fn rustc_version() -> String {
85155
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
86156
command(rustc, &["-V"]).unwrap_or_else(|_| "rustc [unknown]".into())
87157
}
88158

159+
fn python_version(major: usize, minor: usize, micro: usize, release: &str, serial: usize) {
160+
println!("cargo:rustc-env=MAJOR_CPY={major}");
161+
println!("cargo:rustc-env=MINOR_CPY={minor}");
162+
println!("cargo:rustc-env=MICRO_CPY={micro}");
163+
println!("cargo:rustc-env=RELEASE_LEVEL_CPY={release}");
164+
println!(
165+
"cargo:rustc-env=RELEASE_LEVEL_N_CPY={}",
166+
release_to_n(release)
167+
);
168+
println!("cargo:rustc-env=SERIAL_CPY={serial}");
169+
170+
println!("cargo:rustc-env=WINVER_CPY={major}.{minor}",);
171+
172+
let cpy_version = format!("{major}.{minor}.{micro}.{release}");
173+
174+
let (left, right) = get_version(&cpy_version);
175+
println!("cargo:rustc-env=RUSTPYTHON_VERSION_LEFT={left}");
176+
println!("cargo:rustc-env=RUSTPYTHON_VERSION_RIGHT={right}");
177+
}
178+
179+
#[must_use]
180+
pub fn get_version(cpy_version: &str) -> (String, String) {
181+
// Windows: include MSC v. for compatibility with ctypes.util.find_library
182+
// MSC v.1929 = VS 2019, version 14+ makes find_msvcrt() return None
183+
let msc_info = cfg_select! {
184+
windows => {{
185+
// Include both RustPython identifier and MSC v. for compatibility
186+
if cfg!(target_pointer_width = "64") {
187+
" MSC v.1929 64 bit (AMD64)"
188+
} else {
189+
" MSC v.1929 32 bit (Intel)"
190+
}
191+
}},
192+
_ => "",
193+
};
194+
195+
// `left` and `right` are split by \n like PyPy. Passing a string with a newline to rustc
196+
// truncates everything from the newline onward, so we have to manually combine them later.
197+
let left = format!("{:.80} ({:.80})", cpy_version, get_build_info());
198+
let right = format!(
199+
"[RustPython {} with {:.80}{}]",
200+
env!("CARGO_PKG_VERSION"),
201+
rustc_version(),
202+
msc_info,
203+
);
204+
(left, right)
205+
}
206+
207+
fn release_to_n(release: &str) -> usize {
208+
match release {
209+
"alpha" => 0xA,
210+
"beta" => 0xB,
211+
"candidate" => 0xC,
212+
"final" => 0xD,
213+
_ => unreachable!(),
214+
}
215+
}
216+
89217
fn command(cmd: impl AsRef<std::ffi::OsStr>, args: &[&str]) -> io::Result<String> {
90218
Command::new(&cmd).args(args).output().and_then(|output| {
91219
// TODO: Switch to exit_ok()? when stable.

crates/vm/src/stdlib/sys.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ pub mod sys {
649649
fn _git(vm: &VirtualMachine) -> PyTupleRef {
650650
vm.new_tuple((
651651
ascii!("RustPython"),
652-
version::get_git_identifier(),
652+
version::GIT_IDENTIFIER,
653653
version::GIT_REVISION,
654654
))
655655
}
@@ -706,17 +706,13 @@ pub mod sys {
706706
vm.ctx.none()
707707
}
708708

709-
#[pyattr]
710-
fn version(_vm: &VirtualMachine) -> String {
711-
version::get_version()
712-
}
709+
#[pyattr(name = "version")]
710+
const VERSION: &str = version::RUSTPYTHON_VERSION;
713711

712+
// Note: This is Python DLL version in CPython, but we arbitrary fill it for compatibility
714713
#[cfg(windows)]
715-
#[pyattr]
716-
fn winver(_vm: &VirtualMachine) -> String {
717-
// Note: This is Python DLL version in CPython, but we arbitrary fill it for compatibility
718-
version::get_winver_number()
719-
}
714+
#[pyattr(name = "winver")]
715+
const WINVER: &str = version::WINVER;
720716

721717
#[pyattr]
722718
fn _xoptions(vm: &VirtualMachine) -> PyDictRef {

0 commit comments

Comments
 (0)