Skip to main content

sp1_cli/commands/
build_toolchain.rs

1use anyhow::{Context, Result};
2use clap::Parser;
3use std::{
4    path::PathBuf,
5    process::{Command, Stdio},
6};
7
8use crate::{get_target, LATEST_SUPPORTED_TOOLCHAIN_VERSION_TAG, RUSTUP_TOOLCHAIN_NAME};
9
10// There is a lot of Commands in this module, having this trait back can
11// help us simplify the code a bit.
12trait CommandExecutor {
13    fn run(&mut self) -> Result<()>;
14}
15
16impl CommandExecutor for Command {
17    fn run(&mut self) -> Result<()> {
18        self.stderr(Stdio::inherit())
19            .stdout(Stdio::inherit())
20            .stdin(Stdio::inherit())
21            .output()
22            .with_context(|| format!("while executing `{:?}`", self))
23            .map(|_| ())
24    }
25}
26
27#[derive(Parser)]
28#[command(name = "build-toolchain", about = "Build the cargo-prove toolchain.")]
29pub struct BuildToolchainCmd {}
30
31impl BuildToolchainCmd {
32    pub fn run(&self) -> Result<()> {
33        // Get environment variables.
34        let github_access_token = std::env::var("GITHUB_ACCESS_TOKEN");
35        let build_dir = std::env::var("SP1_BUILD_DIR");
36
37        // Clone our rust fork, if necessary.
38        let rust_dir = match build_dir {
39            Ok(build_dir) => {
40                println!("Detected SP1_BUILD_DIR, skipping cloning rust.");
41                PathBuf::from(build_dir).join("rust")
42            }
43            Err(_) => {
44                let temp_dir = std::env::temp_dir();
45                let dir = temp_dir.join("sp1-rust");
46                if dir.exists() {
47                    std::fs::remove_dir_all(&dir)?;
48                }
49
50                println!("No SP1_BUILD_DIR detected, cloning rust.");
51                let repo_url = match github_access_token {
52                    Ok(github_access_token) => {
53                        println!("Detected GITHUB_ACCESS_TOKEN, using it to clone rust.");
54                        format!("https://{github_access_token}@github.com/succinctlabs/rust")
55                    }
56                    Err(_) => {
57                        println!("No GITHUB_ACCESS_TOKEN detected. If you get throttled by Github, set it to bypass the rate limit.");
58                        "ssh://git@github.com/succinctlabs/rust".to_string()
59                    }
60                };
61                Command::new("git")
62                    .args([
63                        "clone",
64                        &repo_url,
65                        "--depth=1",
66                        "--single-branch",
67                        &format!("--branch={LATEST_SUPPORTED_TOOLCHAIN_VERSION_TAG}"),
68                        "sp1-rust",
69                    ])
70                    .current_dir(&temp_dir)
71                    .run()?;
72                Command::new("git").args(["reset", "--hard"]).current_dir(&dir).run()?;
73                Command::new("git")
74                    .args(["submodule", "update", "--init", "--recursive", "--progress"])
75                    .current_dir(&dir)
76                    .run()?;
77                dir
78            }
79        };
80
81        // Install our bootstrap.toml.
82        let bootstrap_toml = include_str!("bootstrap.toml");
83        let bootstrap_file = rust_dir.join("bootstrap.toml");
84        std::fs::write(&bootstrap_file, bootstrap_toml)
85            .with_context(|| format!("while writing configuration to {bootstrap_file:?}"))?;
86
87        // Work around target sanity check added in
88        // rust-lang/rust@09c076810cb7649e5817f316215010d49e78e8d7.
89        let temp_dir = std::env::temp_dir().join("rustc-targets");
90        if !temp_dir.exists() {
91            std::fs::create_dir_all(&temp_dir)?;
92        }
93        std::fs::File::create(temp_dir.join("riscv32im-succinct-zkvm-elf.json"))?;
94
95        // Build the toolchain.
96        Command::new("python3")
97            .env("RUST_TARGET_PATH", &temp_dir)
98            .env("CARGO_TARGET_RISCV32IM_SUCCINCT_ZKVM_ELF_RUSTFLAGS", "-Cpasses=lower-atomic")
99            .env("CARGO_TARGET_RISCV64IM_SUCCINCT_ZKVM_ELF_RUSTFLAGS", "-Cpasses=lower-atomic")
100            .args([
101                "x.py",
102                "build",
103                "--stage",
104                "2",
105                "compiler/rustc",
106                "library",
107                "--target",
108                &format!(
109                    "riscv32im-succinct-zkvm-elf,riscv64im-succinct-zkvm-elf,{}",
110                    get_target()
111                ),
112            ])
113            .current_dir(&rust_dir)
114            .run()?;
115
116        // Remove the existing toolchain from rustup, if it exists.
117        match Command::new("rustup").args(["toolchain", "remove", RUSTUP_TOOLCHAIN_NAME]).run() {
118            Ok(_) => println!("Successfully removed existing toolchain."),
119            Err(_) => println!("No existing toolchain to remove."),
120        }
121
122        // Find the toolchain directory.
123        let mut toolchain_dir = None;
124        for wentry in std::fs::read_dir(rust_dir.join("build"))? {
125            let entry = wentry?;
126            let toolchain_dir_candidate = entry.path().join("stage2");
127            if toolchain_dir_candidate.is_dir() {
128                toolchain_dir = Some(toolchain_dir_candidate);
129                break;
130            }
131        }
132        let toolchain_dir = toolchain_dir.unwrap();
133        println!(
134            "Found built toolchain directory at {}.",
135            toolchain_dir.as_path().to_str().unwrap()
136        );
137
138        // Link the toolchain to rustup.
139        Command::new("rustup")
140            .args(["toolchain", "link", RUSTUP_TOOLCHAIN_NAME])
141            .arg(&toolchain_dir)
142            .run()?;
143        println!("Successfully linked the toolchain to rustup.");
144
145        // Compressing toolchain directory to tar.gz.
146        let target = get_target();
147        let tar_gz_path = format!("rust-toolchain-{target}.tar.gz");
148        Command::new("tar")
149            .args([
150                "--exclude",
151                "lib/rustlib/src",
152                "--exclude",
153                "lib/rustlib/rustc-src",
154                "-hczvf",
155                &tar_gz_path,
156                "-C",
157                toolchain_dir.to_str().unwrap(),
158                ".",
159            ])
160            .run()?;
161        println!("Successfully compressed the toolchain to {tar_gz_path}.");
162
163        Ok(())
164    }
165}