Step 1: WebAssembly compilation (#286)
scripts/wasm/build.sh cross-compiles the unmodified CLI entry point and the
Autotools library source lists. .github/workflows/build-wasm.yml uses the official
setup-emsdk action with Emscripten
4.0.23. It runs on pull requests, pushes to this repository’s master branch,
and manual dispatches. Build tools (Autotools, pkg-config, a native C++ compiler,
curl, tar and bzip2) must be available.
Reproduce locally
Activate Emscripten 4.0.23 with source /path/to/emsdk/emsdk_env.sh, then run:
bash scripts/wasm/build.sh
node scripts/wasm/smoke.mjs
node scripts/wasm/smoke-protocol.mjs
WASM_BUILD_DIR changes the build directory; JOBS defaults to 2. Local builds
pin ViennaRNA 2.7.2 and its official GitHub release asset SHA-256 in
scripts/wasm/vienna-release.json. On every run, the GitHub workflow resolves
the newest stable 2.x release (at least 2.7.2), excludes drafts/prereleases
and major 3, and uses the asset’s published SHA-256. Selection compares numeric
versions across all API pages, rather than using GitHub’s /latest endpoint.
Missing release assets, digests or failed API requests stop the build.
To use that same selection locally (optionally set GITHUB_TOKEN for API limits):
python3 scripts/wasm/vienna-release.py --latest > /tmp/vienna-release.json
WASM_VIENNA_RELEASE=/tmp/vienna-release.json bash scripts/wasm/build.sh
Keep the resolved manifest to reproduce that exact release later with
WASM_VIENNA_RELEASE. build-info.json and build-info.txt record the selected
archive URL and checksum as well as the actual compiled library version.
Source archives are downloaded into build-wasm/downloads and verified by the
selected SHA-256 hashes on every invocation. Use the official GitHub release
asset, rather than an autogenerated source archive; the university endpoint
returned an HTTP 200 text response instead of the archive in PR #291’s build.
scripts/wasm/download.sh removes invalid cached archives and checks new
downloads before moving them from .part into the cache. Failed transfers and
checksum mismatches leave no cached archive or partial file, so a subsequent
build can retry. Run python3 scripts/wasm/test-downloads.py to check download,
cache recovery and failure handling against a local HTTP server, and
python3 scripts/wasm/test-vienna-release.py for release selection and manifest
validation, without an SDK or external network access. The build directory is
ignored. ViennaRNA sources, its installation prefix and IntaRNA objects are
separated by the selected version and checksum, so upgrades cannot reuse an
older RNAlib or IntaRNA object cache (including the old 2.7.0 prefix). zlib and
Boost use a separate build-wasm/deps/common prefix, so legacy ViennaRNA headers
cannot override the selected release through Boost’s include path. Their caches
remain reusable across ViennaRNA upgrades. Delete the build directory for a fresh build
after changing compiler flags or SDKs. Native libraries are never accepted
from system pkg-config paths.
The script supplies absolute source and generated-header include paths for the out-of-tree IntaRNA build, and uses generated files from the build directory. ViennaRNA also needs an explicit source include path when its no-SVM build omits the target’s normal include flags. ViennaRNA 2.7.2 bundles dlib 20.0, which already supports modern libc++; no downloaded sources are patched. Staging takes the ViennaRNA, dlib and Naview notices from the source archive identified by the compiled provenance, and displays their selected versions.
The legacy Boost and zlib configure macros declare dummy test functions with
incorrect signatures (exit() and inflateEnd()), which wasm-ld correctly
rejects. Before caching just those probes, dependency-probe.cpp links and executes actual
filesystem, regex, options, archive, gzip and RNAlib APIs against the WASM
libraries. Configure still checks C++23, headers, serialization and RNAlib APIs;
the smoke test executes the real prediction. VRNA_LIBS supplies the static
RNAlib link flags because the upstream .pc includes native-only linker flags.
Dependencies and runtime
- zlib 1.3.1: static library for compressed accessibility and Boost.Iostreams.
- Boost 1.87.0: headers plus static filesystem, program_options, regex, iostreams, serialization and system libraries, built with Boost.Build’s Emscripten toolset. Optional bzip2, lzma and zstd backends are disabled.
- ViennaRNA 2.7.2 locally, newest stable 2.x in CI: static RNAlib, without OpenMP, pthreads, SIMD, external solvers, bindings, documentation or companion applications. Compiling only RNAlib avoids executing cross-compiled helper applications on the host.
- IntaRNA: C++23, bundled Kokkos mdspan, standard CLI, default energy models
and accessibility calculations. Single-thread Emscripten builds omit
-pthread; native builds retain their existing threading flags.
All C++ compilation and linking enables exceptions (-fexceptions), including
Boost (exception-handling-method=js) and the CLI’s input/error handling. The SDK’s single-thread mutex support
retains ViennaRNA’s normal setup/logger locking without requiring browser
SharedArrayBuffer or COOP/COEP headers (unavailable on ordinary GitHub Pages).
The output is an ES module factory (createIntaRNA) in
build-wasm/site/wasm/intarna.js, paired with intarna.wasm. The factory does
not run main automatically; it exports FS and callMain. The runtime exits
and flushes C++ streams after one call. Each prediction must use a new instance.
Memory grows on demand to a maximum of 1 GiB; stack size is 8 MiB. Allocation
failure is reported as a failed prediction; devices may impose lower limits.
See Emscripten’s Autotools guidance
for emconfigure/emmake. Generated configure files, dependency sources and WASM
binaries are build outputs, not tracked source. The artifact must keep JS and
WASM from the same build together. The Node smoke test runs real
Turner/default-accessibility predictions and checks energy, coordinates,
nonzero accessibility costs, suboptimal counts and C++ error handling against
the native CLI.
Compiled version information and virtual overrides (PR #287 review)
After copying the paired JS/WASM assets, write-build-info.mjs invokes their
actual --version entry point. It records IntaRNA package version, ViennaRNA and
Boost versions, source repository/revision and dirty status, Emscripten version,
zlib version and asset hashes in build-info.txt and build-info.json. The
interface displays those versions and links both files. Staging checks the
hashes and preserves provenance even if the staging checkout has moved.
IntaRNA’s inherited virtual methods and destructors declare override, including
previously unannotated energy/accessibility, seed/helix, predictor and output
families. The Emscripten build treats inconsistent missing overrides as errors.
Vendored Easylogging++ remains unchanged. This annotation-only C++ change does
not alter algorithms, virtual extension points or scientific defaults.