Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
5 changes: 5 additions & 0 deletions .github/workflows/website-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ jobs:
WEBSITE_INCLUDE_DEVGUIDE: "true"
WEBSITE_INCLUDE_INITIALIZR: "auto"
WEBSITE_INCLUDE_PLAYGROUND: "auto"
# The Initializr's JavaScript app is built with the local ParparVM
# target, whose builder lives in the repo (8.0-SNAPSHOT) plugin rather
# than the pinned release. Bootstrap the local snapshot artifacts so
# the initializr (and the other site apps) build against repo HEAD.
WEBSITE_BOOTSTRAP_CN1_SNAPSHOTS: "true"
# PR previews build with future-dated posts visible so reviewers
# can read posts staged for later in the week. Production deploys
# (push to master) keep the default so future posts only appear
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2026 Codename One and contributors.
* Licensed under the PolyForm Noncommercial License 1.0.0.
* You may use this file only in compliance with that license.
* The license notice for this subtree is available in Ports/JavaScriptPort/LICENSE.md.
*/
package com.codename1.impl.platform.js;

/**
* Host bridge that dispatches Codename One {@code NativeInterface} method calls
* to their JavaScript implementation registered in {@code cn1_native_interfaces}.
*
* <p>The generated {@code <Interface>Impl} classes (emitted by the JavaScript
* builder) delegate every interface method to one of the {@code call*} natives
* below, picked by the method's return type. These natives are runtime-
* implemented in {@code parparvm_runtime.js}: the worker suspends, the call is
* replayed on the <em>main thread</em> (via {@code browser_bridge.js}) where the
* developer-authored JS stub runs with full DOM access and completes the call
* through its callback, and the worker resumes with the result coerced to the
* declared Java type.</p>
*
* <p>Supported types mirror {@code NativeInterface}: all primitives, {@code String},
* primitive arrays plus {@code String[]} (via {@link #callArray}), and
* {@code com.codename1.ui.PeerComponent} (routed through {@link #callObject}).</p>
*
* <p>{@code iface} is the interface class name with dots replaced by underscores
* (the {@code cn1_native_interfaces} registry key), {@code method} is the
* trailing-underscore method key (e.g. {@code "isDarkMode_"}), and {@code args}
* holds the (boxed) Java arguments, or an empty array for a no-arg method.</p>
*/
public final class NativeInterfaceBridge {
private NativeInterfaceBridge() {
}

public static native boolean callBoolean(String iface, String method, Object[] args);

public static native byte callByte(String iface, String method, Object[] args);

public static native short callShort(String iface, String method, Object[] args);

public static native int callInt(String iface, String method, Object[] args);

public static native char callChar(String iface, String method, Object[] args);

public static native long callLong(String iface, String method, Object[] args);

public static native float callFloat(String iface, String method, Object[] args);

public static native double callDouble(String iface, String method, Object[] args);

public static native String callString(String iface, String method, Object[] args);

public static native Object callObject(String iface, String method, Object[] args);

public static native void callVoid(String iface, String method, Object[] args);

/**
* Array-returning call. {@code componentToken} identifies the element type so
* the runtime can build the correctly-typed Java array: {@code "JAVA_INT"},
* {@code "JAVA_BYTE"}, {@code "JAVA_LONG"}, {@code "JAVA_DOUBLE"},
* {@code "JAVA_FLOAT"}, {@code "JAVA_BOOLEAN"}, {@code "JAVA_CHAR"},
* {@code "JAVA_SHORT"} or {@code "java_lang_String"}. The caller casts the
* result to the concrete array type.
*/
public static native Object callArray(String iface, String method, Object[] args, String componentToken);
}

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion scripts/initializr/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ mvn -DskipTests install
[source,bash]
----
cd ../scripts/initializr
./mvnw package -Dcodename1.platform=javascript -Dcodename1.buildTarget=javascript -Dcn1.localWorkspace=true
./mvnw package -Dcodename1.platform=javascript -Dcodename1.buildTarget=local-javascript -Dcn1.localWorkspace=true
----

This switches Initializr to `8.0-SNAPSHOT` so JavaScript builds use your local Codename One code.
2 changes: 1 addition & 1 deletion scripts/initializr/build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ goto :EOF

