fix: use git-commit-info for version information

This PR adds support for fetching version information from the
`git-commit-info` file when building the compiler from a source tarball.
This commit is contained in:
dawnofmidnight 2022-08-24 12:36:08 -04:00
parent 17a627fe87
commit fdb39551dd
8 changed files with 126 additions and 46 deletions

View file

@ -521,6 +521,12 @@ changelog-seen = 2
# A descriptive string to be appended to `rustc --version` output, which is # A descriptive string to be appended to `rustc --version` output, which is
# also used in places like debuginfo `DW_AT_producer`. This may be useful for # also used in places like debuginfo `DW_AT_producer`. This may be useful for
# supplementary build information, like distro-specific package versions. # supplementary build information, like distro-specific package versions.
#
# The Rust compiler will differentiate between versions of itself, including
# based on this string, which means that if you wish to be compatible with
# upstream Rust you need to set this to "". However, note that if you are not
# actually compatible -- for example if you've backported patches that change
# behavior -- this may lead to miscompilations or other bugs.
#description = <none> (string) #description = <none> (string)
# The root location of the musl installation directory. The library directory # The root location of the musl installation directory. The library directory

View file

@ -5,10 +5,12 @@
//! `package_vers`, and otherwise indicating to the compiler what it should //! `package_vers`, and otherwise indicating to the compiler what it should
//! print out as part of its version information. //! print out as part of its version information.
use std::fs;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use crate::util::output; use crate::util::output;
use crate::util::t;
use crate::Build; use crate::Build;
pub enum GitInfo { pub enum GitInfo {
@ -18,19 +20,25 @@ pub enum GitInfo {
/// If the info should be used (`ignore_git` is false), this will be /// If the info should be used (`ignore_git` is false), this will be
/// `Some`, otherwise it will be `None`. /// `Some`, otherwise it will be `None`.
Present(Option<Info>), Present(Option<Info>),
/// This is not a git repostory, but the info can be fetched from the
/// `git-commit-info` file.
RecordedForTarball(Info),
} }
pub struct Info { pub struct Info {
commit_date: String, pub commit_date: String,
sha: String, pub sha: String,
short_sha: String, pub short_sha: String,
} }
impl GitInfo { impl GitInfo {
pub fn new(ignore_git: bool, dir: &Path) -> GitInfo { pub fn new(ignore_git: bool, dir: &Path) -> GitInfo {
// See if this even begins to look like a git dir // See if this even begins to look like a git dir
if !dir.join(".git").exists() { if !dir.join(".git").exists() {
return GitInfo::Absent; match read_commit_info_file(dir) {
Some(info) => return GitInfo::RecordedForTarball(info),
None => return GitInfo::Absent,
}
} }
// Make sure git commands work // Make sure git commands work
@ -65,10 +73,11 @@ impl GitInfo {
})) }))
} }
fn info(&self) -> Option<&Info> { pub fn info(&self) -> Option<&Info> {
match self { match self {
GitInfo::Present(info) => info.as_ref(),
GitInfo::Absent => None, GitInfo::Absent => None,
GitInfo::Present(info) => info.as_ref(),
GitInfo::RecordedForTarball(info) => Some(info),
} }
} }
@ -96,10 +105,48 @@ impl GitInfo {
version version
} }
pub fn is_git(&self) -> bool { /// Returns whether this directory has a `.git` directory which should be managed by bootstrap.
pub fn is_managed_git_subrepository(&self) -> bool {
match self { match self {
GitInfo::Absent => false, GitInfo::Absent | GitInfo::RecordedForTarball(_) => false,
GitInfo::Present(_) => true, GitInfo::Present(_) => true,
} }
} }
/// Returns whether this is being built from a tarball.
pub fn is_from_tarball(&self) -> bool {
match self {
GitInfo::Absent | GitInfo::Present(_) => false,
GitInfo::RecordedForTarball(_) => true,
}
}
}
/// Read the commit information from the `git-commit-info` file given the
/// project root.
pub fn read_commit_info_file(root: &Path) -> Option<Info> {
if let Ok(contents) = fs::read_to_string(root.join("git-commit-info")) {
let mut lines = contents.lines();
let sha = lines.next();
let short_sha = lines.next();
let commit_date = lines.next();
let info = match (commit_date, sha, short_sha) {
(Some(commit_date), Some(sha), Some(short_sha)) => Info {
commit_date: commit_date.to_owned(),
sha: sha.to_owned(),
short_sha: short_sha.to_owned(),
},
_ => panic!("the `git-comit-info` file is malformed"),
};
Some(info)
} else {
None
}
}
/// Write the commit information to the `git-commit-info` file given the project
/// root.
pub fn write_commit_info_file(root: &Path, info: &Info) {
let commit_info = format!("{}\n{}\n{}\n", info.sha, info.short_sha, info.commit_date);
t!(fs::write(root.join("git-commit-info"), &commit_info));
} }

