Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8d2cfcf
Check build hints at compile time instead of shipping them inert
shai-almog Aug 22, 2026
d243535
Commit the catalog sources that .gitignore was swallowing
shai-almog Aug 22, 2026
d727c7d
Stop the bootstrap doing its work at import time
shai-almog Aug 22, 2026
96bff90
Make the generated docs and sources survive the ASCII and prose gates
shai-almog Aug 22, 2026
a343fe3
Give ThreadSafeDatabaseTest headroom under the FormTest timeout
shai-almog Aug 22, 2026
0edef42
Refuse to migrate a project that never runs process-annotations
shai-almog Aug 22, 2026
2803847
Do not migrate the guide's snippet project, and keep Settings from du…
shai-almog Aug 22, 2026
53da7c3
Defer the generated-project templates to a follow-up
shai-almog Aug 23, 2026
8b50565
Annotate inside the integration test, not in the archetype
shai-almog Aug 23, 2026
b2e0e86
Four migration and Settings defects from review
shai-almog Aug 23, 2026
fd05dfa
Require the process-annotations binding on the module that owns the m…
shai-almog Aug 23, 2026
3037e76
Require an execution that can actually see compiled classes
shai-almog Aug 23, 2026
af8ff86
Stop the generated simulator schema duplicating the hand-written one
shai-almog Aug 23, 2026
586e6bc
Prove the annotations are processed instead of predicting it
shai-almog Aug 23, 2026
b1dff60
Round-trip the rollback snapshot, and stop trusting a stale manifest
shai-almog Aug 23, 2026
76cd1dc
Preserve source bytes, and stop comments confusing the annotation sca…
shai-almog Aug 23, 2026
8be6d5f
Refuse a build whose annotations were never processed
shai-almog Aug 23, 2026
fb5a6c7
Do not refuse a build over an annotation that sets nothing
shai-almog Aug 23, 2026
51fb76a
Tell a current annotation manifest from last build's
shai-almog Aug 23, 2026
9c5a586
Give the same answer whether or not target/classes was cleaned
shai-almog Aug 23, 2026
43f73f1
Merge remote-tracking branch 'origin/master' into build-hint-annotations
shai-almog Aug 23, 2026
b6f16f2
Catalogue the ten build hints the Wear change added
shai-almog Aug 23, 2026
eb58dac
Write the new catalog rows in the guide's own voice
shai-almog Aug 23, 2026
a5f8dbb
Merge remote-tracking branch 'origin/master' into build-hint-annotations
shai-almog Aug 24, 2026
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
32 changes: 32 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ on:
- 'scripts/ci/retry.sh'
- 'scripts/ci/apt-get-update.sh'
- 'scripts/ci/apt-get-install.sh'
# The build hint gates are run from this workflow and nowhere else, and one
# of them holds an empty baseline. Ignoring the whole directory meant a
# change that breaks a gate, or that adds a line to the baseline, could
# merge without the gate it weakens ever running.
- 'scripts/check-build-hint-catalog.sh'
- 'scripts/check-build-hint-catalog.py'
- 'scripts/build_hint_miner.py'
- 'scripts/build-hint-catalog-baseline.txt'
- 'scripts/gen-build-hint-annotations.sh'
- '!docs/**'
- '!**/*.md'
- '!.github/workflows/developer-guide-docs.yml'
Expand Down Expand Up @@ -59,6 +68,15 @@ on:
- 'scripts/ci/retry.sh'
- 'scripts/ci/apt-get-update.sh'
- 'scripts/ci/apt-get-install.sh'
# The build hint gates are run from this workflow and nowhere else, and one
# of them holds an empty baseline. Ignoring the whole directory meant a
# change that breaks a gate, or that adds a line to the baseline, could
# merge without the gate it weakens ever running.
- 'scripts/check-build-hint-catalog.sh'
- 'scripts/check-build-hint-catalog.py'
- 'scripts/build_hint_miner.py'
- 'scripts/build-hint-catalog-baseline.txt'
- 'scripts/gen-build-hint-annotations.sh'
- '!docs/**'
- '!**/*.md'
- '!.github/workflows/developer-guide-docs.yml'
Expand Down Expand Up @@ -416,6 +434,20 @@ jobs:
- name: Run SpotBugs for ByteCodeTranslator
if: ${{ matrix.java-version == 8 }}
run: mvn -B -DskipTests=true -f vm/ByteCodeTranslator/pom.xml verify
# A build hint is a string nothing checks: a misspelled name is accepted,
# never read, and silently does nothing. The catalog in
# maven/build-hint-catalog is what gives every hint a type, a default and a
# value domain, and it is what the @Ios/@Android annotations, the developer
# guide table and the Settings tool are generated from. These two steps keep
# the catalog complete and the generated files in step with it.
- name: Check build hint catalog
if: ${{ matrix.java-version == 8 }}
run: scripts/check-build-hint-catalog.sh
Comment thread
shai-almog marked this conversation as resolved.
- name: Check generated build hint annotations
if: ${{ matrix.java-version == 8 }}
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
scripts/gen-build-hint-annotations.sh --check
# ParparVM's CHECKCAST is unchecked, so a failed cast does not throw
# ClassCastException on iOS -- code written to catch it silently uses the
# wrong object instead (issue #5531). core/android/ios are already compiled
Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@
!maven/cn1app-archetype/src/main/resources/archetype-resources/.idea
!maven/cn1app-archetype/src/main/resources/archetype-resources/.idea/**
**/build/*
# ...but `build` is also a legitimate Java package name, and com.codename1.build.shared
# is where the catalogs shared with the build service live. Without these, a new file
# there is silently untracked: `git add` skips it, the module compiles locally from the
# working tree, and CI fails with "No sources to compile". The existing files in that
# package survive only because they were added before the rule above.
!**/src/main/java/**/build/
!**/src/main/java/**/build/**
!**/src/test/java/**/build/
!**/src/test/java/**/build/**
**/dist/*
*.zip
CodenameOneDesigner/src/version.properties
Expand Down
38 changes: 38 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,44 @@ removing one can make a previously-used private method dead.

Findings land in each module's `target/spotbugsXml.xml`.

### Build hints are a catalog, not free-form strings

A build hint is a `codename1.arg.<name>=<value>` line that reaches a builder as
`request.getArg(name, default)`. Nothing used to check the name, so a misspelled
hint was accepted, never read, and silently did nothing -- a green build with the
setting simply not applied. Our own agent reference shipped
`android.xPermissions`, `android.minSdkVersion` and `android.sdkVersion` for
years; the builders read `android.xpermissions`, `android.min_sdk_version`, and
nothing at all.

**`maven/build-hint-catalog` is the single source of truth.** Every hint's name,
type, default, value domain, merge separator and documentation lives there, and
everything else is generated from it:

- the `com.codename1.annotations.buildhints` annotations in `CodenameOne/src`
- `BuildHintAnnotationBinding`, which the annotation processor reads back
- the developer guide's build hint table (`docs/developer-guide/_generated-build-hints.adoc`)
- the simulator's Build Hint editor schema (`BuildHintCatalogDefaults`)
- the agent reference's annotation table (`skill/references/build-hints.md`)

Adding a hint to a builder means adding it to the catalog in the same change.
Regenerate with:

```bash
source tools/env.sh
scripts/gen-build-hint-annotations.sh # rewrite the generated files
scripts/gen-build-hint-annotations.sh --check # what CI runs
scripts/check-build-hint-catalog.sh # every hint the code reads is catalogued
```

`scripts/build-hint-catalog-baseline.txt` is a ratchet, and it is **empty**: every
hint the code reads is described. A new entry means a hint went in without a
catalog row. The same gate refuses a `codename1.arg.*` key in our own docs and
project templates that no builder reads.

Do not re-run `tools/build-hint-bootstrap/` -- it seeded the catalog once and
would overwrite hand edits.

### Never rely on ClassCastException

**ParparVM's `CHECKCAST` is unchecked.** `BC_CHECKCAST` expands to nothing and the
Expand Down
160 changes: 160 additions & 0 deletions CodenameOne/src/com/codename1/annotations/buildhints/Android.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.annotations.buildhints;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/// Android build hints, checked by the compiler.
///
/// Place this on your application's main class -- the class named by
/// `codename1.mainName`. An attribute you do not set is not written at all, so
/// the builder's own default applies; the values shown here are that default,
/// for reference.
///
/// Generated from com.codename1.build.shared.BuildHints by
/// BuildHintCodeGenerator. Do not edit by hand -- edit the catalog and
/// re-run scripts/gen-build-hint-annotations.sh.
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Android {

/// Allows explicitly setting the `android:launchMode` attribute of the main
/// activity in android. Default is "singleTop," but for some applications you
/// may need to change this behaviour. In particular, apps that are meant to
/// open a file type will need to set this to "singleTask." See
/// https://developer.android.com/guide/topics/manifest/activity-element.html[Android
/// docs for the activity element] for more information about the
/// `android:launchMode` attribute.
String activityLaunchMode() default "singleTop";

/// Produces an Android App Bundle (.aab) rather than an APK. Required for new
/// Play Store submissions.
boolean appBundle() default false;

/// Android build-tools version. It also selects the compile SDK, so there is no
/// separate compile-SDK hint.
String buildToolsVersion() default "";

/// Indicates whether the `RECORD_AUDIO` permission should be requested. Can be
/// `enabled` or any other value to disable this option
String captureRecord() default "enabled";

/// true/false defaults to true - indicates whether to include the debug version
/// in the build. Defaults conditionally rather than to a fixed value: when
/// android.release is on it defaults to false, and when release is off it
/// defaults to true, so a build that selects neither still produces something
/// installable (AndroidGradleBuilder.java:447-451).
boolean debug() default false;

/// Turns off R8, falling back to the older shrinker. Note that hardening
/// requires R8, so this conflicts with harden.level.
boolean disableR8() default false;

/// Boolean true/false defaults to true. Allows disabling the proguard
/// obfuscation even on release builds, notice that this isn't recommended
boolean enableProguard() default true;

/// Gradle dependency statements to add to the app module, such as
/// implementation 'com.example:lib:1.0'.
/// Values are joined with `;` when the hint is written.
String[] gradleDep() default {};

/// Hides the Android status bar.
boolean hideStatusBar() default false;

/// Maps to android:installLocation manifest entry defaults to auto. Can also be
/// set to internalOnly or preferExternal.
InstallLocation installLocation() default InstallLocation.AUTO;

/// The license key for the Android app, this is required if you use in-app
/// purchase on Android
String licenseKey() default "";

/// The least SDK required to run this app, the default value changes based on
/// functionality but can be as low as 7. This corresponds to the XML attribute
/// `android:minSdkVersion`.
int minSdkVersion() default 19;

/// Boolean true/false defaults to false. Multidex allows Android binaries to
/// reference more than 65536 methods. This slows builds a bit so you have it
/// off by default but if you get a build error mentioning this limit you should
/// turn this on.
boolean multidex() default true;

/// Uses the current Firebase Cloud Messaging integration. Requires AndroidX and
/// Gradle 8.13 or newer.
boolean newFirebaseMessaging() default true;

/// Arguments for the keep option in proguard allowing you to keep a pattern of
/// files for example, `-keep class com.mypackage.ProblemClass { *; }`
/// Values are joined with `\n` when the hint is written.
String[] proguardKeep() default {};

/// true/false defaults to true - indicates whether to include the release
/// version in the build
boolean release() default true;

/// Extra Gradle repositories to resolve dependencies from.
/// Values are joined with `\n` when the hint is written.
String[] repositories() default {};

/// Indicates the Android SDK used to compile the Android build defaults to 21.
/// Notice that not all targets will work since the source might have some
/// limitations and not all SDK targets are installed on the build servers.
int targetSDKVersion() default 0;

/// `auto`, `modern` / `material`, `hololight` (default for existing apps),
/// `legacy`. `auto` and `modern` / `material` opt in to the CSS-generated
/// Android Material 3 theme from `native-themes/android-material/theme.css`.
/// `hololight` is Android Holo Light (what the framework shipped on API 14+
/// before this refactor). `legacy` loads the pre-Holo Android theme. The legacy
/// alias `cn1.androidTheme` is still accepted, and `and.hololight=true` still
/// maps to `hololight`. The default stays on `hololight` for existing apps
/// until you flip in a future release.
AndroidThemeMode themeMode() default AndroidThemeMode.AUTO;

/// Statements added to the top-level Gradle build file rather than the app
/// module.
/// Values are joined with `\n` when the hint is written.
String[] topDependency() default {};

/// Use Android X instead of support libraries. This will also run a
/// find/replace on all source files to replace support libraries and artifacts
/// with AndroidX equivalents.
boolean useAndroidX() default false;

/// defaults to an empty string. Allows developers of native Android code to add
/// text within the application block to define things such as widgets, services
/// etc.
String xapplication() default "";

/// Arbitrary text spliced into the generated app-module Gradle file.
/// Values are joined with `\n` when the hint is written.
String[] xgradle() default {};

/// more permissions for the Android manifest
String xpermissions() default "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.annotations.buildhints;

/// Accepted values of the `and.themeMode` build hint.
///
/// Each constant carries the string the build actually receives, which is not
/// always the constant's own name.
///
/// Generated from com.codename1.build.shared.BuildHints by
/// BuildHintCodeGenerator. Do not edit by hand -- edit the catalog and
/// re-run scripts/gen-build-hint-annotations.sh.
public enum AndroidThemeMode {
AUTO("auto"),
MODERN("modern"),
HOLOLIGHT("hololight"),
LEGACY("legacy");

private final String wire;

AndroidThemeMode(String wire) {
this.wire = wire;
}

/// The value written into the build hint.
public String wireValue() {
return wire;
}
}
64 changes: 64 additions & 0 deletions CodenameOne/src/com/codename1/annotations/buildhints/Build.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.annotations.buildhints;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/// Build hints that are not specific to one platform.
///
/// Place this on your application's main class -- the class named by
/// `codename1.mainName`. An attribute you do not set is not written at all, so
/// the builder's own default applies; the values shown here are that default,
/// for reference.
///
/// Generated from com.codename1.build.shared.BuildHints by
/// BuildHintCodeGenerator. Do not edit by hand -- edit the catalog and
/// re-run scripts/gen-build-hint-annotations.sh.
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Build {

/// The application ID for an app that requires native Facebook login
/// integration, this defaults to null which means native Facebook support
/// shouldn't be in the app
String facebookAppId() default "706695982682332";

/// The Android/chrome push identifier, see the push section for more details
String gcmSenderId() default "";

/// `modern`, `legacy`, `custom` (default unset). Cross-platform override that
/// sets both `ios.themeMode` and `and.themeMode` together when those aren't set
/// explicitly. `modern` = liquid glass + Material 3, `legacy` = iOS 7 flat +
/// Holo Light, `custom` disables the framework native theme entirely. The
/// legacy alias `cn1.nativeTheme` is still accepted.
NativeThemeMode nativeTheme() default NativeThemeMode.MODERN;

/// true/false (defaults to false). Blocks codename one from injecting its own
/// resources when set to true, the only effect this has is in slightly reducing
/// archive size. This might have adverse effects on some features of Codename
/// One so it isn't recommended.
boolean noExtraResources() default false;
}
Loading
Loading