diff --git a/recipes/recipes_emscripten/h5py/build.sh b/recipes/recipes_emscripten/h5py/build.sh index 7a23296ef28..a1baa60802e 100644 --- a/recipes/recipes_emscripten/h5py/build.sh +++ b/recipes/recipes_emscripten/h5py/build.sh @@ -1,11 +1,4 @@ -# add ad-hoc code to load the shared libs -tmp_file="$(mktemp)" -printf 'import ctypes;ctypes.CDLL("/lib/libhdf5.so");ctypes.CDLL("/lib/libhdf5_hl.so")\n' > "$tmp_file" -cat "h5py/__init__.py" >> "$tmp_file" -mv "$tmp_file" "h5py/__init__.py" - - # remove the emcc symlink in the $BUILD_PREFIX/bin rm $BUILD_PREFIX/bin/emcc @@ -30,7 +23,7 @@ export H5PY_DIRECT_VFD='0' export HDF5_MPI=OFF # Explitly set HDF5 version -export HDF5_VERSION=1.12.3 +export HDF5_VERSION=1.14.6 ${PYTHON} -m pip install . -vvv diff --git a/recipes/recipes_emscripten/h5py/recipe.yaml b/recipes/recipes_emscripten/h5py/recipe.yaml index 66945f613e1..ee535737eda 100644 --- a/recipes/recipes_emscripten/h5py/recipe.yaml +++ b/recipes/recipes_emscripten/h5py/recipe.yaml @@ -13,7 +13,7 @@ source: - patches/configure.patch build: - number: 1 + number: 2 requirements: build: diff --git a/recipes/recipes_emscripten/h5py/test_h5py.py b/recipes/recipes_emscripten/h5py/test_h5py.py index 410ddf9c660..03760fc2c1d 100644 --- a/recipes/recipes_emscripten/h5py/test_h5py.py +++ b/recipes/recipes_emscripten/h5py/test_h5py.py @@ -34,4 +34,20 @@ def test_usage(): assert dset2.name == "/subgroup/another_dataset" f = h5py.File("mytestfile.hdf5", "r") - assert sorted(list(f.keys())) == ["mydataset", "subgroup"] \ No newline at end of file + assert sorted(list(f.keys())) == ["mydataset", "subgroup"] + + +def test_dimscales(tmp_path): + """Exercises the HL dimscale reopen path across a File close/open cycle.""" + import h5py + + path = str(tmp_path / "dimscales.h5") + with h5py.File(path, "w") as f: + scale = f.create_dataset("x", data=[0.0, 1.0, 2.0, 3.0]) + scale.make_scale("x") + data = f.create_dataset("temp", data=[10.0, 20.0, 30.0, 40.0]) + data.dims[0].attach_scale(scale) + + with h5py.File(path, "r") as f: + assert h5py.h5ds.is_scale(f["x"].id) + assert f["temp"].dims[0][0].name == "/x" \ No newline at end of file diff --git a/recipes/recipes_emscripten/hdf5/build.sh b/recipes/recipes_emscripten/hdf5/build.sh index f9c221b4f2f..ce1c3879936 100644 --- a/recipes/recipes_emscripten/hdf5/build.sh +++ b/recipes/recipes_emscripten/hdf5/build.sh @@ -3,8 +3,6 @@ cd build export LDFLAGS="-sNODERAWFS=1 -sUSE_ZLIB=1 -sFORCE_FILESYSTEM=1" -cp ${RECIPE_DIR}/settings/* ../src/ - emcmake cmake .. \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DH5_HAVE_GETPWUID=OFF \ @@ -21,13 +19,11 @@ emcmake cmake .. \ -DHDF5_BUILD_EXAMPLES=OFF \ -DHDF5_BUILD_TOOLS=OFF \ -DHDF5_BUILD_UTILS=OFF \ + -DHDF5_BUILD_HL_LIB=ON \ -DHDF5_ENABLE_Z_LIB_SUPPORT=ON \ -DHDF5_ENABLE_ROS3_VFD=OFF \ -DZLIB_INCLUDE_DIR=${PREFIX}/include \ -DZLIB_LIBRARY=${PREFIX}/lib/libz.a -cp ${RECIPE_DIR}/settings/* ../src/ -cp ${RECIPE_DIR}/settings/* src/ - emmake make -j 4 emmake make install diff --git a/recipes/recipes_emscripten/hdf5/patches/0001-fenv-fe-invalid-guard.patch b/recipes/recipes_emscripten/hdf5/patches/0001-fenv-fe-invalid-guard.patch new file mode 100644 index 00000000000..045fc082527 --- /dev/null +++ b/recipes/recipes_emscripten/hdf5/patches/0001-fenv-fe-invalid-guard.patch @@ -0,0 +1,19 @@ +Emscripten's defines only rounding modes and FE_ALL_EXCEPT=0; +FE_INVALID is not defined because WASM has no floating-point exception +flags. Guard the FE_INVALID clear so the build proceeds; under WASM there +are no FP exceptions to clear, so the guarded branch is a legitimate no-op. + +--- a/src/H5Tinit_float.c ++++ b/src/H5Tinit_float.c +@@ -608,9 +608,11 @@ + #endif + + done: ++#ifdef FE_INVALID + /* Clear any FE_INVALID exceptions from NaN handling */ + if (feclearexcept(FE_INVALID) != 0) + HSYS_GOTO_ERROR(H5E_DATATYPE, H5E_CANTSET, FAIL, "can't clear floating-point exceptions"); ++#endif + + /* Restore the original environment */ + if (feupdateenv(&saved_fenv) != 0) diff --git a/recipes/recipes_emscripten/hdf5/patches/0002-drop-soversion.patch b/recipes/recipes_emscripten/hdf5/patches/0002-drop-soversion.patch new file mode 100644 index 00000000000..7672f7442e3 --- /dev/null +++ b/recipes/recipes_emscripten/hdf5/patches/0002-drop-soversion.patch @@ -0,0 +1,28 @@ +Under emscripten the loader dedupes shared libraries by string identity of +their NEEDED entry, not by realpath. When libhdf5_hl links against libhdf5 +with SOVERSION set, it records NEEDED "libhdf5.so.310.5.1", while consumers +like h5py link with bare "-lhdf5" and record NEEDED "libhdf5.so". Both paths +resolve to the same file via symlinks, but the loader sees them as two +distinct libraries and creates two separate instances — each with its own +copy of libhdf5.so's file-static globals (H5CX_head_g etc.), which breaks +the metadata cache when calls cross between the instances. + +Skip VERSION/SOVERSION for emscripten builds so every consumer records the +bare "libhdf5.so" NEEDED entry and the loader deduplicates properly. + +--- a/config/cmake/HDF5Macros.cmake ++++ b/config/cmake/HDF5Macros.cmake +@@ -22,10 +22,10 @@ + else () + set (LIBHDF_VERSION ${HDF5_${libpackage}_PACKAGE_SOVERSION_MAJOR}) + endif () +- set_target_properties (${libtarget} PROPERTIES VERSION ${PACKAGE_SOVERSION}) ++ if (NOT EMSCRIPTEN) ++ set_target_properties (${libtarget} PROPERTIES VERSION ${PACKAGE_SOVERSION}) ++ endif () + if (WIN32) + set (${LIB_OUT_NAME} "${LIB_OUT_NAME}-${LIBHDF_VERSION}") +- else () ++ elseif (NOT EMSCRIPTEN) + set_target_properties (${libtarget} PROPERTIES SOVERSION ${LIBHDF_VERSION}) + endif () diff --git a/recipes/recipes_emscripten/hdf5/recipe.yaml b/recipes/recipes_emscripten/hdf5/recipe.yaml index 19c4ee756cf..a0075b9bff8 100644 --- a/recipes/recipes_emscripten/hdf5/recipe.yaml +++ b/recipes/recipes_emscripten/hdf5/recipe.yaml @@ -1,14 +1,17 @@ context: - version: 1.12.3 - maj_min_ver: 1.12 + version: 1.14.6 + maj_min_ver: 1.14 package: name: hdf5 version: ${{ version }} source: - url: https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-${{ maj_min_ver }}/hdf5-${{version }}/src/hdf5-${{ version }}.tar.gz - sha256: c15adf34647918dd48150ea1bd9dffd3b32a3aec5298991d56048cc3d39b4f6f + url: https://support.hdfgroup.org/releases/hdf5/v${{ maj_min_ver | replace(".", "_") }}/v${{ version | replace(".", "_") }}/downloads/hdf5-${{ version }}.tar.gz + sha256: e4defbac30f50d64e1556374aa49e574417c9e72c6b1de7a4ff88c4b1bea6e9b + patches: + - patches/0001-fenv-fe-invalid-guard.patch + - patches/0002-drop-soversion.patch build: number: 0 @@ -21,6 +24,14 @@ requirements: host: - zlib +tests: +- package_contents: + files: + - include/hdf5.h + - include/hdf5_hl.h + - lib/libhdf5.so + - lib/libhdf5_hl.so + about: homepage: https://www.hdfgroup.org/solutions/hdf5/ license: BSD-3-Clause diff --git a/recipes/recipes_emscripten/libnetcdf/patches/skip-daos-emscripten.patch b/recipes/recipes_emscripten/libnetcdf/patches/skip-daos-emscripten.patch new file mode 100644 index 00000000000..34ebaaf31b6 --- /dev/null +++ b/recipes/recipes_emscripten/libnetcdf/patches/skip-daos-emscripten.patch @@ -0,0 +1,25 @@ +DAOS container detection uses listxattr/getxattr which are declared in +Emscripten's but not implemented at runtime — the built .so +fails to load with "cannot resolve symbol listxattr". DAOS makes no sense +in a WASM sandbox anyway. Short-circuit isdaoscontainer() under +__EMSCRIPTEN__ so the xattr code is never reached. + +--- a/libdispatch/dinfermodel.c ++++ b/libdispatch/dinfermodel.c +@@ -1594,6 +1594,10 @@ + static int + isdaoscontainer(const char* path) + { ++#ifdef __EMSCRIPTEN__ ++ (void)path; ++ return NC_ENOTNC; ++#else + int stat = NC_ENOTNC; /* default is that this is not a DAOS container */ + #ifndef _WIN32 + #ifdef USE_HDF5 +@@ -1669,6 +1673,7 @@ + #endif + errno = 0; /* reset */ + return stat; ++#endif /* __EMSCRIPTEN__ */ + } diff --git a/recipes/recipes_emscripten/libnetcdf/recipe.yaml b/recipes/recipes_emscripten/libnetcdf/recipe.yaml index e6e0460d7e5..c9fc8aac73f 100644 --- a/recipes/recipes_emscripten/libnetcdf/recipe.yaml +++ b/recipes/recipes_emscripten/libnetcdf/recipe.yaml @@ -12,9 +12,10 @@ source: patches: - patches/parallel-disable.patch - patches/hdf5-parallel-disable.patch + - patches/skip-daos-emscripten.patch build: - number: 0 + number: 1 files: exclude: diff --git a/recipes/recipes_emscripten/netcdf4/recipe.yaml b/recipes/recipes_emscripten/netcdf4/recipe.yaml index 1820b3af27e..b76c233ac20 100644 --- a/recipes/recipes_emscripten/netcdf4/recipe.yaml +++ b/recipes/recipes_emscripten/netcdf4/recipe.yaml @@ -13,9 +13,9 @@ source: - patches/0001-Fix-unknown-arg-R-PREFIX-lib.patch build: - number: 2 + number: 3 script: - - export LDFLAGS="-L$PREFIX/lib -lhdf5 $LDFLAGS" + - export LDFLAGS="-L$PREFIX/lib -lhdf5 -lhdf5_hl $LDFLAGS" - ${PYTHON} -m pip install . -vvv files: diff --git a/recipes/recipes_emscripten/netcdf4/test_netcdf4.py b/recipes/recipes_emscripten/netcdf4/test_netcdf4.py index fd8c6ece350..c93103159e0 100644 --- a/recipes/recipes_emscripten/netcdf4/test_netcdf4.py +++ b/recipes/recipes_emscripten/netcdf4/test_netcdf4.py @@ -1,7 +1,33 @@ -def test_netcdf4(): - from netCDF4 import Dataset - rootgrp = Dataset("test.nc", "w", format="NETCDF4") - rootgrp.data_model == "NETCDF4" - rootgrp.close() +import numpy as np +import netCDF4 +from netCDF4 import Dataset +def test_versions(): + print("netCDF4:", netCDF4.__version__) + print("libnetcdf:", netCDF4.__netcdf4libversion__) + print("libhdf5:", netCDF4.__hdf5libversion__) + + +def test_netcdf3_write_read(tmp_path): + path = str(tmp_path / "c.nc") + with Dataset(path, "w", format="NETCDF3_CLASSIC") as ds: + ds.createDimension("x", 4) + v = ds.createVariable("temp", "f8", ("x",)) + v[:] = np.arange(4.0) + with Dataset(path, "r") as ds: + assert ds.variables["temp"].shape == (4,) + + +def test_netcdf4_roundtrip(tmp_path): + """End-to-end netcdf-4 round-trip: dim + variable + attribute.""" + path = str(tmp_path / "v.nc") + with Dataset(path, "w", format="NETCDF4") as ds: + ds.title = "hello" + ds.createDimension("x", 4) + v = ds.createVariable("temp", "f8", ("x",)) + v[:] = np.arange(4.0) + with Dataset(path, "r") as ds: + assert ds.title == "hello" + assert "x" in ds.dimensions + np.testing.assert_array_equal(ds.variables["temp"][:], np.arange(4.0)) diff --git a/variant.yaml b/variant.yaml index 47a8b53c23a..dc78831bfce 100644 --- a/variant.yaml +++ b/variant.yaml @@ -383,7 +383,7 @@ harfbuzz: hdf4: - 4.2 hdf5: - - 1.12.3 + - 1.14.6 icu: - '73' ipopt: @@ -449,7 +449,7 @@ libmatio: libmicrohttpd: - 0.9 libnetcdf: - - 4.9.3 + - 4.10.1 libopencv: - 4.5.5 libpcap: