summaryrefslogtreecommitdiffstats
path: root/src/tools/bump-stage0
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /src/tools/bump-stage0
parentInitial commit. (diff)
downloadrustc-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.toml14
-rw-r--r--src/tools/bump-stage0/src/main.rs233
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>,
+}