Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/risc0/risc0/llms.txt

Use this file to discover all available pages before exploring further.

The risc0-build crate provides build-time tools for compiling RISC-V guest programs and embedding them into your host application. It automates the process of building guest code and generating the necessary constants for execution and verification.

Project Structure

A typical RISC Zero project has this structure:
my-project/
├── Cargo.toml
├── src/
│   └── main.rs          # Host code
└── methods/
    ├── Cargo.toml       # Methods crate manifest
    ├── build.rs         # Build script
    ├── src/
    │   └── lib.rs       # Generated constants
    └── guest/
        ├── Cargo.toml   # Guest program manifest
        └── src/
            └── main.rs  # Guest code

Setting Up build.rs

The build.rs file in your methods crate triggers the guest build:
fn main() {
    risc0_build::embed_methods();
}
This single line does a lot:
  • Compiles all guest programs specified in Cargo.toml
  • Generates ELF binaries for the RISC-V target
  • Computes image IDs for verification
  • Creates a methods.rs file with constants

Configuring Methods in Cargo.toml

Specify which guest packages to build:
[package]
name = "hello-world-methods"
version = "0.1.0"
edition = "2021"

[build-dependencies]
risc0-build = { version = "1.0" }

[package.metadata.risc0]
methods = ["guest"]
The methods array lists relative paths to guest crates.

Generated Constants

After building, risc0-build generates constants in $OUT_DIR/methods.rs:
// Auto-generated - do not edit
pub const MULTIPLY_ELF: &[u8] = include_bytes!("...");
pub const MULTIPLY_PATH: &str = "/path/to/guest/binary";
pub const MULTIPLY_ID: [u32; 8] = [
    0x12345678,
    0x9abcdef0,
    // ... 8 words total
];

Including Generated Code

In your methods/src/lib.rs:
include!(concat!(env!("OUT_DIR"), "/methods.rs"));

Using the Constants

In your host code:
use my_project_methods::{MULTIPLY_ELF, MULTIPLY_ID};
use risc0_zkvm::{ExecutorEnv, default_prover};

fn main() {
    let env = ExecutorEnv::builder().build().unwrap();
    let prover = default_prover();
    
    // Use the ELF to execute
    let receipt = prover.prove(env, MULTIPLY_ELF).unwrap().receipt;
    
    // Use the ID to verify
    receipt.verify(MULTIPLY_ID).unwrap();
}

Custom Guest Options

Configure individual guest packages with GuestOptions:
use risc0_build::{embed_methods_with_options, GuestOptions};
use std::collections::HashMap;

fn main() {
    let mut opts = HashMap::new();
    
    opts.insert(
        "guest",
        GuestOptions::builder()
            .features(vec!["my-feature".to_string()])
            .build()
            .unwrap(),
    );
    
    embed_methods_with_options(opts);
}

Available Options

  • Features - Enable Cargo features in the guest
  • Docker build - Build in Docker for reproducibility
  • Kernel ELF - Use a custom kernel

Docker Builds

For reproducible builds, use Docker:
use risc0_build::{
    embed_methods_with_options,
    GuestOptions,
    DockerOptions,
};
use std::collections::HashMap;

fn main() {
    let mut opts = HashMap::new();
    
    let docker_opts = DockerOptions::builder()
        .root_dir("/path/to/project")
        .build()
        .unwrap();
    
    opts.insert(
        "guest",
        GuestOptions::builder()
            .use_docker(docker_opts)
            .build()
            .unwrap(),
    );
    
    embed_methods_with_options(opts);
}
Docker builds require the RISC Zero Docker image to be available. The default tag is set in risc0-build.

Building Without Embedding

For faster iteration during development, skip embedding the full ELF:
use risc0_build::embed_method_metadata_with_options;

fn main() {
    // Only embed metadata (name and path)
    embed_method_metadata_with_options(HashMap::new());
}
This generates minimal constants:
pub const MULTIPLY_PATH: &str = "/path/to/guest/binary";

Environment Variables

risc0-build respects several environment variables:

RISC0_SKIP_BUILD

Skip building guest programs:
export RISC0_SKIP_BUILD=1
cargo build

RISC0_BUILD_DEBUG

Build guest programs in debug mode:
export RISC0_BUILD_DEBUG=1
cargo build
Debug builds are much larger and slower. Only use for debugging.

RISC0_BUILD_LOCKED

Use --locked when building guest programs:
export RISC0_BUILD_LOCKED=1
cargo build

RISC0_GUEST_LOGFILE

Redirect guest build output:
export RISC0_GUEST_LOGFILE=/tmp/guest-build.log
cargo build

RISC0_RUST_SRC

Build Rust std from source:
export RISC0_RUST_SRC=/path/to/rust/src
cargo build

Guest Program Configuration

Your guest Cargo.toml should target the zkVM:
[package]
name = "guest"
version = "0.1.0"
edition = "2021"

[dependencies]
risc0-zkvm = { version = "1.0", default-features = false, features = ["std"] }
Set default-features = false to avoid pulling in host-only features.

Optional: Enable std

For more Rust std functionality:
[dependencies]
risc0-zkvm = { version = "1.0", default-features = false, features = ["std"] }
With std enabled, you can omit #![no_std] and #![no_main].

Toolchain Requirements

The RISC Zero Rust toolchain must be installed:
curl -L https://risczero.com/install | bash
rzup install rust
risc0-build automatically uses the installed toolchain.

Advanced: cargo_command

For custom build workflows, use cargo_command directly:
use risc0_build::cargo_command;

fn main() {
    let mut cmd = cargo_command(
        "build",
        &["--cfg".to_string(), "my_config".to_string()],
    );
    
    cmd.args(["--release"]);
    
    let status = cmd.status().expect("Failed to run cargo");
    assert!(status.success());
}

Advanced: Building Specific Packages

Build individual guest packages programmatically:
use risc0_build::{build_package, get_package, GuestOptions};
use std::path::Path;

fn main() {
    let pkg = get_package("methods/guest");
    let target_dir = Path::new("target/riscv-guest");
    let opts = GuestOptions::default();
    
    let guests = build_package(&pkg, target_dir, opts)
        .expect("Failed to build guest");
    
    for guest in guests {
        println!("Built guest: {}", guest.name);
        println!("Image ID: {:?}", guest.image_id);
    }
}

Troubleshooting

Build Fails with “Rust toolchain not found”

Install the RISC Zero toolchain:
rzup install rust

Linker Errors

Ensure you’re not using host-only features:
risc0-zkvm = { version = "1.0", default-features = false }

Out of Memory During Build

Reduce parallel builds:
cargo build -j 1

C/C++ Compilation Issues

Install the C++ toolchain:
rzup install cpp

Image ID Computation

The image ID is a cryptographic hash of the guest binary:
use risc0_binfmt::compute_image_id;

fn compute_id(elf: &[u8]) -> Result<Digest> {
    compute_image_id(elf)
}
risc0-build uses the r0vm tool for fast image ID computation. If r0vm is unavailable, it falls back to the slower method.

Best Practices

1
Use embed_methods() for Standard Projects
2
The default embed_methods() is sufficient for most use cases.
3
Version Lock Dependencies
4
Use RISC0_BUILD_LOCKED=1 in CI to ensure reproducible builds.
5
Cache Guest Builds
6
Guest builds are expensive. Use cargo’s incremental compilation and CI caching.
7
Test Image IDs
8
Verify that image IDs are deterministic across builds for production systems.

See Also