Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
5a5ad23
first successful build and run in simulator using in-repo test app
matthargett Oct 17, 2025
ea40648
adjust optimization and C++ flags. -Oz produces a 2MB smaller intl bi…
matthargett Oct 18, 2025
4b4310f
Update scripts/start.sh
matthargett Oct 19, 2025
bf0a760
clang bug in NDK 27 makes loop vectorization pass error. disable the …
matthargett Oct 19, 2025
daf4c7e
[ci] cleanup ubuntu runner disk (#188)
Kudo Oct 19, 2025
055fcd8
patches on top of bun, scripts to make things more dynamically discov…
matthargett Nov 2, 2025
b2e4240
lto=thin is clang-only, even in Ubuntu 24.x, and we only really care …
matthargett Nov 3, 2025
79cf238
be more flexible with suboptimal ABI requests, but still favor armv8a…
matthargett Nov 3, 2025
509e65b
filter 32bit platforms out
matthargett Nov 4, 2025
abbf732
upstream update fixes a problem where Promise reactions weren't first…
matthargett Nov 5, 2025
d7dbeb7
build and publish separate artifacts for NDK 27, 28, and 29 so decoup…
matthargett Nov 5, 2025
940c071
cache the NDKs instead of re-downloading and re-unpacking each time. …
matthargett Nov 5, 2025
3951b75
nointl build was broken due to incorrect codepoint comparison that wa…
matthargett Nov 7, 2025
aa5d326
ignore NDK-specific dist directories
matthargett Nov 7, 2025
74de2cf
fix overly specific shell usage
matthargett Nov 11, 2025
dbab32c
don't add clang flags to gcc. I thought for sure this would be fine, …
matthargett Nov 11, 2025
5ba223a
fix corrupt patch
matthargett Nov 12, 2025
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
cache the NDKs instead of re-downloading and re-unpacking each time. …
…for that matter, use cccache so that incremental updates from upstream bun-sh/webkit don't trigger the 90+ minute (time three, for each NDK) build.
  • Loading branch information
matthargett committed Nov 5, 2025
commit 940c07175c35f1c50535c1ce51006c81b2016860
197 changes: 179 additions & 18 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
name: Build jsc-android and test

on:
workflow_dispatch: {}
workflow_dispatch:
inputs:
publish:
description: 'Publish npm packages after build/test succeed'
type: boolean
default: false
npm-tag:
description: 'npm dist-tag'
required: false
default: latest
dry-run:
description: 'Run npm publish in dry-run mode'
type: boolean
default: true
push:
branches: [main]
pull_request:
Expand All @@ -12,6 +25,9 @@ jobs:
env:
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SIGNING_PASSWORD }}
NDK_VERSION_27: '27.1.12297006'
NDK_VERSION_28: '28.2.13676358'
BUILD_CACHE_VERSION: v1

steps:
- uses: actions/checkout@v4
Expand All @@ -30,39 +46,126 @@ jobs:
with:
node-version: 22

- name: Compute build cache key
id: build-hash
run: |
HASH=$(node scripts/build-hash.js)
echo "hash=$HASH" >> "$GITHUB_OUTPUT"

- name: Restore JSC build artifacts
id: cache-dist
uses: actions/cache@v4
with:
path: |
dist-ndk27
dist-ndk27.unstripped
dist-ndk28
dist-ndk28.unstripped
dist-ndk29
dist-ndk29.unstripped
key: jsc-dist-${{ env.BUILD_CACHE_VERSION }}-${{ steps.build-hash.outputs.hash }}

- name: Restore WebKit sources
id: cache-download
uses: actions/cache@v4
with:
path: build/download
key: jsc-download-${{ env.BUILD_CACHE_VERSION }}-${{ steps.build-hash.outputs.hash }}
restore-keys: |
jsc-download-${{ env.BUILD_CACHE_VERSION }}-

- name: Install packages
run: |
sudo apt-get update
sudo apt-get install coreutils curl git wget python3 ruby gperf -y
sudo apt-get install coreutils curl git wget python3 ruby gperf ccache -y
shell: bash

- name: Restore ccache
id: cache-ccache
uses: actions/cache@v4
with:
path: ~/.cache/ccache
key: ccache-${{ env.BUILD_CACHE_VERSION }}-${{ runner.os }}-${{ env.NDK_VERSION_27 }}-${{ steps.build-hash.outputs.hash }}
restore-keys: |
ccache-${{ env.BUILD_CACHE_VERSION }}-${{ runner.os }}-${{ env.NDK_VERSION_27 }}-

- name: Configure ccache
run: |
mkdir -p ~/.cache/ccache
if command -v ccache >/dev/null 2>&1; then
ccache --max-size=5G
ccache --zero-stats || true
fi
shell: bash