View file

@ -1280,11 +1280,22 @@ impl Config {
git git
} }
pub(crate) fn artifact_channel(&self, commit: &str) -> String { pub(crate) fn artifact_channel(&self, builder: &Builder<'_>, commit: &str) -> String {
if builder.rust_info.is_managed_git_subrepository() {
let mut channel = self.git(); let mut channel = self.git();
channel.arg("show").arg(format!("{}:src/ci/channel", commit)); channel.arg("show").arg(format!("{}:src/ci/channel", commit));
let channel = output(&mut channel); let channel = output(&mut channel);
channel.trim().to_owned() channel.trim().to_owned()
} else if let Ok(channel) = fs::read_to_string(builder.src.join("src/ci/channel")) {
channel.trim().to_owned()
} else {
let src = builder.src.display();
eprintln!("error: failed to determine artifact channel");
eprintln!(
"help: either use git or ensure that {src}/src/ci/channel contains the name of the channel to use"
);
panic!();
}
} }
/// Try to find the relative path of `bindir`, otherwise return it in full. /// Try to find the relative path of `bindir`, otherwise return it in full.
@ -1421,7 +1432,7 @@ impl Config {
} }
pub fn submodules(&self, rust_info: &GitInfo) -> bool { pub fn submodules(&self, rust_info: &GitInfo) -> bool {
self.submodules.unwrap_or(rust_info.is_git()) self.submodules.unwrap_or(rust_info.is_managed_git_subrepository())
} }
} }
@ -1526,7 +1537,7 @@ fn maybe_download_rustfmt(builder: &Builder<'_>) -> Option<PathBuf> {
fn download_ci_rustc(builder: &Builder<'_>, commit: &str) { fn download_ci_rustc(builder: &Builder<'_>, commit: &str) {
builder.verbose(&format!("using downloaded stage2 artifacts from CI (commit {commit})")); builder.verbose(&format!("using downloaded stage2 artifacts from CI (commit {commit})"));
let channel = builder.config.artifact_channel(commit); let channel = builder.config.artifact_channel(builder, commit);
let host = builder.config.build.triple; let host = builder.config.build.triple;
let bin_root = builder.out.join(host).join("ci-rustc"); let bin_root = builder.out.join(host).join("ci-rustc");
let rustc_stamp = bin_root.join(".rustc-stamp"); let rustc_stamp = bin_root.join(".rustc-stamp");

View file

@ -16,6 +16,7 @@ use std::process::Command;
use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
use crate::cache::{Interned, INTERNER}; use crate::cache::{Interned, INTERNER};
use crate::channel;
use crate::compile; use crate::compile;
use crate::config::TargetSelection; use crate::config::TargetSelection;
use crate::tarball::{GeneratedTarball, OverlayKind, Tarball}; use crate::tarball::{GeneratedTarball, OverlayKind, Tarball};
@ -897,12 +898,12 @@ impl Step for PlainSourceTarball {
// Create the version file // Create the version file
builder.create(&plain_dst_src.join("version"), &builder.rust_version()); builder.create(&plain_dst_src.join("version"), &builder.rust_version());
if let Some(sha) = builder.rust_sha() { if let Some(info) = builder.rust_info.info() {
builder.create(&plain_dst_src.join("git-commit-hash"), &sha); channel::write_commit_info_file(&plain_dst_src, info);
} }
// If we're building from git sources, we need to vendor a complete distribution. // If we're building from git sources, we need to vendor a complete distribution.
if builder.rust_info.is_git() { if builder.rust_info.is_managed_git_subrepository() {
// Ensure we have the submodules checked out. // Ensure we have the submodules checked out.
builder.update_submodule(Path::new("src/tools/rust-analyzer")); builder.update_submodule(Path::new("src/tools/rust-analyzer"));

View file

@ -395,7 +395,7 @@ impl Build {
/// line and the filesystem `config`. /// line and the filesystem `config`.
/// ///
/// By default all build output will be placed in the current directory. /// By default all build output will be placed in the current directory.
pub fn new(config: Config) -> Build { pub fn new(mut config: Config) -> Build {
let src = config.src.clone(); let src = config.src.clone();
let out = config.out.clone(); let out = config.out.clone();
@ -470,6 +470,10 @@ impl Build {
bootstrap_out bootstrap_out
}; };
if rust_info.is_from_tarball() && config.description.is_none() {
config.description = Some("built from a source tarball".to_owned());
}
let mut build = Build { let mut build = Build {
initial_rustc: config.initial_rustc.clone(), initial_rustc: config.initial_rustc.clone(),
initial_cargo: config.initial_cargo.clone(), initial_cargo: config.initial_cargo.clone(),
@ -574,7 +578,9 @@ impl Build {
// NOTE: The check for the empty directory is here because when running x.py the first time, // NOTE: The check for the empty directory is here because when running x.py the first time,
// the submodule won't be checked out. Check it out now so we can build it. // the submodule won't be checked out. Check it out now so we can build it.
if !channel::GitInfo::new(false, &absolute_path).is_git() && !dir_is_empty(&absolute_path) { if !channel::GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
&& !dir_is_empty(&absolute_path)
{
return; return;
} }
@ -645,7 +651,7 @@ impl Build {
// Sample output: `submodule.src/rust-installer.path src/tools/rust-installer` // Sample output: `submodule.src/rust-installer.path src/tools/rust-installer`
let submodule = Path::new(line.splitn(2, ' ').nth(1).unwrap()); let submodule = Path::new(line.splitn(2, ' ').nth(1).unwrap());
// Don't update the submodule unless it's already been cloned. // Don't update the submodule unless it's already been cloned.
if channel::GitInfo::new(false, submodule).is_git() { if channel::GitInfo::new(false, submodule).is_managed_git_subrepository() {
self.update_submodule(submodule); self.update_submodule(submodule);
} }
} }
@ -1253,7 +1259,7 @@ impl Build {
match &self.config.channel[..] { match &self.config.channel[..] {
"stable" => num.to_string(), "stable" => num.to_string(),
"beta" => { "beta" => {
if self.rust_info.is_git() && !self.config.ignore_git { if self.rust_info.is_managed_git_subrepository() && !self.config.ignore_git {
format!("{}-beta.{}", num, self.beta_prerelease_version()) format!("{}-beta.{}", num, self.beta_prerelease_version())
} else { } else {
format!("{}-beta", num) format!("{}-beta", num)

View file

@ -17,6 +17,7 @@ use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::channel;
use crate::config::TargetSelection; use crate::config::TargetSelection;
use crate::util::get_clang_cl_resource_dir; use crate::util::get_clang_cl_resource_dir;
use crate::util::{self, exe, output, program_out_of_date, t, up_to_date}; use crate::util::{self, exe, output, program_out_of_date, t, up_to_date};
@ -115,7 +116,8 @@ pub fn prebuilt_llvm_config(
} }
/// This retrieves the LLVM sha we *want* to use, according to git history. /// This retrieves the LLVM sha we *want* to use, according to git history.
pub(crate) fn detect_llvm_sha(config: &crate::config::Config) -> String { pub(crate) fn detect_llvm_sha(config: &crate::config::Config, is_git: bool) -> String {
let llvm_sha = if is_git {
let mut rev_list = config.git(); let mut rev_list = config.git();
rev_list.args(&[ rev_list.args(&[
PathBuf::from("rev-list"), PathBuf::from("rev-list"),
@ -129,10 +131,14 @@ pub(crate) fn detect_llvm_sha(config: &crate::config::Config) -> String {
// the LLVM shared object file is named `LLVM-12-rust-{version}-nightly` // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly`
config.src.join("src/version"), config.src.join("src/version"),
]); ]);
let llvm_sha = output(&mut rev_list); output(&mut rev_list).trim().to_owned()
let llvm_sha = llvm_sha.trim(); } else if let Some(info) = channel::read_commit_info_file(&config.src) {
info.sha.trim().to_owned()
} else {
"".to_owned()
};
if llvm_sha == "" { if &llvm_sha == "" {
eprintln!("error: could not find commit hash for downloading LLVM"); eprintln!("error: could not find commit hash for downloading LLVM");
eprintln!("help: maybe your repository history is too shallow?"); eprintln!("help: maybe your repository history is too shallow?");
eprintln!("help: consider disabling `download-ci-llvm`"); eprintln!("help: consider disabling `download-ci-llvm`");
@ -140,7 +146,7 @@ pub(crate) fn detect_llvm_sha(config: &crate::config::Config) -> String {
panic!(); panic!();
} }
llvm_sha.to_owned() llvm_sha
} }
/// Returns whether the CI-found LLVM is currently usable. /// Returns whether the CI-found LLVM is currently usable.
@ -194,7 +200,9 @@ pub(crate) fn is_ci_llvm_available(config: &crate::config::Config, asserts: bool
} }
if crate::util::CiEnv::is_ci() { if crate::util::CiEnv::is_ci() {
let llvm_sha = detect_llvm_sha(config); // We assume we have access to git, so it's okay to unconditionally pass
// `true` here.
let llvm_sha = detect_llvm_sha(config, true);
let head_sha = output(config.git().arg("rev-parse").arg("HEAD")); let head_sha = output(config.git().arg("rev-parse").arg("HEAD"));
let head_sha = head_sha.trim(); let head_sha = head_sha.trim();
if llvm_sha == head_sha { if llvm_sha == head_sha {
@ -215,7 +223,7 @@ pub(crate) fn maybe_download_ci_llvm(builder: &Builder<'_>) {
} }
let llvm_root = config.ci_llvm_root(); let llvm_root = config.ci_llvm_root();
let llvm_stamp = llvm_root.join(".llvm-stamp"); let llvm_stamp = llvm_root.join(".llvm-stamp");
let llvm_sha = detect_llvm_sha(&config); let llvm_sha = detect_llvm_sha(&config, builder.rust_info.is_managed_git_subrepository());
let key = format!("{}{}", llvm_sha, config.llvm_assertions); let key = format!("{}{}", llvm_sha, config.llvm_assertions);
if program_out_of_date(&llvm_stamp, &key) && !config.dry_run { if program_out_of_date(&llvm_stamp, &key) && !config.dry_run {
download_ci_llvm(builder, &llvm_sha); download_ci_llvm(builder, &llvm_sha);
@ -260,7 +268,7 @@ fn download_ci_llvm(builder: &Builder<'_>, llvm_sha: &str) {
} else { } else {
&builder.config.stage0_metadata.config.artifacts_server &builder.config.stage0_metadata.config.artifacts_server
}; };
let channel = builder.config.artifact_channel(llvm_sha); let channel = builder.config.artifact_channel(builder, llvm_sha);
let filename = format!("rust-dev-{}-{}.tar.xz", channel, builder.build.build.triple); let filename = format!("rust-dev-{}-{}.tar.xz", channel, builder.build.build.triple);
let tarball = rustc_cache.join(&filename); let tarball = rustc_cache.join(&filename);
if !tarball.exists() { if !tarball.exists() {

View file

@ -74,7 +74,7 @@ pub fn check(build: &mut Build) {
let mut cmd_finder = Finder::new(); let mut cmd_finder = Finder::new();
// If we've got a git directory we're gonna need git to update // If we've got a git directory we're gonna need git to update
// submodules and learn about various other aspects. // submodules and learn about various other aspects.
if build.rust_info.is_git() { if build.rust_info.is_managed_git_subrepository() {
cmd_finder.must_have("git"); cmd_finder.must_have("git");
} }

View file

@ -4,6 +4,7 @@ use std::{
}; };
use crate::builder::Builder; use crate::builder::Builder;
use crate::channel;
use crate::util::t; use crate::util::t;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -297,8 +298,8 @@ impl<'a> Tarball<'a> {
fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> GeneratedTarball { fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> GeneratedTarball {
t!(std::fs::create_dir_all(&self.overlay_dir)); t!(std::fs::create_dir_all(&self.overlay_dir));
self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder)); self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder));
if let Some(sha) = self.builder.rust_sha() { if let Some(info) = self.builder.rust_info.info() {
self.builder.create(&self.overlay_dir.join("git-commit-hash"), &sha); channel::write_commit_info_file(&self.overlay_dir, info);
} }
for file in self.overlay.legal_and_readme() { for file in self.overlay.legal_and_readme() {
self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644); self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644);