Skip to content

Swing, SwingX and JavaFX compatibility layers: desktop Java apps as Codename One apps #1348

Swing, SwingX and JavaFX compatibility layers: desktop Java apps as Codename One apps

Swing, SwingX and JavaFX compatibility layers: desktop Java apps as Codename One apps #1348

name: ParparVM Self-Hosting
# Translates the ByteCodeTranslator with itself and compares the result against
# the same translation run on a JVM.
#
# What this buys that the existing suites do not: ByteCodeTranslator is a 37.6k
# line real program that hammers collections, strings, exceptions, file I/O and
# the GC at a scale no unit test reaches, and the emitted C is a byte-exact
# expected value that costs nothing to maintain -- it is whatever the JVM
# produced from the same inputs. A VM defect that changes behaviour rather than
# crashing (a wrong hash order, a dropped write barrier, a mis-mangled symbol)
# shows up as a diff instead of passing silently.
#
# Gates, cheapest first:
# D native vs native, two fresh processes, same input. If the native side is
# not self-consistent nothing else means anything, so it runs first.
# A JVM vs native over the same corpus. The headline.
# J the same comparison for the JavaScript target: the native translator
# carries the JavaScript backend too, and must emit the same bundle.
# C, S JVM vs the translator TRANSLATED TO JAVASCRIPT and run under Node, for
# the C and the JavaScript targets (verify-selfhost-js.sh): self-hosting on
# the JavaScript target.
# Negative control: after a green comparison one emitted byte is flipped and
# the comparator MUST report exactly that file. A comparator nobody has
# watched fail is not a comparator.
#
# Not on the PR leg by default: a full run builds the translator twice and
# translates a large corpus several times. It runs nightly, on demand, and on a
# PR that opts in with the `selfhost` label.
on:
schedule:
# 04:20 UTC daily, off the hour to avoid the runner rush.
- cron: '20 4 * * *'
workflow_dispatch:
pull_request:
types: [ opened, synchronize, reopened, labeled ]
paths:
- 'vm/**'
- '.github/workflows/parparvm-selfhost.yml'
- '!vm/**/README.md'
- '!vm/**/docs/**'
concurrency:
# Only the latest revision needs validation. PR numbers keep forks isolated;
# event names keep a manual/scheduled run from cancelling a push or PR run.
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
CN1_NATIVE_VERIFY: strict
# A method the translator culled as unreachable is still emitted as a stub,
# because dispatch tables name it. With this on, the stub aborts with the
# method's name instead of returning 0, so a wrong cull fails the suite that
# reached it rather than shipping a silent null. Ours to enforce, like
# CN1_NATIVE_VERIFY: customer builds keep the plain stub. See
# BytecodeMethod.appendMethodC. Not in the size or perf benchmarks, whose
# binaries should be the ones customers get.
CN1_CULL_TRAP: '1'
jobs:
selfhost:
# On a pull_request only when the author asked for it; the schedule and
# workflow_dispatch legs always run.
if: >-
github.event_name != 'pull_request' ||
contains(github.event.pull_request.labels.*.name, 'selfhost')
runs-on: ubuntu-24.04
timeout-minutes: 120
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Install native build tools
run: |
bash scripts/ci/apt-get-update.sh
# `time` is GNU time, which is what bench-selfhost.sh's Linux peak-memory
# probe reads ("Maximum resident set size"). The shell builtin is not it.
sudo apt-get install -y clang time
# ONLY A JDK 8 HERE. This workflow used to set up a JDK 25 as well, for a
# performance step that has since moved into each platform's own build (see the
# note further down); nothing left in this job reads JDK_25_HOME, so setting it up
# would just be a slower runner. bench-selfhost.py names each arm after the JDK's
# real feature version, so a missing JDK 25 raises instead of silently measuring
# JDK 8 against itself.
- name: Set up JDK 8
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '8'
cache: 'maven'
- name: Save JDK 8 path
run: echo "JDK_8_HOME=$JAVA_HOME" >> $GITHUB_ENV
# The translator has to exist as classes before it can translate itself.
- name: Build the translator
run: >-
"$GITHUB_WORKSPACE/scripts/ci/retry.sh" mvn -q -B
-pl ByteCodeTranslator -am package -DskipTests
working-directory: vm
# -O1: the diff gates care about the EMITTED C, not about how well clang
# optimised the binary that emitted it, and -O1 links several times faster.
#
# NO -DCN1_GC_MARK_THREADS HERE ANY MORE. This step used to pin 4 markers
# because "the source default resolves to a single marker and makes a large
# corpus take hours" -- both halves of that are now false: the default is
# CPU-derived (capped at 4) on POSIX. Pinning it would leave the one workflow
# that actually self-hosts unable to notice a regression in the very default
# it exists to exercise, which is how the single-marker default survived in
# the first place. Build what ships.
- name: Build the self-hosted translator
run: vm/selfhost/build-selfhost.sh
# The corpus is the translator's OWN classes plus ASM, which build-selfhost.sh
# fetches and extracts as corpus only -- the translator does not use it. verify-selfhost.sh
# prepends vm/selfhost/target/javaapi-classes itself, so it is not repeated
# here. Absolute paths: the script runs both sides under `env -i` into one
# fixed output directory, so a relative path would not survive.
- name: Gate D and Gate A, with the negative control
run: |
vm/selfhost/verify-selfhost.sh \
"$PWD/vm/selfhost/target/asm-classes;$PWD/vm/selfhost/target/classes" \
com_codename1_tools_translator_ByteCodeTranslator \
com.codename1.tools.translator
# PERFORMANCE MOVED OUT, into every platform's own build: vm/selfhost/ci-perf-gate.sh
# runs in linux-build-run.yml (x64, arm64), parparvm-tests-windows.yml (x64, arm64)
# and scripts-macos.yml, fails that build on a regression and puts the per-benchmark
# table in that platform's PR comment.
#
# It used to live here, building the translator a second time at -O3 and then
# reporting (never gating) the ratio against JDK 25. Two reasons it is gone:
#
# - This workflow is the CORRECTNESS suite and it only runs on a PR that asks
# for it by label. A performance regression has to fail on the PR that causes
# it, not on whichever nightly somebody reads next, so the perf gate runs on
# every pull request touching vm/** instead.
# - Keeping it in both places would pay for the expensive -O3 self-host build
# twice on any PR carrying the `selfhost` label.
#
# The baselines and tolerance live in vm/selfhost/perf-baseline/, the
# measurement in vm/selfhost/perf-gate.py.
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: '20'
# The translator as a JavaScript program, against the JVM translator, over the
# same corpus. build-selfhost.sh has already compiled it against JavaAPI.
- name: Gate C and Gate S, self-hosted on JavaScript
run: |
vm/selfhost/verify-selfhost-js.sh \
"$PWD/vm/selfhost/target/asm-classes;$PWD/vm/selfhost/target/classes" \
com_codename1_tools_translator_ByteCodeTranslator \
com.codename1.tools.translator
# Both trees, so a divergence can be inspected rather than guessed at from
# a one-line summary.
- name: Upload the compared trees on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: selfhost-trees
path: |
vm/selfhost/target/verify/jvm-tree
vm/selfhost/target/verify/parpar1-tree
vm/selfhost/target/verify/parpar2-tree
vm/selfhost/target/verify/*.txt
vm/selfhost/target/verify/*.log
vm/selfhost/target/verify-js/*-tree
vm/selfhost/target/verify-js/*.txt
vm/selfhost/target/verify-js/*.log
retention-days: 7
if-no-files-found: ignore