1 use anyhow::{Context, Error};
3 use indexmap::IndexMap;
4 use std::collections::HashMap;
5 use std::convert::TryInto;
7 const PATH: &str = "src/stage0.json";
8 const COMPILER_COMPONENTS: &[&str] = &["rustc", "rust-std", "cargo"];
9 const RUSTFMT_COMPONENTS: &[&str] = &["rustfmt-preview", "rustc"];
13 comments: Vec<String>,
18 checksums: IndexMap<String, String>,
22 fn new(date: Option<String>) -> Result<Self, Error> {
23 let channel = match std::fs::read_to_string("src/ci/channel")?.trim() {
24 "stable" => Channel::Stable,
25 "beta" => Channel::Beta,
26 "nightly" => Channel::Nightly,
27 other => anyhow::bail!("unsupported channel: {}", other),
30 // Split "1.42.0" into [1, 42, 0]
31 let version = std::fs::read_to_string("src/version")?
34 .map(|val| val.parse())
35 .collect::<Result<Vec<_>, _>>()?
37 .map_err(|_| anyhow::anyhow!("failed to parse version"))?;
39 let existing: Stage0 = serde_json::from_slice(&std::fs::read(PATH)?)?;
45 config: existing.config,
46 comments: existing.comments,
47 checksums: IndexMap::new(),
51 fn update_json(mut self) -> Result<(), Error> {
56 serde_json::to_string_pretty(&Stage0 {
57 compiler: self.detect_compiler()?,
58 rustfmt: self.detect_rustfmt()?,
60 // Keys are sorted here instead of beforehand because values in this map
61 // are added while filling the other struct fields just above this block.
62 self.checksums.sort_keys();
66 comments: self.comments,
73 // Currently Rust always bootstraps from the previous stable release, and in our train model
74 // this means that the master branch bootstraps from beta, beta bootstraps from current stable,
75 // and stable bootstraps from the previous stable release.
77 // On the master branch the compiler version is configured to `beta` whereas if you're looking
78 // at the beta or stable channel you'll likely see `1.x.0` as the version, with the previous
79 // release's version number.
80 fn detect_compiler(&mut self) -> Result<Stage0Toolchain, Error> {
81 let channel = match self.channel {
82 Channel::Stable | Channel::Beta => {
83 // The 1.XX manifest points to the latest point release of that minor release.
84 format!("{}.{}", self.version[0], self.version[1] - 1)
86 Channel::Nightly => "beta".to_string(),
89 let manifest = fetch_manifest(&self.config, &channel, self.date.as_deref())?;
90 self.collect_checksums(&manifest, COMPILER_COMPONENTS)?;
93 version: if self.channel == Channel::Nightly {
96 // The version field is like "1.42.0 (abcdef1234 1970-01-01)"
100 .expect("invalid version field")
107 /// We use a nightly rustfmt to format the source because it solves some bootstrapping issues
108 /// with use of new syntax in this repo. For the beta/stable channels rustfmt is not provided,
109 /// as we don't want to depend on rustfmt from nightly there.
110 fn detect_rustfmt(&mut self) -> Result<Option<Stage0Toolchain>, Error> {
111 if self.channel != Channel::Nightly {
115 let manifest = fetch_manifest(&self.config, "nightly", self.date.as_deref())?;
116 self.collect_checksums(&manifest, RUSTFMT_COMPONENTS)?;
117 Ok(Some(Stage0Toolchain { date: manifest.date, version: "nightly".into() }))
120 fn collect_checksums(&mut self, manifest: &Manifest, components: &[&str]) -> Result<(), Error> {
121 let prefix = format!("{}/", self.config.dist_server);
122 for component in components {
126 .ok_or_else(|| anyhow::anyhow!("missing component from manifest: {}", component))?;
127 for target in pkg.target.values() {
128 for pair in &[(&target.url, &target.hash), (&target.xz_url, &target.xz_hash)] {
129 if let (Some(url), Some(sha256)) = pair {
131 .strip_prefix(&prefix)
133 anyhow::anyhow!("url doesn't start with dist server base: {}", url)
136 self.checksums.insert(url, sha256.clone());
145 fn main() -> Result<(), Error> {
146 let tool = Tool::new(std::env::args().nth(1))?;
151 fn fetch_manifest(config: &Config, channel: &str, date: Option<&str>) -> Result<Manifest, Error> {
152 let url = if let Some(date) = date {
153 format!("{}/dist/{}/channel-rust-{}.toml", config.dist_server, date, channel)
155 format!("{}/dist/channel-rust-{}.toml", config.dist_server, channel)
158 Ok(toml::from_slice(&http_get(&url)?)?)
161 fn http_get(url: &str) -> Result<Vec<u8>, Error> {
162 let mut data = Vec::new();
163 let mut handle = Easy::new();
164 handle.fail_on_error(true)?;
167 let mut transfer = handle.transfer();
168 transfer.write_function(|new_data| {
169 data.extend_from_slice(new_data);
172 transfer.perform().context(format!("failed to fetch {url}"))?;
177 #[derive(Debug, PartialEq, Eq)]
184 #[derive(Debug, serde::Serialize, serde::Deserialize)]
187 // Comments are explicitly below the config, do not move them above.
189 // Downstream forks of the compiler codebase can change the configuration values defined above,
190 // but doing so would risk merge conflicts whenever they import new changes that include a
191 // bootstrap compiler bump.
193 // To lessen the pain, a big block of comments is placed between the configuration and the
194 // auto-generated parts of the file, preventing git diffs of the config to include parts of the
195 // auto-generated content and vice versa. This should prevent merge conflicts.
196 #[serde(rename = "__comments")]
197 comments: Vec<String>,
198 compiler: Stage0Toolchain,
199 rustfmt: Option<Stage0Toolchain>,
200 checksums_sha256: IndexMap<String, String>,
203 #[derive(Debug, serde::Serialize, serde::Deserialize)]
206 // There are other fields in the configuration, which will be read by src/bootstrap or other
207 // tools consuming stage0.json. To avoid the need to update bump-stage0 every time a new field
208 // is added, we collect all the fields in an untyped Value and serialize them back with the
209 // same order and structure they were deserialized in.
211 other: serde_json::Value,
214 #[derive(Debug, serde::Serialize, serde::Deserialize)]
215 struct Stage0Toolchain {
220 #[derive(Debug, serde::Serialize, serde::Deserialize)]
223 pkg: HashMap<String, ManifestPackage>,
226 #[derive(Debug, serde::Serialize, serde::Deserialize)]
227 struct ManifestPackage {
229 target: HashMap<String, ManifestTargetPackage>,
232 #[derive(Debug, serde::Serialize, serde::Deserialize)]
233 struct ManifestTargetPackage {
235 hash: Option<String>,
236 xz_url: Option<String>,
237 xz_hash: Option<String>,