Skip to content

Build bytecode compilers #4

Build bytecode compilers

Build bytecode compilers #4

name: Build bytecode compilers
# Builds the per-host bytecode compiler CLIs for every engine that supports
# ahead-of-time bytecode, by cloning each engine's UPSTREAM repo (not the
# android-runtime vendored sources) and building the CLI there.
#
# Output: one artifact per (engine, host), named `bytecode-compiler-<engine>-<host>`,
# each containing `<engine>/<host>/<binary>` so the downloads merge straight into
# tools/bytecode-compiler/bin/.
#
# NOTE ON COMPATIBILITY: the produced bytecode must be loadable by the engine
# library the runtime ships. Hermes/PrimJS runtimes link prebuilt .so's and
# QuickJS/QuickJS-NG compile from vendored source, so the refs below must be kept
# in step with what the runtime actually bundles. Pin these to exact commits once
# validated instead of moving branches.
on:
workflow_dispatch:
inputs:
hermes_ref:
description: "facebook/hermes ref"
default: "10a40b5f1de81d007cba6afe567c4ba7a0b45f5d"
quickjs_ref:
# MUST match the quickjs submodule pin (BC_VERSION must agree with the
# runtime). See .gitmodules / scripts/vendor-engines-as-submodules.sh.
description: "bellard/quickjs ref (match the submodule pin)"
default: "04be246001599f5995fa2f2d8c91a0f198d3f34c"
quickjs_ng_ref:
# MUST match the quickjs_ng submodule pin (BC_VERSION must agree with the
# runtime, or JS_ReadObject rejects the blob). See .gitmodules /
# scripts/vendor-engines-as-submodules.sh.
description: "quickjs-ng/quickjs ref (match the submodule pin)"
default: "d950d55e950dd7994a96c669c31efa967b8b79f3"
primjs_ref:
description: "lynx-family/primjs ref"
default: "9c1fe150179b86f0ae5c6fbe670af7d76eca9df6"
env:
HERMES_REPO: https://github.com/facebook/hermes
QUICKJS_REPO: https://github.com/bellard/quickjs
QUICKJS_NG_REPO: https://github.com/quickjs-ng/quickjs
PRIMJS_REPO: https://github.com/lynx-family/primjs
jobs:
# ---------------------------------------------------------------------------
# Hermes — `hermesc` already emits a loadable HBC blob; no shim needed.
# ---------------------------------------------------------------------------
hermes:
name: hermes (${{ matrix.host }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- { host: linux-x64, runner: ubuntu-24.04 }
- { host: linux-arm64, runner: ubuntu-24.04-arm }
- { host: darwin-x64, runner: macos-13 }
- { host: darwin-arm64, runner: macos-14 }
- { host: win32-x64, runner: windows-2022 }
# win32-arm64 dropped: hermes' Boost.Context ARM64 assembler + CMake's
# ASM_ARMASM linker support fail on windows-11-arm. The win32-x64
# hermesc runs on ARM Windows via x64 emulation (HBC output is portable).
steps:
- name: Install toolchain (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y cmake ninja-build build-essential python3
- name: Install toolchain (macOS)
if: runner.os == 'macOS'
run: brew install cmake ninja
- name: Install toolchain (Windows)
if: runner.os == 'Windows'
run: choco install ninja -y
- name: Clone Hermes
shell: bash
# Fetch by ref so an exact commit SHA works (git clone --branch only
# accepts branch/tag names). GitHub allows fetching arbitrary SHAs.
run: |
set -euxo pipefail
ref="${{ github.event.inputs.hermes_ref || 'master' }}"
git init hermes
git -C hermes remote add origin "$HERMES_REPO"
git -C hermes fetch --depth 1 origin "$ref"
git -C hermes -c advice.detachedHead=false checkout FETCH_HEAD
- name: Build hermesc (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
set -euxo pipefail
cmake -S hermes -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DHERMES_BUILD_APPLE_FRAMEWORK=OFF
cmake --build build --target hermesc
- name: Build hermesc (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
set -euxo pipefail
# Use the default (Visual Studio) generator; Ninja+MSVC also works if the
# VS dev environment is on PATH.
cmake -S hermes -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --target hermesc --config Release
- name: Stage binary
shell: bash
run: |
set -euxo pipefail
mkdir -p "out/hermes/${{ matrix.host }}"
bin="$(find build -type f \( -name hermesc -o -name hermesc.exe \) | head -n1)"
test -n "$bin"
cp "$bin" "out/hermes/${{ matrix.host }}/"
- uses: actions/upload-artifact@v4
with:
name: bytecode-compiler-hermes-${{ matrix.host }}
path: out/hermes/${{ matrix.host }}
if-no-files-found: error
# ---------------------------------------------------------------------------
# QuickJS (bellard) — build via its own Makefile (libquickjs.a, which injects
# -D_GNU_SOURCE -DCONFIG_VERSION), then link our blob-emitting shim.
# win32-x64 is CROSS-compiled on Linux via bellard's CONFIG_WIN32 (mingw-w64);
# win32-arm64 is not possible (mingw-w64 targets x86 only).
# ---------------------------------------------------------------------------
quickjs:
name: quickjs (${{ matrix.host }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- { host: linux-x64, runner: ubuntu-24.04 }
- { host: linux-arm64, runner: ubuntu-24.04-arm }
- { host: darwin-x64, runner: macos-13 }
- { host: darwin-arm64, runner: macos-14 }
- { host: win32-x64, runner: ubuntu-24.04 } # mingw cross-compile
steps:
- uses: actions/checkout@v4
- name: Install toolchain (Linux)
if: runner.os == 'Linux'
run: |
set -euxo pipefail
sudo apt-get update
sudo apt-get install -y build-essential
# mingw-w64 for the win32-x64 cross build.
if [ "${{ matrix.host }}" = "win32-x64" ]; then sudo apt-get install -y mingw-w64; fi
# macOS: make + clang ship with the Xcode command-line tools.
- name: Clone QuickJS
shell: bash
run: |
set -euxo pipefail
ref="${{ github.event.inputs.quickjs_ref || 'master' }}"
git init quickjs
git -C quickjs remote add origin "$QUICKJS_REPO"
git -C quickjs fetch --depth 1 origin "$ref"
git -C quickjs -c advice.detachedHead=false checkout FETCH_HEAD
- name: Build lib + shim
shell: bash
env:
SHIM: ${{ github.workspace }}/tools/bytecode-compiler/native/qjs-compile.c
run: |
set -euxo pipefail
cd quickjs
jobs="$(getconf _NPROCESSORS_ONLN)"
out="../out/quickjs/${{ matrix.host }}"; mkdir -p "$out"
if [ "${{ matrix.host }}" = "win32-x64" ]; then
# CONFIG_WIN32=y selects the x86_64-w64-mingw32- cross toolchain.
make -j"$jobs" CONFIG_WIN32=y libquickjs.a
x86_64-w64-mingw32-gcc -O2 -DNSBC_MAGIC='"NSBCQJS"' -I. \
-o "$out/nsbc-quickjs.exe" "$SHIM" libquickjs.a -lm
# Cross-built Windows binary — not runnable on the Linux host.
else
# Use bellard's own Makefile so CONFIG_VERSION / _GNU_SOURCE and the
# correct source set come from upstream.
make -j"$jobs" libquickjs.a
cc -O2 -DNSBC_MAGIC='"NSBCQJS"' -I. -o "$out/nsbc-quickjs" "$SHIM" libquickjs.a -lm
"$out/nsbc-quickjs" || true # usage line (exit 2) proves it links & runs
fi
- uses: actions/upload-artifact@v4
with:
name: bytecode-compiler-quickjs-${{ matrix.host }}
path: out/quickjs/${{ matrix.host }}
if-no-files-found: error
# ---------------------------------------------------------------------------
# QuickJS-NG — CMake build of libqjs, then our shim (same JS_* API).
# ---------------------------------------------------------------------------
quickjs-ng:
name: quickjs-ng (${{ matrix.host }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- { host: linux-x64, runner: ubuntu-24.04 }
- { host: linux-arm64, runner: ubuntu-24.04-arm }
- { host: darwin-x64, runner: macos-13 }
- { host: darwin-arm64, runner: macos-14 }
- { host: win32-x64, runner: windows-2022 }
- { host: win32-arm64, runner: windows-11-arm }
steps:
- uses: actions/checkout@v4
- name: Install toolchain (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y cmake ninja-build build-essential
- name: Install toolchain (macOS)
if: runner.os == 'macOS'
run: brew install cmake ninja
- name: Install toolchain (Windows)
if: runner.os == 'Windows'
run: choco install ninja llvm -y
- name: Clone QuickJS-NG
shell: bash
run: |
set -euxo pipefail
ref="${{ github.event.inputs.quickjs_ng_ref || 'master' }}"
git init quickjs-ng
git -C quickjs-ng remote add origin "$QUICKJS_NG_REPO"
git -C quickjs-ng fetch --depth 1 origin "$ref"
git -C quickjs-ng -c advice.detachedHead=false checkout FETCH_HEAD
- name: Build lib + shim
shell: bash
env:
SHIM: ${{ github.workspace }}/tools/bytecode-compiler/native/qjs-compile.c
run: |
set -euxo pipefail
# Build only the static engine lib target ('qjs' -> libqjs.a / qjs.lib);
# skip the CLI/test executables.
cmake -S quickjs-ng -B quickjs-ng/build -DCMAKE_BUILD_TYPE=Release
cmake --build quickjs-ng/build --config Release --target qjs
lib="$(find quickjs-ng/build -type f \( -name 'libqjs.a' -o -name 'qjs.lib' \) | head -n1)"
test -n "$lib"
# Windows has no libm (math is in the CRT); only pass -lm off-Windows.
CC="cc"; LIBM="-lm"
if [ "${{ runner.os }}" = "Windows" ]; then CC="clang"; LIBM=""; fi
out="out/quickjs-ng/${{ matrix.host }}"; mkdir -p "$out"
bin="$out/nsbc-quickjs-ng"; [ "${{ runner.os }}" = "Windows" ] && bin="$bin.exe"
$CC -O2 -DNSBC_MAGIC='"NSBCNGS"' -I quickjs-ng -o "$bin" "$SHIM" "$lib" $LIBM
"$bin" || true
- uses: actions/upload-artifact@v4
with:
name: bytecode-compiler-quickjs-ng-${{ matrix.host }}
path: out/quickjs-ng/${{ matrix.host }}
if-no-files-found: error
# ---------------------------------------------------------------------------
# PrimJS — build via its own toolchain: `hab sync` fetches gn + ninja + a
# bundled clang + sysroot, then GN/ninja build the engine. We add our LEPUS_*
# shim as a sibling GN executable (reusing the qjs-cli engine deps).
#
# Unix only, and NOT linux-arm64: PrimJS's envsetup.sh aborts on Windows, and
# its DEPS only provides a linux sysroot for x86_64. So: linux-x64 + macOS.
#
# NOTE: this is the first automated PrimJS build; `hab sync` pulls a large
# toolchain and the exact gn/ninja target may need tweaking on the first run.
# ---------------------------------------------------------------------------
primjs:
name: primjs (${{ matrix.host }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- { host: linux-x64, runner: ubuntu-24.04 }
- { host: darwin-x64, runner: macos-13 }
- { host: darwin-arm64, runner: macos-14 }
steps:
- uses: actions/checkout@v4
- name: Install prerequisites (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y python3 curl
- name: Clone PrimJS
shell: bash
run: |
set -euxo pipefail
ref="${{ github.event.inputs.primjs_ref || 'develop' }}"
git init primjs
git -C primjs remote add origin "$PRIMJS_REPO"
git -C primjs fetch --depth 1 origin "$ref"
git -C primjs -c advice.detachedHead=false checkout FETCH_HEAD
- name: Add shim as a GN target
shell: bash
env:
SHIM: ${{ github.workspace }}/tools/bytecode-compiler/native/primjs-compile.c
run: |
set -euxo pipefail
# Build as C++ (.cc) to match the working qjs-cli target; the LEPUS_*
# API is extern "C", so linkage is fine either way.
cp "$SHIM" primjs/tools/qjs-cli/nsbc-primjs.cc
# A standalone executable that links the same engine as qjs-cli.
cat >> primjs/tools/qjs-cli/BUILD.gn <<'GN'
executable("nsbc-primjs") {
cflags = [ "-Wno-c99-designator" ]
sources = [ "nsbc-primjs.cc" ]
include_dirs = [ "//include" ]
deps = [ ":qjs_wasm_binding" ]
configs += [ ":wasm_internal_config" ]
}
GN
- name: Build (hab sync + gn + ninja)
shell: bash
working-directory: primjs
run: |
set -euxo pipefail
# hab (in-repo) fetches gn, ninja, the bundled clang toolchain + sysroot.
source tools/envsetup.sh
hab sync .
gn gen out/Default --args="is_debug=false"
ninja -C out/Default nsbc-primjs
out="../out/primjs/${{ matrix.host }}"; mkdir -p "$out"
cp out/Default/nsbc-primjs "$out/nsbc-primjs"
"$out/nsbc-primjs" || true # usage line (exit 2) proves it links & runs
- uses: actions/upload-artifact@v4
with:
name: bytecode-compiler-primjs-${{ matrix.host }}
path: out/primjs/${{ matrix.host }}
if-no-files-found: error