Skip to content

Commit fdb3955

Browse files
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.
1 parent 17a627f commit fdb3955

File tree

8 files changed

+127
-47
lines changed

8 files changed

+127
-47
lines changed

config.toml.example

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

526532
# The root location of the musl installation directory. The library directory

src/bootstrap/channel.rs

+55-8
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
//! `package_vers`, and otherwise indicating to the compiler what it should
66
//! print out as part of its version information.
77
8+
use std::fs;
89
use std::path::Path;
910
use std::process::Command;
1011

1112
use crate::util::output;
13+
use crate::util::t;
1214
use crate::Build;
1315

1416
pub enum GitInfo {
@@ -18,19 +20,25 @@ pub enum GitInfo {
1820
/// If the info should be used (`ignore_git` is false), this will be
1921
/// `Some`, otherwise it will be `None`.
2022
Present(Option<Info>),
23+
/// This is not a git repostory, but the info can be fetched from the
24+
/// `git-commit-info` file.
25+
RecordedForTarball(Info),
2126
}
2227

2328
pub struct Info {
24-
commit_date: String,
25-
sha: String,
26-
short_sha: String,
29+
pub commit_date: String,
30+
pub sha: String,
31+
pub short_sha: String,
2732
}
2833

2934
impl GitInfo {
3035
pub fn new(ignore_git: bool, dir: &Path) -> GitInfo {
3136
// See if this even begins to look like a git dir
3237
if !dir.join(".git").exists() {
33-
return GitInfo::Absent;
38+
match read_commit_info_file(dir) {
39+
Some(info) => return GitInfo::RecordedForTarball(info),
40+
None => return GitInfo::Absent,
41+
}
3442
}
3543

3644
// Make sure git commands work
@@ -65,10 +73,11 @@ impl GitInfo {
6573
}))
6674
}
6775

