Last active
January 31, 2025 14:39
-
-
Save TheNathannator/9350a183f147c93cd0846328e320ddaf to your computer and use it in GitHub Desktop.
Unity editor script for extracting the Wasmtime NuGet package's runtime binaries when using NugetForUnity, because NugetForUnity doesn't handle them for you.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(import "host" "print_integer" (func $print_integer (param i32))) | |
(memory 1) | |
(global $frame_count (mut i32) (i32.const 0)) | |
(export "frame_count" (global $frame_count)) | |
(func (export "on_update") | |
(i32.add (global.get $frame_count) (i32.const 1)) | |
global.set $frame_count | |
global.get $frame_count | |
call $print_integer | |
) | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.IO; | |
using Wasmtime; | |
using UnityEngine.Assertions; | |
using System; | |
namespace Playground | |
{ | |
public class WasmTest : MonoBehaviour | |
{ | |
private Engine _wasmEngine; | |
private Linker _wasmLinker; | |
private Store _wasmStore; | |
private Module _wasmModule; | |
private Instance _wasmInstance; | |
private int _frameCount = 0; | |
private Action _wasmOnUpdate; | |
private Global.Accessor<int> _wasmFrameCount; | |
private void Awake() | |
{ | |
_wasmEngine = new Engine(); | |
_wasmLinker = new Linker(_wasmEngine); | |
_wasmStore = new Store(_wasmEngine); | |
_wasmLinker.DefineFunction( | |
"native", | |
"print_integer", | |
(int value) => Debug.Log($"From WASM: {value}") | |
); | |
string scriptPath = Path.Join(Application.dataPath, "Scripts", "wasm_test.wast"); | |
_wasmModule = Module.FromTextFile(_wasmEngine, scriptPath); | |
_wasmInstance = _wasmLinker.Instantiate(_wasmStore, _wasmModule); | |
_wasmOnUpdate = _wasmInstance.GetAction("on_update"); | |
_wasmFrameCount = _wasmInstance.GetGlobal("frame_count").Wrap<int>(); | |
} | |
private void OnDestroy() | |
{ | |
// _wasmInstance.Dispose(); | |
_wasmModule.Dispose(); | |
_wasmStore.Dispose(); | |
_wasmLinker.Dispose(); | |
_wasmEngine.Dispose(); | |
} | |
private void Update() | |
{ | |
_frameCount++; | |
_wasmOnUpdate(); | |
var frameCount = _wasmFrameCount.GetValue(); | |
Assert.AreEqual(_frameCount, frameCount); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.IO.Compression; | |
using System.Linq; | |
using UnityEditor; | |
using UnityEngine; | |
namespace Playground.Editor | |
{ | |
/// <summary> | |
/// Manually extracts the <c>runtimes</c> folder for Wasmtime, | |
/// since NugetForUnity doesn't do it for you. | |
/// </summary> | |
public class WasmtimeExtractor : AssetPostprocessor | |
{ | |
private const string ASSET_LABEL = "Wasmtime"; | |
// This directory should be added to your source control ignore file. | |
private const string OUTPUT_DIRECTORY = "Plugins/Wasmtime-runtime"; | |
static WasmtimeExtractor() | |
{ | |
Extract(force: false); | |
} | |
[MenuItem("Playground/Re-extract Wasmtime", false, 0)] | |
public static void ReExtract() => Extract(force: true); | |
private static void Extract(bool force) | |
{ | |
// Because the version number is part of the folder name for packages, | |
// we need to check each folder by hand to find the right one | |
string allPackagesFolder = Path.Join(Application.dataPath, "Packages"); | |
string packageFolder = Directory.EnumerateDirectories(allPackagesFolder, "*", SearchOption.TopDirectoryOnly) | |
.FirstOrDefault((pkg) => Path.GetFileName(pkg.TrimEnd('/', '\\')).StartsWith("Wasmtime")); | |
if (packageFolder == null) | |
{ | |
Debug.LogError("Could not find Wasmtime package folder!"); | |
return; | |
} | |
string packageName = Path.GetFileName(packageFolder.TrimEnd('/', '\\')); | |
string packagePath = Path.Join(packageFolder, packageName + ".nupkg"); | |
string outputFolder = Path.Join(Application.dataPath, OUTPUT_DIRECTORY); | |
if (!Directory.Exists(outputFolder)) | |
Directory.CreateDirectory(outputFolder); | |
string versionFile = Path.Join(outputFolder, "version.txt"); | |
if (File.Exists(versionFile)) | |
{ | |
string version = File.ReadAllText(versionFile); | |
if (!force && version == packageName) | |
return; | |
} | |
using var packageFile = ZipFile.OpenRead(packagePath); | |
foreach (var entry in packageFile.Entries) | |
{ | |
// The `runtimes` folder is structured like this: | |
// runtimes/win-x64/native/wasmtime.dll | |
string[] components = entry.FullName.Split('/'); | |
if (components.Length < 4 || components[0] != "runtimes") | |
continue; | |
string runtime = components[1]; | |
// string "native" = components[2]; | |
string file = components[3]; | |
// Linux ARM64 is not supported by Unity, and trying to delete during the | |
// post-process stage results in errors in the console | |
if (runtime == "linux-arm64") | |
continue; | |
string outputPath = Path.Combine(outputFolder, runtime, file); | |
string outputDir = Path.GetDirectoryName(outputPath); | |
if (!Directory.Exists(outputDir)) | |
Directory.CreateDirectory(outputDir); | |
entry.ExtractToFile(outputPath, overwrite: true); | |
string importPath = Path.Combine("Assets", OUTPUT_DIRECTORY, runtime, file); | |
AssetDatabase.ImportAsset(importPath); | |
} | |
File.WriteAllText(versionFile, packageName); | |
} | |
// Custom post-processing to automatically apply the right labels | |
private static void OnPostprocessAllAssets( | |
string[] importedAssets, | |
string[] deletedAssets, | |
string[] movedAssets, | |
string[] movedFromAssetPaths, | |
bool didDomainReload | |
) | |
{ | |
try | |
{ | |
AssetDatabase.StartAssetEditing(); | |
foreach (string asset in importedAssets) | |
{ | |
string[] components = asset.Split('/', '\\'); | |
if (components.Length < 5 || | |
components[0] != "Assets" || | |
components[1] != "Plugins" || | |
components[2] != "Wasmtime-runtime") | |
{ | |
continue; | |
} | |
string runtime = components[3]; | |
// string file = components[4]; | |
var plugin = AssetImporter.GetAtPath(asset) as PluginImporter; | |
if (plugin == null) | |
continue; | |
SetPluginTargetProperties(plugin, runtime); | |
AssetDatabase.SetLabels(plugin, new[] { ASSET_LABEL }); | |
} | |
} | |
catch (Exception ex) | |
{ | |
Debug.LogError($"Error while importing Wasmtime runtime assets!"); | |
Debug.LogException(ex); | |
} | |
finally | |
{ | |
AssetDatabase.StopAssetEditing(); | |
} | |
} | |
private static void SetPluginTargetProperties(PluginImporter plugin, string runtime) | |
{ | |
void SetProperties(PluginImporter plugin, BuildTarget target, string editorPlatform, string cpu) | |
{ | |
plugin.SetCompatibleWithPlatform(target, true); | |
plugin.SetPlatformData(target, "CPU", cpu); | |
plugin.SetCompatibleWithEditor(true); | |
plugin.SetEditorData("OS", editorPlatform); | |
plugin.SetEditorData("CPU", cpu); | |
} | |
plugin.SetCompatibleWithAnyPlatform(false); | |
switch (runtime) | |
{ | |
case "win-x86": | |
case "win7-x86": | |
case "win10-x86": | |
SetProperties(plugin, BuildTarget.StandaloneWindows, "Windows", "AnyCPU"); | |
break; | |
case "win-x64": | |
case "win7-x64": | |
case "win10-x64": | |
SetProperties(plugin, BuildTarget.StandaloneWindows64, "Windows", "x86_64"); | |
break; | |
case "osx-x64": | |
SetProperties(plugin, BuildTarget.StandaloneOSX, "OSX", "x86_64"); | |
break; | |
case "osx-arm64": | |
SetProperties(plugin, BuildTarget.StandaloneOSX, "OSX", "ARM64"); | |
break; | |
case "linux-x64": | |
SetProperties(plugin, BuildTarget.StandaloneLinux64, "Linux", "x86_64"); | |
break; | |
case "linux-arm64": | |
// Not supported by Unity | |
break; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment