diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 91fc44d88c..fd1edea388 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -184,3 +184,41 @@ jobs: run: | cd rust cargo clippy + + wasm: + name: cargo:wasm + runs-on: ubuntu-latest + env: + # Keep these versions in sync with .github/workflows/wasm.yml. + WASI_SDK_VERSION: "33" + WASI_SDK_RELEASE: "33.0" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - name: Set up Ruby + uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0 + with: + ruby-version: ruby + bundler: none + - name: Update rubygems & bundler + run: gem update --system + - name: Install gems + run: | + bundle config set --local without libs:profilers + bundle install --jobs 4 --retry 3 + - name: Set up vendored RBS source + run: bundle exec rake rust:rbs:sync + - name: Install Rust target + run: | + rustup update --no-self-update stable + rustup default stable + rustup target add wasm32-wasip1 + - name: Install the WASI SDK + run: | + url="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_RELEASE}-x86_64-linux.tar.gz" + mkdir -p "$RUNNER_TEMP/wasi-sdk" + curl -sSL "$url" | tar xz --strip-components=1 -C "$RUNNER_TEMP/wasi-sdk" + echo "WASI_SDK_PATH=$RUNNER_TEMP/wasi-sdk" >> "$GITHUB_ENV" + - name: Build Rust crates for wasm + working-directory: rust + run: cargo build --locked --target wasm32-wasip1 -p ruby-rbs-sys -p ruby-rbs diff --git a/rust/ruby-rbs-sys/build.rs b/rust/ruby-rbs-sys/build.rs index f382a3dcaa..0eb285cc09 100644 --- a/rust/ruby-rbs-sys/build.rs +++ b/rust/ruby-rbs-sys/build.rs @@ -11,15 +11,18 @@ fn main() -> Result<(), Box> { let include = vendor_rbs.join("include"); let c_src = vendor_rbs.join("src"); - build(&include, &c_src)?; + let target = env::var("TARGET").unwrap_or_default(); + let is_wasi = target.contains("wasm32") && target.contains("wasi"); - let bindings = generate_bindings(&include)?; + build(&include, &c_src, is_wasi)?; + + let bindings = generate_bindings(&include, is_wasi)?; write_bindings(&bindings)?; Ok(()) } -fn build(include_dir: &Path, src_dir: &Path) -> Result<(), Box> { +fn build(include_dir: &Path, src_dir: &Path, is_wasi: bool) -> Result<(), Box> { let mut build = cc::Build::new(); build.include(include_dir); @@ -27,6 +30,28 @@ fn build(include_dir: &Path, src_dir: &Path) -> Result<(), Box> { // Suppress unused parameter warnings from C code build.flag_if_supported("-Wno-unused-parameter"); + if is_wasi { + // Compile the C parser with the WASI SDK so that its headers and + // runtime support match the target platform. + println!("cargo:rerun-if-env-changed=WASI_SDK_PATH"); + + let wasi_sdk = PathBuf::from( + env::var("WASI_SDK_PATH").expect("WASI_SDK_PATH must be set for wasm builds"), + ); + build.compiler(wasi_sdk.join("bin").join("clang")); + + let sysroot = wasi_sdk.join("share").join("wasi-sysroot"); + build.flag(format!("--sysroot={}", sysroot.display())); + build.include(sysroot.join("include")); + + println!( + "cargo:rustc-link-search=native={}", + sysroot.join("lib/wasm32-wasi").display() + ); + build.define("_WASI_EMULATED_MMAN", "1"); + println!("cargo:rustc-link-lib=wasi-emulated-mman"); + } + build.files(source_files(src_dir)?); build.try_compile("rbs")?; @@ -64,8 +89,11 @@ fn source_files>(root_dir: P) -> Result, Box Result> { - let bindings = bindgen::Builder::default() +fn generate_bindings( + include_path: &Path, + is_wasi: bool, +) -> Result> { + let mut builder = bindgen::Builder::default() .header("wrapper.h") .clang_arg(format!("-I{}", include_path.display())) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) @@ -172,7 +200,20 @@ fn generate_bindings(include_path: &Path) -> Result