68-
fn info(&self) -> Option<&Info> {
76+
pub fn info(&self) -> Option<&Info> {
6977
match self {
70-
GitInfo::Present(info) => info.as_ref(),
7178
GitInfo::Absent => None,
79+
GitInfo::Present(info) => info.as_ref(),
80+
GitInfo::RecordedForTarball(info) => Some(info),
7281
}
7382
}
7483

@@ -96,10 +105,48 @@ impl GitInfo {
96105
version
97106
}
98107

99-
pub fn is_git(&self) -> bool {
108+
/// Returns whether this directory has a `.git` directory which should be managed by bootstrap.
109+
pub fn is_managed_git_subrepository(&self) -> bool {
100110
match self {
101-
GitInfo::Absent => false,
111+
GitInfo::Absent | GitInfo::RecordedForTarball(_) => false,
102112
GitInfo::Present(_) => true,
103113
}
104114
}
115+
116+
/// Returns whether this is being built from a tarball.
117+
pub fn is_from_tarball(&self) -> bool {
118+
match self {
119+
GitInfo::Absent | GitInfo::Present(_) => false,
120+
GitInfo::RecordedForTarball(_) => true,
121+
}
122+
}
123+
}
124+
125+
/// Read the commit information from the `git-commit-info` file given the
126+
/// project root.
127+
pub fn read_commit_info_file(root: &Path) -> Option<Info> {
128+
if let Ok(contents) = fs::read_to_string(root.join("git-commit-info")) {
129+
let mut lines = contents.lines();
130+
let sha = lines.next();
131+
let short_sha = lines.next();
132+
let commit_date = lines.next();
133+
let info = match (commit_date, sha, short_sha) {
134+
(Some(commit_date), Some(sha), Some(short_sha)) => Info {
135+
commit_date: commit_date.to_owned(),
136+
sha: sha.to_owned(),
137+
short_sha: short_sha.to_owned(),
138+
},
139+
_ => panic!("the `git-comit-info` file is malformed"),
140+
};
141+
Some(info)
142+
} else {
143+
None
144+
}
145+
}
146+
147+
/// Write the commit information to the `git-commit-info` file given the project
148+
/// root.
149+
pub fn write_commit_info_file(root: &Path, info: &Info) {
150+
let commit_info = format!("{}\n{}\n{}\n", info.sha, info.short_sha, info.commit_date);
151+
t!(fs::write(root.join("git-commit-info"), &commit_info));
105152
}

src/bootstrap/config.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -1280,11 +1280,22 @@ impl Config {
12801280
git
12811281
}
12821282

1283-
pub(crate) fn artifact_channel(&self, commit: &str) -> String {
1284-
let mut channel = self.git();
1285-
channel.arg("show").arg(format!("{}:src/ci/channel", commit));
1286-
let channel = output(&mut channel);
1287-
channel.trim().to_owned()
1283+
pub(crate) fn artifact_channel(&self, builder: &Builder<'_>, commit: &str) -> String {
1284+
if builder.rust_info.is_managed_git_subrepository() {
1285+
let mut channel = self.git();
1286+
channel.arg("show").arg(format!("{}:src/ci/channel", commit));
1287+
let channel = output(&mut channel);
1288+
channel.trim().to_owned()
1289+
} else if let Ok(channel) = fs::read_to_string(builder.src.join("src/ci/channel")) {
1290+
channel.trim().to_owned()
1291+
} else {
1292+
let src = builder.src.display();
1293+
eprintln!("error: failed to determine artifact channel");
1294+
eprintln!(
1295+
"help: either use git or ensure that {src}/src/ci/channel contains the name of the channel to use"
1296+
);
1297+
panic!();
1298+
}
12881299
}
12891300

12901301
/// Try to find the relative path of `bindir`, otherwise return it in full.
@@ -1421,7 +1432,7 @@ impl Config {
14211432
}
14221433

14231434
pub fn submodules(&self, rust_info: &GitInfo) -> bool {
1424-
self.submodules.unwrap_or(rust_info.is_git())
1435+
self.submodules.unwrap_or(rust_info.is_managed_git_subrepository())
14251436
}
14261437
}
14271438

@@ -1526,7 +1537,7 @@ fn maybe_download_rustfmt(builder: &Builder<'_>) -> Option<PathBuf> {
15261537

15271538
fn download_ci_rustc(builder: &Builder<'_>, commit: &str) {
15281539
builder.verbose(&format!("using downloaded stage2 artifacts from CI (commit {commit})"));
1529-
let channel = builder.config.artifact_channel(commit);
1540+
let channel = builder.config.artifact_channel(builder, commit);
15301541
let host = builder.config.build.triple;
15311542
let bin_root = builder.out.join(host).join("ci-rustc");
15321543
let rustc_stamp = bin_root.join(".rustc-stamp");

src/bootstrap/dist.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::process::Command;
1616

1717
use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
1818
use crate::cache::{Interned, INTERNER};
19+
use crate::channel;
1920
use crate::compile;
2021
use crate::config::TargetSelection;
2122
use crate::tarball::{GeneratedTarball, OverlayKind, Tarball};
@@ -897,12 +898,12 @@ impl Step for PlainSourceTarball {
897898

898899
// Create the version file
899900
builder.create(&plain_dst_src.join("version"), &builder.rust_version());
900-
if let Some(sha) = builder.rust_sha() {
901-
builder.create(&plain_dst_src.join("git-commit-hash"), &sha);
901+
if let Some(info) = builder.rust_info.info() {
902+
channel::write_commit_info_file(&plain_dst_src, info);
902903
}
903904

904905
// If we're building from git sources, we need to vendor a complete distribution.
905-
if builder.rust_info.is_git() {
906+
if builder.rust_info.is_managed_git_subrepository() {
906907
// Ensure we have the submodules checked out.
907908
builder.update_submodule(Path::new("src/tools/rust-analyzer"));
908909

src/bootstrap/lib.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ impl Build {
395395
/// line and the filesystem `config`.
396396
///
397397
/// By default all build output will be placed in the current directory.
398-
pub fn new(config: Config) -> Build {
398+
pub fn new(mut config: Config) -> Build {
399399
let src = config.src.clone();
400400
let out = config.out.clone();
401401

@@ -470,6 +470,10 @@ impl Build {
470470
bootstrap_out
471471
};
472472

473+
if rust_info.is_from_tarball() && config.description.is_none() {
474+
config.description = Some("built from a source tarball".to_owned());
475+
}
476+
473477
let mut build = Build {
474478
initial_rustc: config.initial_rustc.clone(),
475479
initial_cargo: config.initial_cargo.clone(),
@@ -574,7 +578,9 @@ impl Build {
574578

575579
// NOTE: The check for the empty directory is here because when running x.py the first time,
576580
// the submodule won't be checked out. Check it out now so we can build it.
577-
if !channel::GitInfo::new(false, &absolute_path).is_git() && !dir_is_empty(&absolute_path) {
581+
if !channel::GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
582+
&& !dir_is_empty(&absolute_path)
583+
{
578584
return;
579585
}
580586

@@ -645,7 +651,7 @@ impl Build {
645651
// Sample output: `submodule.src/rust-installer.path src/tools/rust-installer`
646652
let submodule = Path::new(line.splitn(2, ' ').nth(1).unwrap());
647653
// Don't update the submodule unless it's already been cloned.
648-
if channel::GitInfo::new(false, submodule).is_git() {
654+
if channel::GitInfo::new(false, submodule).is_managed_git_subrepository() {
649655
self.update_submodule(submodule);
650656
}
651657
}
@@ -1253,7 +1259,7 @@ impl Build {
12531259
match &self.config.channel[..] {
12541260
"stable" => num.to_string(),
12551261
"beta" => {
1256-
if self.rust_info.is_git() && !self.config.ignore_git {
1262+
if self.rust_info.is_managed_git_subrepository() && !self.config.ignore_git {
12571263
format!("{}-beta.{}", num, self.beta_prerelease_version())
12581264
} else {
12591265
format!("{}-beta", num)

src/bootstrap/native.rs

+30-22
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::path::{Path, PathBuf};
1717
use std::process::Command;
1818

1919
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
20+
use crate::channel;
2021
use crate::config::TargetSelection;
2122
use crate::util::get_clang_cl_resource_dir;
2223
use crate::util::{self, exe, output, program_out_of_date, t, up_to_date};
@@ -115,32 +116,37 @@ pub fn prebuilt_llvm_config(
115116
}
116117

117118
/// This retrieves the LLVM sha we *want* to use, according to git history.
118-
pub(crate) fn detect_llvm_sha(config: &crate::config::Config) -> String {
119-
let mut rev_list = config.git();
120-
rev_list.args(&[
121-
PathBuf::from("rev-list"),
122-
format!("--author={}", config.stage0_metadata.config.git_merge_commit_email).into(),
123-
"-n1".into(),
124-
"--first-parent".into(),
125-
"HEAD".into(),
126-
"--".into(),
127-
config.src.join("src/llvm-project"),
128-
config.src.join("src/bootstrap/download-ci-llvm-stamp"),
129-
// the LLVM shared object file is named `LLVM-12-rust-{version}-nightly`
130-
config.src.join("src/version"),
131-
]);
132-
let llvm_sha = output(&mut rev_list);
133-
let llvm_sha = llvm_sha.trim();
134-
135-
if llvm_sha == "" {
119+
pub(crate) fn detect_llvm_sha(config: &crate::config::Config, is_git: bool) -> String {
120+
let llvm_sha = if is_git {
121+
let mut rev_list = config.git();
122+
rev_list.args(&[
123+
PathBuf::from("rev-list"),
124+
format!("--author={}", config.stage0_metadata.config.git_merge_commit_email).into(),
125+
"-n1".into(),
126+
"--first-parent".into(),
127+
"HEAD".into(),
128+
"--".into(),
129+
config.src.join("src/llvm-project"),
130+
config.src.join("src/bootstrap/download-ci-llvm-stamp"),
131+
// the LLVM shared object file is named `LLVM-12-rust-{version}-nightly`
132+
config.src.join("src/version"),
133+
]);
134+
output(&mut rev_list).trim().to_owned()
135+
} else if let Some(info) = channel::read_commit_info_file(&config.src) {
136+
info.sha.trim().to_owned()
137+
} else {
138+
"".to_owned()
139+
};
140+
141+
if &llvm_sha == "" {
136142
eprintln!("error: could not find commit hash for downloading LLVM");
137143
eprintln!("help: maybe your repository history is too shallow?");
138144
eprintln!("help: consider disabling `download-ci-llvm`");
139145
eprintln!("help: or fetch enough history to include one upstream commit");
140146
panic!();
141147
}
142148

143-
llvm_sha.to_owned()
149+
llvm_sha
144150
}
145151

146152
/// 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
194200
}
195201

196202
if crate::util::CiEnv::is_ci() {
197-
let llvm_sha = detect_llvm_sha(config);
203+
// We assume we have access to git, so it's okay to unconditionally pass
204+
// `true` here.
205+
let llvm_sha = detect_llvm_sha(config, true);
198206
let head_sha = output(config.git().arg("rev-parse").arg("HEAD"));
199207
let head_sha = head_sha.trim();
200208
if llvm_sha == head_sha {
@@ -215,7 +223,7 @@ pub(crate) fn maybe_download_ci_llvm(builder: &Builder<'_>) {
215223
}
216224
let llvm_root = config.ci_llvm_root();
217225
let llvm_stamp = llvm_root.join(".llvm-stamp");
218-
let llvm_sha = detect_llvm_sha(&config);
226+
let llvm_sha = detect_llvm_sha(&config, builder.rust_info.is_managed_git_subrepository());
219227
let key = format!("{}{}", llvm_sha, config.llvm_assertions);
220228
if program_out_of_date(&llvm_stamp, &key) && !config.dry_run {
221229
download_ci_llvm(builder, &llvm_sha);
@@ -260,7 +268,7 @@ fn download_ci_llvm(builder: &Builder<'_>, llvm_sha: &str) {
260268
} else {
261269
&builder.config.stage0_metadata.config.artifacts_server
262270
};
263-
let channel = builder.config.artifact_channel(llvm_sha);
271+
let channel = builder.config.artifact_channel(builder, llvm_sha);
264272
let filename = format!("rust-dev-{}-{}.tar.xz", channel, builder.build.build.triple);
265273
let tarball = rustc_cache.join(&filename);
266274
if !tarball.exists() {

src/bootstrap/sanity.rs

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

src/bootstrap/tarball.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::{
44
};
55

66
use crate::builder::Builder;
7+
use crate::channel;
78
use crate::util::t;
89

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

0 commit comments

Comments
 (0)