- name: Cache Android NDK r27
id: cache-ndk-27
uses: actions/cache@v4
with:
path: ${{ env.ANDROID_HOME }}/ndk/${{ env.NDK_VERSION_27 }}
key: android-ndk-${{ runner.os }}-${{ env.NDK_VERSION_27 }}

- name: Install Android packages
run: |
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools
yes | sdkmanager --licenses || true
sdkmanager \
"cmake;3.22.1" \
"ndk;27.1.12297006"
# move out builtin icu headers from ndk and prevent icu build errors
mv "${ANDROID_HOME}/ndk/27.1.12297006/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/unicode" "${ANDROID_HOME}/ndk/27.1.12297006/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/unicode2"

echo "ANDROID_NDK=$ANDROID_HOME/ndk/27.1.12297006" >> $GITHUB_ENV
echo "PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools" >> $GITHUB_ENV
sdkmanager "cmake;3.22.1"
if [[ ! -d "${ANDROID_HOME}/ndk/${NDK_VERSION_27}" ]]; then
sdkmanager "ndk;${NDK_VERSION_27}"
fi
UNICODE_DIR="${ANDROID_HOME}/ndk/${NDK_VERSION_27}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/unicode"
if [[ -d "${UNICODE_DIR}" ]]; then
mv "${UNICODE_DIR}" "${UNICODE_DIR}2"
fi
echo "ANDROID_NDK=$ANDROID_HOME/ndk/${NDK_VERSION_27}" >> $GITHUB_ENV
echo "PATH=$PATH" >> $GITHUB_ENV
shell: bash

- name: Install dependencies
run: yarn install --frozen-lockfile
shell: bash

- name: Clean previous build outputs
if: steps.cache-dist.outputs.cache-hit != 'true'
run: |
rm -rf dist dist.unstripped dist-ndk* build/target* build/compiled* build/cppruntime*
shell: bash

- name: Download sources
if: steps.cache-download.outputs.cache-hit != 'true'
run: yarn download
shell: bash

- name: Build
if: steps.cache-dist.outputs.cache-hit != 'true'
run: yarn start
shell: bash

- name: Show ccache stats
if: steps.cache-dist.outputs.cache-hit != 'true'
run: |
yarn install --frozen-lockfile
yarn clean
yarn download
yarn start
if command -v ccache >/dev/null 2>&1; then
ccache --show-stats
fi
shell: bash

- name: Archive
run: |
rm -rf archive
mkdir -p archive
mv dist archive/
mv dist.unstripped archive/
shopt -s nullglob
found=0
for dir in dist-ndk*; do
if [[ -d "$dir" ]]; then
cp -R "$dir" archive/
found=1
fi
done
shopt -u nullglob
if [[ $found -eq 0 ]]; then
echo "No distribution directories were produced." >&2
exit 1
fi
shell: bash

- uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -99,8 +202,12 @@ jobs:

- name: Extract archive
run: |
mv archive/dist dist
mv archive/dist.unstripped dist.unstripped
shopt -s nullglob
for dir in archive/dist-ndk*; do
dest=$(basename "$dir")
mv "$dir" "$dest"
done
shopt -u nullglob
rmdir archive
shell: bash

Expand Down Expand Up @@ -128,6 +235,7 @@ jobs:
target: google_apis
working-directory: test
script: |
export JSC_GRADLE_DIST_PATH=../../dist-ndk27
npx expo run:android --variant release --no-bundler
adb logcat -c
set +e
Expand All @@ -145,3 +253,56 @@ jobs:
$HOME/.maestro/tests/**/*
test/android/app/build/outputs/apk/release/app-release.apk
test/adb.log

publish:
if: github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true'
needs:
- build
- test
runs-on: ubuntu-latest
environment:
name: npm-publish
url: https://www.npmjs.com/package/jsc-android
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- name: ⬢ Setup Node
uses: actions/setup-node@v4
with:
node-version: 22

- uses: actions/download-artifact@v4
with:
name: archive
path: archive

- name: Install dependencies
run: yarn install --frozen-lockfile
shell: bash

- name: Verify npm token availability
if: github.event.inputs.dry-run != 'true'
run: |
if [[ -z "${NPM_TOKEN:-}" ]]; then
echo "NPM_TOKEN secret is required for publishing." >&2
exit 1
fi
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
shell: bash

