#!/usr/bin/env bash # The JPEG AI exact decoder module, built and checked: the module of record of WAISM's float # level. # # bash wai/wai-det-jpegai/build.sh [--out DIR] [--planted] # # Builds the crate for wasm32-unknown-unknown, scalar, and holds it to the profile with both of # its checkers (../wai-module-check): the Rust checker and check-module.mjs must each give # `held` at the float level and `refused R3` at the integer level, the same verdict at each. # Then it writes it to DIR (default: the target directory's `module/`) as wai_det_jpegai.wasm # and prints its size and SHA-256. With --planted it also builds the planted fault (the # feature `planted-nan-branch`, README *The planted faults*), held to the same verdicts, as # wai_det_jpegai.planted.wasm. # # The module names no path of this machine: the checkout, the Cargo home and the toolchain are # remapped to /open-standards, /cargo and /rustup. Its bytes still depend on where and on what # it is built (../wai-int-module/build.sh says why), so record/ is named by its SHA-256 # (record/manifest.json), and a build of this source on another host is held to the corpus, # not to record/'s bytes. # # Needs bash, cargo with the wasm32-unknown-unknown target of the pinned toolchain, python3 # and node >= 18. set -euo pipefail CRATE="$(cd "$(dirname "$0")" && pwd -P)" WAI="$(cd "$CRATE/.." && pwd -P)" ROOT="$(cd "$WAI/.." && pwd -P)" OUT= PLANTED=0 while [ $# -gt 0 ]; do case "$1" in --out) OUT="$2"; shift 2 ;; --planted) PLANTED=1; shift ;; *) echo "usage: bash build.sh [--out DIR] [--planted]" >&2; exit 2 ;; esac done for t in cargo rustc node python3; do command -v "$t" >/dev/null || { echo "wai-det-jpegai: needs $t" >&2; exit 1; } done cd "$CRATE" TARGET_BASE="${CARGO_TARGET_DIR:-$CRATE/target}" OUT="${OUT:-$TARGET_BASE/module}" mkdir -p "$OUT" CARGO_HOME_DIR="$(cd "${CARGO_HOME:-$HOME/.cargo}" && pwd -P)" RUSTUP_HOME_DIR="$(cd "${RUSTUP_HOME:-$HOME/.rustup}" && pwd -P)" REMAP="--remap-path-prefix=$ROOT=/open-standards --remap-path-prefix=$CARGO_HOME_DIR=/cargo --remap-path-prefix=$RUSTUP_HOME_DIR=/rustup" # The checker, built once. cargo build --locked --release -q --manifest-path "$WAI/wai-module-check/Cargo.toml" --target-dir "$TARGET_BASE/checker" CHECK="$TARGET_BASE/checker/release/wai-module-check" variants=record [ "$PLANTED" = 1 ] && variants="record planted" for variant in $variants; do name=wai_det_jpegai.wasm features=() if [ "$variant" = planted ]; then name=wai_det_jpegai.planted.wasm features=(--features planted-nan-branch) fi RUSTFLAGS="$REMAP" cargo build --locked --release --target wasm32-unknown-unknown --target-dir "$TARGET_BASE/$variant" ${features[@]+"${features[@]}"} built="$TARGET_BASE/$variant/wasm32-unknown-unknown/release/wai_det_jpegai.wasm" [ -f "$built" ] || { echo "wai-det-jpegai: the build left no $built" >&2; exit 1; } rm -f "$OUT/$name" # Both checkers, at both levels: held at the float level, refused R3 at the integer level, # and the same verdict from each. for level in float integer; do rust=$("$CHECK" --level "$level" "$built" || true) js=$(node "$WAI/wai-module-check/check-module.mjs" --level "$level" "$built" || true) if [ "$rust" != "$js" ]; then echo "wai-det-jpegai: $variant at the $level level: the Rust checker says '$rust', check-module.mjs '$js'" >&2 exit 1 fi case "$level:$rust" in float:held | integer:"refused R3 "*) ;; *) echo "wai-det-jpegai: $variant at the $level level: $rust" >&2; exit 1 ;; esac echo "wai-det-jpegai: $variant at the $level level: $rust, from both checkers" done cp "$built" "$OUT/$name" sum=$(python3 -I -c 'import hashlib,sys; print(hashlib.sha256(open(sys.argv[1],"rb").read()).hexdigest())' "$OUT/$name") size=$(wc -c < "$OUT/$name" | tr -d ' ') echo "wai-det-jpegai: $OUT/$name: $size bytes, SHA-256 $sum, held at the float level by both checkers" done