monerochan_cli/commands/
build_toolchain.rs

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