Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 99 additions & 4 deletions .github/workflows/CreateRelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,89 @@ on:

permissions:
contents: write
id-token: write # Required for crates.io trusted publishing
id-token: write # Required for crates.io and NuGet trusted publishing

jobs:

benchmarks:
uses: ./.github/workflows/Benchmarks.yml

# Builds the native libraries of the experimental .NET packages.
dotnet-native:
strategy:
matrix:
rid: [linux-x64, win-x64]
runs-on: ${{ matrix.rid == 'win-x64' && fromJSON(format('["self-hosted", "Windows", "X64", "1ES.Pool=hld-win2025-amd", "JobId=dotnet-native-{0}-{1}-{2}"]', github.run_id, github.run_number, github.run_attempt)) || 'ubuntu-24.04' }}
steps:
- uses: actions/checkout@v5
- name: Hyperlight setup
uses: hyperlight-dev/ci-setup-workflow@v2.3.1
with:
rust-toolchain: "1.94"
just-version: 1.59.0
- name: Build native libraries
run: just dotnet-native
- name: Upload native libraries
uses: actions/upload-artifact@v4
with:
name: dotnet-native-${{ matrix.rid }}
path: dotnet/native/
if-no-files-found: error

# Packs the experimental .NET packages with every native library.
dotnet-pack:
needs: [ dotnet-native ]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: extractions/setup-just@v3
with:
just-version: "1.59.0"
- uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: 8.0.x
- name: Download native libraries
uses: actions/download-artifact@v5
with:
pattern: dotnet-native-*
merge-multiple: true
path: dotnet/native
- name: Pack
# Release packages take hyperlight-wasm's version with the preview suffix from
# Directory.Build.props. Other builds are development builds of the next minor release.
run: |
version=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "hyperlight-wasm") | .version')
if [[ "${{ github.ref }}" == refs/heads/release/* ]]; then
just dotnet-pack-prebuilt "$version"
else
IFS=. read -r major minor _ <<< "$version"
just dotnet-pack-prebuilt "$major.$((minor + 1)).0" "dev.${{ github.run_number }}"
fi
env:
# Reading metadata needs no particular toolchain.
RUSTUP_TOOLCHAIN: stable
shell: bash
- name: Check native libraries in packages
run: |
check() {
unzip -l $1 | grep -qF "$2" || { echo "::error::$1 is missing $2"; exit 1; }
}
check "dotnet/artifacts/Hyperlight.Wasm.[0-9]*.nupkg" runtimes/linux-x64/native/libhyperlight_wasm_capi.so
check "dotnet/artifacts/Hyperlight.Wasm.[0-9]*.nupkg" runtimes/win-x64/native/hyperlight_wasm_capi.dll
check "dotnet/artifacts/Hyperlight.Wasm.Build.*.nupkg" runtimes/linux-x64/native/libhyperlight_wasm_csharp_bindgen.so
check "dotnet/artifacts/Hyperlight.Wasm.Build.*.nupkg" runtimes/win-x64/native/hyperlight_wasm_csharp_bindgen.dll
shell: bash
- name: Upload packages
uses: actions/upload-artifact@v4
with:
name: dotnet-packages
path: dotnet/artifacts/*.nupkg
if-no-files-found: error

publish:
# see https://github.com/orgs/community/discussions/26286#discussioncomment-3251208 for why we need to check the ref
if: ${{ contains(github.ref, 'refs/heads/release/') }} || ${{ github.ref=='refs/heads/main' }}
needs: [ benchmarks ]
needs: [ benchmarks, dotnet-pack ]
# LLVM 18 has no apt.llvm.org packages for Ubuntu 26.04.
runs-on: ubuntu-24.04
steps:
Expand Down Expand Up @@ -47,14 +119,19 @@ jobs:
for BENCHMARK in benchmarks_*; do
tar -zcvf $BENCHMARK.tar.gz $BENCHMARK
done
- name: Download .NET packages
uses: actions/download-artifact@v5
with:
name: dotnet-packages
path: dotnet-packages
- name: Create pre-release
if: ${{ github.ref=='refs/heads/main' }}
run: |
echo "PWD: $PWD"
ls -alR
gh release delete dev-latest -y --cleanup-tag || true
gh release create dev-latest -t "Latest Development Build From Dev Branch" --latest=false -p \
benchmarks_*.tar.gz
benchmarks_*.tar.gz dotnet-packages/*.nupkg
env:
GH_TOKEN: ${{ github.token }}
shell: bash
Expand All @@ -64,7 +141,7 @@ jobs:
echo "PWD: $PWD"
ls -alR
gh release create v${{ env.HYPERLIGHTWASM_VERSION }} -t "Release v${{ env.HYPERLIGHTWASM_VERSION }}" --generate-notes \
benchmarks_*.tar.gz
benchmarks_*.tar.gz dotnet-packages/*.nupkg
env:
GH_TOKEN: ${{ github.token }}
shell: bash
Expand Down Expand Up @@ -98,3 +175,21 @@ jobs:
env:
CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }}
shell: bash

- name: Set up .NET
if: ${{ contains(github.ref, 'refs/heads/release/') }}
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: 8.0.x
- name: Authenticate with NuGet
if: ${{ contains(github.ref, 'refs/heads/release/') }}
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1
id: nuget-login
with:
user: ${{ vars.NUGET_USER }}
- name: Publish to NuGet
if: ${{ contains(github.ref, 'refs/heads/release/') }}
run: dotnet nuget push "dotnet-packages/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key "$NUGET_API_KEY" --skip-duplicate
env:
NUGET_API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }}
shell: bash
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ BenchmarkDotNet.Artifacts/
project.lock.json
project.fragment.lock.json
artifacts/
dotnet/native/

# Tye
.tye/
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
* Add `SandboxBuilder::with_interrupt_vcpu_sigrtmin_offset` to change the signal that interrupts a guest on Linux.
* Experimental: add the `experimental_runtime_file` feature, with `SandboxBuilder::with_runtime_file` to load the guest runtime from a file. Snapshots of such a sandbox cannot be saved.
* Experimental: add the `hyperlight-wasm-capi` crate, a C API that calls component functions with arguments and results in Hyperlight's component wire format. It is not published.
* Experimental: add the `Hyperlight.Wasm` and `Hyperlight.Wasm.Build` .NET packages, which run components from .NET with typed C# bindings generated from WIT at build time. See `dotnet/README.md`. They are not published.
* Experimental: add the `Hyperlight.Wasm` and `Hyperlight.Wasm.Build` .NET packages, which run components from .NET with typed C# bindings generated from WIT at build time. See `dotnet/README.md`. They are published to NuGet as prereleases for linux-x64 and win-x64.
* Experimental: add `hyperlight-wasm-aot experimental-build-runtime`, which builds a guest runtime for a WIT world.

### Changed
Expand Down
16 changes: 16 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,22 @@ dotnet-pack target="release":
dotnet pack dotnet/src/Hyperlight.Wasm -c {{ if target == "debug" {"Debug"} else {"Release"} }} -o dotnet/artifacts
dotnet pack dotnet/src/Hyperlight.Wasm.Build -c {{ if target == "debug" {"Debug"} else {"Release"} }} -o dotnet/artifacts

dotnet-rid := (if os() == "windows" { "win" } else if os() == "macos" { "osx" } else { "linux" }) + (if arch == "x86_64" { "-x64" } else { "-arm64" })
native-lib-prefix := if os() == "windows" { "" } else { "lib" }
native-lib-suffix := if os() == "windows" { ".dll" } else if os() == "macos" { ".dylib" } else { ".so" }

# Builds the .NET packages' native libraries in release and copies them to dotnet/native/runtimes/<rid>/native
dotnet-native:
cargo build -p hyperlight-wasm-capi -p hyperlight-wasm-csharp-bindgen --release
mkdir {{ mkdir-arg }} dotnet/native/runtimes/{{ dotnet-rid }}/native
{{ if os() == "windows" { "Copy-Item -Force" } else { "cp" } }} target/release/{{ native-lib-prefix }}hyperlight_wasm_capi{{ native-lib-suffix }} dotnet/native/runtimes/{{ dotnet-rid }}/native/
{{ if os() == "windows" { "Copy-Item -Force" } else { "cp" } }} target/release/{{ native-lib-prefix }}hyperlight_wasm_csharp_bindgen{{ native-lib-suffix }} dotnet/native/runtimes/{{ dotnet-rid }}/native/

# Packs the .NET packages with every runtime in dotnet/native/runtimes into dotnet/artifacts
dotnet-pack-prebuilt version="" suffix="":
dotnet pack dotnet/src/Hyperlight.Wasm -c Release -o dotnet/artifacts -p:HyperlightNativeRuntimesDir={{ justfile_directory() }}/dotnet/native/runtimes/ {{ if version == "" {""} else {"-p:VersionPrefix=" + version} }} {{ if suffix == "" {""} else {"-p:VersionSuffix=" + suffix} }}
dotnet pack dotnet/src/Hyperlight.Wasm.Build -c Release -o dotnet/artifacts -p:HyperlightNativeRuntimesDir={{ justfile_directory() }}/dotnet/native/runtimes/ {{ if version == "" {""} else {"-p:VersionPrefix=" + version} }} {{ if suffix == "" {""} else {"-p:VersionSuffix=" + suffix} }}

# Test a component and a module compiled with pulley
examples-pulley target=default-target features="": (build-pulley-rust-component-examples target features) (build-pulley-rust-wasm-examples target features) (run-examples-pulley target features)

Expand Down
4 changes: 2 additions & 2 deletions dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Run WebAssembly components in Hyperlight micro VMs from .NET, with typed binding
* `Hyperlight.Wasm` is the runtime library. It wraps the native `hyperlight-wasm-capi` library.
* `Hyperlight.Wasm.Build` generates C# from WIT at build time. It needs no Rust toolchain.

Both target .NET 8 or later. The packages hold native libraries for the runtime identifier they were packed on.
Both target .NET 8 or later. The published packages hold native libraries for linux-x64 and win-x64.

## Usage

Expand Down Expand Up @@ -90,7 +90,7 @@ Hyperlight runs a helper process, `hyperlight_surrogate_<hash>.exe`, for each sa

```sh
just dotnet-test # builds the native libraries, a test runtime and component, then runs the tests
just dotnet-pack # packs both packages into dotnet/artifacts
just dotnet-pack # packs both packages for this machine's runtime identifier into dotnet/artifacts
```

`HyperlightNativeDir` overrides where the projects find the native libraries.
11 changes: 10 additions & 1 deletion dotnet/src/Hyperlight.Wasm.Build/Hyperlight.Wasm.Build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,22 @@
<ItemGroup>
<None Include="build/**" Pack="true" PackagePath="build/" />
<None Include="$(OutputPath)$(AssemblyName).dll" Pack="true" PackagePath="tasks/$(TargetFramework)/" Visible="false" />
</ItemGroup>
<!-- Packs the build machine's library, or every runtime in HyperlightNativeRuntimesDir when it is set. -->
<ItemGroup Condition="'$(HyperlightNativeRuntimesDir)' == ''">
<None Include="$(HyperlightBindgenNative)"
Pack="true"
PackagePath="tasks/$(TargetFramework)/runtimes/$(HyperlightNativeRid)/native/"
Visible="false" />
</ItemGroup>
<ItemGroup Condition="'$(HyperlightNativeRuntimesDir)' != ''">
<None Include="$(HyperlightNativeRuntimesDir)**/libhyperlight_wasm_csharp_bindgen.so;$(HyperlightNativeRuntimesDir)**/hyperlight_wasm_csharp_bindgen.dll;$(HyperlightNativeRuntimesDir)**/libhyperlight_wasm_csharp_bindgen.dylib"
Pack="true"
PackagePath="tasks/$(TargetFramework)/runtimes/%(RecursiveDir)"
Visible="false" />
</ItemGroup>

<Target Name="CheckHyperlightNativeLibrary" BeforeTargets="GenerateNuspec">
<Target Name="CheckHyperlightNativeLibrary" BeforeTargets="GenerateNuspec" Condition="'$(HyperlightNativeRuntimesDir)' == ''">
<Error Condition="!Exists('$(HyperlightBindgenNative)')"
Text="Missing '$(HyperlightBindgenNative)'. Build it with `cargo build -p hyperlight-wasm-csharp-bindgen --profile $(HyperlightCargoProfile)`, using `dev` for debug, or set HyperlightNativeDir." />
</Target>
Expand Down
13 changes: 10 additions & 3 deletions dotnet/src/Hyperlight.Wasm/Hyperlight.Wasm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,26 @@
<InternalsVisibleTo Include="Hyperlight.Wasm.Tests" />
</ItemGroup>

<!-- The native library is copied next to referencing apps and packed for the build machine's runtime identifier. -->
<!-- The native library is copied next to referencing apps and packed for the build machine's runtime identifier.
With HyperlightNativeRuntimesDir set, every runtime in it is packed instead. -->
<PropertyGroup>
<HyperlightCapiLibrary>$(HyperlightNativeDir)$(HyperlightNativePrefix)hyperlight_wasm_capi$(HyperlightNativeSuffix)</HyperlightCapiLibrary>
</PropertyGroup>
<ItemGroup>
<ItemGroup Condition="'$(HyperlightNativeRuntimesDir)' == ''">
<None Include="$(HyperlightCapiLibrary)"
Link="$(HyperlightNativePrefix)hyperlight_wasm_capi$(HyperlightNativeSuffix)"
CopyToOutputDirectory="PreserveNewest"
Pack="true"
PackagePath="runtimes/$(HyperlightNativeRid)/native/" />
</ItemGroup>
<ItemGroup Condition="'$(HyperlightNativeRuntimesDir)' != ''">
<None Include="$(HyperlightNativeRuntimesDir)**/libhyperlight_wasm_capi.so;$(HyperlightNativeRuntimesDir)**/hyperlight_wasm_capi.dll;$(HyperlightNativeRuntimesDir)**/libhyperlight_wasm_capi.dylib"
Pack="true"
PackagePath="runtimes/%(RecursiveDir)"
Visible="false" />
</ItemGroup>

<Target Name="CheckHyperlightNativeLibrary" BeforeTargets="PrepareForBuild;GenerateNuspec">
<Target Name="CheckHyperlightNativeLibrary" BeforeTargets="PrepareForBuild;GenerateNuspec" Condition="'$(HyperlightNativeRuntimesDir)' == ''">
<Error Condition="!Exists('$(HyperlightCapiLibrary)')"
Text="Missing '$(HyperlightCapiLibrary)'. Build it with `cargo build -p hyperlight-wasm-capi --profile $(HyperlightCargoProfile)`, using `dev` for debug, or set HyperlightNativeDir." />
</Target>
Expand Down
Loading