diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/tools/bump-stage0 | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/bump-stage0')
-rw-r--r-- | src/tools/bump-stage0/Cargo.toml | 14 | ||||
-rw-r--r-- | src/tools/bump-stage0/src/main.rs | 233 |
2 files changed, 247 insertions, 0 deletions
diff --git a/src/tools/bump-stage0/Cargo.toml b/src/tools/bump-stage0/Cargo.toml new file mode 100644 index 000000000..758b1b139 --- /dev/null +++ b/src/tools/bump-stage0/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "bump-stage0" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.34" +curl = "0.4.38" +indexmap = { version = "1.9.1", features = ["serde"] } +serde = { version = "1.0.125", features = ["derive"] } +serde_json = { version = "1.0.59", features = ["preserve_order"] } +toml = "0.5.7" diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs new file mode 100644 index 000000000..aa346daf7 --- /dev/null +++ b/src/tools/bump-stage0/src/main.rs @@ -0,0 +1,233 @@ +use anyhow::Error; +use curl::easy::Easy; +use indexmap::IndexMap; +use std::collections::HashMap; +use std::convert::TryInto; + +const PATH: &str = "src/stage0.json"; +const COMPILER_COMPONENTS: &[&str] = &["rustc", "rust-std", "cargo"]; +const RUSTFMT_COMPONENTS: &[&str] = &["rustfmt-preview"]; + +struct Tool { + config: Config, + comments: Vec<String>, + + channel: Channel, + version: [u16; 3], + checksums: IndexMap<String, String>, +} + +impl Tool { + fn new() -> Result<Self, Error> { + let channel = match std::fs::read_to_string("src/ci/channel")?.trim() { + "stable" => Channel::Stable, + "beta" => Channel::Beta, + "nightly" => Channel::Nightly, + other => anyhow::bail!("unsupported channel: {}", other), + }; + + // Split "1.42.0" into [1, 42, 0] + let version = std::fs::read_to_string("src/version")? + .trim() + .split('.') + .map(|val| val.parse()) + .collect::<Result<Vec<_>, _>>()? + .try_into() + .map_err(|_| anyhow::anyhow!("failed to parse version"))?; + + let existing: Stage0 = serde_json::from_slice(&std::fs::read(PATH)?)?; + + Ok(Self { + channel, + version, + config: existing.config, + comments: existing.comments, + checksums: IndexMap::new(), + }) + } + + fn update_json(mut self) -> Result<(), Error> { + std::fs::write( + PATH, + format!( + "{}\n", + serde_json::to_string_pretty(&Stage0 { + compiler: self.detect_compiler()?, + rustfmt: self.detect_rustfmt()?, + checksums_sha256: { + // Keys are sorted here instead of beforehand because values in this map + // are added while filling the other struct fields just above this block. + self.checksums.sort_keys(); + self.checksums + }, + config: self.config, + comments: self.comments, + })? + ), + )?; + Ok(()) + } + + // Currently Rust always bootstraps from the previous stable release, and in our train model + // this means that the master branch bootstraps from beta, beta bootstraps from current stable, + // and stable bootstraps from the previous stable release. + // + // On the master branch the compiler version is configured to `beta` whereas if you're looking + // at the beta or stable channel you'll likely see `1.x.0` as the version, with the previous + // release's version number. + fn detect_compiler(&mut self) -> Result<Stage0Toolchain, Error> { + let channel = match self.channel { + Channel::Stable | Channel::Beta => { + // The 1.XX manifest points to the latest point release of that minor release. + format!("{}.{}", self.version[0], self.version[1] - 1) + } + Channel::Nightly => "beta".to_string(), + }; + + let manifest = fetch_manifest(&self.config, &channel)?; + self.collect_checksums(&manifest, COMPILER_COMPONENTS)?; + Ok(Stage0Toolchain { + date: manifest.date, + version: if self.channel == Channel::Nightly { + "beta".to_string() + } else { + // The version field is like "1.42.0 (abcdef1234 1970-01-01)" + manifest.pkg["rust"] + .version + .split_once(' ') + .expect("invalid version field") + .0 + .to_string() + }, + }) + } + + /// We use a nightly rustfmt to format the source because it solves some bootstrapping issues + /// with use of new syntax in this repo. For the beta/stable channels rustfmt is not provided, + /// as we don't want to depend on rustfmt from nightly there. + fn detect_rustfmt(&mut self) -> Result<Option<Stage0Toolchain>, Error> { + if self.channel != Channel::Nightly { + return Ok(None); + } + + let manifest = fetch_manifest(&self.config, "nightly")?; + self.collect_checksums(&manifest, RUSTFMT_COMPONENTS)?; + Ok(Some(Stage0Toolchain { date: manifest.date, version: "nightly".into() })) + } + + fn collect_checksums(&mut self, manifest: &Manifest, components: &[&str]) -> Result<(), Error> { + let prefix = format!("{}/", self.config.dist_server); + for component in components { + let pkg = manifest + .pkg + .get(*component) + .ok_or_else(|| anyhow::anyhow!("missing component from manifest: {}", component))?; + for target in pkg.target.values() { + for pair in &[(&target.url, &target.hash), (&target.xz_url, &target.xz_hash)] { + if let (Some(url), Some(sha256)) = pair { + let url = url + .strip_prefix(&prefix) + .ok_or_else(|| { + anyhow::anyhow!("url doesn't start with dist server base: {}", url) + })? + .to_string(); + self.checksums.insert(url, sha256.clone()); + } + } + } + } + Ok(()) + } +} + +fn main() -> Result<(), Error> { + let tool = Tool::new()?; + tool.update_json()?; + Ok(()) +} + +fn fetch_manifest(config: &Config, channel: &str) -> Result<Manifest, Error> { + Ok(toml::from_slice(&http_get(&format!( + "{}/dist/channel-rust-{}.toml", + config.dist_server, channel + ))?)?) +} + +fn http_get(url: &str) -> Result<Vec<u8>, Error> { + let mut data = Vec::new(); + let mut handle = Easy::new(); + handle.fail_on_error(true)?; + handle.url(url)?; + { + let mut transfer = handle.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + })?; + transfer.perform()?; + } + Ok(data) +} + +#[derive(Debug, PartialEq, Eq)] +enum Channel { + Stable, + Beta, + Nightly, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct Stage0 { + config: Config, + // Comments are explicitly below the config, do not move them above. + // + // Downstream forks of the compiler codebase can change the configuration values defined above, + // but doing so would risk merge conflicts whenever they import new changes that include a + // bootstrap compiler bump. + // + // To lessen the pain, a big block of comments is placed between the configuration and the + // auto-generated parts of the file, preventing git diffs of the config to include parts of the + // auto-generated content and vice versa. This should prevent merge conflicts. + #[serde(rename = "__comments")] + comments: Vec<String>, + compiler: Stage0Toolchain, + rustfmt: Option<Stage0Toolchain>, + checksums_sha256: IndexMap<String, String>, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct Config { + dist_server: String, + // There are other fields in the configuration, which will be read by src/bootstrap or other + // tools consuming stage0.json. To avoid the need to update bump-stage0 every time a new field + // is added, we collect all the fields in an untyped Value and serialize them back with the + // same order and structure they were deserialized in. + #[serde(flatten)] + other: serde_json::Value, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct Stage0Toolchain { + date: String, + version: String, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct Manifest { + date: String, + pkg: HashMap<String, ManifestPackage>, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct ManifestPackage { + version: String, + target: HashMap<String, ManifestTargetPackage>, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct ManifestTargetPackage { + url: Option<String>, + hash: Option<String>, + xz_url: Option<String>, + xz_hash: Option<String>, +} |