Skip to content
Closed
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
30 changes: 20 additions & 10 deletions .github/scripts/build_javadocs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ else
JAVADOC_CMD="javadoc"
fi

TMP_SNIPPETS_JSONL="$(mktemp)"
cleanup() {
rm -f "$TMP_SNIPPETS_JSONL"
run_with_timeout() {
local seconds="$1"
shift
if command -v timeout >/dev/null 2>&1; then
timeout "${seconds}" "$@"
return $?
fi
"$@"
}
trap cleanup EXIT

echo "Extracting JavaDoc snippets prior to JavaDoc generation..."
"$ROOT_DIR/scripts/extract-javadoc-java-snippets.sh" "$CN1_DIR/src" > "$TMP_SNIPPETS_JSONL"

echo "Validating JavaDoc snippets prior to JavaDoc generation..."
"$ROOT_DIR/scripts/validate-extracted-javadoc-snippets.sh" "$TMP_SNIPPETS_JSONL"

rm -rf "$CN1_DIR/dist/javadoc"
rm -rf "$CN1_DIR/build/tempJavaSources"
Expand All @@ -33,6 +31,18 @@ mkdir -p "$CN1_DIR/dist/javadoc"

cp -r "$CN1_DIR/src/"* "$CN1_DIR/build/tempJavaSources/"

VALIDATE_SNIPPETS_SCRIPT="${SCRIPT_DIR}/validate-extracted-javadoc-snippets.sh"
if [ -x "${VALIDATE_SNIPPETS_SCRIPT}" ]; then
echo "Validating JavaDoc snippets prior to JavaDoc generation..." >&2
if ! run_with_timeout 300 "${VALIDATE_SNIPPETS_SCRIPT}"; then
status=$?
if [ "${status}" -eq 124 ]; then
echo "JavaDoc snippet validation timed out after 300 seconds." >&2
fi
exit "${status}"
fi
fi

cat > "$CN1_DIR/build/tempJavaSources/com/codename1/impl/ImplementationFactory.java" <<'EOF'
package com.codename1.impl;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr
interpreter.set("theme", context.getTheme());
interpreter.set("hostForm", context.getHostForm());
interpreter.set("previewRoot", context.getPreviewRoot());
interpreter.set("Display", resolveDisplayBinding());
interpreter.set("UIManager", resolveUiManagerBinding());
interpreter.set("Display", Display.getInstance());
interpreter.set("UIManager", UIManager.getInstance());
interpreter.set("FontImage", FontImage.class);
interpreter.set("CN", com.codename1.ui.CN.class);
interpreter.set("BoxLayout", BoxLayout.class);
Expand All @@ -134,29 +134,15 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr
interpreter.set("GridLayout", GridLayout.class);
interpreter.set("LayeredLayout", LayeredLayout.class);
interpreter.set("Style", Style.class);
interpreter.set("Component", Component.class);
namespace.importPackage("com.codename1.ui");
namespace.importPackage("com.codename1.ui.layouts");
namespace.importPackage("com.codename1.components");
namespace.importPackage("com.codename1.ui.geom");
namespace.importClass("com.codename1.ui.Component");
namespace.importClass("com.codenameone.playground.PlaygroundContext");
}

private Object resolveDisplayBinding() {
try {
return Display.getInstance();
} catch (Throwable ex) {
return Display.class;
}
}

private Object resolveUiManagerBinding() {
try {
return UIManager.getInstance();
} catch (Throwable ex) {
return UIManager.class;
}
}

