A cargo subcommand to build hyperlight guest binaries.
Write a hyperlight guest binary in Rust, and build it with a simple
cargo hyperlight buildAnd there's no need for any extra configuration.
Your binary, or any of its dependencies, can have a build.rs script using cc and bindgen to compile C code and generate bindings.
They will work out of the box!
Note
Your crate must have hyperlight-guest-bin as a transitive dependency.
cargo install cargo-hyperlightCreate a new crate for your hyperlight guest binary:
In your Cargo.toml
[package]
name = "guest"
version = "0.1.0"
edition = "2024"
[dependencies]
hyperlight-common = { version = "0.11.0", default-features = false }
hyperlight-guest = "0.11.0"
hyperlight-guest-bin = "0.11.0"The in your src/main.rs
#![no_std]
#![no_main]
extern crate alloc;
use alloc::vec::Vec;
use hyperlight_common::flatbuffer_wrappers::{function_call::*, function_types::*, util::*};
use hyperlight_guest::error::Result;
use hyperlight_guest_bin::guest_function::{definition::*, register::*};
use hyperlight_guest_bin::host_comm::*;
pub fn hello_world(_: &FunctionCall) -> Result<Vec<u8>> {
call_host_function::<i32>(
"HostPrint",
Some([ParameterValue::String("hello world".into())].into()),
ReturnType::Int,
)?;
Ok(get_flatbuffer_result(()))
}
#[unsafe(no_mangle)]
pub extern "C" fn hyperlight_main() {
register_function(GuestFunctionDefinition::new(
"HelloWorld".into(),
[ParameterType::String].into(),
ReturnType::Void,
hello_world as usize,
));
}
#[unsafe(no_mangle)]
pub fn guest_dispatch_function(_: FunctionCall) -> Result<Vec<u8>> {
panic!("Invalid guest function call");
}Then to build the hyperlight guest binary, run
cargo hyperlight build --releaseYour binary will be built for the x86_64-hyperlight-none target (or aarch64-hyperlight-none on ARM) by default, and placed in target/<arch>-hyperlight-none/release/guest.
There's no need for any extra configuration, the command will take care of everything.
When calling cargo-hyperlight as a library (for example from a host build.rs),
select the guest's toolchain explicitly:
fn main() -> anyhow::Result<()> {
cargo_hyperlight::cargo()?
.toolchain_file("guest/rust-toolchain.toml")?
.env_clear_cargo()
.current_dir("guest")
.arg("build")
.status()
}For example, a newer host compiler can build a guest pinned to Rust 1.94.1:
[toolchain]
channel = "1.94.1"
components = ["rust-src"]toolchain_file reads the TOML file immediately and returns an error if it is
invalid, unreadable, or rustup cannot provision the toolchain. Relative file paths resolve
against the calling process's current directory, independently of current_dir.
The selection overrides CARGO, RUSTC, RUSTDOC, and RUSTUP_TOOLCHAIN for
preparation and the final guest command, including after environment clearing.
Rustup installs the selected toolchain using the file's components, targets,
and profile settings without updating installed toolchains or Rustup itself.
Selection uses the builder's resolved environment, including any PATH and
RUSTUP_HOME settings made before toolchain_file.
Sysroot preparation also installs rust-src when needed.
The parent process environment is unchanged. Select a Rust version using channel;
selecting a local compiler directory using [toolchain].path is not supported.
To disable toolchain and component installation, set RUSTUP_AUTO_INSTALL=0 in
your shell. Library calls inherit this setting:
RUSTUP_AUTO_INSTALL=0 cargo buildFor library usage, you can also call .env("RUSTUP_AUTO_INSTALL", "0") before
.toolchain_file(...). Explicit settings override the inherited value. The file
is processed during the toolchain_file call, so settings made afterwards cannot
prevent installation that has already happened.
With installation disabled, the toolchain file still selects the Rust version.
The requested toolchain, required components (including rust-src), and targets
must already be installed; missing prerequisites cause an error. Sysroot
preparation uses the same environment setting to decide whether to install rust-src.
cargo-hyperlight also supports building C guest binaries. To get the compiler and linker flags needed to build a C guest, use:
cargo hyperlight cflags
cargo hyperlight ldflags
cargo hyperlight libsFor example, to compile and link a C guest:
clang $(cargo hyperlight cflags) $(cargo hyperlight ldflags) -o guest main.c $(cargo hyperlight libs)To produce a self-contained, redistributable C sysroot (including headers, libraries, a hyperlight-config utility, and a clang wrapper), use:
cargo hyperlight build-c-sysroot --c-sysroot-dir /path/to/sysrootThis copies the following into the specified directory:
bin/— ahyperlight-configexecutable and aclangwrapperinclude/— header files for the guest C APIlib/— static libraries needed to link a C guest
The produced hyperlight-config executable provides the same --cflags, --ldflags, and --libs flags, and the clang wrapper automatically injects the correct flags when invoked. This allows downstream consumers to build C guests without installing cargo-hyperlight themselves.
To publish a new version:
- Update the version in
Cargo.toml - Commit the change:
git commit -S -s -am "Bump version to X.Y.Z"and open a PR - Create and push a tag:
git tag -s vX.Y.Z && git push origin vX.Y.Z
The CI will automatically run tests, publish to crates.io, and create a GitHub release.