You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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会自动执行它。


验证效果

  1. 修改foo-runtime/src/lib.rs中的代码,比如修改add_i64的实现
  2. 执行cargo build(在工作区根目录或foo目录)
  3. 观察到foo-runtime会先重新编译,随后foo也会触发重建,嵌入的Wasm文件自动更新

内容的提问来源于stack exchange,提问作者Aaron Roth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 11:02:51