From 3d2c317e4d7b575c1659f192d36580758c30ba27 Mon Sep 17 00:00:00 2001 From: xks <2777389616@qq.com> Date: Sat, 7 Feb 2026 22:23:54 +0800 Subject: [PATCH 1/9] added wasm-unknown-emscripten support --- build.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index 677988e..6493df6 100644 --- a/build.rs +++ b/build.rs @@ -36,11 +36,24 @@ fn generate_bindings<'a>( // The bindgen::Builder is the main entry point // to bindgen, and lets you build up options for // the resulting bindings. - let builder = include_paths + let mut builder = include_paths .into_iter() .fold(bindgen::Builder::default(), |builder, path| { builder.clang_arg(format!("-I{}", path.to_string_lossy())) }); + if env::var("TARGET") + .map(|target| target.contains("emscripten")) + .unwrap_or(false) + { + println!("cargo:rerun-if-env-changed=EMSDK"); + if let Ok(emsdk) = env::var("EMSDK") { + let sysroot = Path::new(&emsdk).join("upstream/emscripten/cache/sysroot"); + if sysroot.is_dir() { + builder = builder.clang_arg(format!("--sysroot={}", sysroot.display())); + } + } + } + let c_bindings = builder // The input header we would like to generate bindings for. // This is a trivial wrapper header so that the HiGHS headers @@ -64,12 +77,23 @@ fn generate_bindings<'a>( #[cfg(feature = "build")] fn build() -> bool { use cmake::Config; + let target = env::var("TARGET").unwrap(); + let emscripten = target.contains("emscripten"); let mut dst = Config::new("HiGHS"); if cfg!(feature = "ninja") { dst.generator("Ninja"); } + // `cmake` crate default C++ flags inject `-fno-exceptions` for this target, + // but HiGHS requires C++ exceptions. Use explicit flags for emscripten. + if emscripten { + dst.no_default_flags(true); + dst.cxxflag("-fexceptions"); + dst.define("BUILD_CXX_EXE", "OFF"); + dst.define("BUILD_EXAMPLES", "OFF"); + } + // Avoid using downstream project's profile setting for HiGHS build. if cfg!(feature = "highs_release") { dst.profile("Release"); @@ -94,11 +118,10 @@ fn build() -> bool { println!("cargo:rustc-link-lib=z"); } - let target = env::var("TARGET").unwrap(); let apple = target.contains("apple"); let linux = target.contains("linux"); let mingw = target.contains("pc-windows-gnu"); - if apple { + if apple || emscripten { println!("cargo:rustc-link-lib=c++"); } else if linux || mingw { println!("cargo:rustc-link-lib=stdc++"); From 26a938a40aca203410aaf0082aae87bfd235980c Mon Sep 17 00:00:00 2001 From: xks <2777389616@qq.com> Date: Tue, 10 Feb 2026 22:36:03 +0800 Subject: [PATCH 2/9] ci: add wasm32-unknown-emscripten build job --- .github/workflows/rust.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4bdafac..fe0f5cf 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -47,3 +47,21 @@ jobs: cd .. cargo clean cargo build --no-default-features --features "discover" + + wasm-emscripten: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Install emsdk + run: | + git clone https://github.com/emscripten-core/emsdk.git + ./emsdk/emsdk install latest + ./emsdk/emsdk activate latest + - name: Build wasm32-unknown-emscripten + run: | + EMSDK_QUIET=1 source ./emsdk/emsdk_env.sh + rustup target add wasm32-unknown-emscripten + cargo clean + cargo build --release --target wasm32-unknown-emscripten From 59c774ee93055e3f2ce6df593cb299cc92ef89b0 Mon Sep 17 00:00:00 2001 From: xks <2777389616@qq.com> Date: Tue, 10 Feb 2026 22:37:01 +0800 Subject: [PATCH 3/9] ci: add workflow_dispatch trigger --- .github/workflows/rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fe0f5cf..b99fec3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,6 +1,7 @@ name: Rust on: + workflow_dispatch: push: branches: [ master ] pull_request: From 07fbeb3d95697f4fdd125930f4311104aec69f9e Mon Sep 17 00:00:00 2001 From: xks <2777389616@qq.com> Date: Tue, 10 Feb 2026 22:52:38 +0800 Subject: [PATCH 4/9] removed workflow dispatch --- .github/workflows/rust.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b99fec3..fe0f5cf 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,7 +1,6 @@ name: Rust on: - workflow_dispatch: push: branches: [ master ] pull_request: From b92d7a6efcdb12dc13198a7b432814d0b8b18675 Mon Sep 17 00:00:00 2001 From: xks <2777389616@qq.com> Date: Sun, 15 Feb 2026 14:50:17 +0800 Subject: [PATCH 5/9] fixed according to pr comments --- .github/workflows/rust.yml | 14 ++++++++------ build.rs | 25 ++++++++++++++----------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fe0f5cf..0db48c1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -50,18 +50,20 @@ jobs: wasm-emscripten: runs-on: ubuntu-latest + env: + EMSDK_VERSION: 4.0.23 + EMSDK_CACHE_FOLDER: emsdk-cache steps: - uses: actions/checkout@v4 with: submodules: recursive - - name: Install emsdk - run: | - git clone https://github.com/emscripten-core/emsdk.git - ./emsdk/emsdk install latest - ./emsdk/emsdk activate latest + - name: Setup emsdk + uses: mymindstorm/setup-emsdk@v14 + with: + version: ${{ env.EMSDK_VERSION }} + actions-cache-folder: ${{ env.EMSDK_CACHE_FOLDER }} - name: Build wasm32-unknown-emscripten run: | - EMSDK_QUIET=1 source ./emsdk/emsdk_env.sh rustup target add wasm32-unknown-emscripten cargo clean cargo build --release --target wasm32-unknown-emscripten diff --git a/build.rs b/build.rs index 6493df6..d540db0 100644 --- a/build.rs +++ b/build.rs @@ -41,17 +41,19 @@ fn generate_bindings<'a>( .fold(bindgen::Builder::default(), |builder, path| { builder.clang_arg(format!("-I{}", path.to_string_lossy())) }); - if env::var("TARGET") - .map(|target| target.contains("emscripten")) - .unwrap_or(false) - { + + if env::var("TARGET").unwrap().contains("emscripten") { println!("cargo:rerun-if-env-changed=EMSDK"); - if let Ok(emsdk) = env::var("EMSDK") { - let sysroot = Path::new(&emsdk).join("upstream/emscripten/cache/sysroot"); - if sysroot.is_dir() { - builder = builder.clang_arg(format!("--sysroot={}", sysroot.display())); - } + let emsdk = env::var("EMSDK") + .expect("TARGET is emscripten but EMSDK is not set; needed for bindgen sysroot"); + let sysroot = Path::new(&emsdk).join("upstream/emscripten/cache/sysroot"); + if !sysroot.is_dir() { + panic!( + "TARGET is emscripten but EMSDK sysroot is missing: {}", + sysroot.display() + ); } + builder = builder.clang_arg(format!("--sysroot={}", sysroot.display())); } let c_bindings = builder @@ -85,13 +87,14 @@ fn build() -> bool { dst.generator("Ninja"); } + dst.define("BUILD_CXX_EXE", "OFF"); + dst.define("BUILD_EXAMPLES", "OFF"); + // `cmake` crate default C++ flags inject `-fno-exceptions` for this target, // but HiGHS requires C++ exceptions. Use explicit flags for emscripten. if emscripten { dst.no_default_flags(true); dst.cxxflag("-fexceptions"); - dst.define("BUILD_CXX_EXE", "OFF"); - dst.define("BUILD_EXAMPLES", "OFF"); } // Avoid using downstream project's profile setting for HiGHS build. From bf2a3f8e158663641fc2663d8fef27d9e3af3d11 Mon Sep 17 00:00:00 2001 From: xks <2777389616@qq.com> Date: Sun, 15 Feb 2026 15:06:18 +0800 Subject: [PATCH 6/9] rerun --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0db48c1..a634836 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -51,7 +51,7 @@ jobs: wasm-emscripten: runs-on: ubuntu-latest env: - EMSDK_VERSION: 4.0.23 + EMSDK_VERSION: 5.0.0 EMSDK_CACHE_FOLDER: emsdk-cache steps: - uses: actions/checkout@v4 From 785f1fe1df161c72e60a4f620dea66da79671374 Mon Sep 17 00:00:00 2001 From: xks <2777389616@qq.com> Date: Sun, 15 Feb 2026 15:17:58 +0800 Subject: [PATCH 7/9] use pyodide fork --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a634836..64dcb76 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -58,7 +58,7 @@ jobs: with: submodules: recursive - name: Setup emsdk - uses: mymindstorm/setup-emsdk@v14 + uses: pyodide/setup-emsdk@v15 with: version: ${{ env.EMSDK_VERSION }} actions-cache-folder: ${{ env.EMSDK_CACHE_FOLDER }} From e0f105b33b562f6c26fd1d56ed5ccb953eeecd68 Mon Sep 17 00:00:00 2001 From: xks <2777389616@qq.com> Date: Sun, 15 Feb 2026 15:31:17 +0800 Subject: [PATCH 8/9] ablated ci --- .github/workflows/rust.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 64dcb76..be728ab 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -65,5 +65,4 @@ jobs: - name: Build wasm32-unknown-emscripten run: | rustup target add wasm32-unknown-emscripten - cargo clean - cargo build --release --target wasm32-unknown-emscripten + cargo build --target wasm32-unknown-emscripten From c4ae87151d73d42ffd2df8c424cc5126d6972daf Mon Sep 17 00:00:00 2001 From: xks <2777389616@qq.com> Date: Sun, 15 Feb 2026 16:57:09 +0800 Subject: [PATCH 9/9] Fix bindgen generation for wasm32-unknown-emscripten For emscripten targets, parse headers as HOST in bindgen and disable layout tests.\n\nThis avoids missing Highs_* function declarations in generated bindings, while keeping the existing CMake exception flags fix. --- build.rs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/build.rs b/build.rs index d540db0..ac8f558 100644 --- a/build.rs +++ b/build.rs @@ -33,28 +33,32 @@ fn generate_bindings<'a>( exclude_dir: Option, include_paths: impl IntoIterator, ) { + let target = env::var("TARGET").unwrap(); + let emscripten = target.contains("emscripten"); + // The bindgen::Builder is the main entry point // to bindgen, and lets you build up options for // the resulting bindings. - let mut builder = include_paths + let builder = include_paths .into_iter() .fold(bindgen::Builder::default(), |builder, path| { builder.clang_arg(format!("-I{}", path.to_string_lossy())) }); - if env::var("TARGET").unwrap().contains("emscripten") { - println!("cargo:rerun-if-env-changed=EMSDK"); - let emsdk = env::var("EMSDK") - .expect("TARGET is emscripten but EMSDK is not set; needed for bindgen sysroot"); - let sysroot = Path::new(&emsdk).join("upstream/emscripten/cache/sysroot"); - if !sysroot.is_dir() { - panic!( - "TARGET is emscripten but EMSDK sysroot is missing: {}", - sysroot.display() - ); - } - builder = builder.clang_arg(format!("--sysroot={}", sysroot.display())); - } + // Work around bindgen/libclang missing `Highs_*` function declarations for + // `wasm32-unknown-emscripten` when parsing with emscripten's sysroot. + // The C API signatures are target-independent, so parse as host and skip + // cross-target layout assertions. + let builder = if emscripten { + println!("cargo:rerun-if-env-changed=HOST"); + let host = env::var("HOST") + .expect("TARGET is emscripten but HOST is not set; needed for bindgen host parsing"); + builder + .clang_arg(format!("--target={host}")) + .layout_tests(false) + } else { + builder + }; let c_bindings = builder // The input header we would like to generate bindings for.