Skip to content
Open
Show file tree
Hide file tree
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
276 changes: 276 additions & 0 deletions .github/workflows/linux-build-run.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
# Native Linux (GTK3) build + run pipeline. The Linux port builds natively on the
# runner -- no cross-compile -- so each architecture both builds and runs the
# binary. It runs on BOTH architectures GitHub hosts natively: x86_64
# (ubuntu-latest) and arm64 (ubuntu-24.04-arm), giving the port the same two-arch
# screenshot coverage the native Windows port has.
#
# The hellocodenameone screenshot suite is compiled by the codenameone maven
# plugin, which depends on CEF -- and CEF has no linux-arm64 build, so the plugin
# (hence the suite's *.class) only builds on x64. The translated C is
# architecture-independent, so the suite classes are built once on x64 and shared;
# each arch then builds only core + the Linux port (which build fine on arm64),
# translates the shared classes with the "linux" app type, native-builds the ELF
# (CMake/Ninja), runs it headless under Xvfb and captures the suite over the cn1ss
# WebSocket.
#
# Jobs:
# prepare-suite (x64) -> builds the plugin + hellocodenameone-common,
# uploads the suite + ads-mock classes
# build-run (matrix x64 + arm64) -> core + Linux port, translate + build + run,
# uploads PNGs/arch
# compare-comment (ubuntu) -> diffs each arch vs its in-repo baseline,
# posts the PR comment
name: Linux native build + run (GTK3, x64 + arm64)

on:
pull_request:
paths:
- '.github/workflows/linux-build-run.yml'
- 'vm/**'
- 'Ports/LinuxPort/**'
- 'maven/linux/**'
- 'scripts/linux/**'
- 'scripts/hellocodenameone/**'
- '!vm/**/README.md'
- '!vm/**/readme.md'
- '!vm/**/docs/**'
push:
branches: [ master, main ]
paths:
- '.github/workflows/linux-build-run.yml'
- 'vm/**'
- 'Ports/LinuxPort/**'
- 'maven/linux/**'

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
# Build the codenameone maven plugin (it needs CEF, which is x64-only on Linux)
# and the hellocodenameone screenshot suite, then publish the suite + ads-mock
# *.class. These are architecture-independent (they are translated to C later),
# so both arch legs translate from these same classes.
prepare-suite:
name: build suite classes (x64)
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Install Xvfb
run: |
set -e
sudo apt-get update
# The CN1 CSS compiler renders via CEF/AWT and throws HeadlessException
# without a display, so the common build runs under a virtual X server.
sudo apt-get install -y --no-install-recommends xvfb

- name: Set up JDK 8
uses: actions/setup-java@v5
with: { distribution: 'temurin', java-version: '8' }
- run: echo "JDK_8_HOME=$JAVA_HOME" >> $GITHUB_ENV
- name: Set up JDK 17
uses: actions/setup-java@v5
with: { distribution: 'temurin', java-version: '17' }
- run: echo "JDK_17_HOME=$JAVA_HOME" >> $GITHUB_ENV
- name: Restore JDK 8 as the active JDK
uses: actions/setup-java@v5
with: { distribution: 'temurin', java-version: '8' }

- name: Build plugin + cn1-ads-mock (JDK 8)
run: |
cd maven
JAVA_HOME="$JDK_8_HOME" mvn -B -pl codenameone-maven-plugin,cn1-ads-mock -am \
-DskipTests '-Dmaven.javadoc.skip=true' '-Plocal-dev-javase' install

- name: Build hellocodenameone-common (JDK 17)
run: |
set -e
export JAVA_HOME="$JDK_17_HOME"
Xvfb :99 -screen 0 1600x1200x24 >/tmp/xvfb-build.log 2>&1 &
export DISPLAY=:99
sleep 2
mkdir -p "$HOME/.codenameone"
[ -f "$HOME/.codenameone/guibuilder.jar" ] || : > "$HOME/.codenameone/guibuilder.jar"
mvn -B -f scripts/hellocodenameone/pom.xml -pl common -am install -DskipTests '-Dmaven.javadoc.skip=true'

