Imported from xpenatan/gdx-teavm (
AGENTS.md). Install upstream withnpx skills add xpenatan/gdx-teavm. Copyright stays with the author.
AGENTS Guide for gdx-teavm
Mandatory Notes
- Keep a
CURRENT_CHAT_CONTEXTfile with the current chat flow so work can be recovered after session failures. CURRENT_CHAT_CONTEXTis for last-chat context only; do not keep long-term history there.- At session start, always check whether
CURRENT_CHAT_CONTEXTexists; if it does, load it before proceeding. - Always validate that a class, member, method, task, or Gradle property already exists before using it. Never guess API names or signatures.
- Never write or modify any file without explicit user permission. Planning and review steps are allowed without writing.
- After making changes, run a simple Gradle build to confirm code still compiles. Prefer the smallest affected module/task, or
./gradlew buildwhen unclear.
Common Workflows
- Inspect project graph:
./gradlew projects
- Build everything:
./gradlew build
- Compile the Gradle plugin:
./gradlew :gdx-teavm-plugin:compileKotlin
- Compile the shared/web backends:
./gradlew :backends:backend-shared:compileJava :backends:backend-web:compileJava
- Compile native backends:
./gradlew :backends:backend-glfw:compileJava :backends:backend-ios:compileJava
Example Tasks
- Manual builder web example:
./gradlew :examples:basic:platforms:web:builder:basic_web_run
- Manual builder desktop C example:
./gradlew :examples:basic:platforms:desktop:teavm-c:builder:basic_desktop_c_generate./gradlew :examples:basic:platforms:desktop:teavm-c:builder:basic_desktop_c_debug_build./gradlew :examples:basic:platforms:desktop:teavm-c:builder:basic_desktop_c_debug_run
- GraalVM Native Image basic example:
./gradlew :examples:basic:platforms:desktop:graalvm:basic_desktop_graalvm_build./gradlew :examples:basic:platforms:desktop:graalvm:basic_desktop_graalvm_run
- Gradle plugin basic example:
./gradlew :examples:basic:platforms:web:plugin:gdx_teavm_web_js_run./gradlew :examples:basic:platforms:web:plugin:gdx_teavm_web_wasm_run./gradlew :examples:basic:platforms:web:plugin:gdx_teavm_web_js_release_build./gradlew :examples:basic:platforms:web:plugin:gdx_teavm_web_wasm_release_build./gradlew :examples:basic:platforms:desktop:teavm-c:plugin:gdx_teavm_glfw_generate./gradlew :examples:basic:platforms:desktop:teavm-c:plugin:gdx_teavm_glfw_build./gradlew :examples:basic:platforms:desktop:teavm-c:plugin:gdx_teavm_glfw_run./gradlew :examples:basic:platforms:desktop:teavm-c:plugin:gdx_teavm_glfw_release_generate
- Gradle plugin FreeType web example:
./gradlew :examples:freetype:platforms:web:plugin:gdx_teavm_web_js_run./gradlew :examples:freetype:platforms:web:plugin:gdx_teavm_web_wasm_run
- Gradle plugin gdx-controllers web example:
./gradlew :examples:controllers:platforms:web:plugin:gdx_teavm_web_js_run./gradlew :examples:controllers:platforms:web:plugin:gdx_teavm_web_wasm_run
- Publishing entry points are root tasks provided by
com.github.xpenatan.easy-publishingand shown under theeasy-publishingtask group:prepareSnapshotprepareReleasepublishSnapshotpublishRelease
Project Shape
- This is a multi-module Gradle build. Active modules live under
backends/,extensions/,tools/gdx-teavm-plugin, andexamples/. - Each example keeps portable code in
examples/<name>/core, optional shared runtime assets inexamples/<name>/assets, and runnable launchers underexamples/<name>/platforms. - Desktop implementations are grouped under
platforms/desktop; TeaVM C and web usebuilderandpluginleaves only when both build styles exist. - Intermediate example directories are organizational parents. Runnable platform leaves depend directly on their example's
core, not on sibling platform projects. settings.gradle.ktsincludes the local Gradle plugin build withincludeBuild("tools/gdx-teavm-plugin"); the included build appears as project path:gdx-teavm-pluginin Gradle output.- Dependency aliases and dependency, plugin, release, and snapshot versions are centralized in
gradle/libs.versions.toml. - Local source paths and composite-build switches remain in
gradle.properties. - Root
build.gradle.ktsapplies shared Java 17 settings and Maven repositories to subprojects. gradle.propertiescan enable composite builds for local libGDX or TeaVM source:includeLibgdxSourceincludeTeaVMSourcegdxSourcePathteavmPath
Core Compiler Pipeline
- The manual builder API lives in
backends/backend-shared. TeaBuilderis the fluent public entry point. It stores configuration inTeaBuilderData.TeaBuilder.build(File output)delegates to a concreteTeaBackend.TeaBackend.compile(...)performs shared setup:- classpath collection
- TeaVM tool configuration
- reflection metadata setup
- asset planning and copying
- backend-specific build hooks
- Put behavior in
TeaBackendonly when it is shared by all targets. Keep target-specific behavior in:WebBackendTeaGLFWBackend
Backends
backends/backend-web- Runtime classes:
WebApplication,WebApplicationConfiguration, WebGL/audio/filesystem implementations. - Builder backend:
WebBackend. - Targets JavaScript or Wasm based on
WebBackend.setWebAssembly(boolean). - Generates
index.html,WEB-INF/web.xml, web assets, preload manifest, scripts, and optional Jetty serving.
- Runtime classes:
backends/backend-glfw- Runtime classes:
GLFWApplication,GLFWApplicationConfiguration. - Builder backend:
TeaGLFWBackend. - Targets TeaVM C output and writes a native CMake project with debug/release build scripts.
- Output is typically under
build/dist/glfw/cin plugin mode orbuild/dist/cin builder mode.
- Runtime classes:
Gradle Plugin
- Plugin source lives in
tools/gdx-teavm-plugin. - Plugin id:
com.github.xpenatan.gdx-teavm. - Extension block:
gdxTeaVM { ... }. - The plugin applies Java and TeaVM's Gradle plugin internally.
- Target blocks are declarative. Tasks are created only for declared blocks:
- Unnamed
js { ... },wasm { ... },glfw { ... },ios { ... }, andandroid { ... }blocks retain their legacy task names. - Named
js("name") { ... },wasm("name") { ... },glfw("name") { ... }, andios("name") { ... }blocks create independent target variants. webDefaults { ... }andnativeDefaults { ... }are optional conventions and do not declare targets.
- Unnamed
- Plugin-generated tasks use group
gdx-teavm. - TeaVM's own low-level tasks still exist internally, but the plugin clears their task group so normal users are guided toward the
gdx_teavm_*tasks. - The plugin forces TeaVM generation tasks to run each invocation so assets and generated web/native wrappers are refreshed.
- The plugin adds the selected backend artifact to both Java
implementationand TeaVM's generation classpath:- web targets add
backend-web - GLFW adds
backend-glfw - iOS adds
backend-ios
- web targets add
- In the repository build, backend dependencies resolve to local projects. In a published build, they resolve from Maven using the generated plugin version in
GdxTeaVMPluginInfo.
Plugin Tasks
- Web JavaScript:
gdx_teavm_web_js_buildgdx_teavm_web_js_run
- Web Wasm:
gdx_teavm_web_wasm_buildgdx_teavm_web_wasm_run
- GLFW native:
gdx_teavm_glfw_generategdx_teavm_glfw_buildgdx_teavm_glfw_run
- iOS native:
gdx_teavm_ios_generategdx_teavm_ios_prepare_anglegdx_teavm_ios_init_xcodegdx_teavm_ios_regenerate_xcodegdx_teavm_ios_open_xcodegdx_teavm_ios_build_simulatorgdx_teavm_ios_run_simulator
- Named task patterns insert the normalized target name before the action:
- Web:
gdx_teavm_web_<js|wasm>_<name>_<build|run> - GLFW:
gdx_teavm_glfw_<name>_<generate|build|run> - iOS:
gdx_teavm_ios_<name>_<existing-action>
- Web:
Plugin Configuration Model
- Shared plugin properties and target registries are defined in
GdxTeaVMExtension. - Only backend-agnostic settings belong in the root
gdxTeaVM { ... }block, such as assets and reflection. - Optional inherited target conventions are defined in
GdxTeaVMWebDefaultsandGdxTeaVMNativeDefaults. An explicit target value wins over defaults, which win over built-in conventions. - Per-target TeaVM properties are defined in
GdxTeaVMTargetExtensionand subclasses:GdxTeaVMWebExtensionGdxTeaVMJsExtensionGdxTeaVMWasmExtensionGdxTeaVMGlfwExtensionGdxTeaVMIosExtension
- Web-only settings such as
htmlTitle,htmlWidth,htmlHeight,entryPointName,mainClassArgs,logoPath,copyLoadingAsset,webappEnabled, andserverPortbelong inwebDefaults {},js {}, orwasm {}, not in the root extension. - GLFW build mode is selected with
glfw.buildType(GlfwBuildType.DEBUGorGlfwBuildType.RELEASE); plugin tasks are not split by build type. - Web targets usually share the same launcher class.
- Native targets usually need native-specific launcher classes because they start different backend application classes.
- iOS is an experimental native plugin target with TeaVM C/assets generation plus WIP Xcode and simulator tasks.
- Default output directories:
- JS:
build/dist/js - Wasm:
build/dist/wasm - GLFW:
build/dist/glfw - iOS:
build/dist/ios - Named target: the matching directory above plus
/<normalized-name>
- JS:
- Default generated app subdirectories:
- Web targets:
webapp - Native targets:
c/src
- Web targets:
TeaVM Runtime Plugins
- Backend runtime plugins implement
org.teavm.vm.spi.TeaVMPluginand are registered throughMETA-INF/services/org.teavm.vm.spi.TeaVMPlugin. WebPluginsupports JavaScript and Wasm by checking:TeaVMJavaScriptHostTeaVMWasmGCHost
WebPlugininstalls:WebClassTransformerJavaObjectExporterDependency- reflection support
GdxWebTargetWrapperwhen webapp generation is enabled
GLFWPlugin,AndroidPlugin, andIOSPluginsupport TeaVM C output by checkingTeaVMCHost.- Native plugins use
gdx.teavm.native.backendto decide which native backend is selected. - Plugin properties are transported through
TeaVMHost.getProperties()and parsed byGdxTeaVMPluginConfig.
Assets And Resources
- Asset copying is centralized in
AssetsCopy. - Do not create a second asset-copy implementation unless there is a strong reason.
- Builder and plugin paths both use the same planning/copying logic.
- Web targets compile preload metadata into TeaVM output through
TeaAssetManifest; native targets copy assets directly and do not create a preload manifest file. - Asset entries preserve libGDX file type:
- disk assets are normally
Internal - classpath resources are
Classpath
- disk assets are normally
AssetFileHandleis used by the builder API.- The Gradle plugin exposes:
assets(...)orassets.from(...)classpathAssets(...)
- Libraries can auto-contribute resources through
META-INF/gdx-teavm.properties, parsed byTeaVMResourceProperties:resources=ignore-resources=classpath-resources=
- Scripts (
.js,.wasm) and/external_cpp/resources are partitioned byTeaBackend.partitionResources(...)and copied by the relevant backend. - Packaged resource metadata exists in:
backends/backend-shared/src/main/resources/META-INF/gdx-teavm.propertiesbackends/backend-web/src/main/resources/META-INF/gdx-teavm.propertiesbackends/backend-glfw/src/main/resources/META-INF/gdx-teavm.properties
Reflection
- libGDX reflection emulation is backed by generated TeaVM metadata.
- Builder API:
TeaBuilder.addReflectionClass(Class<?>)TeaBuilder.addReflectionClass(String)TeaBuilder.setReflectionListener(DefaultReflectionListener)
- Gradle plugin API:
reflection("com.example.Type")reflection("com.example.package**")reflectionEnabledreflectionDefaultsreflectionScanreflectionDebug
- Runtime emulation classes read reflection metadata through
TeaReflectionSupplier. - Plugin generation installs reflection support through
TeaVMPluginReflectionSupport.
JSO And Wasm Strict Mode
- TeaVM Wasm strict mode emits runtime checks for non-transparent
@JSClassoverlays. - If a class is only a Java facade over an existing JavaScript object and does not exist as a real JavaScript global constructor, annotate it with:
@JSClass(transparent = true)
- Existing examples include DOM/WebGL facades and
WebGL20.CustomIntMap. - Do not annotate real JavaScript classes such as browser constructors or library constructors as transparent without checking their TeaVM usage.
Development Notes
- Source sets for
backend-webandbackend-glfwintentionally compile from bothemuandsrc/main/java. - If adding packaged resources, update the module's
META-INF/gdx-teavm.propertiesso assets are discoverable. - Preserve fluent API style in compiler and backend config classes; setters return
this. - Prefer proving changes through existing Gradle tasks instead of ad-hoc commands.
- On Windows, Gradle daemons can hold
backend-webJARs open. If:backends:backend-web:cleanfails on a locked JAR, run./gradlew --stopand retry.
TeaVM C Performance Work
- For GLFW TeaVM C performance work, first identify a hot generated-C or Java method through benchmark/profile evidence.
- If that hot path repeatedly pays TeaVM null checks, bounds checks, type checks, primitive array access, or tiny Java calls inside a per-frame, per-sprite, or per-vertex loop, prefer an internal C helper exposed through
@Importand installed by a transformer/substitution. - Keep public libGDX APIs unchanged. Do not require users to switch to custom public classes for performance work.
- Do not optimize benchmark-only classes as the real solution. Benchmark-only C paths are acceptable only when explicitly marked as comparison code.
- Do not put all optimized logic directly into TeaVM C bridge files. TeaVM generated C types, fields, and method names are hard to read, so bridge code should stay small.
- Put readable, reusable CPU-heavy native kernels in
backends/backend-shared/src/main/resources/external_cpp/teavm_optimizations/pure. - Pure C files may have
.cand.hfiles with clean function names and normal C signatures. - For measured per-frame, per-sprite, or per-vertex hot kernels, prioritize pure header-inline helpers (
static inline,__forceinline, or compiler-specificalways_inline) so TeaVM bridge callers can inline the work into the hot loop. - Use a separate pure
.cfunction only for cold/medium paths, large code where size/readability matters more than call overhead, or when benchmark evidence shows no regression from the function boundary. - Pure C files must not include TeaVM generated headers, access TeaVM object layouts, throw TeaVM exceptions, or use TeaVM GC barriers.
- Put TeaVM-dependent wrapper code in
backends/backend-shared/src/main/resources/external_cpp/teavm_optimizations/teavm. - When adding a new TeaVM-dependent optimization bridge, remember that native targets default to TeaVM
shortFileNames=true. Any C includes, generated-header checks, and CMake source gating must support both long paths such asc/src/classes/com/example/Foo.hand shortened paths such asc/src/c/e/Foo.h; prefer detecting generated classes throughc/src/all.txtwhen possible. - TeaVM C bridge files should only unpack TeaVM objects/arrays, validate inputs, handle exceptions/barriers/flush calls, and call pure C kernels.
- When optimizing a hot method, move only the expensive computation into pure C. Keep simple getters, setters, and lightweight state changes in Java unless profiling proves otherwise.
- Preserve semantics with cold Java fallback/error paths where needed, and validate with the smallest Gradle compile task plus bounded native benchmark runs that force-close stuck apps.
Documentation Entry Points
- Root README:
README.md - Usage guide:
docs/usage.md - Plugin property reference:
docs/plugin-properties.md - Backend architecture guide:
docs/backend-architecture.md - Examples overview:
examples/README.md - Desktop C native build notes:
examples/basic/platforms/desktop/teavm-c/builder/README.md
