Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions scripts/wasm-without-nix/build-wasm-libs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#!/usr/bin/env bash
# Build libsodium, libsecp256k1, and libblst for wasm32-wasi and install them
# into a single prefix that cabal.project can point at via extra-lib-dirs /
# extra-include-dirs. Mirrors ./nix/{libsodium,secp256k1,blst}.nix but uses
Comment on lines +1 to +4
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR appears to be missing the required Herald changelog fragment in .changes/ (CI enforces this). Please add a new .changes/*.yml entry for this change (pick the appropriate project: and kind:).

Copilot uses AI. Check for mistakes.
# wasm32-wasi-clang directly (no Nix).
#
# Requirements on PATH: wasm32-wasi-clang (from wasi-sdk), autoreconf,
# automake, libtool, make, git, pkg-config.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to say this in the help message too


set -euo pipefail

# Script must be run from the root of the project
PROJECT_DIR="$(pwd)"
[ -f "$PROJECT_DIR/cabal.project" ] || {
echo "Error: cabal.project not found in $PROJECT_DIR. Run this script from the root of the project" >&2
exit 1
}

missing=()
[ -n "${PREFIX:-}" ] || missing+=(PREFIX)
[ -n "${SRC_ROOT:-}" ] || missing+=(SRC_ROOT)
if [ "${#missing[@]}" -gt 0 ]; then
cat >&2 <<EOF
Error: required environment variable(s) not set: ${missing[*]}

Both values are interpreted relative to the cabal.project directory
($PROJECT_DIR) unless given as an absolute path.

PREFIX where the wasm libs/headers will be installed, e.g. wasm-libs.
Will be created if missing. Point cabal.project's
extra-lib-dirs / extra-include-dirs at \$PREFIX/lib and
\$PREFIX/include.

SRC_ROOT where the libsodium / secp256k1 / blst source trees live (or
will be cloned into as \$SRC_ROOT/{libsodium,secp256k1,blst}),
e.g. wasm-deps.

Example:
PREFIX=wasm-libs SRC_ROOT=wasm-libs-src $0
EOF
exit 1
fi

resolve_rel() {
case "$1" in
/*) printf '%s\n' "$1" ;;
*) printf '%s\n' "$PROJECT_DIR/$1" ;;
esac
}

ABS_PREFIX="$(resolve_rel "$PREFIX")"
ABS_SRC_ROOT="$(resolve_rel "$SRC_ROOT")"
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SRC_ROOT is documented as a directory the script will clone into, but the script never creates $ABS_SRC_ROOT. If the parent directory doesn’t exist, git clone will fail; consider mkdir -p "$ABS_SRC_ROOT" before any clones.

Suggested change
ABS_SRC_ROOT="$(resolve_rel "$SRC_ROOT")"
ABS_SRC_ROOT="$(resolve_rel "$SRC_ROOT")"
mkdir -p "$ABS_SRC_ROOT"

Copilot uses AI. Check for mistakes.

# Toolchain check: wasm32-wasi-clang must be on PATH (drives both compile and
# link). The other wasm32-wasi-* tools come along with it via the wasi-sdk /
# ghc-wasm env.
toolchain_missing=()
for t in wasm32-wasi-clang wasm-ld; do
Comment on lines +54 to +58
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script checks for wasm32-wasi-clang/wasm-ld, but later also depends on tools like ar and file (used in verify_wasm) and nproc. Consider extending the prerequisite checks (or the header docs) so failures are detected early with a clear message.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script checks for wasm32-wasi-clang/wasm-ld, but later also depends on tools like ar and file (used in verify_wasm) and nproc. Consider extending the prerequisite checks (or the header docs) so failures are detected early with a clear message.

I was going to say this too, the comment at the beginning says it needs: autoreconf, automake, libtool, make, git, pkg-config. All these could be checked here

command -v "$t" >/dev/null 2>&1 || toolchain_missing+=("$t")
done
if [ "${#toolchain_missing[@]}" -gt 0 ]; then
cat >&2 <<EOF
Error: wasm toolchain not on PATH (missing: ${toolchain_missing[*]}).

The ghc-wasm / wasi-sdk environment doesn't appear to be active. Activate it
with:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


source ~/.ghc-wasm/env

then re-run this script.
EOF
exit 1
fi

LIBSODIUM_REV="9511c982fb1d046470a8b42aa36556cdb7da15de"
LIBSODIUM_REPO="https://github.com/jedisct1/libsodium.git"
SECP256K1_REPO="https://github.com/bitcoin-core/secp256k1.git"
BLST_REPO="https://github.com/supranational/blst.git"

mkdir -p "$ABS_PREFIX/lib" "$ABS_PREFIX/include" "$ABS_PREFIX/lib/pkgconfig"

clone_if_missing() {
local repo="$1" dir="$2" rev="${3:-}"
if [ ! -d "$dir/.git" ]; then
git clone "$repo" "$dir"
fi
Comment on lines +79 to +86
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SECP256K1_REPO and BLST_REPO are cloned without a pinned revision/tag, so running this script at different times can produce different binaries (and potentially break builds). Consider pinning these to the same revisions used by the Nix build (from the repo’s pinned nixpkgs/flake.lock) and passing those revs into clone_if_missing.

Suggested change
mkdir -p "$ABS_PREFIX/lib" "$ABS_PREFIX/include" "$ABS_PREFIX/lib/pkgconfig"
clone_if_missing() {
local repo="$1" dir="$2" rev="${3:-}"
if [ ! -d "$dir/.git" ]; then
git clone "$repo" "$dir"
fi
# Pin these to the same revisions used by the Nix build. They may also be
# overridden from the environment when invoking this script.
SECP256K1_REV="${SECP256K1_REV:-}"
BLST_REV="${BLST_REV:-}"
mkdir -p "$ABS_PREFIX/lib" "$ABS_PREFIX/include" "$ABS_PREFIX/lib/pkgconfig"
pinned_rev_for_repo() {
local repo="$1"
case "$repo" in
"$LIBSODIUM_REPO") printf '%s\n' "$LIBSODIUM_REV" ;;
"$SECP256K1_REPO") printf '%s\n' "$SECP256K1_REV" ;;
"$BLST_REPO") printf '%s\n' "$BLST_REV" ;;
*) printf '%s\n' "" ;;
esac
}
clone_if_missing() {
local repo="$1" dir="$2" rev="${3:-}"
if [ -z "$rev" ]; then
rev="$(pinned_rev_for_repo "$repo")"
fi
if [ ! -d "$dir/.git" ]; then
if [ -z "$rev" ]; then
echo "Error: refusing to clone $repo without a pinned revision. Configure the matching *_REV to the revision used by the Nix build." >&2
exit 1
fi
git clone "$repo" "$dir"
fi

Copilot uses AI. Check for mistakes.
if [ -n "$rev" ]; then
git -C "$dir" fetch --tags origin "$rev" 2>/dev/null || true
git -C "$dir" checkout "$rev"
fi
}

build_libsodium() {
local dir="$ABS_SRC_ROOT/libsodium"
clone_if_missing "$LIBSODIUM_REPO" "$dir" "$LIBSODIUM_REV"
cd "$dir"
[ -x ./configure ] || ./autogen.sh -s
./configure --host=wasm32-wasi --prefix="$ABS_PREFIX"
make -j"$(nproc)"
Comment on lines +97 to +99
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build_libsodium relies on ./configure --host=wasm32-wasi to pick the right compiler, but doesn’t explicitly set CC=wasm32-wasi-clang (or related tools). If the environment isn’t set up as expected, this can silently compile for the wrong target and only fail late; consider exporting CC (and possibly AR, RANLIB) for this build step.

Copilot uses AI. Check for mistakes.
make install
wasm32-wasi-clang -shared -Wl,--whole-archive \
"$ABS_PREFIX/lib/libsodium.a" \
-o "$ABS_PREFIX/lib/libsodium.so"
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
cd -
}

I would suggest returning to the previous folder to make this function more self-contained. Currently this does not make a difference but if we move the call to the function to somewhere else it could break things


build_secp256k1() {
local dir="$ABS_SRC_ROOT/secp256k1"
clone_if_missing "$SECP256K1_REPO" "$dir"
cd "$dir"
[ -x ./configure ] || ./autogen.sh
./configure \
--host=wasm32-wasi \
--enable-module-schnorrsig \
--prefix="$ABS_PREFIX" \
SECP_CFLAGS=-fPIC
make -j"$(nproc)"
make install
Comment on lines +108 to +117
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build_secp256k1 doesn’t explicitly set CC=wasm32-wasi-clang (only --host=wasm32-wasi). To reduce reliance on ambient toolchain configuration, consider setting CC (and related binutils) for the configure/make step, similar to how build_blst sets CC.

Suggested change
clone_if_missing "$SECP256K1_REPO" "$dir"
cd "$dir"
[ -x ./configure ] || ./autogen.sh
./configure \
--host=wasm32-wasi \
--enable-module-schnorrsig \
--prefix="$ABS_PREFIX" \
SECP_CFLAGS=-fPIC
make -j"$(nproc)"
make install
local cc="wasm32-wasi-clang"
local ar="llvm-ar"
local ranlib="llvm-ranlib"
clone_if_missing "$SECP256K1_REPO" "$dir"
cd "$dir"
[ -x ./configure ] || ./autogen.sh
CC="$cc" AR="$ar" RANLIB="$ranlib" ./configure \
--host=wasm32-wasi \
--enable-module-schnorrsig \
--prefix="$ABS_PREFIX" \
SECP_CFLAGS=-fPIC
make -j"$(nproc)" CC="$cc" AR="$ar" RANLIB="$ranlib"
make install CC="$cc" AR="$ar" RANLIB="$ranlib"

Copilot uses AI. Check for mistakes.
wasm32-wasi-clang -shared -Wl,--whole-archive \
"$ABS_PREFIX/lib/libsecp256k1.a" \
-o "$ABS_PREFIX/lib/libsecp256k1.so"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-o "$ABS_PREFIX/lib/libsecp256k1.so"
-o "$ABS_PREFIX/lib/libsecp256k1.so"
cd -

Same here

}

build_blst() {
local dir="$ABS_SRC_ROOT/blst"
clone_if_missing "$BLST_REPO" "$dir"
cd "$dir"
CC=wasm32-wasi-clang ./build.sh

cp libblst.a "$ABS_PREFIX/lib/"
cp bindings/blst.h bindings/blst_aux.h "$ABS_PREFIX/include/"

wasm32-wasi-clang -shared -Wl,--whole-archive \
"$ABS_PREFIX/lib/libblst.a" \
-o "$ABS_PREFIX/lib/libblst.so"

local version
version="$(git -C "$dir" describe --tags --always 2>/dev/null || echo 0.0.0)"
cat > "$ABS_PREFIX/lib/pkgconfig/libblst.pc" <<EOF
prefix=$ABS_PREFIX
exec_prefix=\${prefix}
libdir=\${exec_prefix}/lib
includedir=\${prefix}/include

Name: libblst
Description: blst BLS12-381 signature library
URL: https://github.com/supranational/blst
Version: $version
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When testing the script I found an issue with the version of libblst in the pkg-config file (libblst.pc), which was Version: v0.3.16-36-gdece82e and the "v" at the beginning was tripping cabal. Not sure why that happens. Did you get that too?

Maybe it would be fixed by fixing a revision?


Cflags: -I\${includedir}
Libs: -L\${libdir} -lblst
Libs.private:
EOF
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
EOF
EOF
cd -

And here

}

verify_wasm() {
local archive="$1"
local tmp
tmp="$(mktemp -d)"
( cd "$tmp" && ar x "$archive" && \
first_obj="$(ls *.o 2>/dev/null | head -1)" && \
[ -n "$first_obj" ] && file "$first_obj" | grep -q WebAssembly )
local rc=$?
rm -rf "$tmp"
if [ "$rc" -ne 0 ]; then
echo "ERROR: $archive does not appear to contain wasm objects" >&2
exit 1
fi
}

build_libsodium 2>&1 | sed -u $'s/^/\033[1;32mlibsodium\033[0m > /'
build_secp256k1 2>&1 | sed -u $'s/^/\033[1;36msecp256k1\033[0m > /'
build_blst 2>&1 | sed -u $'s/^/\033[1;33mblst\033[0m > /'

verify_wasm "$ABS_PREFIX/lib/libsodium.a"
verify_wasm "$ABS_PREFIX/lib/libsecp256k1.a"
verify_wasm "$ABS_PREFIX/lib/libblst.a"

echo
echo "Done. Installed to: $ABS_PREFIX"

FRAGMENT="$PROJECT_DIR/wasm-libs-without-nix.cabal"
cat > "$FRAGMENT" <<EOF
shared: True
Comment on lines +181 to +183
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated wasm-libs-without-nix.cabal is written into the repo root and uses absolute paths ($ABS_PREFIX). This can clutter the working tree and makes the fragment non-portable if the checkout moves; consider writing it under scripts/wasm-without-nix/ (or similar) and using relative paths when possible.

Copilot uses AI. Check for mistakes.

package cardano-crypto-class
extra-lib-dirs: $ABS_PREFIX/lib
extra-include-dirs: $ABS_PREFIX/include

package cardano-crypto-praos
extra-lib-dirs: $ABS_PREFIX/lib
extra-include-dirs: $ABS_PREFIX/include
EOF
echo "Wrote project fragment: $FRAGMENT"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe would be good to mention adding the pkg-config path to PKG_CONFIG_PATH if it is not standard, and print which it is


CABAL_PROJECT="$PROJECT_DIR/cabal.project"

if grep -qF "wasm-libs-without-nix.cabal" "$CABAL_PROJECT"; then
echo "cabal.project already imports wasm-libs-without-nix.cabal — nothing to do."
else
read -r -p "Add 'if arch(wasm32) / import: wasm-libs-without-nix.cabal' block to cabal.project? [y/N] " answer || answer=""
case "${answer,,}" in
y|yes)
cat >> "$CABAL_PROJECT" <<'EOF'

if arch(wasm32)
import: wasm-libs-without-nix.cabal
EOF
echo "Added to $CABAL_PROJECT"
;;
*)
cat <<'EOF'
Skipped. To enable, append to cabal.project:

if arch(wasm32)
import: wasm-libs-without-nix.cabal
EOF
;;
esac
fi
Loading