Skip to content

Commit native theme .res files; add native-themes-sync workflow#4976

Merged
shai-almog merged 8 commits into
masterfrom
commit-native-theme-res-files
May 19, 2026
Merged

Commit native theme .res files; add native-themes-sync workflow#4976
shai-almog merged 8 commits into
masterfrom
commit-native-theme-res-files

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

Summary

The CSS-compiled native theme .res files (iOSModernTheme.res, AndroidMaterialTheme.res) used to be gitignored build artifacts regenerated on every iOS / Android / website build from native-themes/*/theme.css via scripts/build-native-themes.sh. The released codenameone-ios artifact, the BuildDaemon's sibling cn1 checkout, and every local build could each be producing slightly different bytes depending on when they last ran the CSS compiler and which compiler revision was in their ~/.m2 — the source of the drift report in discussion #4968.

This PR commits the six shipping .res copies and adds a workflow that keeps master coherent:

  • Six committed copies: Themes/, Ports/JavaScriptPort/src/main/webapp/assets/, Ports/iOSPort/nativeSources/, Ports/Android/src/. Same pattern as the legacy iOS7Theme.res / iPhoneTheme.res.
  • New workflow .github/workflows/native-themes-sync.yml: on push to master with paths-filter for native-themes/**, maven/css-compiler/**, or the build script itself, rebuilds the .res and commits the diff directly back to master (no PR). Pushes via GITHUB_TOKEN do not retrigger workflows, so the commit-back step cannot loop.
  • Pipelines simplified — they just use the committed .res:
    • scripts/build-ios-port.sh, scripts/build-android-port.sh drop the build-native-themes.sh invocation and the cp staging step
    • scripts/website/build.sh drops ensure_native_themes()
    • cn1playground / initializr poms drop failonerror="false" on the Themes/ copy
    • maven/javase/pom.xml drops failonerror="false" on the simulator fat-jar copy
    • Ports/iOSPort/build.xml, Ports/Android/build.xml drop failonerror="false" on the modern-theme copy
  • scripts/build-native-themes.sh now also mirrors directly into the iOS / Android port staging dirs, so the workflow only has to git add the fixed list.

Companion PR for the BuildDaemon (drops the matching failonerror="false" on the sibling cn1 copy) will follow.

Heads up before merge

The workflow's commit-back step needs contents:write on GITHUB_TOKEN plus permission to push to master. If branch protection on master requires PR + review and doesn't include GitHub Actions in the bypass list, that push will fail. Adding github-actions[bot] to the "Allow specified actors to bypass required pull requests" list (or switching to a fine-grained PAT) fixes it.

Test plan

  • Confirm .github/workflows/native-themes-sync.yml is allowed to push to master (branch protection bypass / token scope)
  • Merge this PR, watch the workflow run once on the merge commit — it should no-op (no .res change) since the .res files in this PR are already current
  • Edit native-themes/ios-modern/theme.css in a follow-up PR, merge, and verify the workflow regenerates + commits Themes/iOSModernTheme.res and the five mirror copies
  • Verify a local ./scripts/build-ios-port.sh still produces an iOS port jar with iOSModernTheme.res inside nativeios.jar
  • Verify ./scripts/build-android-port.sh still bundles AndroidMaterialTheme.res into the Android port jar

🤖 Generated with Claude Code

iOSModernTheme.res and AndroidMaterialTheme.res used to be regenerated
on every iOS / Android / website build from native-themes/*/theme.css
via scripts/build-native-themes.sh, but the .res files themselves were
gitignored. That meant the released codenameone-ios artifact, the
BuildDaemon's sibling cn1 checkout, and every local build could each be
producing slightly different bytes depending on when they last ran the
CSS compiler and which compiler revision was in their ~/.m2.

Commit the six shipping copies (Themes/, JS webapp assets, iOS port
nativeSources, Android port src) and add a workflow that regenerates
them on master whenever native-themes/, the CSS compiler, or the
build-native-themes.sh script changes, committing the result back
directly (no PR). Pushes via GITHUB_TOKEN do not retrigger workflows
so the commit-back step cannot loop.

Downstream pipelines stop invoking build-native-themes.sh and just use
the committed .res file:
  - scripts/build-ios-port.sh, scripts/build-android-port.sh drop the
    compile + cp staging step
  - scripts/website/build.sh drops ensure_native_themes()
  - cn1playground / initializr poms drop failonerror=false on the
    Themes/ copy
  - maven/javase/pom.xml drops failonerror=false on the simulator
    fat-jar copy
  - Ports/iOSPort/build.xml, Ports/Android/build.xml drop failonerror
    on the modern-theme copy step

