sp1_build/
lib.rs

1mod build;
2mod command;
3mod utils;
4use std::{collections::HashMap, fs::File, io::Read};
5
6use build::build_program_internal;
7pub use build::{execute_build_program, generate_elf_paths};
8pub use command::TOOLCHAIN_NAME;
9
10use clap::{Parser, ValueEnum};
11use sp1_prover::{components::CpuProverComponents, HashableKey, SP1Prover};
12
13const DEFAULT_DOCKER_TAG: &str = concat!("v", env!("CARGO_PKG_VERSION"));
14const BUILD_TARGET: &str = "riscv32im-succinct-zkvm-elf";
15const HELPER_TARGET_SUBDIR: &str = "elf-compilation";
16
17/// Controls the warning message verbosity in the build process.
18#[derive(Clone, Copy, ValueEnum, Debug, Default)]
19pub enum WarningLevel {
20    /// Show all warning messages (default).
21    #[default]
22    All,
23    /// Suppress non-essential warnings; show only critical stuff.
24    Minimal,
25}
26
27/// Compile an SP1 program.
28///
29/// Additional arguments are useful for configuring the build process, including options for using
30/// Docker, specifying binary and ELF names, ignoring Rust version checks, and enabling specific
31/// features.
32#[derive(Clone, Parser, Debug)]
33pub struct BuildArgs {
34    #[arg(
35        long,
36        action,
37        help = "Run compilation using a Docker container for reproducible builds."
38    )]
39    pub docker: bool,
40    #[arg(
41        long,
42        help = "The ghcr.io/succinctlabs/sp1 image tag to use when building with Docker.",
43        default_value = DEFAULT_DOCKER_TAG
44    )]
45    pub tag: String,
46    #[arg(
47        long,
48        action,
49        value_delimiter = ',',
50        help = "Space or comma separated list of features to activate"
51    )]
52    pub features: Vec<String>,
53    #[arg(
54        long,
55        action,
56        value_delimiter = ',',
57        help = "Space or comma separated list of extra flags to invokes `rustc` with"
58    )]
59    pub rustflags: Vec<String>,
60    #[arg(long, action, help = "Do not activate the `default` feature")]
61    pub no_default_features: bool,
62    #[arg(long, action, help = "Ignore `rust-version` specification in packages")]
63    pub ignore_rust_version: bool,
64    #[arg(long, action, help = "Assert that `Cargo.lock` will remain unchanged")]
65    pub locked: bool,
66    #[arg(
67        short,
68        long,
69        action,
70        help = "Build only the specified packages",
71        num_args = 1..
72    )]
73    pub packages: Vec<String>,
74    #[arg(
75        alias = "bin",
76        long,
77        action,
78        help = "Build only the specified binaries",
79        num_args = 1..
80    )]
81    pub binaries: Vec<String>,
82    #[arg(long, action, requires = "output_directory", help = "ELF binary name")]
83    pub elf_name: Option<String>,
84    #[arg(alias = "out-dir", long, action, help = "Copy the compiled ELF to this directory")]
85    pub output_directory: Option<String>,
86
87    #[arg(
88        alias = "workspace-dir",
89        long,
90        action,
91        help = "The top level directory to be used in the docker invocation."
92    )]
93    pub workspace_directory: Option<String>,
94
95    #[arg(long, value_enum, default_value = "all", help = "Control warning message verbosity")]
96    pub warning_level: WarningLevel,
97}
98
99// Implement default args to match clap defaults.
100impl Default for BuildArgs {
101    fn default() -> Self {
102        Self {
103            docker: false,
104            tag: DEFAULT_DOCKER_TAG.to_string(),
105            features: vec![],
106            rustflags: vec![],
107            ignore_rust_version: false,
108            packages: vec![],
109            binaries: vec![],
110            elf_name: None,
111            output_directory: None,
112            locked: false,
113            no_default_features: false,
114            workspace_directory: None,
115            warning_level: WarningLevel::All,
116        }
117    }
118}
119
120/// Builds the program if the program at the specified path, or one of its dependencies, changes.
121///
122/// This function monitors the program and its dependencies for changes. If any changes are
123/// detected, it triggers a rebuild of the program.
124///
125/// # Arguments
126///
127/// * `path` - A string slice that holds the path to the program directory.
128///
129/// This function is useful for automatically rebuilding the program during development
130/// when changes are made to the source code or its dependencies.
131///
132/// Set the `SP1_SKIP_PROGRAM_BUILD` environment variable to `true` to skip building the program.
133pub fn build_program(path: &str) {
134    build_program_internal(path, None)
135}
136
137/// Builds the program with the given arguments if the program at path, or one of its dependencies,
138/// changes.
139///
140/// # Arguments
141///
142/// * `path` - A string slice that holds the path to the program directory.
143/// * `args` - A [`BuildArgs`] struct that contains various build configuration options.
144///
145/// Set the `SP1_SKIP_PROGRAM_BUILD` environment variable to `true` to skip building the program.
146pub fn build_program_with_args(path: &str, args: BuildArgs) {
147    build_program_internal(path, Some(args))
148}
149
150/// Returns the verification key for the provided program.
151///
152/// # Arguments
153///
154/// * `path` - A string slice that holds the path to the program directory.
155/// * `target_name` - A string slice that holds the binary target.
156///
157/// Note: If used in a script `build.rs`, this function should be called *after* [`build_program`]
158/// to returns the vkey corresponding to the latest program version which has just been compiled.
159pub fn vkey(path: &str, target_name: &str) -> String {
160    let program_dir = std::path::Path::new(path);
161    let metadata_file = program_dir.join("Cargo.toml");
162    let mut metadata_cmd = cargo_metadata::MetadataCommand::new();
163    let metadata = metadata_cmd.manifest_path(metadata_file).exec().unwrap();
164    let target_elf_paths =
165        generate_elf_paths(&metadata, None).expect("failed to collect target ELF paths");
166    let (_, path) =
167        target_elf_paths.iter().find(|(t, _)| t == target_name).expect("failed to find the target");
168    let prover = SP1Prover::<CpuProverComponents>::new();
169    let mut file = File::open(path).unwrap();
170    let mut elf = Vec::new();
171
172    file.read_to_end(&mut elf).unwrap();
173    let (_, _, _, vk) = prover.setup(&elf);
174    vk.bytes32()
175}
176
177/// Returns the verification keys for the provided programs in a [`HashMap`] with the target names
178/// as keys and vkeys as values.
179///
180/// # Arguments
181///
182/// * `path` - A string slice that holds the path to the program directory.
183/// * `args` - A [`BuildArgs`] struct that contains various build configuration options.
184///
185/// Note: If used in a script `build.rs`, this function should be called *after* [`build_program`]
186/// to returns the vkey corresponding to the latest program version which has just been compiled.
187pub fn vkeys(path: &str, args: BuildArgs) -> HashMap<String, String> {
188    let program_dir = std::path::Path::new(path);
189    let metadata_file = program_dir.join("Cargo.toml");
190    let mut metadata_cmd = cargo_metadata::MetadataCommand::new();
191    let metadata = metadata_cmd.manifest_path(metadata_file).exec().unwrap();
192    let target_elf_paths =
193        generate_elf_paths(&metadata, Some(&args)).expect("failed to collect target ELF paths");
194    let prover = SP1Prover::<CpuProverComponents>::new();
195
196    target_elf_paths
197        .into_iter()
198        .map(|(target_name, elf_path)| {
199            let mut file = File::open(elf_path).unwrap();
200            let mut elf = Vec::new();
201            file.read_to_end(&mut elf).unwrap();
202
203            let (_, _, _, vk) = prover.setup(&elf);
204            let vk = vk.bytes32();
205
206            (target_name, vk)
207        })
208        .collect()
209}
210
211/// Returns the raw ELF bytes by the zkVM program target name.
212///
213/// Note that this only works when using `sp1_build::build_program` or
214/// `sp1_build::build_program_with_args` in a build script.
215///
216/// By default, the program target name is the same as the program crate name. However, this might
217/// not be the case for non-standard project structures. For example, placing the entrypoint source
218/// file at `src/bin/my_entry.rs` would result in the program target being named `my_entry`, in
219/// which case the invocation should be `include_elf!("my_entry")` instead.
220#[macro_export]
221macro_rules! include_elf {
222    ($arg:tt) => {{
223        include_bytes!(env!(concat!("SP1_ELF_", $arg)))
224    }};
225}