- name: Package suite classes
run: |
set -e
tar czf suite-classes.tgz \
scripts/hellocodenameone/common/target/classes \
maven/cn1-ads-mock/target/classes
- name: Upload suite classes
uses: actions/upload-artifact@v4
with:
name: linux-suite-classes
path: suite-classes.tgz
retention-days: 1

build-run:
name: build + run suite (${{ matrix.arch }})
needs: prepare-suite
runs-on: ${{ matrix.runner }}
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
include:
- arch: x64
runner: ubuntu-latest
- arch: arm64
runner: ubuntu-24.04-arm
steps:
- name: Check out repository
uses: actions/checkout@v6

# The native Linux port's full dependency stack: GTK3 + Cairo + Pango +
# GdkPixbuf + GLib/GIO (render/widgets), FontConfig/FreeType (bundled-font
# registration), libcurl (HTTP), GStreamer (media/camera/audio), WebKitGTK
# (browser), libsecret (secure storage), libnotify (notifications), GeoClue
# (location), libepoxy + EGL/GLES + the Mesa software driver (the offscreen
# 3D backend). Plus CMake/Ninja, Xvfb and a font for Pango to lay out.
- name: Install the GTK build stack + toolchain
run: |
set -e
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
cmake ninja-build pkg-config unzip xvfb fonts-dejavu-core \
libgtk-3-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf-2.0-dev libglib2.0-dev \
libfontconfig1-dev libfreetype-dev \
libcurl4-openssl-dev \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
libwebkit2gtk-4.1-dev libsecret-1-dev libnotify-dev libgeoclue-2-dev \
libepoxy-dev libegl1-mesa-dev libgles2-mesa-dev libgl1-mesa-dri

- name: Set up JDK 8
uses: actions/setup-java@v5
with: { distribution: 'temurin', java-version: '8' }
- run: echo "JDK_8_HOME=$JAVA_HOME" >> $GITHUB_ENV
- name: Set up JDK 11
uses: actions/setup-java@v5
with: { distribution: 'temurin', java-version: '11' }
- run: echo "JDK_11_HOME=$JAVA_HOME" >> $GITHUB_ENV
- name: Set up JDK 17
uses: actions/setup-java@v5
with: { distribution: 'temurin', java-version: '17' }
- run: echo "JDK_17_HOME=$JAVA_HOME" >> $GITHUB_ENV
- name: Restore JDK 8 as the active JDK
uses: actions/setup-java@v5
with: { distribution: 'temurin', java-version: '8' }

# Build codename1-core + the Linux port only -- NOT the CEF-dependent maven
# plugin -- so this step works on arm64 too. The suite classes the translator
# also needs come from the prepare-suite artifact below.
- name: Build core + Linux port (JDK 8)
run: |
cd maven
JAVA_HOME="$JDK_8_HOME" mvn -B -pl linux -am \
-DskipTests '-Dmaven.javadoc.skip=true' '-Plocal-dev-javase' install

- name: Download suite classes
uses: actions/download-artifact@v4
with:
name: linux-suite-classes
- name: Unpack suite classes
run: tar xzf suite-classes.tgz

# Translate the suite with the "linux" app type, native-build the ELF
# (CMake/Ninja), run it under Xvfb and capture the suite over the cn1ss
# WebSocket. The capture test does the whole build+run; ~112 PNGs land in
# CN1_SHOT_OUTPUT_DIR. LIBGL_ALWAYS_SOFTWARE makes the GLES 3D backend use
# Mesa llvmpipe (no GPU on the runner).
- name: Translate + build + run suite, capture screenshots
working-directory: vm
env:
CN1_SHOT_OUTPUT_DIR: ${{ github.workspace }}/artifacts/linux-port/raw
LIBGL_ALWAYS_SOFTWARE: '1'
run: |
set -e
Xvfb :99 -screen 0 1200x1600x24 >/tmp/xvfb-run.log 2>&1 &
export DISPLAY=:99
sleep 2
mvn -B clean package -pl JavaAPI -am -DskipTests
mvn -B test -pl tests -am \
'-Dtest=CleanTargetLinuxIntegrationTest#capturesHelloSuiteOverWebSocketLinux' \
'-Dsurefire.failIfNoSpecifiedTests=false'

