From 72bf900142eda8e4a841beb07940bb57b39fb2c6 Mon Sep 17 00:00:00 2001 From: Julien Jerphanion Date: Fri, 7 Aug 2026 16:46:06 +0200 Subject: [PATCH 1/8] Have `numpy` use `openblas` for BLAS and LAPACK Signed-off-by: Julien Jerphanion --- recipes/recipes_emscripten/numpy/build.sh | 12 +++++++++--- recipes/recipes_emscripten/numpy/recipe.yaml | 6 +++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/recipes/recipes_emscripten/numpy/build.sh b/recipes/recipes_emscripten/numpy/build.sh index 5b26306b9c9..bb609c86a08 100644 --- a/recipes/recipes_emscripten/numpy/build.sh +++ b/recipes/recipes_emscripten/numpy/build.sh @@ -1,15 +1,20 @@ #!/bin/bash +set -ex + echo "PYTHON" rm -r -f branding +# necessary for cross-compilation to point to the right env +export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig + export CFLAGS="$CFLAGS -Wno-return-type -Wno-implicit-function-declaration -msimd128 -fwasm-exceptions -s SUPPORT_LONGJMP" -export MESON_CROSS_FILE=$RECIPE_DIR/emscripten.meson.cross +export MESON_CROSS_FILE=$RECIPE_DIR/emscripten.meson.cross export LDFLAGS="$LDFLAGS -sWASM_BIGINT -s WASM_BIGINT -fwasm-exceptions -s SUPPORT_LONGJMP" cp $RECIPE_DIR/config/config.h.in numpy/_core/config.h.in -# +# # otherwise "cython" is not properly executable echo "add shebang to cython file" @@ -22,5 +27,6 @@ sed -i 's/-fexceptions/-fwasm-exceptions/g' numpy/_core/meson.build MESON_ARGS="-Dhave_backtrace=false" ${PYTHON} -m pip install . -vvv --no-deps --no-build-isolation \ - -Csetup-args="-Dallow-noblas=true" \ + -Csetup-args="-Dblas=openblas" \ + -Csetup-args="-Dlapack=openblas" \ -Csetup-args="--cross-file=$MESON_CROSS_FILE" diff --git a/recipes/recipes_emscripten/numpy/recipe.yaml b/recipes/recipes_emscripten/numpy/recipe.yaml index 62346d5e6c3..4a3e146dc6e 100644 --- a/recipes/recipes_emscripten/numpy/recipe.yaml +++ b/recipes/recipes_emscripten/numpy/recipe.yaml @@ -11,7 +11,7 @@ source: url: https://github.com/numpy/numpy/releases/download/v${{ version }}/numpy-${{ version }}.tar.gz sha256: a48a113e6afea91f5608793bafa7ef2ad481fefbda87ec5069f483de61cb9fa3 build: - number: 0 + number: 1 files: exclude: @@ -35,6 +35,10 @@ requirements: - pkg-config - python host: + - openblas + - python + run: + - openblas - python run_exports: - numpy >=${{ default_abi_level }},<3 From 113bc7e623855b8937ce52278d067ae15ba11411 Mon Sep 17 00:00:00 2001 From: Julien Jerphanion Date: Fri, 7 Aug 2026 17:21:53 +0200 Subject: [PATCH 2/8] Add test on BLAS and LAPACK code paths Signed-off-by: Julien Jerphanion --- .../recipes_emscripten/numpy/test_numpy.py | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/recipes/recipes_emscripten/numpy/test_numpy.py b/recipes/recipes_emscripten/numpy/test_numpy.py index 839ca268a61..62e0259e3bb 100644 --- a/recipes/recipes_emscripten/numpy/test_numpy.py +++ b/recipes/recipes_emscripten/numpy/test_numpy.py @@ -1,5 +1,48 @@ +import numpy as np +from numpy.testing import assert_allclose + + def test_numpy(): - import numpy + ones = np.ones(shape=[2, 3]) + assert ones.shape == (2, 3) + + +# Large enough that OpenBLAS blocked kernels are exercised (not tiny fallbacks). +N = 300 + + +def test_blas_matmul(): + rng = np.random.default_rng(0) + a = rng.standard_normal((N, N)) + b = rng.standard_normal((N, N)) + c = a @ b + assert c.shape == (N, N) + assert_allclose(c, np.dot(a, b)) + assert_allclose(c.T, b.T @ a.T) + + +def test_lapack_cholesky(): + rng = np.random.default_rng(1) + x = rng.standard_normal((N, N)) + a = x @ x.T + N * np.eye(N) + l = np.linalg.cholesky(a) + assert_allclose(l @ l.T, a) + + +def test_lapack_solve(): + rng = np.random.default_rng(2) + a = rng.standard_normal((N, N)) + b = rng.standard_normal(N) + x = np.linalg.solve(a, b) + assert_allclose(a @ x, b) + - ones = numpy.ones(shape=[2,3]) - assert ones.shape == (2,3) \ No newline at end of file +def test_lapack_eigh(): + rng = np.random.default_rng(3) + x = rng.standard_normal((N, N)) + a = x @ x.T + evals, evecs = np.linalg.eigh(a) + assert evals.shape == (N,) + assert evecs.shape == (N, N) + assert_allclose(evecs.T @ evecs, np.eye(N), atol=1e-8) + assert_allclose(evecs @ np.diag(evals) @ evecs.T, a) From ff840ca7760e4c06cb805cac41870276cbd3dcdb Mon Sep 17 00:00:00 2001 From: Julien Jerphanion Date: Mon, 10 Aug 2026 10:13:45 +0200 Subject: [PATCH 3/8] Build and link against OpenBLAS Signed-off-by: Julien Jerphanion --- recipes/recipes_emscripten/numpy/build.sh | 62 ++++- .../numpy/emscripten.meson.cross | 2 +- .../numpy/fix_flang_fortran_abi.py | 225 ++++++++++++++++++ .../recipes_emscripten/numpy/test_numpy.py | 12 + 4 files changed, 293 insertions(+), 8 deletions(-) create mode 100644 recipes/recipes_emscripten/numpy/fix_flang_fortran_abi.py diff --git a/recipes/recipes_emscripten/numpy/build.sh b/recipes/recipes_emscripten/numpy/build.sh index bb609c86a08..cad7c494d15 100644 --- a/recipes/recipes_emscripten/numpy/build.sh +++ b/recipes/recipes_emscripten/numpy/build.sh @@ -6,27 +6,75 @@ echo "PYTHON" rm -r -f branding -# necessary for cross-compilation to point to the right env -export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig +# OpenBLAS must be visible to Meson via pkg-config during the cross build. +# Channel packages may still embed the original build-machine placeholder +# paths in openblas.pc; write corrected .pc files into a build-local +# pkgconfig dir (not $PREFIX — that would ship them in the numpy package). +# +# Also install a scipy-openblas.pc alias. NumPy prefers that name and looks +# it up with plain pkg-config (same pattern as SciPy). That bypasses Meson's +# packages['openblas'] factory, whose void dgemm_()/cblas_dgemm() symbol +# probes fail under wasm-ld signature matching against flang-built OpenBLAS. +PKGCONFIG_DIR="${SRC_DIR}/.emscripten-pkgconfig" +mkdir -p "${PKGCONFIG_DIR}" +for pc_name in openblas scipy-openblas; do + cat > "${PKGCONFIG_DIR}/${pc_name}.pc" <> "${SRC_DIR}/emscripten.meson.cross" <