- name: Publish packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: 'true'
run: |
TAG="${{ github.event.inputs.npm-tag }}"
DRY_RUN="${{ github.event.inputs.dry-run }}"
PUBLISH_ARGS=("-T" "$TAG")
if [[ "$DRY_RUN" == 'true' ]]; then
PUBLISH_ARGS+=("--dry-run")
fi
node scripts/publish.js "${PUBLISH_ARGS[@]}" archive
shell: bash
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"/dist"
],
"scripts": {
"clean": "rm -rf dist; rm -rf build",
"clean": "rm -rf dist dist.unstripped dist-ndk* dist-ndk*.unstripped; rm -rf build",
"info": "./scripts/info.sh",
"download": "./scripts/download.sh",
"start": "./scripts/start.sh"
Expand Down
75 changes: 75 additions & 0 deletions scripts/build-hash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env node

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');

const rootDir = process.cwd();

const includePaths = [
'package.json',
'yarn.lock',
'patches',
'scripts',
'lib',
];

const excludePatterns = [/^scripts\/publish\.js$/];

const hash = crypto.createHash('sha256');

function shouldInclude(relPath) {
if (relPath.includes('node_modules')) {
return false;
}
if (relPath.startsWith('.git')) {
return false;
}
return true;
}

function shouldExclude(relPath) {
return excludePatterns.some((pattern) => pattern.test(relPath));
}

function addFile(filePath, relPath) {
const content = fs.readFileSync(filePath);
hash.update(relPath);
hash.update('\0');
hash.update(content);
}

function walk(currentPath, basePath) {
const entries = fs.readdirSync(currentPath).sort();
entries.forEach((entry) => {
const absPath = path.join(currentPath, entry);
const relPath = path.relative(basePath, absPath).replace(/\\/g, '/');

if (!shouldInclude(relPath) || shouldExclude(relPath)) {
return;
}

const stat = fs.statSync(absPath);
if (stat.isDirectory()) {
walk(absPath, basePath);
} else if (stat.isFile()) {
addFile(absPath, relPath);
}
});
}

includePaths.forEach((relativePath) => {
const relative = relativePath.replace(/\\/g, '/');
const abs = path.join(rootDir, relative);
if (!fs.existsSync(abs)) {
return;
}
const stat = fs.statSync(abs);
if (stat.isDirectory()) {
walk(abs, rootDir);
} else if (stat.isFile()) {
addFile(abs, path.relative(rootDir, abs).replace(/\\/g, '/'));
}
});

process.stdout.write(hash.digest('hex'));
11 changes: 9 additions & 2 deletions scripts/compile/icu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ else
CONFIGURE_PREFIX=()
fi

CC_BIN=$CROSS_COMPILE_PLATFORM_CC-clang
CXX_BIN=$CROSS_COMPILE_PLATFORM_CC-clang++
if [[ -n "$JSC_CCACHE_BIN" ]]; then
CC_BIN="$JSC_CCACHE_BIN $CC_BIN"
CXX_BIN="$JSC_CCACHE_BIN $CXX_BIN"
fi

"${CONFIGURE_PREFIX[@]}" $TARGETDIR/icu/source/configure --prefix=${INSTALL_DIR} \
$BUILD_TYPE_CONFIG \
--host=$CROSS_COMPILE_PLATFORM \
Expand All @@ -52,8 +59,8 @@ fi
CFLAGS="$ICU_CFLAGS" \
CXXFLAGS="$ICU_CXXFLAGS" \
LDFLAGS="$ICU_LDFLAGS" \
CC=$CROSS_COMPILE_PLATFORM_CC-clang \
CXX=$CROSS_COMPILE_PLATFORM_CC-clang++ \
CC="$CC_BIN" \
CXX="$CXX_BIN" \
AR=$TOOLCHAIN_DIR/bin/llvm-ar \
LD=$TOOLCHAIN_DIR/bin/ld \
RANLIB=$TOOLCHAIN_DIR/bin/llvm-ranlib \
Expand Down
6 changes: 6 additions & 0 deletions scripts/compile/jsc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ source $SCRIPT_DIR/common.sh
CMAKE_FOLDER=$(cd $ANDROID_HOME/cmake && ls -1 | sort -r | head -1)
PATH=$TOOLCHAIN_DIR/bin:$ANDROID_HOME/cmake/$CMAKE_FOLDER/bin/:$PATH

CCACHE_CMAKE_ARGS=""
if [[ -n "$JSC_CCACHE_BIN" ]]; then
CCACHE_CMAKE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=${JSC_CCACHE_BIN} -DCMAKE_CXX_COMPILER_LAUNCHER=${JSC_CCACHE_BIN}"
fi

rm -rf $TARGETDIR/webkit/$CROSS_COMPILE_PLATFORM-${FLAVOR}
rm -rf $TARGETDIR/webkit/WebKitBuild
cd $TARGETDIR/webkit/Tools/Scripts
Expand Down Expand Up @@ -75,6 +80,7 @@ $TARGETDIR/webkit/Tools/Scripts/build-webkit \
-DCMAKE_CXX_STANDARD_REQUIRED=ON \
-DCMAKE_CXX_EXTENSIONS=OFF \
-DCMAKE_VERBOSE_MAKEFILE=on \
$CCACHE_CMAKE_ARGS \
-DENABLE_API_TESTS=OFF \
-DENABLE_SAMPLING_PROFILER=OFF \
-DENABLE_DFG_JIT=OFF \
Expand Down
Loading
Loading