Skip to content

Add scaffold subcommand#36

Open
ludfjig wants to merge 2 commits intohyperlight-dev:mainfrom
ludfjig:scaffold
Open

Add scaffold subcommand#36
ludfjig wants to merge 2 commits intohyperlight-dev:mainfrom
ludfjig:scaffold

Conversation

@ludfjig
Copy link
Copy Markdown
Contributor

@ludfjig ludfjig commented Apr 1, 2026

Initial draft for a scaffolding command to create a ready-to-compile/run hyperlight project (with guest). Currently just targets 0.13. Might be useful in the future to decouple the templating from the template, so we can have multiple versions etc etc, and releasing a new hyperlight version won't need a new cargo-hyperlight release.

I plan to include this command in main hyperlight repo docs/markdown in something like the README.md or getting-started.md docs.

I'm particularly looking for feedback on the actual templated code. I made it slightly longer than I initially intended, but I think it makes sense given it shows off a lot important hyperlight features. I also think it makes sense to add a wit host+guest to this command as well, but can do that later

  • TODO, I'm not yet sure how to decide between using debug/release target folder for loading guest

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
@ludfjig ludfjig requested a review from jprendes April 1, 2026 04:35
@ludfjig ludfjig marked this pull request as ready for review April 1, 2026 04:36
@ludfjig ludfjig requested a review from jsturtevant April 1, 2026 04:36
Comment thread src/scaffold/mod.rs
.file_name()
.context("Invalid project path")?
.to_str()
.context("Project name must be valid UTF-8")?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might want to do a bit more validation here on the name? I am not sure what makes up a valid project name but we would want to strip strings from beginning end etc. Maybe special characters?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread src/scaffold/mod.rs
use anyhow::{Context, Result, ensure};
use clap::Parser;

const HYPERLIGHT_VERSION: &str = "0.13";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we take this as input so we don't need to bump the tool everytime?

Copy link
Copy Markdown
Contributor Author

@ludfjig ludfjig Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this, but I'm not sure it makes sense, since we might have breaking changes. I think it's better we just bump it manually and at that time make sure the template is up to date. Perhaps we could do a quick check to make sure the version is the latest released version and add a warning if not to make users aware they are using an old version. Although this does require a cargo-hyperlight release for every hyperlight release, which I am unsure if we currently do.

Copy link
Copy Markdown
Contributor

@jsturtevant jsturtevant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will make it much easier to get started and be successful! Did you consider any existing templating libraries in the eco system? This is pretty straight forward for now but there are often a lot of edge cases for projects that are larger.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
@ludfjig
Copy link
Copy Markdown
Contributor Author

ludfjig commented Apr 9, 2026

I think this will make it much easier to get started and be successful! Did you consider any existing templating libraries in the eco system? This is pretty straight forward for now but there are often a lot of edge cases for projects that are larger.

I thought this was simple enough so didn't really consider it, but happy to use something if you have ideas

[dependencies]
hyperlight-guest = "{version}"
hyperlight-guest-bin = "{version}"
hyperlight-common = { version = "{version}", default-features = false }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should fix hyperlight-guest to re-export the things from common needed for the most common workflows.
Ideally we should only depend on one crate, the others should be re-export, so that we don't force users to keep versions in sync.

Copy link
Copy Markdown
Contributor

@jprendes jprendes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. It will be very useful.
I left a few comments, mostly with my preferences, feel free to dismiss if you don't agree.

#![no_std]
#![no_main]
extern crate alloc;
extern crate hyperlight_guest_bin;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need this, right?

Comment on lines +43 to +44
COUNTER.fetch_add(1, Ordering::Relaxed);
Ok(COUNTER.load(Ordering::Relaxed))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
COUNTER.fetch_add(1, Ordering::Relaxed);
Ok(COUNTER.load(Ordering::Relaxed))
let old = COUNTER.fetch_add(1, Ordering::Relaxed);
Ok(old + 1)

Comment on lines +9 to +15
let guest_path = ["debug", "release"]
.iter()
.map(|p| base.join(p).join("{guest_name}"))
.find(|p| p.exists())
.expect(
"guest binary not found - build it first with: cd ../guest && cargo hyperlight build",
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would either use std::env::args or a use debug, to make the example as minimal as possible

// Create a sandbox from the guest binary. It starts uninitialized so you
// can register host functions before the guest begins executing.
let mut sandbox = UninitializedSandbox::new(
GuestBinary::FilePath(guest_path.display().to_string()),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-O GuestBinary::FilePath takes a String and not a PathBuf?? We must fix that (not all valid paths are valid utf-8 valid in all platforms)

Comment on lines +61 to +75
let days = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
];
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock before Unix epoch")
.as_secs();
// January 1, 1970 was a Thursday (day index 3 when Monday = 0).
Ok(days[((secs / (60 * 60 * 24) + 3) % 7) as usize].to_string())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have it allways return "Monday", to keep it as minimal as possible and not distract from the logic we really care about

maybe with a funny comment

// It's always mondays here, sorry Garfield!

or similar, so that people don't think it's a bug

use hyperlight_host::{GuestBinary, MultiUseSandbox, UninitializedSandbox};

fn main() -> hyperlight_host::Result<()> {
// TODO: support aarch64-hyperlight-none when aarch64 guests are supported.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the TODO from here, we don't want a TODO in the generated file.
This is not a TODO for the user, it's for us.
Maybe do something like
"../target/{arch}-hyperlight-none"
and we put the TODO when we replace it in the template
.replace("{arch}", "x86_64") // TODO: ...

Comment thread src/scaffold/mod.rs
Comment on lines +24 to +26
/// Generate only a guest project instead of both host and guest.
#[arg(long, default_value_t = false)]
guest_only: bool,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about
--no-host
and
--no-guest

specifying both would be incompatible, I think you can use #[arg(conflicts_with = "no_host")]

Comment thread src/scaffold/mod.rs
Comment on lines +19 to +22
struct ScaffoldCli {
/// Path to create the project at. The directory name is used as the crate
/// name (like `cargo new`).
path: PathBuf,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is similar to cargo new, why no call this "new"? cargo hyperlight new

Comment thread src/main.rs
std::process::exit(1);
}
}
Some(a) if a == "scaffold" => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use "new" or "init" like the cargo subcommands?

Comment on lines +47 to +58
// Called once when the guest binary is loaded, during evolve().
// Use this for initialization.
#[unsafe(no_mangle)]
pub extern "C" fn hyperlight_main() {}

// Called when the host calls a guest function not handled by #[guest_function].
// You usually don't need to modify this.
#[unsafe(no_mangle)]
pub fn guest_dispatch_function(function_call: FunctionCall) -> Result<Vec<u8>> {
let function_name = function_call.function_name;
bail!(ErrorCode::GuestFunctionNotFound => "{function_name}");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to work on this so that these two functions are optional :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants