summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-24 14:35:56 +0000
committerbors <bors@rust-lang.org>2020-09-24 14:35:56 +0000
commit07f65dac5fbf3ea413b985af6cf7fc9d50bd5246 (patch)
treec8377d4e22c55eff92196d029a814cbeaa12f68f
parentAuto merge of #77049 - lcnr:const-eval-function-signature, r=oli-obk (diff)
parentbuild-manifest: add documentation on the PkgType methods (diff)
downloadrust-nightly-dev.tar.gz
rust-nightly-dev.tar.bz2
rust-nightly-dev.tar.xz
Auto merge of #77145 - pietroalbini:refactor-build-manifest-versions, r=<try>nightly-dev
Refactor versions detection in build-manifest This PR refactors how `build-manifest` handles versions, making the following changes: * `build-manifest` now detects the "package releases" on its own, without relying on rustbuild providing them through CLI arguments. This drastically simplifies calling the tool outside of `x.py`, and will allow to ship the prebuilt tool in a tarball in the future, with the goal of stopping to invoke `x.py` during `promote-release`. * The `tar` command is not used to extract the version and the git hash from tarballs anymore. The `flate2` and `tar` crates are used instead. This makes detecting those pieces of data way faster, as the archive is decompressed just once and we stop parsing the archive once all the information is retrieved. * The code to extract the version and the git hash now stores all the collected data dynamically, without requiring to add new fields to the `Builder` struct every time. I tested the changes locally and it should behave the same as before. r? `@Mark-Simulacrum`
-rw-r--r--Cargo.lock3
-rw-r--r--src/bootstrap/dist.rs10
-rw-r--r--src/tools/build-manifest/Cargo.toml3
-rw-r--r--src/tools/build-manifest/README.md9
-rw-r--r--src/tools/build-manifest/src/main.rs233
-rw-r--r--src/tools/build-manifest/src/versions.rs248
6 files changed, 289 insertions, 217 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 26a9e64..277608a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -231,8 +231,11 @@ dependencies = [
231name = "build-manifest" 231name = "build-manifest"
232version = "0.1.0" 232version = "0.1.0"
233dependencies = [ 233dependencies = [
234 "anyhow",
235 "flate2",
234 "serde", 236 "serde",
235 "serde_json", 237 "serde_json",
238 "tar",
236 "toml", 239 "toml",
237] 240]
238 241
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index c119ae3..b9bc7d2 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -2368,15 +2368,9 @@ impl Step for HashSign {
2368 cmd.arg(sign); 2368 cmd.arg(sign);
2369 cmd.arg(distdir(builder)); 2369 cmd.arg(distdir(builder));
2370 cmd.arg(today.trim()); 2370 cmd.arg(today.trim());
2371 cmd.arg(builder.rust_package_vers());
2372 cmd.arg(addr); 2371 cmd.arg(addr);
2373 cmd.arg(builder.package_vers(&builder.release_num("cargo"))); 2372 cmd.arg(&builder.config.channel);
2374 cmd.arg(builder.package_vers(&builder.release_num("rls"))); 2373 cmd.arg(&builder.src);
2375 cmd.arg(builder.package_vers(&builder.release_num("rust-analyzer/crates/rust-analyzer")));
2376 cmd.arg(builder.package_vers(&builder.release_num("clippy")));
2377 cmd.arg(builder.package_vers(&builder.release_num("miri")));
2378 cmd.arg(builder.package_vers(&builder.release_num("rustfmt")));
2379 cmd.arg(builder.llvm_tools_package_vers());
2380 2374
2381 builder.create_dir(&distdir(builder)); 2375 builder.create_dir(&distdir(builder));
2382 2376
diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml
index 0bbbabd..4f89c31 100644
--- a/src/tools/build-manifest/Cargo.toml
+++ b/src/tools/build-manifest/Cargo.toml
@@ -8,3 +8,6 @@ edition = "2018"
8toml = "0.5" 8toml = "0.5"
9serde = { version = "1.0", features = ["derive"] } 9serde = { version = "1.0", features = ["derive"] }
10serde_json = "1.0" 10serde_json = "1.0"
11anyhow = "1.0.32"
12flate2 = "1.0.16"
13tar = "0.4.29"
diff --git a/src/tools/build-manifest/README.md b/src/tools/build-manifest/README.md
index a80f36d..4d7d9f7 100644
--- a/src/tools/build-manifest/README.md
+++ b/src/tools/build-manifest/README.md
@@ -21,10 +21,9 @@ Then, you can generate the manifest and all the packages from `path/to/dist` to
21 21
22``` 22```
23$ BUILD_MANIFEST_DISABLE_SIGNING=1 cargo +nightly run \ 23$ BUILD_MANIFEST_DISABLE_SIGNING=1 cargo +nightly run \
24 path/to/dist path/to/output 1970-01-01 \ 24 path/to/dist path/to/output 1970-01-01 http://example.com \
25 nightly nightly nightly nightly nightly nightly nightly nightly \ 25 CHANNEL path/to/rust/repo
26 http://example.com
27``` 26```
28 27
29In the future, if the tool complains about missing arguments just add more 28Remember to replace `CHANNEL` with the channel you produced dist artifacts of
30`nightly`s in the middle. 29and `path/to/rust/repo` with the path to your checkout of the Rust repository.
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index 125f2d1..be3e862 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -4,8 +4,10 @@
4//! via `x.py dist hash-and-sign`; the cmdline arguments are set up 4//! via `x.py dist hash-and-sign`; the cmdline arguments are set up
5//! by rustbuild (in `src/bootstrap/dist.rs`). 5//! by rustbuild (in `src/bootstrap/dist.rs`).
6 6
7use serde::Serialize; 7mod versions;
8 8
9use crate::versions::{PkgType, Versions};
10use serde::Serialize;
9use std::collections::BTreeMap; 11use std::collections::BTreeMap;
10use std::collections::HashMap; 12use std::collections::HashMap;
11use std::env; 13use std::env;
@@ -226,14 +228,7 @@ macro_rules! t {
226} 228}
227 229
228struct Builder { 230struct Builder {
229 rust_release: String, 231 versions: Versions,
230 cargo_release: String,
231 rls_release: String,
232 rust_analyzer_release: String,
233 clippy_release: String,
234 rustfmt_release: String,
235 llvm_tools_release: String,
236 miri_release: String,
237 232
238 input: PathBuf, 233 input: PathBuf,
239 output: PathBuf, 234 output: PathBuf,
@@ -242,24 +237,6 @@ struct Builder {
242 s3_address: String, 237 s3_address: String,
243 date: String, 238 date: String,
244 239
245 rust_version: Option<String>,
246 cargo_version: Option<String>,
247 rls_version: Option<String>,
248 rust_analyzer_version: Option<String>,
249 clippy_version: Option<String>,
250 rustfmt_version: Option<String>,
251 llvm_tools_version: Option<String>,
252 miri_version: Option<String>,
253
254 rust_git_commit_hash: Option<String>,
255 cargo_git_commit_hash: Option<String>,
256 rls_git_commit_hash: Option<String>,
257 rust_analyzer_git_commit_hash: Option<String>,
258 clippy_git_commit_hash: Option<String>,
259 rustfmt_git_commit_hash: Option<String>,
260 llvm_tools_git_commit_hash: Option<String>,
261 miri_git_commit_hash: Option<String>,
262
263 should_sign: bool, 240 should_sign: bool,
264} 241}
265 242
@@ -280,15 +257,9 @@ fn main() {
280 let input = PathBuf::from(args.next().unwrap()); 257 let input = PathBuf::from(args.next().unwrap());
281 let output = PathBuf::from(args.next().unwrap()); 258 let output = PathBuf::from(args.next().unwrap());
282 let date = args.next().unwrap(); 259 let date = args.next().unwrap();
283 let rust_release = args.next().unwrap();
284 let s3_address = args.next().unwrap(); 260 let s3_address = args.next().unwrap();
285 let cargo_release = args.next().unwrap(); 261 let channel = args.next().unwrap();
286 let rls_release = args.next().unwrap(); 262 let monorepo_path = args.next().unwrap();
287 let rust_analyzer_release = args.next().unwrap();
288 let clippy_release = args.next().unwrap();
289 let miri_release = args.next().unwrap();
290 let rustfmt_release = args.next().unwrap();
291 let llvm_tools_release = args.next().unwrap();
292 263
293 // Do not ask for a passphrase while manually testing 264 // Do not ask for a passphrase while manually testing
294 let mut passphrase = String::new(); 265 let mut passphrase = String::new();
@@ -298,14 +269,7 @@ fn main() {
298 } 269 }
299 270
300 Builder { 271 Builder {
301 rust_release, 272 versions: Versions::new(&channel, &input, Path::new(&monorepo_path)).unwrap(),
302 cargo_release,
303 rls_release,
304 rust_analyzer_release,
305 clippy_release,
306 rustfmt_release,
307 llvm_tools_release,
308 miri_release,
309 273
310 input, 274 input,
311 output, 275 output,
@@ -314,87 +278,21 @@ fn main() {
314 s3_address, 278 s3_address,
315 date, 279 date,
316 280
317 rust_version: None,
318 cargo_version: None,
319 rls_version: None,
320 rust_analyzer_version: None,
321 clippy_version: None,
322 rustfmt_version: None,
323 llvm_tools_version: None,
324 miri_version: None,
325
326 rust_git_commit_hash: None,
327 cargo_git_commit_hash: None,
328 rls_git_commit_hash: None,
329 rust_analyzer_git_commit_hash: None,
330 clippy_git_commit_hash: None,
331 rustfmt_git_commit_hash: None,
332 llvm_tools_git_commit_hash: None,
333 miri_git_commit_hash: None,
334
335 should_sign, 281 should_sign,
336 } 282 }
337 .build(); 283 .build();
338} 284}
339 285
340enum PkgType {
341 RustSrc,
342 Cargo,
343 Rls,
344 RustAnalyzer,
345 Clippy,
346 Rustfmt,
347 LlvmTools,
348 Miri,
349 Other,
350}
351
352impl PkgType {
353 fn from_component(component: &str) -> Self {
354 use PkgType::*;
355 match component {
356 "rust-src" => RustSrc,
357 "cargo" => Cargo,
358 "rls" | "rls-preview" => Rls,
359 "rust-analyzer" | "rust-analyzer-preview" => RustAnalyzer,
360 "clippy" | "clippy-preview" => Clippy,
361 "rustfmt" | "rustfmt-preview" => Rustfmt,
362 "llvm-tools" | "llvm-tools-preview" => LlvmTools,
363 "miri" | "miri-preview" => Miri,
364 _ => Other,
365 }
366 }
367}
368
369impl Builder { 286impl Builder {
370 fn build(&mut self) { 287 fn build(&mut self) {
371 self.rust_version = self.version("rust", "x86_64-unknown-linux-gnu");
372 self.cargo_version = self.version("cargo", "x86_64-unknown-linux-gnu");
373 self.rls_version = self.version("rls", "x86_64-unknown-linux-gnu");
374 self.rust_analyzer_version = self.version("rust-analyzer", "x86_64-unknown-linux-gnu");
375 self.clippy_version = self.version("clippy", "x86_64-unknown-linux-gnu");
376 self.rustfmt_version = self.version("rustfmt", "x86_64-unknown-linux-gnu");
377 self.llvm_tools_version = self.version("llvm-tools", "x86_64-unknown-linux-gnu");
378 self.miri_version = self.version("miri", "x86_64-unknown-linux-gnu");
379
380 self.rust_git_commit_hash = self.git_commit_hash("rust", "x86_64-unknown-linux-gnu");
381 self.cargo_git_commit_hash = self.git_commit_hash("cargo", "x86_64-unknown-linux-gnu");
382 self.rls_git_commit_hash = self.git_commit_hash("rls", "x86_64-unknown-linux-gnu");
383 self.rust_analyzer_git_commit_hash =
384 self.git_commit_hash("rust-analyzer", "x86_64-unknown-linux-gnu");
385 self.clippy_git_commit_hash = self.git_commit_hash("clippy", "x86_64-unknown-linux-gnu");
386 self.rustfmt_git_commit_hash = self.git_commit_hash("rustfmt", "x86_64-unknown-linux-gnu");
387 self.llvm_tools_git_commit_hash =
388 self.git_commit_hash("llvm-tools", "x86_64-unknown-linux-gnu");
389 self.miri_git_commit_hash = self.git_commit_hash("miri", "x86_64-unknown-linux-gnu");
390
391 self.check_toolstate(); 288 self.check_toolstate();
392 self.digest_and_sign(); 289 self.digest_and_sign();
393 let manifest = self.build_manifest(); 290 let manifest = self.build_manifest();
394 self.write_channel_files(&self.rust_release, &manifest);
395 291
396 if self.rust_release != "beta" && self.rust_release != "nightly" { 292 let rust_version = self.versions.package_version(&PkgType::Rust).unwrap();
397 self.write_channel_files("stable", &manifest); 293 self.write_channel_files(self.versions.channel(), &manifest);
294 if self.versions.channel() != rust_version {
295 self.write_channel_files(&rust_version, &manifest);
398 } 296 }
399 } 297 }
400 298
@@ -415,8 +313,7 @@ impl Builder {
415 // Mark some tools as missing based on toolstate. 313 // Mark some tools as missing based on toolstate.
416 if toolstates.get("miri").map(|s| &*s as &str) != Some("test-pass") { 314 if toolstates.get("miri").map(|s| &*s as &str) != Some("test-pass") {
417 println!("Miri tests are not passing, removing component"); 315 println!("Miri tests are not passing, removing component");
418 self.miri_version = None; 316 self.versions.disable_version(&PkgType::Miri);
419 self.miri_git_commit_hash = None;
420 } 317 }
421 } 318 }
422 319
@@ -501,7 +398,7 @@ impl Builder {
501 // The compiler libraries are not stable for end users, and they're also huge, so we only 398 // The compiler libraries are not stable for end users, and they're also huge, so we only
502 // `rustc-dev` for nightly users, and only in the "complete" profile. It's still possible 399 // `rustc-dev` for nightly users, and only in the "complete" profile. It's still possible
503 // for users to install the additional component manually, if needed. 400 // for users to install the additional component manually, if needed.
504 if self.rust_release == "nightly" { 401 if self.versions.channel() == "nightly" {
505 self.extend_profile("complete", &mut manifest.profiles, &["rustc-dev"]); 402 self.extend_profile("complete", &mut manifest.profiles, &["rustc-dev"]);
506 self.extend_profile("complete", &mut manifest.profiles, &["rustc-docs"]); 403 self.extend_profile("complete", &mut manifest.profiles, &["rustc-docs"]);
507 } 404 }
@@ -518,13 +415,10 @@ impl Builder {
518 } 415 }
519 416
520 fn rust_package(&mut self, manifest: &Manifest) -> Package { 417 fn rust_package(&mut self, manifest: &Manifest) -> Package {
418 let version_info = self.versions.version(&PkgType::Rust).expect("missing Rust tarball");
521 let mut pkg = Package { 419 let mut pkg = Package {
522 version: self 420 version: version_info.version.expect("missing Rust version"),
523 .cached_version("rust") 421 git_commit_hash: version_info.git_commit,
524 .as_ref()
525 .expect("Couldn't find Rust version")
526 .clone(),
527 git_commit_hash: self.cached_git_commit_hash("rust").clone(),
528 target: BTreeMap::new(), 422 target: BTreeMap::new(),
529 }; 423 };
530 for host in HOSTS { 424 for host in HOSTS {
@@ -539,7 +433,7 @@ impl Builder {
539 } 433 }
540 434
541 fn target_host_combination(&mut self, host: &str, manifest: &Manifest) -> Option<Target> { 435 fn target_host_combination(&mut self, host: &str, manifest: &Manifest) -> Option<Target> {
542 let filename = self.filename("rust", host); 436 let filename = self.versions.tarball_name(&PkgType::Rust, host).unwrap();
543 let digest = self.digests.remove(&filename)?; 437 let digest = self.digests.remove(&filename)?;
544 let xz_filename = filename.replace(".tar.gz", ".tar.xz"); 438 let xz_filename = filename.replace(".tar.gz", ".tar.xz");
545 let xz_digest = self.digests.remove(&xz_filename); 439 let xz_digest = self.digests.remove(&xz_filename);
@@ -630,15 +524,14 @@ impl Builder {
630 } 524 }
631 525
632 fn package(&mut self, pkgname: &str, dst: &mut BTreeMap<String, Package>, targets: &[&str]) { 526 fn package(&mut self, pkgname: &str, dst: &mut BTreeMap<String, Package>, targets: &[&str]) {
633 let (version, mut is_present) = self 527 let version_info = self
634 .cached_version(pkgname) 528 .versions
635 .as_ref() 529 .version(&PkgType::from_component(pkgname))
636 .cloned() 530 .expect("failed to load package version");
637 .map(|version| (version, true)) 531 let mut is_present = version_info.present;
638 .unwrap_or_default(); // `is_present` defaults to `false` here.
639 532
640 // Never ship nightly-only components for other trains. 533 // Never ship nightly-only components for other trains.
641 if self.rust_release != "nightly" && NIGHTLY_ONLY_COMPONENTS.contains(&pkgname) { 534 if self.versions.channel() != "nightly" && NIGHTLY_ONLY_COMPONENTS.contains(&pkgname) {
642 is_present = false; // Pretend the component is entirely missing. 535 is_present = false; // Pretend the component is entirely missing.
643 } 536 }
644 537
@@ -647,7 +540,10 @@ impl Builder {
647 .map(|name| { 540 .map(|name| {
648 if is_present { 541 if is_present {
649 // The component generally exists, but it might still be missing for this target. 542 // The component generally exists, but it might still be missing for this target.
650 let filename = self.filename(pkgname, name); 543 let filename = self
544 .versions
545 .tarball_name(&PkgType::from_component(pkgname), name)
546 .unwrap();
651 let digest = match self.digests.remove(&filename) { 547 let digest = match self.digests.remove(&filename) {
652 Some(digest) => digest, 548 Some(digest) => digest,
653 // This component does not exist for this target -- skip it. 549 // This component does not exist for this target -- skip it.
@@ -679,8 +575,8 @@ impl Builder {
679 dst.insert( 575 dst.insert(
680 pkgname.to_string(), 576 pkgname.to_string(),
681 Package { 577 Package {
682 version, 578 version: version_info.version.unwrap_or_default(),
683 git_commit_hash: self.cached_git_commit_hash(pkgname).clone(), 579 git_commit_hash: version_info.git_commit,
684 target: targets, 580 target: targets,
685 }, 581 },
686 ); 582 );
@@ -690,77 +586,6 @@ impl Builder {
690 format!("{}/{}/{}", self.s3_address, self.date, filename) 586 format!("{}/{}/{}", self.s3_address, self.date, filename)
691 } 587 }
692 588
693 fn filename(&self, component: &str, target: &str) -> String {
694 use PkgType::*;
695 match PkgType::from_component(component) {
696 RustSrc => format!("rust-src-{}.tar.gz", self.rust_release),
697 Cargo => format!("cargo-{}-{}.tar.gz", self.cargo_release, target),
698 Rls => format!("rls-{}-{}.tar.gz", self.rls_release, target),
699 RustAnalyzer => {
700 format!("rust-analyzer-{}-{}.tar.gz", self.rust_analyzer_release, target)
701 }
702 Clippy => format!("clippy-{}-{}.tar.gz", self.clippy_release, target),
703 Rustfmt => format!("rustfmt-{}-{}.tar.gz", self.rustfmt_release, target),
704 LlvmTools => format!("llvm-tools-{}-{}.tar.gz", self.llvm_tools_release, target),
705 Miri => format!("miri-{}-{}.tar.gz", self.miri_release, target),
706 Other => format!("{}-{}-{}.tar.gz", component, self.rust_release, target),
707 }
708 }
709
710 fn cached_version(&self, component: &str) -> &Option<String> {
711 use PkgType::*;
712 match PkgType::from_component(component) {
713 Cargo => &self.cargo_version,
714 Rls => &self.rls_version,
715 RustAnalyzer => &self.rust_analyzer_version,
716 Clippy => &self.clippy_version,
717 Rustfmt => &self.rustfmt_version,
718 LlvmTools => &self.llvm_tools_version,
719 Miri => &self.miri_version,
720 _ => &self.rust_version,
721 }
722 }
723
724 fn cached_git_commit_hash(&self, component: &str) -> &Option<String> {
725 use PkgType::*;
726 match PkgType::from_component(component) {
727 Cargo => &self.cargo_git_commit_hash,
728 Rls => &self.rls_git_commit_hash,
729 RustAnalyzer => &self.rust_analyzer_git_commit_hash,
730 Clippy => &self.clippy_git_commit_hash,
731 Rustfmt => &self.rustfmt_git_commit_hash,
732 LlvmTools => &self.llvm_tools_git_commit_hash,
733 Miri => &self.miri_git_commit_hash,
734 _ => &self.rust_git_commit_hash,
735 }
736 }
737
738 fn version(&self, component: &str, target: &str) -> Option<String> {
739 self.untar(component, target, |filename| format!("{}/version", filename))
740 }
741
742 fn git_commit_hash(&self, component: &str, target: &str) -> Option<String> {
743 self.untar(component, target, |filename| format!("{}/git-commit-hash", filename))
744 }
745
746 fn untar<F>(&self, component: &str, target: &str, dir: F) -> Option<String>
747 where
748 F: FnOnce(String) -> String,
749 {
750 let mut cmd = Command::new("tar");
751 let filename = self.filename(component, target);
752 cmd.arg("xf")
753 .arg(self.input.join(&filename))
754 .arg(dir(filename.replace(".tar.gz", "")))
755 .arg("-O");
756 let output = t!(cmd.output());
757 if output.status.success() {
758 Some(String::from_utf8_lossy(&output.stdout).trim().to_string())
759 } else {
760 None
761 }
762 }
763
764 fn hash(&self, path: &Path) -> String { 589 fn hash(&self, path: &Path) -> String {
765 let sha = t!(Command::new("shasum") 590 let sha = t!(Command::new("shasum")
766 .arg("-a") 591 .arg("-a")
diff --git a/src/tools/build-manifest/src/versions.rs b/src/tools/build-manifest/src/versions.rs
new file mode 100644
index 0000000..0721e6a
--- /dev/null
+++ b/src/tools/build-manifest/src/versions.rs
@@ -0,0 +1,248 @@
1use anyhow::{Context, Error};
2use flate2::read::GzDecoder;
3use std::collections::HashMap;
4use std::fs::File;
5use std::io::Read;
6use std::path::{Path, PathBuf};
7use tar::Archive;
8
9const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu";
10
11#[derive(Debug, Hash, Eq, PartialEq, Clone)]
12pub(crate) enum PkgType {
13 Rust,
14 RustSrc,
15 Cargo,
16 Rls,
17 RustAnalyzer,
18 Clippy,
19 Rustfmt,
20 LlvmTools,
21 Miri,
22 Other(String),
23}
24
25impl PkgType {
26 pub(crate) fn from_component(component: &str) -> Self {
27 match component {
28 "rust" => PkgType::Rust,
29 "rust-src" => PkgType::RustSrc,
30 "cargo" => PkgType::Cargo,
31 "rls" | "rls-preview" => PkgType::Rls,
32 "rust-analyzer" | "rust-analyzer-preview" => PkgType::RustAnalyzer,
33 "clippy" | "clippy-preview" => PkgType::Clippy,
34 "rustfmt" | "rustfmt-preview" => PkgType::Rustfmt,
35 "llvm-tools" | "llvm-tools-preview" => PkgType::LlvmTools,
36 "miri" | "miri-preview" => PkgType::Miri,
37 other => PkgType::Other(other.into()),
38 }
39 }
40
41 /// The directory containing the `Cargo.toml` of this component inside the monorepo, to
42 /// retrieve the source code version. If `None` is returned Rust's version will be used.
43 fn rust_monorepo_path(&self) -> Option<&'static str> {
44 match self {
45 PkgType::Cargo => Some("src/tools/cargo"),
46 PkgType::Rls => Some("src/tools/rls"),
47 PkgType::RustAnalyzer => Some("src/tools/rust-analyzer/crates/rust-analyzer"),
48 PkgType::Clippy => Some("src/tools/clippy"),
49 PkgType::Rustfmt => Some("src/tools/rustfmt"),
50 PkgType::Miri => Some("src/tools/miri"),
51 PkgType::Rust => None,
52 PkgType::RustSrc => None,
53 PkgType::LlvmTools => None,
54 PkgType::Other(_) => None,
55 }
56 }
57
58 /// First part of the tarball name.
59 fn tarball_component_name(&self) -> &str {
60 match self {
61 PkgType::Rust => "rust",
62 PkgType::RustSrc => "rust-src",
63 PkgType::Cargo => "cargo",
64 PkgType::Rls => "rls",
65 PkgType::RustAnalyzer => "rust-analyzer",
66 PkgType::Clippy => "clippy",
67 PkgType::Rustfmt => "rustfmt",
68 PkgType::LlvmTools => "llvm-tools",
69 PkgType::Miri => "miri",
70 PkgType::Other(component) => component,
71 }
72 }
73
74 /// Whether this package has the same version as Rust itself, or has its own `version` and
75 /// `git-commit-hash` files inside the tarball.
76 fn should_use_rust_version(&self) -> bool {
77 match self {
78 PkgType::Cargo => false,
79 PkgType::Rls => false,
80 PkgType::RustAnalyzer => false,
81 PkgType::Clippy => false,
82 PkgType::Rustfmt => false,
83 PkgType::LlvmTools => false,
84 PkgType::Miri => false,
85
86 PkgType::Rust => true,
87 PkgType::RustSrc => true,
88 PkgType::Other(_) => true,
89 }
90 }
91}
92
93#[derive(Debug, Default, Clone)]
94pub(crate) struct VersionInfo {
95 pub(crate) version: Option<String>,
96 pub(crate) git_commit: Option<String>,
97 pub(crate) present: bool,
98}
99
100pub(crate) struct Versions {
101 channel: String,
102 rustc_version: String,
103 monorepo_root: PathBuf,
104 dist_path: PathBuf,
105 package_versions: HashMap<PkgType, String>,
106 versions: HashMap<PkgType, VersionInfo>,
107}
108
109impl Versions {
110 pub(crate) fn new(
111 channel: &str,
112 dist_path: &Path,
113 monorepo_root: &Path,
114 ) -> Result<Self, Error> {
115 Ok(Self {
116 channel: channel.into(),
117 rustc_version: std::fs::read_to_string(monorepo_root.join("src").join("version"))
118 .context("failed to read the rustc version from src/version")?
119 .trim()
120 .to_string(),
121 monorepo_root: monorepo_root.into(),
122 dist_path: dist_path.into(),
123 package_versions: HashMap::new(),
124 versions: HashMap::new(),
125 })
126 }
127
128 pub(crate) fn channel(&self) -> &str {
129 &self.channel
130 }
131
132 pub(crate) fn version(&mut self, mut package: &PkgType) -> Result<VersionInfo, Error> {
133 if package.should_use_rust_version() {
134 package = &PkgType::Rust;
135 }
136
137 match self.versions.get(package) {
138 Some(version) => Ok(version.clone()),
139 None => {
140 let version_info = self.load_version_from_tarball(package)?;
141 self.versions.insert(package.clone(), version_info.clone());
142 println!("{:?} => {:?}", package, &version_info);
143 Ok(version_info)
144 }
145 }
146 }
147
148 fn load_version_from_tarball(&mut self, package: &PkgType) -> Result<VersionInfo, Error> {
149 let tarball_name = self.tarball_name(package, DEFAULT_TARGET)?;
150 let tarball = self.dist_path.join(tarball_name);
151
152 let file = match File::open(&tarball) {
153 Ok(file) => file,
154 Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
155 // Missing tarballs do not return an error, but return empty data.
156 return Ok(VersionInfo::default());
157 }
158 Err(err) => return Err(err.into()),
159 };
160 let mut tar = Archive::new(GzDecoder::new(file));
161
162 let mut version = None;
163 let mut git_commit = None;
164 for entry in tar.entries()? {
165 let mut entry = entry?;
166
167 let dest;
168 match entry.path()?.components().nth(1).and_then(|c| c.as_os_str().to_str()) {
169 Some("version") => dest = &mut version,
170 Some("git-commit-hash") => dest = &mut git_commit,
171 _ => continue,
172 }
173 let mut buf = String::new();
174 entry.read_to_string(&mut buf)?;
175 *dest = Some(buf);
176
177 // Short circuit to avoid reading the whole tar file if not necessary.
178 if version.is_some() && git_commit.is_some() {
179 break;
180 }
181 }
182
183 Ok(VersionInfo { version, git_commit, present: true })
184 }
185
186 pub(crate) fn disable_version(&mut self, package: &PkgType) {
187 match self.versions.get_mut(package) {
188 Some(version) => {
189 *version = VersionInfo::default();
190 }
191 None => {
192 self.versions.insert(package.clone(), VersionInfo::default());
193 }
194 }
195 }
196
197 pub(crate) fn tarball_name(
198 &mut self,
199 package: &PkgType,
200 target: &str,
201 ) -> Result<String, Error> {
202 Ok(format!(
203 "{}-{}-{}.tar.gz",
204 package.tarball_component_name(),
205 self.package_version(package).with_context(|| format!(
206 "failed to get the package version for component {:?}",
207 package,
208 ))?,
209 target
210 ))
211 }
212
213 pub(crate) fn package_version(&mut self, package: &PkgType) -> Result<String, Error> {
214 match self.package_versions.get(package) {
215 Some(release) => Ok(release.clone()),
216 None => {
217 let version = match package.rust_monorepo_path() {
218 Some(path) => {
219 let path = self.monorepo_root.join(path).join("Cargo.toml");
220 let cargo_toml: CargoToml = toml::from_slice(&std::fs::read(path)?)?;
221 cargo_toml.package.version
222 }
223 None => self.rustc_version.clone(),
224 };
225
226 let release = match self.channel.as_str() {
227 "stable" => version,
228 "beta" => "beta".into(),
229 "nightly" => "nightly".into(),
230 _ => format!("{}-dev", version),
231 };
232
233 self.package_versions.insert(package.clone(), release.clone());
234 Ok(release)
235 }
236 }
237 }
238}
239
240#[derive(serde::Deserialize)]
241struct CargoToml {
242 package: CargoTomlPackage,
243}
244
245#[derive(serde::Deserialize)]
246struct CargoTomlPackage {
247 version: String,
248}