- name: Upload screenshot artifact (${{ matrix.arch }})
if: always()
uses: actions/upload-artifact@v4
with:
name: linux-screenshot-raw-${{ matrix.arch }}
path: artifacts/linux-port/raw
if-no-files-found: warn
retention-days: 14

# Diffs each arch's screenshots against its in-repo baseline (x64 ->
# scripts/linux/screenshots, arm64 -> scripts/linux/screenshots-arm) and posts
# them to the PR. Report-only until the baselines are seeded (see the READMEs in
# those dirs); flip CN1SS_FAIL_ON_MISMATCH=1 to gate once seeded.
compare-comment:
name: screenshot-comment
needs: build-run
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
env:
GITHUB_TOKEN: ${{ secrets.CN1SS_GH_TOKEN }}
GH_TOKEN: ${{ secrets.CN1SS_GH_TOKEN }}
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v5
with: { distribution: 'temurin', java-version: '17' }

- name: Download x64 screenshots
uses: actions/download-artifact@v4
with:
name: linux-screenshot-raw-x64
path: artifacts/linux-port/raw-x64
continue-on-error: true
- name: Download arm64 screenshots
uses: actions/download-artifact@v4
with:
name: linux-screenshot-raw-arm64
path: artifacts/linux-port/raw-arm64
continue-on-error: true

- name: Post screenshots to PR
shell: bash
run: |
set -e
source scripts/lib/cn1ss.sh
cn1ss_setup "$JAVA_HOME/bin/java" "$(pwd)/scripts/common/java"
ART="artifacts/linux-port"
posted=0
for arch in x64 arm64; do
raw="$ART/raw-$arch"
[ -d "$raw" ] || continue
entries=()
for f in "$raw"/*.png; do
[ -f "$f" ] || continue
entries+=("$(basename "$f" .png)=$f")
done
[ ${#entries[@]} -eq 0 ] && continue
if [ "$arch" = "arm64" ]; then REF="scripts/linux/screenshots-arm"; else REF="scripts/linux/screenshots"; fi
echo "Posting ${#entries[@]} screenshot(s) for $arch (baseline $REF)"
mkdir -p "$ART/previews-$arch"
CN1SS_COMMENT_MARKER="<!-- CN1SS_LINUX_${arch}_COMMENT -->" \
CN1SS_COMMENT_LOG_PREFIX="[linux-gtk-$arch]" \
CN1SS_PREVIEW_SUBDIR="linux-$arch" \
CN1SS_SUCCESS_MESSAGE="Native Linux port ($arch), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub $arch runner. Baseline: $REF." \
cn1ss_process_and_report \
"Native Linux port ($arch)" \
"$ART/compare-$arch.json" "$ART/summary-$arch.txt" "$ART/comment-$arch.md" \
"$(pwd)/$REF" "$ART/previews-$arch" "$ART" \
"${entries[@]}"
posted=$((posted+1))
done
[ "$posted" -eq 0 ] && echo "No screenshots produced; skipping comment."
8 changes: 8 additions & 0 deletions CodenameOne/src/com/codename1/annotations/Concrete.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,12 @@
/// Windows specialization (e.g. a SIMD helper) translates to its software
/// base instead of pulling in the absent iOS class.
String win() default "";

/// The fully-qualified class name of the concrete implementation to prefer
/// when translating for the native Linux (GTK/Cairo) port. When empty (the
/// default), the native Linux build falls back to the annotated (portable)
/// base class rather than the iOS {@link #name()} target -- mirroring
/// {@link #win()} so a type with no Linux specialization translates to its
/// software base instead of pulling in the absent iOS class.
String linux() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
/// Display specifically for key, pointer events and screen resolution.
///
/// @author Shai Almog
@Concrete(name = "com.codename1.impl.ios.IOSImplementation", win = "com.codename1.impl.windows.WindowsImplementation")
@Concrete(name = "com.codename1.impl.ios.IOSImplementation", win = "com.codename1.impl.windows.WindowsImplementation", linux = "com.codename1.impl.linux.LinuxImplementation")
public abstract class CodenameOneImplementation {
/// Indicates the range of "hard" RTL bidi characters in unicode
private static final int RTL_RANGE_BEGIN = 0x590;
Expand Down
Loading
Loading