goto :EOF
:javascript
!MVNW! package -DskipTests -Dcodename1.platform^=javascript -Dcodename1.buildTarget^=javascript -U -e
!MVNW! package -DskipTests -Dcodename1.platform^=javascript -Dcodename1.buildTarget^=local-javascript -U -e

goto :EOF
:android
Expand Down
2 changes: 1 addition & 1 deletion scripts/initializr/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function windows_desktop {
}
function javascript {

"$MVNW" "package" "-DskipTests" "-Dcodename1.platform=javascript" "-Dcodename1.buildTarget=javascript" "-U" "-e"
"$MVNW" "package" "-DskipTests" "-Dcodename1.platform=javascript" "-Dcodename1.buildTarget=local-javascript" "-U" "-e"
}
function android {

Expand Down
5 changes: 5 additions & 0 deletions scripts/initializr/common/codenameone_settings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ codename1.arg.and.themeMode=modern
codename1.arg.desktop.titleBar=native
codename1.arg.desktop.interactiveScrollbars=true
codename1.arg.java.version=8
# Local ParparVM JavaScript build (codename1.buildTarget=local-javascript) is
# gated to Enterprise-tier accounts in the released plugin. The website build
# logs in via set_cn1_user_token; declare the tier so the released plugin's
# license gate is satisfied. Newer plugins also accept the login directly.
codename1.arg.javascript.userLevel=Enterprise
codename1.displayName=Initializr
codename1.icon=icon.png
codename1.ios.appid=Q5GHSKAL2F.com.codename1.initializr
Expand Down
7 changes: 5 additions & 2 deletions scripts/initializr/common/src/main/resources/skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,13 @@ mvn -pl common cn1:debug
# Execute the CN1 test runner
mvn -pl common cn1:test

# Cloud build for Android/iOS/JS (requires CN1 build server creds)
# Cloud build for Android/iOS (requires CN1 build server creds)
mvn -pl android package -Dcodename1.platform=android -Dcodename1.buildTarget=android-device
mvn -pl ios package -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-device
mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=javascript

# JavaScript / web bundle, built locally via the ParparVM → JS translator (Enterprise-gated).
# Use -Dcodename1.buildTarget=javascript instead for the cloud builder.
mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=local-javascript
```

See `references/build-and-run.md` for the local-vs-cloud matrix, automated-build mode (Enterprise), iOS local-build prerequisites, and the complete goal list. The full `codename1.arg.*` index lives in `references/build-hints.md`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A Codename One project can produce four kinds of artifacts. Some build entirely
| iOS app | Cloud, **or** locally as an Xcode project via `ios-source` | `mvn -pl ios package -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-device` (cloud) or `…-Dcodename1.buildTarget=ios-source` (local Xcode project) |
| Mac Native app (AOT-compiled, same pipeline as iOS) | Cloud, **or** locally as an Xcode project via `mac-source` | `mvn -pl ios package -Dcodename1.platform=ios -Dcodename1.buildTarget=mac-os-x-native` (cloud) or `…-Dcodename1.buildTarget=mac-source` (local Xcode project) |
| Native Windows `.exe` (`win32`, ParparVM → clang-cl, no JVM) | Cloud (Linux build server cross-compiles); **also** locally on Windows, or as a project via `windows-source` | `mvn -pl common package -Dcodename1.platform=windows -Dcodename1.buildTarget=windows-device` (cloud) or `…-Dcodename1.buildTarget=local-windows-device` (local). A regular build returns x64 + arm64 release exes; add the `windows.debug` build hint for a single x64 debug exe. |
| JavaScript / web bundle | Cloud | `mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=javascript` |
| JavaScript / web bundle | Local (ParparVM → JavaScript translator; Enterprise-gated). Cloud still available via `…-Dcodename1.buildTarget=javascript`. | `mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=local-javascript` |

The two big "local-only" outputs are the **simulator** and **tests** — those are everything you need for ordinary development and CI feedback loops. You only invoke the cloud builds when you want a deployable native artifact.

Expand Down Expand Up @@ -102,8 +102,9 @@ mvn -pl ios package -Dcodename1.platform=ios -Dcodename1.buildTarget=mac-source
# Native Android APK/AAB. Cloud-built by default.
mvn -pl android package -Dcodename1.platform=android -Dcodename1.buildTarget=android-device

# JavaScript / web bundle. Cloud-built.
mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=javascript
# JavaScript / web bundle. Built locally via the ParparVM → JavaScript translator (Enterprise-gated).
# Append -Dcodename1.buildTarget=javascript instead to use the cloud builder.
mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=local-javascript

# Standalone Mac / Windows / Linux desktop app. Cloud-built.
mvn -pl javase package -Dcodename1.platform=javase -Dcodename1.buildTarget=mac-os-x-desktop
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ mvn -pl ios package -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-source
mvn -pl android package -Dcodename1.platform=android -Dcodename1.buildTarget=android-device -Dautomated=true

# JavaScript — produces a web bundle; open dev tools and confirm the JS impl is included.
mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=javascript
mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=local-javascript

# Desktop simulator — just run cn1:run and observe the bridge boots without errors.
mvn -pl common cn1:run
Expand Down
2 changes: 1 addition & 1 deletion scripts/initializr/javascript/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<maven.compiler.target>1.8</maven.compiler.target>
<codename1.platform>javascript</codename1.platform>
<codename1.projectPlatform>javascript</codename1.projectPlatform>
<codename1.defaultBuildTarget>javascript</codename1.defaultBuildTarget>
<codename1.defaultBuildTarget>local-javascript</codename1.defaultBuildTarget>
</properties>
<build>
<resources>
Expand Down
8 changes: 7 additions & 1 deletion scripts/initializr/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,14 @@
<value>true</value>
</property>
</activation>
<!-- Build against the locally-built (repo HEAD) Codename One artifacts
instead of the pinned release, so the local ParparVM JavaScript
build reflects repo source. The website CI bootstraps these
8.0-SNAPSHOT artifacts via bootstrap_local_cn1_snapshots before
building the initializr with -Dcn1.localWorkspace=true. -->
<properties>
<cn1.version>7.0.250</cn1.version>
<cn1.version>8.0-SNAPSHOT</cn1.version>
<cn1.plugin.version>8.0-SNAPSHOT</cn1.plugin.version>
</properties>
</profile>
<profile>
Expand Down
27 changes: 26 additions & 1 deletion scripts/run-javascript-headless-browser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,20 @@ let finalizeProfile = async () => {};
const launchArgs = [
'--autoplay-policy=no-user-gesture-required',
'--disable-web-security',
'--allow-file-access-from-files'
'--allow-file-access-from-files',
// Headless pages count as hidden, so Chromium's background-timer machinery
// (IntensiveWakeUpThrottling in particular) batches re-armed setTimeout
// chains to ~one firing per MINUTE once the page's wake-up budget drains.
// The ParparVM worker schedules every Thread.sleep / Object.wait(timeout)
// through host timers, so the whole green-thread scheduler stalls in
// 12-60s bursts during quiet (no-host-event) phases -- observed as the
// screenshot suite crawling ~60s/test through the theme cluster with every
// thread parked past its wake deadline. Disable the throttling: this
// harness IS the foreground workload.
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding',
'--disable-features=IntensiveWakeUpThrottling'
];
if (profileWorker) {
launchArgs.push(`--remote-debugging-port=${remoteDebugPort}`);
Expand Down Expand Up @@ -212,6 +225,18 @@ try {

append(`goto:${url}`);
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
// VM liveness nudge from the Node side. Headless Chromium intensively
// throttles page AND worker timers (re-armed setTimeout chains batch to
// ~1/min once the hidden page's wake-up budget drains), which starves the
// ParparVM scheduler's sleep/wait wakeups and crawls the suite. CDP
// Runtime.evaluate is exempt from that throttling, so a Node interval
// pinging the bridge's __cn1NudgeVm (worker postMessage 'timer-wake' ->
// drain -> fire due wakeups) keeps the VM clock honest regardless of the
// browser's visibility heuristics.
const nudgeTimer = setInterval(() => {
page.evaluate('window.__cn1NudgeVm && window.__cn1NudgeVm()').catch(() => {});
}, 250);
nudgeTimer.unref?.();
await page.waitForTimeout(2000);

const start = Date.now();
Expand Down
44 changes: 43 additions & 1 deletion scripts/website/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,18 @@ bootstrap_local_cn1_snapshots() {
return
fi

# Each site app (initializr/playground/skindesigner) calls this; the full
# reactor build is expensive, so run setup-workspace.sh only once per build.
if [ "${__CN1_SNAPSHOTS_BOOTSTRAPPED:-}" = "true" ]; then
return
fi

echo "Bootstrapping local Codename One snapshot Maven artifacts..." >&2
(
cd "${REPO_ROOT}"
SKIP_CN1_ARCHETYPES=1 ./scripts/setup-workspace.sh -q -DskipTests
)
__CN1_SNAPSHOTS_BOOTSTRAPPED="true"
}

activate_bootstrapped_java17() {
Expand Down Expand Up @@ -569,6 +576,13 @@ build_initializr_for_site() {
return
fi

# The initializr builds the JavaScript app with the local ParparVM target
# (codename1.buildTarget=local-javascript). That builder lives in the repo's
# 8.0-SNAPSHOT plugin, not the pinned release, so bootstrap the local
# snapshots and build with -Dcn1.localWorkspace=true (the cn1-local-workspace
# profile then overrides cn1.version/cn1.plugin.version to the repo build).
bootstrap_local_cn1_snapshots

echo "Building Initializr JavaScript bundle for website..." >&2
(
cd "${REPO_ROOT}/scripts/initializr"
Expand All @@ -581,21 +595,29 @@ build_initializr_for_site() {
fi
}

if [ -n "${JAVA_HOME_8_X64:-}" ]; then
local initializr_workspace_args=()
if [ "${WEBSITE_BOOTSTRAP_CN1_SNAPSHOTS}" = "true" ]; then
# Local ParparVM JS build runs the translator + javac; use the
# bootstrapped JDK 17 (matching the Playground/Skin Designer path).
activate_bootstrapped_java17
initializr_workspace_args+=(-Dcn1.localWorkspace=true)
elif [ -n "${JAVA_HOME_8_X64:-}" ]; then
export JAVA_HOME="${JAVA_HOME_8_X64}"
export PATH="${JAVA_HOME}/bin:${PATH}"
fi

# Ensure attached classifier artifact initializr-ZipSupport:jar:common is present
# in the local Maven repo before building modules that depend on it (e.g. initializr-common).
run_initializr_mvn -q -U -pl cn1libs/ZipSupport -am \
"${initializr_workspace_args[@]}" \
-DskipTests \
-Dcodename1.platform=javascript \
install

set_cn1_user_token "Initializr"

run_initializr_mvn -q -U -pl javascript -am \
"${initializr_workspace_args[@]}" \
-DskipTests \
-Dautomated=true \
-Dcodename1.platform=javascript \
Expand All @@ -617,10 +639,30 @@ build_initializr_for_site() {
mkdir -p "${output_dir}"
unzip -q -o "${result_zip}" -d "${output_dir}"

# The cloud result.zip is flat (index.html at the root), but the local
# ParparVM build (codename1.buildTarget=local-javascript) wraps the bundle in
# a single top-level directory (e.g. Initializr-js/). Flatten that wrapper so
# the served layout is identical regardless of which builder produced the zip.
if [ ! -f "${output_dir}/index.html" ]; then
local inner_dir
inner_dir="$(find "${output_dir}" -mindepth 1 -maxdepth 1 -type d | head -n1 || true)"
if [ -n "${inner_dir}" ] && [ -f "${inner_dir}/index.html" ]; then
( cd "${inner_dir}" && tar cf - . ) | ( cd "${output_dir}" && tar xf - )
rm -rf "${inner_dir}"
fi
fi

if [ ! -f "${output_dir}/index.html" ]; then
echo "Initializr website bundle is missing index.html after extraction." >&2
exit 1
fi

# The Initializr page (layouts/_default/initializr.html) shows the app icon
# from /initializr-app/icon.png. The cloud bundle shipped one; the local
# ParparVM bundle does not, so copy the project icon in when it is absent.
if [ ! -f "${output_dir}/icon.png" ] && [ -f "${REPO_ROOT}/scripts/initializr/common/icon.png" ]; then
cp "${REPO_ROOT}/scripts/initializr/common/icon.png" "${output_dir}/icon.png"
fi
}

build_playground_for_site() {
Expand Down
Loading
Loading