private String adaptScript(String script) {
String adapted = unwrapSingleTopLevelClass(script);
String normalized = adapted == null ? script : adapted;
Expand Down Expand Up @@ -839,7 +825,7 @@ private String wrapLooseScript(String script) {
String body = script.substring(bodyStart);
String rewrittenBody = rewriteLooseScriptBody(body);
return prefix
+ "com.codename1.ui.Component build(com.codenameone.playground.PlaygroundContext ctx) {\n"
+ "Component build(PlaygroundContext ctx) {\n"
+ rewrittenBody
+ "\n}\n"
+ "build(ctx);";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.Button;
import com.codename1.ui.Label;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.plaf.Style;
import com.codename1.ui.css.CSSThemeCompiler;
import com.codename1.ui.util.MutableResource;
import java.util.Hashtable;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -27,36 +23,10 @@ public static void main(String[] args) throws Exception {
smokeLifecycleDemo();
smokeRestScriptWithLambda();
smokeStringMethods();
smokeCssUiidCollectionUsesPreviewOnly();
smokeCssCompilerAcceptsBasicButtonRule();
smokeComponentTypeResolvesWithoutExplicitImport();
System.out.println("Playground smoke tests passed.");
}

private static void smokeCssUiidCollectionUsesPreviewOnly() {
Container preview = new Container(BoxLayout.y());
Button button = new Button("Hello");
Label label = new Label("World");
preview.addAll(button, label);

List<String> uiids = PlaygroundCssSupport.collectVisibleUiids(preview);
require(uiids.contains("Button"), "Expected Button UIID completion from preview");
require(uiids.contains("Label"), "Expected Label UIID completion from preview");
require(!uiids.contains("BrowserComponent"), "Preview UIID collection must not include app shell/editor UIIDs");
for (int i = 0; i < uiids.size(); i++) {
require(!uiids.get(i).startsWith("."), "UIID completions should be plain selector names, not dot-prefixed");
}
}

private static void smokeCssCompilerAcceptsBasicButtonRule() {
String normalized = PlaygroundCssSupport.normalizeCustomCss("Button {\n color: white;\n}");
require("Button {\n color: white;\n}".equals(normalized), "Basic Button CSS should remain unchanged");
CSSThemeCompiler compiler = new CSSThemeCompiler();
MutableResource resource = new MutableResource();
compiler.compile(normalized, resource, "PlaygroundCssSmokeTheme");
Hashtable theme = resource.getTheme("PlaygroundCssSmokeTheme");
require(theme != null && !theme.isEmpty(), "Compiled CSS theme should produce theme properties");
}

private static void smokeGeneratedRegistry() throws Exception {
GeneratedCN1Access access = GeneratedCN1Access.INSTANCE;
require(access.findClass("com.codename1.ui.layouts.BoxLayout") == BoxLayout.class,
Expand Down Expand Up @@ -218,6 +188,30 @@ public void log(String message) {
"Loose script list snippet should log its completion");
}

private static void smokeComponentTypeResolvesWithoutExplicitImport() {
Display.init(null);

Form host = new Form("Host", new BorderLayout());
Container preview = new Container(new BorderLayout());
host.add(BorderLayout.CENTER, preview);
host.show();

PlaygroundContext context = new PlaygroundContext(host, preview, null,
new PlaygroundContext.Logger() {
public void log(String message) {
}
});

PlaygroundRunner runner = new PlaygroundRunner();
PlaygroundRunner.RunResult result = runner.run(
"Component c = new Label(\"Implicit import works\");\n"
+ "c;\n",
context);

require(result.getComponent() instanceof Label,
"Component type should resolve without explicit import: " + summarizeMessages(result));
}

private static void require(boolean condition, String message) {
if (!condition) {
throw new IllegalStateException(message);
Expand Down
19 changes: 15 additions & 4 deletions scripts/cn1playground/tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@
This directory contains local build-time helpers used to prepare the
playground runtime.

`generate-cn1-access-registry.sh` resolves Codename One *source jars* for the
configured `cn1.version` in `scripts/cn1playground/pom.xml`, then runs a Java
tool that scans those sources and emits a hardcoded CN1 access registry for
BeanShell.
`generate-cn1-access-registry.sh` scans **release source jars** for the
configured `cn1.version` in `scripts/cn1playground/pom.xml` by default, then
runs a Java tool that scans those sources and emits a hardcoded CN1 access
registry for BeanShell.

You can force behavior with:

- `CN1_ACCESS_USE_LOCAL_SOURCES=true` (scan local repo sources instead)
- `CN1_ACCESS_USE_LOCAL_SOURCES=false` (default; scan release source jars)

`run-playground-smoke-tests.sh` now regenerates the registry in release mode
and asserts a key set of `com.codename1.ui.*` classes (including `Component`,
`Form`, `Container`, `Dialog`, `Display`, etc.) exist in the generated output
before running the Java smoke harness. It also asserts known internal classes
(`com.codename1.ui.Accessor`, `com.codename1.io.IOAccessor`) are not emitted.
75 changes: 51 additions & 24 deletions scripts/cn1playground/tools/generate-cn1-access-registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,65 @@ SRC="$ROOT/tools/src/main/java/com/codenameone/playground/tools/GenerateCN1Acces
OUT="$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"
BUILD_DIR="$ROOT/target/cn1-access-tool"
POM="$ROOT/pom.xml"
REPO_ROOT="$(CDPATH= cd -- "$ROOT/../.." && pwd)"
LOCAL_CORE_SRC="$REPO_ROOT/CodenameOne/src"
LOCAL_CLDC_SRC="$REPO_ROOT/Ports/CLDC11/src"

read_cn1_version() {
perl -0777 -ne 'if (m|<properties>.*?<cn1\.version>([^<]+)</cn1\.version>|s) { print $1; exit 0 }' "$POM"
}

CN1_VERSION="${CN1_VERSION:-$(read_cn1_version)}"
if [ -z "$CN1_VERSION" ]; then
echo "Unable to determine cn1.version from $POM" >&2
exit 1
fi

SOURCES_BASE="$BUILD_DIR/sources/$CN1_VERSION"
mkdir -p "$SOURCES_BASE"
USE_LOCAL_SOURCES="${CN1_ACCESS_USE_LOCAL_SOURCES:-false}"

download_source_jar() {
artifact="$1"
jar="$SOURCES_BASE/${artifact}-${CN1_VERSION}-sources.jar"
if [ ! -f "$jar" ]; then
url="https://repo.maven.apache.org/maven2/com/codenameone/${artifact}/${CN1_VERSION}/${artifact}-${CN1_VERSION}-sources.jar"
curl -fsSL "$url" -o "$jar"
CN1_SOURCE_ROOTS_VALUE=""
if [ "$USE_LOCAL_SOURCES" = "true" ]; then
if [ ! -d "$LOCAL_CORE_SRC" ] || [ ! -d "$LOCAL_CLDC_SRC" ]; then
echo "Local source mode requested but expected roots are missing:" >&2
echo " $LOCAL_CORE_SRC" >&2
echo " $LOCAL_CLDC_SRC" >&2
exit 1
fi
dest="$SOURCES_BASE/$artifact"
mkdir -p "$dest"
unzip -q -o "$jar" -d "$dest"
printf '%s' "$dest"
}
echo "Using local Codename One sources for registry generation." >&2
else
CN1_VERSION="${CN1_VERSION:-$(read_cn1_version)}"
if [ -z "$CN1_VERSION" ]; then
echo "Unable to determine cn1.version from $POM" >&2
exit 1
fi
echo "Using release source jars for Codename One version: $CN1_VERSION" >&2

CORE_SRC_DIR="$(download_source_jar codenameone-core)"
RUNTIME_SRC_DIR="$(download_source_jar java-runtime)"
JAVASE_SRC_DIR="$(download_source_jar codenameone-javase)"
CN1_SOURCE_ROOTS="${CORE_SRC_DIR}:${RUNTIME_SRC_DIR}:${JAVASE_SRC_DIR}"
SOURCES_BASE="$BUILD_DIR/sources/$CN1_VERSION"
mkdir -p "$SOURCES_BASE"

download_source_jar() {
artifact="$1"
jar="$SOURCES_BASE/${artifact}-${CN1_VERSION}-sources.jar"
if [ ! -f "$jar" ]; then
url="https://repo.maven.apache.org/maven2/com/codenameone/${artifact}/${CN1_VERSION}/${artifact}-${CN1_VERSION}-sources.jar"
if ! curl -fsSL "$url" -o "$jar"; then
echo "Failed to download source jar: $url" >&2
exit 1
fi
fi
dest="$SOURCES_BASE/$artifact"
mkdir -p "$dest"
if ! unzip -q -o "$jar" -d "$dest"; then
echo "Failed to extract source jar: $jar" >&2
exit 1
fi
printf '%s' "$dest"
}

CORE_SRC_DIR="$(download_source_jar codenameone-core)"
RUNTIME_SRC_DIR="$(download_source_jar java-runtime)"
JAVASE_SRC_DIR="$(download_source_jar codenameone-javase)"
CN1_SOURCE_ROOTS_VALUE="${CORE_SRC_DIR}:${RUNTIME_SRC_DIR}:${JAVASE_SRC_DIR}"
fi

mkdir -p "$BUILD_DIR"
javac -d "$BUILD_DIR" "$SRC"
CN1_SOURCE_ROOTS="$CN1_SOURCE_ROOTS" java -cp "$BUILD_DIR" com.codenameone.playground.tools.GenerateCN1AccessRegistry "$OUT"
if [ -n "$CN1_SOURCE_ROOTS_VALUE" ]; then
CN1_SOURCE_ROOTS="$CN1_SOURCE_ROOTS_VALUE" java -cp "$BUILD_DIR" com.codenameone.playground.tools.GenerateCN1AccessRegistry "$OUT"
else
java -cp "$BUILD_DIR" com.codenameone.playground.tools.GenerateCN1AccessRegistry "$OUT"
fi
27 changes: 26 additions & 1 deletion scripts/cn1playground/tools/run-playground-smoke-tests.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
#!/bin/zsh
#!/bin/bash
set -eu

ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"

cd "$ROOT"
echo "Regenerating CN1 access registry from release sources..."
CN1_ACCESS_USE_LOCAL_SOURCES=false bash "$ROOT/tools/generate-cn1-access-registry.sh"

echo "Verifying Component is present in generated registry..."
if ! rg -q 'index.put\("com\.codename1\.ui\.Component"' "$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"; then
echo "GeneratedCN1Access is missing com.codename1.ui.Component" >&2
exit 1
fi

echo "Verifying key com.codename1.ui classes are present in generated registry..."
for cls in Button Container Dialog Display Form Label List TextField BrowserComponent; do
if ! rg -q "index.put\\(\"com\\.codename1\\.ui\\.${cls}\"" "$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"; then
echo "GeneratedCN1Access is missing com.codename1.ui.${cls}" >&2
exit 1
fi
done

echo "Verifying package-private/internal sentinel classes are NOT generated..."
for cls in com.codename1.ui.Accessor com.codename1.io.IOAccessor; do
if rg -q "index.put\\(\"${cls}\"" "$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"; then
echo "GeneratedCN1Access unexpectedly includes internal class ${cls}" >&2
exit 1
fi
done

mvn -pl common -DskipTests test-compile org.codehaus.mojo:exec-maven-plugin:3.0.0:java \
-Dexec.classpathScope=test \
-Dexec.mainClass=com.codenameone.playground.PlaygroundSmokeHarness
Loading