#![allow(
clippy::disallowed_methods,
reason = "build scripts cannot use rustpython-host_env"
)]
use jiff::{Timestamp, Zoned, tz::TimeZone};
use std::{
env, io,
process::Command,
time::{SystemTime, UNIX_EPOCH},
};
fn main() {
let frozen_libs = if cfg!(feature = "freeze-stdlib") {
"Lib/*/*.py"
} else {
"Lib/python_builtins/*.py"
};
for entry in glob::glob(frozen_libs).expect("Lib/ exists?").flatten() {
let display = entry.display();
println!("cargo:rerun-if-changed={display}");
}
println!("cargo:rerun-if-changed=../../Lib/importlib/_bootstrap.py");
python_version(3, 14, 0, "alpha", 0);
println!("cargo:rustc-env=RUSTPYTHON_GIT_HASH={}", git_hash());
println!(
"cargo:rustc-env=RUSTPYTHON_GIT_TIMESTAMP={}",
git_timestamp()
);
println!("cargo:rustc-env=RUSTPYTHON_GIT_TAG={}", git_tag());
println!("cargo:rustc-env=RUSTPYTHON_GIT_BRANCH={}", git_branch());
println!(
"cargo:rustc-env=RUSTPYTHON_GIT_IDENTIFIER={}",
git_identifier()
);
println!("cargo:rustc-env=RUSTPYTHON_BUILD_INFO={}", get_build_info());
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_level_n = release_to_n(release_level);
println!("cargo:rustc-env=RUSTPYTHON_RELEASE_LEVEL_N={release_level_n}");
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()
);
}
fn git_hash() -> String {
git(&["rev-parse", "--short", "HEAD"]).unwrap_or_else(|_| "0000000".into())
}
fn git_timestamp() -> String {
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"]).unwrap_or_else(|_| "unknown-branch".into())
}
fn git(args: &[&str]) -> io::Result<String> {
command("git", args)
}
#[must_use]
fn get_build_info() -> String {
let revision = git_hash();
let separator = if revision.is_empty() { "" } else { ":" };
let identifier = git_identifier();
format!(
"{id}{sep}{revision}, {date:.20}, {time:.9}",
id = if identifier.is_empty() {
"default"
} else {
&identifier
},
sep = separator,
revision = revision,
date = get_git_date(),
time = get_git_time(),
)
}
fn git_identifier() -> String {
let tag = git_tag();
if tag.is_empty() || tag.eq_ignore_ascii_case("undefined") {
git_branch()
} else {
tag
}
}
fn get_git_timestamp_raw() -> Option<Timestamp> {
let git_timestamp = git_timestamp().parse::<i64>().ok()?;
Timestamp::from_second(git_timestamp).ok()
}
fn get_git_timestamp_datetime() -> Zoned {
get_git_timestamp_raw().map_or_else(Zoned::now, |timestamp| {
Zoned::new(timestamp, TimeZone::system())
})
}
#[must_use]
fn get_git_date() -> String {
let datetime = get_git_timestamp_datetime();
datetime.strftime("%b %e %Y").to_string()
}
#[must_use]
fn get_git_time() -> String {
let datetime = get_git_timestamp_datetime();
datetime.strftime("%H:%M:%S").to_string()
}
fn rustc_version() -> String {
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
command(rustc, &["-V"]).unwrap_or_else(|_| "rustc [unknown]".into())
}
fn python_version(major: usize, minor: usize, micro: usize, release: &str, serial: usize) {
println!("cargo:rustc-env=MAJOR_CPY={major}");
println!("cargo:rustc-env=MINOR_CPY={minor}");
println!("cargo:rustc-env=MICRO_CPY={micro}");
println!("cargo:rustc-env=RELEASE_LEVEL_CPY={release}");
println!(
"cargo:rustc-env=RELEASE_LEVEL_N_CPY={}",
release_to_n(release)
);
println!("cargo:rustc-env=SERIAL_CPY={serial}");
println!("cargo:rustc-env=WINVER_CPY={major}.{minor}",);
let cpy_version = format!("{major}.{minor}.{micro}.{release}");
let (left, right) = get_version(&cpy_version);
println!("cargo:rustc-env=RUSTPYTHON_VERSION_LEFT={left}");
println!("cargo:rustc-env=RUSTPYTHON_VERSION_RIGHT={right}");
}
#[must_use]
fn get_version(cpy_version: &str) -> (String, String) {
let msc_info = cfg_select! {
windows => {{
if cfg!(target_pointer_width = "64") {
" MSC v.1929 64 bit (AMD64)"
} else {
" MSC v.1929 32 bit (Intel)"
}
}},
_ => "",
};
let left = format!("{:.80} ({:.80})", cpy_version, get_build_info());
let right = format!(
"[RustPython {} with {:.80}{}]",
env!("CARGO_PKG_VERSION"),
rustc_version(),
msc_info,
);
(left, right)
}
fn release_to_n(release: &str) -> usize {
match release {
"alpha" => 0xA,
"beta" => 0xB,
"candidate" => 0xC,
"final" => 0xD,
_ => panic!("`release` must be one of: 'alpha', 'beta', 'candidate', 'final'"),
}
}
fn command(cmd: impl AsRef<std::ffi::OsStr>, args: &[&str]) -> io::Result<String> {
Command::new(&cmd).args(args).output().and_then(|output| {
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)
})
}