]> git.proxmox.com Git - rustc.git/blob - src/tools/build-manifest/src/versions.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / src / tools / build-manifest / src / versions.rs
1 use anyhow::Error;
2 use flate2::read::GzDecoder;
3 use std::collections::HashMap;
4 use std::fs::File;
5 use std::io::Read;
6 use std::path::{Path, PathBuf};
7 use tar::Archive;
8
9 const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu";
10 const RUSTC_VERSION: &str = include_str!("../../../version");
11
12 #[derive(Debug, Hash, Eq, PartialEq, Clone)]
13 pub(crate) enum PkgType {
14 Rust,
15 RustSrc,
16 Cargo,
17 Rls,
18 RustAnalyzer,
19 Clippy,
20 Rustfmt,
21 LlvmTools,
22 Miri,
23 Other(String),
24 }
25
26 impl PkgType {
27 pub(crate) fn from_component(component: &str) -> Self {
28 match component {
29 "rust" => PkgType::Rust,
30 "rust-src" => PkgType::RustSrc,
31 "cargo" => PkgType::Cargo,
32 "rls" | "rls-preview" => PkgType::Rls,
33 "rust-analyzer" | "rust-analyzer-preview" => PkgType::RustAnalyzer,
34 "clippy" | "clippy-preview" => PkgType::Clippy,
35 "rustfmt" | "rustfmt-preview" => PkgType::Rustfmt,
36 "llvm-tools" | "llvm-tools-preview" => PkgType::LlvmTools,
37 "miri" | "miri-preview" => PkgType::Miri,
38 other => PkgType::Other(other.into()),
39 }
40 }
41
42 /// First part of the tarball name.
43 fn tarball_component_name(&self) -> &str {
44 match self {
45 PkgType::Rust => "rust",
46 PkgType::RustSrc => "rust-src",
47 PkgType::Cargo => "cargo",
48 PkgType::Rls => "rls",
49 PkgType::RustAnalyzer => "rust-analyzer",
50 PkgType::Clippy => "clippy",
51 PkgType::Rustfmt => "rustfmt",
52 PkgType::LlvmTools => "llvm-tools",
53 PkgType::Miri => "miri",
54 PkgType::Other(component) => component,
55 }
56 }
57
58 /// Whether this package has the same version as Rust itself, or has its own `version` and
59 /// `git-commit-hash` files inside the tarball.
60 fn should_use_rust_version(&self) -> bool {
61 match self {
62 PkgType::Cargo => false,
63 PkgType::Rls => false,
64 PkgType::RustAnalyzer => false,
65 PkgType::Clippy => false,
66 PkgType::Rustfmt => false,
67 PkgType::LlvmTools => false,
68 PkgType::Miri => false,
69
70 PkgType::Rust => true,
71 PkgType::RustSrc => true,
72 PkgType::Other(_) => true,
73 }
74 }
75
76 /// Whether this package is target-independent or not.
77 fn target_independent(&self) -> bool {
78 *self == PkgType::RustSrc
79 }
80 }
81
82 #[derive(Debug, Default, Clone)]
83 pub(crate) struct VersionInfo {
84 pub(crate) version: Option<String>,
85 pub(crate) git_commit: Option<String>,
86 pub(crate) present: bool,
87 }
88
89 pub(crate) struct Versions {
90 channel: String,
91 dist_path: PathBuf,
92 versions: HashMap<PkgType, VersionInfo>,
93 }
94
95 impl Versions {
96 pub(crate) fn new(channel: &str, dist_path: &Path) -> Result<Self, Error> {
97 Ok(Self { channel: channel.into(), dist_path: dist_path.into(), versions: HashMap::new() })
98 }
99
100 pub(crate) fn channel(&self) -> &str {
101 &self.channel
102 }
103
104 pub(crate) fn version(&mut self, mut package: &PkgType) -> Result<VersionInfo, Error> {
105 if package.should_use_rust_version() {
106 package = &PkgType::Rust;
107 }
108
109 match self.versions.get(package) {
110 Some(version) => Ok(version.clone()),
111 None => {
112 let version_info = self.load_version_from_tarball(package)?;
113 self.versions.insert(package.clone(), version_info.clone());
114 Ok(version_info)
115 }
116 }
117 }
118
119 fn load_version_from_tarball(&mut self, package: &PkgType) -> Result<VersionInfo, Error> {
120 let tarball_name = self.tarball_name(package, DEFAULT_TARGET)?;
121 let tarball = self.dist_path.join(tarball_name);
122
123 let file = match File::open(&tarball) {
124 Ok(file) => file,
125 Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
126 // Missing tarballs do not return an error, but return empty data.
127 return Ok(VersionInfo::default());
128 }
129 Err(err) => return Err(err.into()),
130 };
131 let mut tar = Archive::new(GzDecoder::new(file));
132
133 let mut version = None;
134 let mut git_commit = None;
135 for entry in tar.entries()? {
136 let mut entry = entry?;
137
138 let dest;
139 match entry.path()?.components().nth(1).and_then(|c| c.as_os_str().to_str()) {
140 Some("version") => dest = &mut version,
141 Some("git-commit-hash") => dest = &mut git_commit,
142 _ => continue,
143 }
144 let mut buf = String::new();
145 entry.read_to_string(&mut buf)?;
146 *dest = Some(buf);
147
148 // Short circuit to avoid reading the whole tar file if not necessary.
149 if version.is_some() && git_commit.is_some() {
150 break;
151 }
152 }
153
154 Ok(VersionInfo { version, git_commit, present: true })
155 }
156
157 pub(crate) fn disable_version(&mut self, package: &PkgType) {
158 match self.versions.get_mut(package) {
159 Some(version) => {
160 *version = VersionInfo::default();
161 }
162 None => {
163 self.versions.insert(package.clone(), VersionInfo::default());
164 }
165 }
166 }
167
168 pub(crate) fn tarball_name(
169 &mut self,
170 package: &PkgType,
171 target: &str,
172 ) -> Result<String, Error> {
173 let component_name = package.tarball_component_name();
174 let version = match self.channel.as_str() {
175 "stable" => RUSTC_VERSION.into(),
176 "beta" => "beta".into(),
177 "nightly" => "nightly".into(),
178 _ => format!("{}-dev", RUSTC_VERSION),
179 };
180
181 if package.target_independent() {
182 Ok(format!("{}-{}.tar.gz", component_name, version))
183 } else {
184 Ok(format!("{}-{}-{}.tar.gz", component_name, version, target))
185 }
186 }
187
188 pub(crate) fn rustc_version(&self) -> &str {
189 RUSTC_VERSION
190 }
191 }