nexus_sdk/legacy/
compile.rs1use std::fmt::Display;
2use std::fs;
3use std::io;
4use std::io::Write;
5use std::path::PathBuf;
6use std::process::Command;
7use std::str::FromStr;
8use uuid::Uuid;
9
10pub use crate::error::BuildError;
11
12#[doc(hidden)]
13#[derive(Default)]
14pub enum ForProver {
15    #[default]
16    Default,
17    Jolt,
18}
19
20impl Display for ForProver {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        match self {
23            Self::Default => write!(f, "default"),
24            Self::Jolt => write!(f, "jolt"),
25        }
26    }
27}
28
29#[derive(Clone)]
33pub struct CompileOpts {
34    pub package: String,
36    pub binary: String,
38    debug: bool,
39    unique: bool,
41    pub(crate) memlimit: Option<usize>, }
43
44impl CompileOpts {
45    pub fn new(package: &str) -> Self {
47        Self {
48            package: package.to_string(),
49            binary: package.to_string(),
50            debug: false,
51            unique: false,
53            memlimit: None,
54        }
55    }
56
57    pub fn new_with_custom_binary(package: &str, binary: &str) -> Self {
59        Self {
60            package: package.to_string(),
61            binary: binary.to_string(),
62            debug: false,
63            unique: false,
65            memlimit: None,
66        }
67    }
68
69    pub fn set_debug_build(&mut self, debug: bool) {
71        self.debug = debug;
72    }
73
74    pub fn set_unique_build(&mut self, unique: bool) {
86        self.unique = unique;
87    }
88
89    pub fn set_memlimit(&mut self, memlimit: usize) {
95        self.memlimit = Some(memlimit);
96    }
97
98    fn set_linker(&mut self, prover: &ForProver) -> Result<PathBuf, BuildError> {
99        let linker_script = match prover {
100            ForProver::Jolt => {
101                if self.memlimit.is_some() {
102                    return Err(BuildError::InvalidMemoryConfiguration);
103                }
104
105                include_str!("./linker-scripts/jolt.x").into()
106            }
107            ForProver::Default => {
108                if self.memlimit.is_none() {
109                    return Err(BuildError::InvalidMemoryConfiguration);
110                }
111
112                include_str!("./linker-scripts/default.x").replace(
113                    "{MEMORY_LIMIT}",
114                    &format!(
115                        "0x{:X}",
116                        &(self.memlimit.unwrap() as u32).saturating_mul(0x100000)
117                    ),
118                )
119            }
120        };
121
122        let linker_path =
123            PathBuf::from_str(&format!("/tmp/nexus-guest-linkers/{}.ld", prover)).unwrap();
124
125        if let Some(parent) = linker_path.parent() {
126            fs::create_dir_all(parent)?;
127        }
128
129        let mut file = fs::File::create(linker_path.clone())?;
130        file.write_all(linker_script.as_bytes())?;
131
132        Ok(linker_path)
133    }
134
135    pub(crate) fn build(&mut self, prover: &ForProver) -> Result<PathBuf, BuildError> {
136        let linker_path = self.set_linker(prover)?;
137
138        let rust_flags = [
139            "-C",
140            &format!("link-arg=-T{}", linker_path.display()),
141            "-C",
142            "panic=abort",
143        ];
144
145        let target = "riscv32im-unknown-none-elf";
153
154        let profile = if self.debug { "debug" } else { "release" };
155
156        let envs = vec![("CARGO_ENCODED_RUSTFLAGS", rust_flags.join("\x1f"))];
157        let prog = self.binary.as_str();
158
159        let mut dest = match std::env::var_os("OUT_DIR") {
160            Some(path) => path.into_string().unwrap(),
161            None => "/tmp/nexus-target".into(),
162        };
163
164        if self.unique {
165            let uuid = Uuid::new_v4();
166            dest = format!("{}-{}", dest, uuid);
167        }
168
169        let cargo_bin = std::env::var("CARGO").unwrap_or_else(|_err| "cargo".into());
170        let mut cmd = Command::new(cargo_bin);
171
172        cmd.envs(envs).args([
173            "build",
174            "--package",
175            self.package.as_str(),
176            "--bin",
177            prog,
178            "--target-dir",
179            &dest,
180            "--target",
181            target,
182            "--profile",
183            profile,
184        ]);
185
186        let res = cmd.output()?;
187
188        if !res.status.success() {
189            io::stderr().write_all(&res.stderr)?;
190            return Err(BuildError::CompilerError);
191        }
192
193        let elf_path =
194            PathBuf::from_str(&format!("{}/{}/{}/{}", dest, target, profile, prog)).unwrap();
195
196        Ok(elf_path)
197    }
198}