如何在Cargo工作区将WASM产物嵌入Rust二进制程序
解决Cargo工作区中嵌入Wasm模块的路径与重建问题
问题背景
你的Cargo工作区结构如下:
Cargo.toml |-foo | |-Cargo.toml | -src | |-main.rs |-foo-runtime | |-Cargo.toml | -src | |-lib.rs |-target
foo-runtime是编译为Wasm的库,配置如下:
cargo-features = ["per-package-target"] [package] default-target = "wasm32-unknown-unknown" [lib] crate-type = ["cdylib"]
编译后生成的Wasm文件路径为target/wasm32-unknown-unknown/<profile>/foo_runtime.wasm,现在需要让foo二进制 crate 嵌入或加载该Wasm文件,并解决路径构建和依赖重建的问题。
1. 正确构建WASM文件路径
编译时嵌入(include_bytes!)
利用Cargo提供的环境变量动态生成路径,自动适配debug/release编译模式:
use std::sync::LazyLock; use wasmer::{imports, Instance, Module, Store}; // 动态拼接Wasm文件路径:适配工作区结构和编译profile static RUNTIME_WASM: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), // 获取foo crate的根目录 "/../target/wasm32-unknown-unknown/", env!("PROFILE"), // 自动取debug或release "/foo_runtime.wasm" )); static INSTANCE: LazyLock<wasmer::Instance> = LazyLock::new(|| { let mut store = Store::default(); let module = Module::from_binary(&store, RUNTIME_WASM) .unwrap_or_else(|e| panic!("couldn't load WASM module: {e}")); let import_object = imports! {}; Instance::new(&mut store, &module, &import_object) .unwrap_or_else(|e| panic!("failed to create wasmer Instance: {e}")) });
运行时加载
如果选择运行时读取文件,同样用环境变量构建路径:
use std::fs; use std::sync::LazyLock; use wasmer::{imports, Instance, Module, Store}; static INSTANCE: LazyLock<wasmer::Instance> = LazyLock::new(|| { // 构建Wasm文件路径 let wasm_path = format!( "{}/../target/wasm32-unknown-unknown/{}/foo_runtime.wasm", env!("CARGO_MANIFEST_DIR"), env!("PROFILE") ); let wasm_bytes = fs::read(&wasm_path) .unwrap_or_else(|e| panic!("failed to read WASM file at {}: {e}", wasm_path)); let mut store = Store::default(); let module = Module::from_binary(&store, &wasm_bytes) .unwrap_or_else(|e| panic!("couldn't load WASM module: {e}")); let import_object = imports! {}; Instance::new(&mut store, &module, &import_object) .unwrap_or_else(|e| panic!("failed to create wasmer Instance: {e}")) });
2. 依赖变更时触发自动重建
默认情况下,Cargo不会跟踪target目录下的文件,也不会感知foo-runtime的代码变更对foo的影响。通过在foo crate中添加build.rs脚本,实现依赖跟踪和预编译:
步骤1:创建foo/build.rs
fn main() { // 当foo-runtime的源码或配置文件变更时,触发foo的重建 println!("cargo:rerun-if-changed=../foo-runtime/src"); println!("cargo:rerun-if-changed=../foo-runtime/Cargo.toml"); // 强制预编译foo-runtime为Wasm目标,确保Wasm文件始终是最新的 let build_status = std::process::Command::new("cargo") .arg("build") .arg("--package") .arg("foo-runtime") .arg("--target") .arg("wasm32-unknown-unknown") // 自动匹配当前编译profile(debug/release) .arg(format!("--{}", env!("PROFILE"))) .status() .expect("Failed to execute cargo build for foo-runtime"); assert!(build_status.success(), "foo-runtime compilation failed"); }
步骤2:确保foo/Cargo.toml识别build脚本
无需额外依赖,只要build.rs存在于foo crate根目录,Cargo会自动执行它。
验证效果
- 修改
foo-runtime/src/lib.rs中的代码,比如修改add_i64的实现 - 执行
cargo build(在工作区根目录或foo目录) - 观察到
foo-runtime会先重新编译,随后foo也会触发重建,嵌入的Wasm文件自动更新
内容的提问来源于stack exchange,提问作者Aaron Roth
相关产品推荐
相关产品推荐