scripts/build-native-themes.sh now also mirrors directly into the iOS
and Android port staging dirs so the workflow only has to git-add the
fixed list.

Run scripts/build-native-themes.sh manually for local iteration on
theme.css; otherwise pull master and the .res is already in place.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 18, 2026

Compared 20 screenshots: 20 matched.
✅ JavaScript-port screenshot tests passed.

@github-actions
Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 18, 2026

Compared 110 screenshots: 110 matched.

Native Android coverage

  • 📊 Line coverage: 11.81% (6569/55602 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.52% (32992/346425), branch 4.12% (1354/32888), complexity 5.18% (1632/31518), method 9.01% (1328/14738), class 15.07% (301/1998)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 11.81% (6569/55602 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.52% (32992/346425), branch 4.12% (1354/32888), complexity 5.18% (1632/31518), method 9.01% (1328/14738), class 15.07% (301/1998)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 826.000 ms
Base64 CN1 encode 139.000 ms
Base64 encode ratio (CN1/native) 0.168x (83.2% faster)
Base64 native decode 706.000 ms
Base64 CN1 decode 162.000 ms
Base64 decode ratio (CN1/native) 0.229x (77.1% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 18, 2026

Compared 110 screenshots: 110 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 169 seconds

Build and Run Timing

Metric Duration
Simulator Boot 63000 ms
Simulator Boot (Run) 0 ms
App Install 12000 ms
App Launch 5000 ms
Test Execution 298000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1246.000 ms
Base64 CN1 encode 1313.000 ms
Base64 encode ratio (CN1/native) 1.054x (5.4% slower)
Base64 native decode 738.000 ms
Base64 CN1 decode 875.000 ms
Base64 decode ratio (CN1/native) 1.186x (18.6% slower)
Base64 SIMD encode 373.000 ms
Base64 encode ratio (SIMD/native) 0.299x (70.1% faster)
Base64 encode ratio (SIMD/CN1) 0.284x (71.6% faster)
Base64 SIMD decode 372.000 ms
Base64 decode ratio (SIMD/native) 0.504x (49.6% faster)
Base64 decode ratio (SIMD/CN1) 0.425x (57.5% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 57.000 ms
Image createMask (SIMD on) 10.000 ms
Image createMask ratio (SIMD on/off) 0.175x (82.5% faster)
Image applyMask (SIMD off) 145.000 ms
Image applyMask (SIMD on) 59.000 ms
Image applyMask ratio (SIMD on/off) 0.407x (59.3% faster)
Image modifyAlpha (SIMD off) 121.000 ms
Image modifyAlpha (SIMD on) 55.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.455x (54.5% faster)
Image modifyAlpha removeColor (SIMD off) 138.000 ms
Image modifyAlpha removeColor (SIMD on) 66.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.478x (52.2% faster)
Image PNG encode (SIMD off) 958.000 ms
Image PNG encode (SIMD on) 985.000 ms
Image PNG encode ratio (SIMD on/off) 1.028x (2.8% slower)
Image JPEG encode 460.000 ms

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 18, 2026

Compared 110 screenshots: 110 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 290 seconds

Build and Run Timing

Metric Duration
Simulator Boot 86000 ms
Simulator Boot (Run) 1000 ms
App Install 23000 ms
App Launch 8000 ms
Test Execution 289000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1712.000 ms
Base64 CN1 encode 1561.000 ms
Base64 encode ratio (CN1/native) 0.912x (8.8% faster)
Base64 native decode 833.000 ms
Base64 CN1 decode 1211.000 ms
Base64 decode ratio (CN1/native) 1.454x (45.4% slower)
Base64 SIMD encode 408.000 ms
Base64 encode ratio (SIMD/native) 0.238x (76.2% faster)
Base64 encode ratio (SIMD/CN1) 0.261x (73.9% faster)
Base64 SIMD decode 566.000 ms
Base64 decode ratio (SIMD/native) 0.679x (32.1% faster)
Base64 decode ratio (SIMD/CN1) 0.467x (53.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 65.000 ms
Image createMask (SIMD on) 12.000 ms
Image createMask ratio (SIMD on/off) 0.185x (81.5% faster)
Image applyMask (SIMD off) 215.000 ms
Image applyMask (SIMD on) 129.000 ms
Image applyMask ratio (SIMD on/off) 0.600x (40.0% faster)
Image modifyAlpha (SIMD off) 214.000 ms
Image modifyAlpha (SIMD on) 139.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.650x (35.0% faster)
Image modifyAlpha removeColor (SIMD off) 411.000 ms
Image modifyAlpha removeColor (SIMD on) 164.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.399x (60.1% faster)
Image PNG encode (SIMD off) 1440.000 ms
Image PNG encode (SIMD on) 980.000 ms
Image PNG encode ratio (SIMD on/off) 0.681x (31.9% faster)
Image JPEG encode 619.000 ms

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 644 total, 0 failed, 2 skipped

Benchmark Results

  • Execution Time: 10152 ms

  • Hotspots (Top 20 sampled methods):

    • 23.56% java.lang.String.indexOf (405 samples)
    • 20.24% com.codename1.tools.translator.Parser.isMethodUsed (348 samples)
    • 10.18% java.util.ArrayList.indexOf (175 samples)
    • 6.98% java.lang.Object.hashCode (120 samples)
    • 5.29% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (91 samples)
    • 3.49% com.codename1.tools.translator.Parser.addToConstantPool (60 samples)
    • 3.20% java.lang.System.identityHashCode (55 samples)
    • 2.44% com.codename1.tools.translator.ByteCodeClass.calcUsedByNative (42 samples)
    • 1.57% com.codename1.tools.translator.Parser.getClassByName (27 samples)
    • 1.45% com.codename1.tools.translator.ByteCodeClass.markDependent (25 samples)
    • 1.45% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (25 samples)
    • 1.28% com.codename1.tools.translator.ByteCodeClass.updateAllDependencies (22 samples)
    • 1.28% com.codename1.tools.translator.BytecodeMethod.optimize (22 samples)
    • 1.11% com.codename1.tools.translator.Parser.cullMethods (19 samples)
    • 0.99% java.lang.StringBuilder.append (17 samples)
    • 0.76% com.codename1.tools.translator.BytecodeMethod.appendCMethodPrefix (13 samples)
    • 0.70% com.codename1.tools.translator.BytecodeMethod.isMethodUsedByNative (12 samples)
    • 0.70% java.lang.StringCoding.encode (12 samples)
    • 0.52% sun.nio.fs.UnixNativeDispatcher.open0 (9 samples)
    • 0.47% java.io.FileOutputStream.writeBytes (8 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

testRoundBorderShadowSpreadAndPaintingCaches was scanning client
property keys "cn1$$-rbcache1".."cn1$$-rbcache50" to locate the cache
RoundBorder.paintBorderBackground stores under the border's instanceVal
suffix. Since instanceVal comes off a static counter shared across the
entire JVM, the scan would miss whenever earlier tests in the same
surefire run created more than 50 RoundBorder instances.

The committed iOSModernTheme.res in this PR unblocks
NativeThemeBindingsTest (it used to silently skip when the .res was a
gitignored build artifact). That test now loads the iOS Modern theme
before BorderAndPlafTest runs, parsing many border definitions and
pushing instanceCounter past 50.

Read instanceVal off the border under test reflectively and look up
exactly that key, so the assertion no longer depends on how many other
RoundBorders earlier tests minted in the same JVM.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

shai-almog and others added 6 commits May 19, 2026 04:17
The previous commit on this branch committed the modern .res files in
six places (Themes/, JS webapp assets, iOSPort/nativeSources,
Android/src). That's exactly the duplication problem this PR is trying
to remove - each port carried its own copy in git, and they could drift.

Drop the four port-side committed copies and have the port poms read
Themes/*.res directly at build time:

  - maven/ios/pom.xml: nativeios.jar zip step gets a second fileset
    that pulls Themes/iOSModernTheme.res in, plus an explicit exclude
    on iOSModernTheme.res from the nativeSources fileset so a stale
    local copy can't shadow the canonical one.
  - maven/android/pom.xml: compile-android profile gets a second
    <resource> entry pointing at Themes/ filtered to
    AndroidMaterialTheme.res.

JavaScriptPort/src/main/webapp/assets is gitignored again for the two
modern .res files; scripts/build-native-themes.sh still mirrors there
for local JS port runs, treating the directory as a build artifact.

scripts/build-native-themes.sh reverts the IOS_NATIVE_DIR /
ANDROID_SRC_DIR mirroring I added in the previous commit, since those
staging copies are no longer needed.

The native-themes-sync workflow now only commits Themes/iOSModernTheme.res
and Themes/AndroidMaterialTheme.res - the two canonical files.

Verified locally: `mvn -pl ios -am install` produces a nativeios.jar
containing iOSModernTheme.res sourced from Themes/, with no staged copy
under Ports/iOSPort/nativeSources/.

Note: a fuller redesign would have the iOS / Android builders DELETE
the .res based on a build hint (e.g. nativeTheme=legacy) rather than
the current "include the .res unconditionally and pick at runtime"
model. That's a larger change left for a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
nativeios.jar / the Android port jar now always ship all three native
themes (modern + flat + legacy on each platform). The runtime
installNativeTheme picks one based on the user's theme-mode build hint
and the other two ride along as dead bytes in the .ipa / .apk.

Resolve the effective mode in IPhoneBuilder and AndroidGradleBuilder
right after the port jar is unzipped, and delete the .res files that
won't be loaded. The mapping mirrors the runtime exactly:

iOS (IOSImplementation.installNativeTheme):
  modern / liquid                    -> iOSModernTheme.res
  ios7 / flat / auto                 -> iOS7Theme.res
  anything else                      -> iPhoneTheme.res

Android (AndroidImplementation.installNativeTheme):
  material / modern / auto           -> AndroidMaterialTheme.res
  hololight / holo                   -> android_holo_light.res
  anything else (incl. legacy)       -> androidTheme.res

Drive-by fixes in the noExtraResources branches: extend the iOS list to
include iOSModernTheme.res (previously missed) and correct the
mis-cased "IPhoneTheme.res" to "iPhoneTheme.res" so the delete actually
fires on case-sensitive file systems. Add AndroidMaterialTheme.res to
the Android list for the same reason.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-res-files

# Conflicts:
#	scripts/build-android-port.sh
#	scripts/build-ios-port.sh
Reverts 19c124f. The mode-driven delete in IPhoneBuilder /
AndroidGradleBuilder assumed apps only ever load the .res that matches
the build-hint-derived mode, but apps (e.g. DualAppearanceBaseTest in
the screenshot suite) load other themes via Resources.open at runtime
to render side-by-side comparisons. Deleting the inactive .res files
from the .ipa / .apk made those loads fail and broke the on-device
screenshot tests.

Smaller-bundle minimization can come back later as an opt-in build hint
(only delete when the user explicitly says they don't dynamically load
themes), but the default has to keep all three themes shipped so
runtime Resources.open lookups work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The .res files I committed initially were produced against the
pre-#4957 CSS compiler (MINOR_VERSION=12). After merging master,
#4957 bumped the resource format to MINOR_VERSION=14, but I hadn't
rebuilt the .res, so what was committed at offset 0x0B was 0x0c (12)
while every fresh CI build produces 0x0e (14). The screenshot
baselines on master were captured against the v14 bytes, so the v12
bytes I committed caused pixel diffs across every theme test in
scripts-android.yml (ButtonTheme_*, ListTheme_*, DialogTheme_*, ...).

Regen Themes/{iOSModernTheme,AndroidMaterialTheme}.res with the
post-merge css-compiler under JDK 8 (matching the pr-ci-container's
toolchain) so the committed bytes match what scripts-android.yml's
emulator renders against the existing baselines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_sources.jar

My earlier maven/android/pom.xml change put AndroidMaterialTheme.res in
the outer codenameone-android.jar's outputDirectory via a <resource>
entry, but the runtime delivery path for the Android port is the
INNER android_port_sources.jar inside that outer jar:
AndroidGradleBuilder unzips that inner jar into the user app's APK
assets/, and AndroidImplementation.installNativeTheme then reads
/AndroidMaterialTheme.res from AssetManager.

The inner jar is built by the bundle-android profile's <zip> step from
${real.src.dir} (= Ports/Android/src/), which doesn't contain the .res
once I un-staged the Ports/Android/src/AndroidMaterialTheme.res copy.
So the .res never reached the APK, the runtime fell back to
android_holo_light.res, and the *Theme_light/_dark screenshot tests
diffed against the material baselines.

Add a second fileset on Themes/ to the bundle-android <zip> step so
android_port_sources.jar carries AndroidMaterialTheme.res, with
Themes/ still as the single source of truth (no Ports/Android/src/
staging copy needed).

Verified locally: the rebuilt android_port_sources.jar now lists
AndroidMaterialTheme.res alongside androidTheme.res /
android_holo_light.res.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog shai-almog merged commit bfba989 into master May 19, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant