Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Populate git information when building Cargo from Rust's source tarball #13832

Merged
merged 2 commits into from
Apr 30, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 66 additions & 15 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,15 @@ fn compress_man() {
encoder.finish().unwrap();
}

fn commit_info() {
if !Path::new(".git").exists() {
return;
}
struct CommitInfo {
hash: String,
short_hash: String,
date: String,
}

// Var set by bootstrap whenever omit-git-hash is enabled in rust-lang/rust's config.toml.
println!("cargo:rerun-if-env-changed=CFG_OMIT_GIT_HASH");
#[allow(clippy::disallowed_methods)]
if std::env::var_os("CFG_OMIT_GIT_HASH").is_some() {
return;
fn commit_info_from_git() -> Option<CommitInfo> {
if !Path::new(".git").exists() {
return None;
}

let output = match Command::new("git")
Expand All @@ -68,14 +67,66 @@ fn commit_info() {
.output()
{
Ok(output) if output.status.success() => output,
_ => return,
_ => return None,
};

let stdout = String::from_utf8(output.stdout).unwrap();
let mut parts = stdout.split_whitespace();
let mut next = || parts.next().unwrap();
println!("cargo:rustc-env=CARGO_COMMIT_HASH={}", next());
println!("cargo:rustc-env=CARGO_COMMIT_SHORT_HASH={}", next());
println!("cargo:rustc-env=CARGO_COMMIT_DATE={}", next())
let mut parts = stdout.split_whitespace().map(|s| s.to_string());

Some(CommitInfo {
hash: parts.next()?,
short_hash: parts.next()?,
date: parts.next()?,
})
}

// The rustc source tarball is meant to contain all the source code to build an exact copy of the
// toolchain, but it doesn't include the git repository itself. It wouldn't thus be possible to
// populate the version information with the commit hash and the commit date.
//
// To work around this, the rustc build process obtains the git information when creating the
// source tarball and writes it to the `git-commit-info` file. The build process actually creates
// at least *two* of those files, one for Rust as a whole (in the root of the tarball) and one
// specifically for Cargo (in src/tools/cargo). This function loads that file.
//
// The file is a newline-separated list of full commit hash, short commit hash, and commit date.
fn commit_info_from_rustc_source_tarball() -> Option<CommitInfo> {
let path = Path::new("git-commit-info");
if !path.exists() {
return None;
}

// Dependency tracking is a nice to have for this (git doesn't do it), so if the path is not
// valid UTF-8 just avoid doing it rather than erroring out.
if let Some(utf8) = path.to_str() {
println!("cargo:rerun-if-changed={utf8}");
}

let content = std::fs::read_to_string(&path).ok()?;
let mut parts = content.split('\n').map(|s| s.to_string());
Some(CommitInfo {
hash: parts.next()?,
short_hash: parts.next()?,
date: parts.next()?,
})
}

fn commit_info() {
// Var set by bootstrap whenever omit-git-hash is enabled in rust-lang/rust's config.toml.
println!("cargo:rerun-if-env-changed=CFG_OMIT_GIT_HASH");
// ALLOWED: Accessing environment during build time shouldn't be prohibited.
#[allow(clippy::disallowed_methods)]
if std::env::var_os("CFG_OMIT_GIT_HASH").is_some() {
return;
}

let Some(git) = commit_info_from_git().or_else(commit_info_from_rustc_source_tarball) else {
return;
};

println!("cargo:rustc-env=CARGO_COMMIT_HASH={}", git.hash);
println!("cargo:rustc-env=CARGO_COMMIT_SHORT_HASH={}", git.short_hash);
println!("cargo:rustc-env=CARGO_COMMIT_DATE={}", git.date);
}

#[allow(clippy::disallowed_methods)]
Expand Down