hyperlight_component_macro/
lib.rs

1/*
2Copyright 2025 The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15 */
16
17//! # Component-model bindgen macros
18//!
19//! These macros make it easy to use Wasm Component Model types
20//! (e.g. those described by WIT) to describe the interface between a
21//! Hyperlight host and guest.
22//!
23//! For both host and guest bindings, bindings generation takes in a
24//! binary-encoded wasm component, which should have roughly the
25//! structure of a binary-encoded WIT (in particular, component
26//! import/export kebab-names should have `wit:package/name` namespace
27//! structure, and the same two-level convention for wrapping a
28//! component type into an actual component should be adhered to). If
29//! you are using WIT as the input, it is easy to build such a file
30//! via `wasm-tools component wit -w -o file.wasm file.wit`.
31//!
32//! Both macros can take the path to such a file as a parameter, or,
33//! if one is not provided, will fall back to using the path in the
34//! environment variable `$WIT_WORLD`. A relative path provided either way
35//! will be resolved relative to `$CARGO_MANIFEST_DIR`.
36//!
37//! ## Debugging
38//!
39//! The generated code can be examined by setting the environment
40//! variable `$HYPERLIGHT_COMPONENT_MACRO_DEBUG=/path/to/file.rs`,
41//! which will result in the generated code being written to that
42//! file, which is then included back into the Rust source.
43//!
44//! The macros also can be configured to output a great deal of debug
45//! information about the internal elaboration and codegen
46//! phases. This is logged via the `log` and `env_logger` crates, so
47//! setting `RUST_LOG=debug` before running the compiler should
48//! suffice to produce this output.
49
50extern crate proc_macro;
51
52use hyperlight_component_util::*;
53
54/// Create host bindings for the wasm component type in the file
55/// passed in (or `$WIT_WORLD`, if nothing is passed in). This will
56/// produce all relevant types and trait implementations for the
57/// component type, as well as functions allowing the component to be
58/// instantiated inside a sandbox.
59///
60/// This includes both a primitive `register_host_functions`, which can
61/// be used to directly register the host functions on any sandbox
62/// (and which can easily be used with Hyperlight-Wasm), as well as an
63/// `instantiate()` method on the component trait that makes
64/// instantiating the sandbox particularly ergonomic in core
65/// Hyperlight.
66#[proc_macro]
67pub fn host_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
68    let _ = env_logger::try_init();
69    let path: Option<syn::LitStr> = syn::parse_macro_input!(input as Option<syn::LitStr>);
70    let path = path
71        .map(|x| x.value().into())
72        .unwrap_or_else(|| std::env::var_os("WIT_WORLD").unwrap());
73    util::read_wit_type_from_file(path, |kebab_name, ct| {
74        let decls = emit::run_state(false, false, |s| {
75            rtypes::emit_toplevel(s, &kebab_name, ct);
76            host::emit_toplevel(s, &kebab_name, ct);
77        });
78        util::emit_decls(decls).into()
79    })
80}
81
82/// Create the hyperlight_guest_init() function (which should be
83/// called in hyperlight_main()) for the wasm component type in the
84/// file passed in (or `$WIT_WORLD`, if nothing is passed in). This
85/// function registers Hyperlight functions for component exports
86/// (which are implemented by calling into the trait provided) and
87/// implements the relevant traits for a trivial Host type (by calling
88/// into the Hyperlight host).
89#[proc_macro]
90pub fn guest_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
91    let _ = env_logger::try_init();
92    let path: Option<syn::LitStr> = syn::parse_macro_input!(input as Option<syn::LitStr>);
93    let path = path
94        .map(|x| x.value().into())
95        .unwrap_or_else(|| std::env::var_os("WIT_WORLD").unwrap());
96    util::read_wit_type_from_file(path, |kebab_name, ct| {
97        let decls = emit::run_state(true, false, |s| {
98            // Emit type/trait definitions for all instances in the world
99            rtypes::emit_toplevel(s, &kebab_name, ct);
100            // Emit the host/guest function registrations
101            guest::emit_toplevel(s, &kebab_name, ct);
102        });
103        // Use util::emit_decls() to choose between emitting the token
104        // stream directly and emitting an include!() pointing at a
105        // temporary file, depending on whether the user has requested
106        // a debug temporary file be created.
107        util::emit_decls(decls).into()
108    })
109}