--- name: making-graalvm-compatible description: Makes a Ballerina library GraalVM-compatible by running the native build/test workflow, sourcing reachability metadata, and marking the package compatible. Use when the user wants to make a Ballerina library or package GraalVM compatible; build or test a Ballerina package with `bal build --graalvm` / `bal test --graalvm`; fix GraalVM native-image class-initialization or reflection/JNI/resource errors in a Ballerina project; run the GraalVM tracing agent for Ballerina tests or a service; pack native-image reachability metadata into META-INF for a Ballerina module; resolve the "Package is not verified with GraalVM" warning; or set graalvmCompatible = true in Ballerina.toml. --- # Making a Ballerina Library GraalVM Compatible An AI-assisted workflow for taking a Ballerina library to a verified, warning-free `bal build --graalvm` / `bal test --graalvm`. It builds and tests natively, resolves build-time class-initialization errors, sources native-image metadata (preferring the vetted `oracle/graalvm-reachability-metadata` repo over the tracing agent), packs the required config under `META-INF/native-image/`, and marks the package compatible in `Ballerina.toml`. Based on `docs/graalvm-compatibility-in-ballerina-libraries.md`, with GraalVM reference material adapted from the Oracle GraalVM community skills. --- ## How This Skill Works Unlike a linear pipeline, this is a **decision tree with loops** driven by what the baseline build/test reveals: ``` Setup → Build & Test ─┬─ (build-time class-init errors) → fix loop → re-build ├─ (all green) ──────────────────────────────► Mark └─ (runtime/metadata gaps) → Reachability repo → Trace JAR / Trace tests → Filter & pack → Mark ``` Each stage is a file under `stages/`. **Load only the active stage's file** into context — do not preload all stages. Routing between stages follows `references/workflow.md`. Scripts in `scripts/` handle all deterministic operations (version derivation, classpath extraction, the version-sensitive `BTestMain` command, config filtering/packing, `Ballerina.toml` edits). Run them via Bash — do not reimplement their logic inline. LLM reasoning is reserved for judgment: class-init strategy, exercising a running service, choosing which configs to keep, dependency upgrades. --- ## Quick Reference | Stage | File | Skip when | Key output | | -------------------- | ---------------------------------- | ------------------------------------ | ------------------------------------------- | | 0. Setup | `stages/00-setup.md` | never | Shared State, GraalVM/JDK check | | 1. Build & Test | `stages/01-build-and-test.md` | never | baseline status + class-init fix loop | | 2. Reachability repo | `stages/02-reachability-repo.md` | no third-party Java deps | repo-sourced metadata (preferred) | | 3. Trace JAR | `stages/03-trace-jar.md` | no main/service, or repo covered all | traced configs (service exercised) | | 4. Trace tests | `stages/04-trace-tests.md` | no tests, or tests pass | traced configs (version-aware BTestMain) | | 5. Filter & pack | `stages/05-filter-and-pack.md` | nothing to pack | `META-INF/native-image///` | | 6. Mark compatible | `stages/06-mark-compatible.md` | never | `graalvmCompatible = true` + final verify | --- ## Entry Point Instructions When this skill is invoked: 1. Print the welcome banner: ``` ╔════════════════════════════════════════════════════╗ ║ Ballerina Library — GraalVM Compatibility Helper ║ ╚════════════════════════════════════════════════════╝ I'll take your Ballerina library to a verified `bal build --graalvm` and `bal test --graalvm`: build/test → resolve errors → source metadata → pack it → mark the package compatible. ``` 2. Read and follow `stages/00-setup.md` to establish all Shared State. Do this before loading any other stage file. 3. Run `stages/01-build-and-test.md`, then route to the remaining stages per the classification in `references/workflow.md`. Skip stages per the table above. 4. When any native build fails at build time with a class-initialization error, read `references/class-init-fix-procedure.md` and invoke it inline before proceeding. 5. If `INTERACTIVE_MODE`, pause and confirm after each stage. --- ## Shared State Set in Setup (stage 00) and used by later stages: | Variable | Description | | ------------------------------------------------------------- | -------------------------------------------------------------------------------- | | `PYTHON_CMD` | Resolved Python 3 command (`python3`/`python`/`py`) | | `BALLERINA_DIR` | Directory containing `Ballerina.toml` | | `BALLERINA_TOML` | Absolute path to that `Ballerina.toml` | | `BAL_ORG` / `BAL_PACKAGE` | Package org / name | | `BAL_DISTRIBUTION` | e.g. `2201.10.3` | | `BAL_UPDATE` | Update number (drives the `BTestMain` signature) | | `REQUIRED_GRAALVM_JDK` | `11` / `17` / `21` | | `PLATFORM_JAVA_VERSION` | `java11` / `java17` / `java21` — the Ballerina.toml platform block | | `GRAALVM_HOME` / `GRAALVM_JDK_ACTUAL` / `GRAALVM_OK` | GraalVM install + detected JDK + match | | `IS_ARM64_MAC` | Apple Silicon flag (experimental native-image warning) | | `GROUP_ID` / `ARTIFACT_ID` | Native-image metadata coordinates | | `HAS_NATIVE_MODULE` / `NATIVE_DIR` / `META_INF_DIR` | Native module presence + paths | | `HAS_MAIN` / `HAS_SERVICE` / `HAS_TESTS` / `JAR_NAME` | Routing flags for tracing | | `THIRD_PARTY_DEPS` | `[[platform.javaXX.dependency]]` entries `{groupId,artifactId,version,path}` | | `REACHABILITY_REPO_HITS` | Deps with published metadata + their staging dirs | | `CONFIG_DIR` | Tracing-agent output directory (default `config-dir`) | | `CLASSPATH_FILE` | `class-path.txt` produced in Stage 04 | | `KEEP_PACKAGE_PREFIXES` | Prefixes kept during filtering (Stage 05) | | `BUILD_STATUS` / `TEST_STATUS` / `NOT_VERIFIED_WARNING` | Baseline results (Stage 01) | | `GRAALVM_COMPATIBLE_ALREADY` | Whether the toml already declares it | | `INTERACTIVE_MODE` | Pause after each stage | --- ## Core Principles **Repo before tracing**: prefer `oracle/graalvm-reachability-metadata` (vetted, deterministic) over the tracing agent; trace only for the gaps it doesn't cover. **Version sensitivity is load-bearing**: the `BTestMain` argument signature changes at Ballerina Update 10. `scripts/build_btest_command.py` is the single source of truth — show its resolved branch and confirm before running. **Deterministic first**: use scripts for anything mechanical. Use the LLM only for judgment — class-init strategy, exercising services, choosing configs, upgrades. **Never claim false compatibility**: do not set `graalvmCompatible = true` unless the final native build and tests pass with no not-verified warning. **Transparency**: print a status line before each sub-step. Use `✓`/`⚠`/`✗`. --- ## Reference Files - `references/workflow.md` — decision tree, stage routing, guardrails, run summary - `references/class-init-fix-procedure.md` — reusable build-time class-init fix loop - `references/tracing-agent.md` — both tracing paths (JAR + version-sensitive tests) - `references/reachability-metadata-repo.md` — using the oracle/graalvm-reachability-metadata repo - `references/reachability-metadata.md` — native-image metadata JSON schema (adapted from Oracle) - `references/troubleshooting.md` — build/runtime failure routing (adapted from Oracle) - `references/native-image-options.md` — raw flags for `--graalvm-build-options` (adapted from Oracle) - `references/pack-and-mark.md` — filtering, packing into META-INF, and marking compatible - `templates/` — Ballerina.toml platform block, metadata skeleton, native module layout --- ## Scripts Reference All scripts are in `/scripts/` and are pure Python (`.py`) — no shell scripts, so they run identically on macOS/Linux/Windows. Invoke with `` (resolved in Setup Step 0), not a hardcoded `python3`. ```bash # Environment + package discovery (Stage 00) scripts/check_environment.py scripts/find_ballerina_toml.py scripts/detect_package_coordinates.py "" scripts/detect_runnable_artifacts.py "" # GraalVM version derivation + verification (Stage 00) scripts/derive_graalvm_requirements.py [--distribution 2201.10.3] scripts/check_graalvm_env.py --required-jdk 17 # Build/test + error classification (Stage 01) scripts/run_bal_command.py "" "" scripts/parse_graalvm_errors.py "" # Reachability-metadata repo (Stage 02) scripts/lookup_reachability_metadata.py --deps-json "" scripts/fetch_reachability_metadata.py --group-id --artifact-id --metadata-version --out # Tracing agent (Stages 03/04) scripts/build_jar_trace_command.py --jar "" --config-output-dir "" scripts/extract_test_classpath.py --out class-path.txt scripts/build_btest_command.py --distribution "" --config-output-dir "" --classpath-file class-path.txt # Filter + pack + mark (Stages 05/06) scripts/filter_trace_configs.py --config-dir "" --out "" --keep-prefixes "" scripts/scaffold_native_module.py --native-dir "" --group-id "" --artifact-id "" scripts/pack_native_configs.py --filtered-dir "" --native-dir "" --group-id "" --artifact-id "" --merge scripts/build_native_config_jar.py --resources-dir "/src/main/resources" --out "" scripts/update_ballerina_toml_graalvm.py --toml "" --java-version java21 --graalvm-compatible true ```