diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index 0b43ff3e..3956921f 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -151,26 +151,15 @@ jobs: python-multiplatform-compose/fixtures/notebook-e2e/build/reports/tests/desktopTest python-multiplatform-compose/fixtures/notebook-e2e/build/test-results/desktopTest - # The notebook E2E in a GraalVM native image on macOS (#249, #251). It takes `ui`, `ui-text` and - # `foundation` from compose-multiplatform-core-extended, which a runner reads from the fork's GitHub - # Packages registry; until the repository variable COMPOSE_EXTENDED_VERSION names a published - # development version (`1.11.1-ext.N-dev.`) the job is skipped. + # The notebook E2E in a GraalVM native image on macOS (#249, #251). It takes the fork's Compose + # (`org.thisisthepy.compose.*:1.11.1-ext.2`, `composeExtended.*` in `gradle.properties`) from + # Maven Central, builds the 20-scenario executable with Liberica NIK 25, and runs it with + # JAVA_HOME and PYTHONHOME removed. notebook-e2e-native-image: name: Notebook E2E in a native image (macOS) - if: ${{ vars.COMPOSE_EXTENDED_VERSION != '' }} runs-on: macos-latest - permissions: - contents: read - packages: read env: WHEEL_DIR: ${{ github.workspace }}/.tmp/pythonx-compose-dist - COMPOSE_EXTENDED_VERSION: ${{ vars.COMPOSE_EXTENDED_VERSION }} - COMPOSE_EXTENDED_GROUP_PREFIX: ${{ vars.COMPOSE_EXTENDED_GROUP_PREFIX || 'org.thisisthepy.compose' }} - COMPOSE_EXTENDED_REPOSITORY: ${{ vars.COMPOSE_EXTENDED_REPOSITORY || 'https://maven.pkg.github.com/thisisthepy/compose-multiplatform-core-extended' }} - # GitHub Packages asks for a token even for a public package. A read:packages PAT in the secret - # GH_PACKAGES_TOKEN is used when it exists; otherwise this repository's own token. - GITHUB_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN || secrets.GITHUB_TOKEN }} - TOKEN_SOURCE: ${{ secrets.GH_PACKAGES_TOKEN != '' && 'GH_PACKAGES_TOKEN' || 'GITHUB_TOKEN' }} steps: - name: Checkout uses: actions/checkout@v4 @@ -183,19 +172,6 @@ jobs: path: .tmp/pythonx-compose fetch-depth: 1 - # Before any build: can this token read the fork's packages? One known POM, so a refusal is - # reported as what it is rather than as a dependency resolution error minutes later. - - name: Check access to the fork's packages - run: | - group_path="${COMPOSE_EXTENDED_GROUP_PREFIX//.//}/ui" - pom="$COMPOSE_EXTENDED_REPOSITORY/$group_path/ui-desktop/$COMPOSE_EXTENDED_VERSION/ui-desktop-$COMPOSE_EXTENDED_VERSION.pom" - status=$(curl -s -o /dev/null -w '%{http_code}' -u "${GITHUB_ACTOR}:${GITHUB_TOKEN}" "$pom") - echo "reading $pom with $TOKEN_SOURCE: HTTP $status" - if [ "$status" != "200" ]; then - echo "::error::The fork's packages could not be read with $TOKEN_SOURCE (HTTP $status at $pom). Add a personal access token (classic) with read:packages as the repository secret GH_PACKAGES_TOKEN, or check COMPOSE_EXTENDED_VERSION / COMPOSE_EXTENDED_REPOSITORY." - exit 1 - fi - - name: Setup uv uses: astral-sh/setup-uv@v6 @@ -225,8 +201,4 @@ jobs: ./gradlew :python-multiplatform-compose:fixtures:notebook-e2e:notebookE2eNativeAgent :python-multiplatform-compose:fixtures:notebook-e2e:notebookE2eNativeRun -PpythonxComposeWheel="$WHEEL_DIR" - -PcomposeExtended.version="$COMPOSE_EXTENDED_VERSION" - -PcomposeExtended.groupPrefix="$COMPOSE_EXTENDED_GROUP_PREFIX" - -PcomposeExtended.mavenLocal=false - -PcomposeExtended.repository="$COMPOSE_EXTENDED_REPOSITORY" --console=plain diff --git a/docs/SPEC.md b/docs/SPEC.md index 1e0697a6..4d982f37 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -628,7 +628,7 @@ All in `PM/commonTest`, so they run wherever the interpreter loads. main-thread work to skiko's Swing queue, which starts AWT, and AWT in an image needs the JDK's `libawt`, which links `libjvm`; the fork sends it to `Dispatchers.Main` when `compose.main.dispatcher=coroutines`, and the fixture supplies a single-thread one with no AWT. - `desktopTest` keeps upstream Compose. Not on CI yet: the fork's artefacts are in mavenLocal only. + CI runs it on every push to `develop` (`desktop.yml`, job `notebook-e2e-native-image`, #251). - **B-11** (#192) The artifact walker skips every declaration whose package has an `internal` segment (`*.internal` and `*.internal.*`, as in `androidx.compose.ui.internal`), on jars and on klibs. That is the Kotlin `.api` dump convention: a library that puts a declaration in such a package has declared it diff --git a/docs/investigations/fork-desktop-paths-macos.md b/docs/investigations/fork-desktop-paths-macos.md index bbdc507c..df39c199 100644 --- a/docs/investigations/fork-desktop-paths-macos.md +++ b/docs/investigations/fork-desktop-paths-macos.md @@ -2,128 +2,90 @@ What python-multiplatform needs before a pythonx-compose app runs on the two desktop paths `compose-multiplatform-core-extended` and `compose-multiplatform-extended` add: a GraalVM native -image and a Kotlin/Native executable, macOS arm64 first. Read on 2026-10-17 from the fork's -`origin/extended` branches (core `61906995435`, plugin fork `fca1473a`), `~/.m2/repository`, and -this repository's `develop` (`af4debac`). Nothing here was built; every fact cites where it was read. - -## 1. What the fork has on macOS +image and a Kotlin/Native executable, macOS arm64 first. Originally read on 2026-10-17 and refreshed +on 2026-11-01 after the fork published to Maven Central (#257, #287), the notebook E2E native-image +milestone landed (#246, #249, #251), and the `#191` decisions were recorded (2026-10-25). + +## 1. What the fork publishes on Maven Central + +The fork publishes its Compose libraries, window modules and Gradle plugins to Maven Central under +`org.thisisthepy.compose`: + +- **Libraries (`1.11.1-ext.2`)**: `org.thisisthepy.compose.runtime:*`, `ui:*`, `foundation:*`, + `material:*`, `material3:*`, `animation:*`, `components:*`, `org.thisisthepy.compose.androidx.lifecycle:*`, + including `macosarm64` and `linuxx64` klibs alongside `desktop` (JVM), `android`, `ios*` and + `wasmjs`. (`material-icons-core` stays on JetBrains `1.7.3`.) +- **Window modules (`1.11.1-ext.2`)**: `org.thisisthepy.compose.window:common`, `scene`, + `graalvm-macos`, `graalvm-linux`, `native-macos`, `native-linux`. +- **Gradle plugins (`1.11.1-ext.3`)**: `org.thisisthepy.compose` and + `org.thisisthepy.kotlin.native.desktop` (`compose-gradle-plugin:1.11.1-ext.3` on Maven Central). +- **Path selection in the fork's plugin**: `compose.desktop.application { output.set(ApplicationOutput.NativeImage) }` + (with `windowing.set(ApplicationWindowing.Awt)` or `AwtFree`, and `nativeImage { ... }` registering + `packageNativeImage`, `runNativeImageAgent`, `checkNativeImageNoAwt` and `packageApplication`), or + `compose.desktop.nativeApplication { ... }` for Kotlin/Native (`createDistributableNative*`, + `runNative*`, `packageKotlinNative`). Per decision 5 on #191, consumers select the path through the + fork plugin's setting directly; `python-multiplatform` adds no duplicate path-selection DSL. | | GraalVM native image | Kotlin/Native | |---|---|---| -| Fork's claim | `extended/README.md` (core, line 12): "macOS works on both: the Kotlin/Native window (AppKit and Metal) and the GraalVM native-image window." | same line | -| Window layer | `extended/window/graalvm/graalvm-macos` (JVM lib, AppKit through `@CFunction`, `upcalls.txt` table, no reflection) | `extended/window/native/macos` (`macosArm64`: AppKit, Metal, `MetalSurface.kt`) | -| Compose host | **none for this path yet**: `extended/window/scene` (JVM and `macosArm64`) hosts a Compose scene through `runComposeWindow(platform, config, surfaceFor, content)` (`scene/src/.../ComposeWindow.kt:285`), but `graalvm-macos` provides no `WindowSurface` to draw into | `scene` with `native/macos`'s `MetalSurface` | -| Measured with Compose content | `extended/native-image/hello` (plugin fork): Material 3 in an **AWT** window (`windowing = Awt`), 82 MB, "draws the same content through Skia offscreen into a PNG byte for byte the one the JVM run draws" (`extended/native-image/README.md`, 2026-10-02) | no Gradle demo in the fork; compose-rust's Amper renderer (`darkpyonix/compose-rust/renderer/macos`) consumes `native-macos` | -| AWT-free demo | `extended/native-image/hello-awtfree-link`: opens, pumps and presents an AppKit window; **draws no Compose** | none | -| Build | the plugin fork's `org.jetbrains.compose` `1.11.1-extended-dev`: `compose.desktop.application { nativeImage { graalvmHome; skikoStaticDirectory } }`, `packageNativeImage`, `runNativeImageAgent`; skiko as a static archive (`extended/skiko/build-skiko-static-jvm.sh`) | `extended/gradle-plugin-kn`: `createDistributableNative...`, `runNative...`, `packageKotlinNative` | - -**Published to mavenLocal** (JetBrains coordinates kept, as #191 says): +| Window layer | `org.thisisthepy.compose.window:graalvm-macos`, `graalvm-linux` (JVM lib, `@CFunction` + `upcalls.txt` table, no reflection) | `org.thisisthepy.compose.window:native-macos` (`macosArm64`: AppKit, Metal, `MetalSurface.kt`), `native-linux` (`linuxX64`) | +| Compose host | `org.thisisthepy.compose.window:scene` (`runComposeWindow`) | `scene` with `native-macos` / `native-linux` | +| Build | `org.thisisthepy.compose` `1.11.1-ext.3`: `compose.desktop.application { output.set(ApplicationOutput.NativeImage); nativeImage { ... } }`, `packageNativeImage`, `runNativeImageAgent` | `org.thisisthepy.kotlin.native.desktop` `1.11.1-ext.3`: `compose.desktop.nativeApplication { ... }`, `createDistributableNative*`, `runNative*` | -- `org.jetbrains.compose:ui*`, `foundation*`, `ui-text*` at `1.11.1001` (2026-10-17), including the - `macosarm64` klibs of `ui` and `foundation`. No `material3` macOS klib. -- `org.jetbrains.compose:compose-gradle-plugin:1.11.1-extended-dev`. -- `org.thisisthepy.compose.window:*:0.1.0`: `common`, `graalvm-macos`, `graalvm-linux`, `native-macos`. Not - `scene`, and not `native-linux`. -- `org.jetbrains.skiko:skiko-awt:0.144.6.1`. -- No publish script producing `1.11.1001` was found in either fork repository; the "Extended window - publish" workflow and compose-rust's `scripts/publish-window.sh` publish the window modules. - -## 2. What this repository has +## 2. What this repository has today +- **Switched to the fork's Maven Central artefacts (#257, #287).** `gradle/libs.versions.toml` uses + `org.thisisthepy.compose` `1.11.1-ext.3` (plugins) and `1.11.1-ext.2` (libraries). The artifact + walker (`ArtifactScanner.kt`) resolves `org.thisisthepy.compose.*` coordinates alongside + `org.jetbrains.compose.*`. - **Targets.** `python-multiplatform` declares `jvm("desktop")`, Android, iOS, androidNative and wasm; - no `macosArm64`, `linuxX64` or `mingwX64` (`python-multiplatform/build.gradle.kts`; the plugin's - `hostDesktopPlatform` knows macOS only as a JVM host). The sample has the same set. -- **A native image already runs Python** (`docs/platforms/graal-native-image-verification.md`, - 2026-08-17, `4687070a`): `:sample:nativeCompile` (an `Exec` of Liberica NIK 25.0.4's `native-image`, - `--no-fallback --enable-native-access=ALL-UNNAMED`) builds a headless `NativeImageMainKt`; 28 - downcalls and 5 upcalls registered, `UPCALL_OK`/`PROXY_OK`, no `libjvm`, `libpython` `dlopen`ed. - It is **headless on purpose**: the sample's `application {}` needs AWT. Not automated, not re-run. + `macosArm64` and `linuxX64` are the next interpreter targets to add (Phase A, decision 2; + `mingwX64` in Phase C). +- **A native image runs Python and stages `PYTHONHOME` beside the executable (#246, SPEC L-9).** + `pythonBindings.nativeImageDirectory` and `stageNativeImagePythonHome` copy the pruned + `python-build-standalone` prefix (`lib/python/**` and `lib/libpython.*`) into + `/python-multiplatform-home`. At run time, inside a GraalVM native image + (`org.graalvm.nativeimage.imagecode == "runtime"`), `PackagedPythonHome.resolve()` looks up + `/python-multiplatform-home` via `ProcessHandle.current().info().command()` + and hands it to CPython with `Py_SetPythonHome`. `:sample:nativeCompile` and `:sample:runNativeUpcallDemo` + stage and verify this layout with `JAVA_HOME` and `PYTHONHOME` unset (`docs/platforms/graal-native-image-verification.md`). - **The upcall table on a native image.** Desktop upcalls are FFM (`Panama.kt`), registered by the generated `META-INF/native-image/org.thisisthepy/python-multiplatform/reachability-metadata.json` - (`generateDesktopReachabilityMetadata`) plus `native-image/reachability-metadata.base.json`. That - metadata ships in the jar, so any native-image build that has the jar on its classpath picks it up. - (The verification note says no reflection registration is needed, while its own log shows 1,822 - types registered and the base file registers `UpcallTarget`; the note should be corrected.) -- **PYTHONHOME.** `stagePythonHome` sets `PYTHONHOME` for `JavaExec`/`Test` tasks and copies the prefix - into Compose's `prepare*AppResources`; the native demo is given `PYTHONHOME` by its run task. A - native image has no `java.home` and no packaged-resources directory, so nothing stages a prefix - **beside the executable** or finds it from there at start. -- **The notebook E2E** (`python-multiplatform-compose/fixtures/notebook-e2e`) needs no window: it - draws `PythonAppView` into an offscreen `ImageComposeScene` (`NotebookHost.kt:132`) and compares - pixels. It is a JVM `desktopTest` suite with `compose.desktop.currentOs` (Skia natives) and - `java.awt.headless=true`; no `java.awt`/`javax.swing` import. - -## 3. What each path needs from this side - -### GraalVM native image - -1. **A desktop app built by the plugin fork's `packageNativeImage`** rather than the sample's hand-made - `Exec`: the fork already solves Compose's own native-image configuration and static skiko. The - generated upcall table and FFM registrations come along in the jar's `META-INF/native-image`, so - **the upcall table carries over unchanged** (build-time table, no reflection: AGENTS.md §12.3, §13). - To check: that the fork's plugin passes `--enable-native-access=ALL-UNNAMED` (or lets an app add it) - and does not set `--no-fallback`-incompatible options that hide a missing FFM registration. -2. **PYTHONHOME beside the executable.** A task that stages the prefix and the installed wheels next to - `build/compose/native-image/main/`, and a runtime lookup (`ProcessHandle`'s executable path, a - sibling `python/` directory) when `PYTHONHOME` is unset, like `IosPythonHome` does for an app bundle. - **Staging does not carry over as is**: today's paths are a Gradle task's environment and the JVM - packaging's resources directory. -3. **The walker over the fork's jars.** Same public API, so the walk must produce the same bindings; - guarded by the stub diff of pythonx-compose #54. -4. **Selecting the path.** A Gradle plugin option (upstream Compose by default, the fork's native image - as a choice), #191's first bullet. - -### Kotlin/Native - -1. **An interpreter target**: `macosArm64` in `python-multiplatform` (cinterop against the macOS - python-build-standalone prefix the desktop JVM already downloads, a static or shipped - `libpython`), with the native `HandleTable`/trampoline path iOS and androidNative already use. - This is new work, not a switch. -2. **The klib walker** (B-2) over the fork's `macosarm64` klibs: since #234/#237 it carries handles and - interface members, but not constructors, properties or composables, and Compose's surface is mostly - composables and extensions; the K/N path needs the walker to reach about where the jar walker is. -3. **PYTHONHOME** beside the executable, as for the native image. -4. `material3` for `macosArm64`: no fork klib is published (the fork says only `ui` and `foundation` - differ from JetBrains; JetBrains' own `material3` macOS klib would have to resolve beside them). - -## 4. First milestone: the notebook E2E in a macOS GraalVM native image - -The E2E draws offscreen, so the milestone needs **no window** and none of the AWT-free pieces: - -1. A `notebook-e2e` native-image entry point (a `main` running the existing scenarios and printing a - pass/fail line per scenario, the way the fork's `hello` self-checks through `HELLO_SELF_CHECK`), - because a JUnit `desktopTest` does not run inside an image. -2. Built with the plugin fork's `packageNativeImage` (`windowing = Awt`, which is what the fork measured; - the scenes are offscreen either way), `--enable-native-access=ALL-UNNAMED`, the fork's static skiko. -3. PYTHONHOME and the pythonx-compose wheel staged beside the executable (§3, GraalVM 2). -4. Pass: the same 20 scenarios as the JVM run (pixel comparisons included), the binary run alone from - an empty directory with `JAVA_HOME` unset, and `otool -L` showing no `libjvm`. Then a CI job on a - macOS runner, as `notebook-e2e` has on the JVM (#213). - -Milestone 2 is the same app in the AWT-free window, once the fork has the pieces in §5. Kotlin/Native -follows the interpreter target (§3, K/N 1). - -## 5. Requests for the fork (through the darkpyonix lead) - -1. **Publish `org.thisisthepy.compose.window:scene`** (its `module.yaml` already enables publishing; - it is missing from mavenLocal and from the "Extended window publish" workflow's output). -2. **A `WindowSurface` for `graalvm-macos`** (Metal or GL into a Skia surface, as `native/macos`'s - `MetalSurface`), so `runComposeWindow` can host Compose content on the GraalVM path; and a demo that - draws Compose through it (`hello-awtfree-link` draws nothing today). -3. **A documented, reproducible publish of `1.11.1001`** (no script producing that version was found) - and one version line for the Compose libraries, the window modules and the Gradle plugin, so a - consumer pins one set. -4. **The native-image flags an app can add**: confirm that `packageNativeImage` lets an app pass - `--enable-native-access=ALL-UNNAMED` and merges a library's `META-INF/native-image` metadata (FFM - `foreign` registrations included). -5. **A hook to stage extra files beside the image** (a prefix directory), or confirmation that a - consumer task after `packageNativeImage` is the supported way. -6. For Kotlin/Native: whether `material3` for `macosArm64` is expected to come from JetBrains, and a - Gradle (not Amper-only) example of `scene` plus `native-macos` hosting Compose content. - -## 6. Open questions - -- Whether CPython's own native-image behaviour holds with the fork's Compose in the same image (one - process, Skia and libpython both `dlopen`ed or linked); not tried. -- Where the native-image E2E runs in CI: GitHub's macOS runners have no GraalVM by default, and the - fork's static skiko must be built or cached. + (`generateDesktopReachabilityMetadata`) plus `native-image/reachability-metadata.base.json` (which + registers `python.native.ffi.UpcallTarget`, corrected in #244). That metadata ships in the jar, so + any native-image build that has the jar on its classpath picks it up. +- **The notebook E2E runs in a macOS GraalVM native image (#249, #251, SPEC B-10).** + `python-multiplatform-compose/fixtures/notebook-e2e` builds `notebook-e2e` with `notebookE2eNativeCompile` + and runs all 20 `UI.ipynb` scenarios via `notebookE2eNativeRun` with `JAVA_HOME` and `PYTHONHOME` + removed, `compose.main.dispatcher=coroutines` (`NotebookE2eMainDispatcherFactory`, no AWT), and an + `otool -L` check forbidding `libjvm`. `.github/workflows/desktop.yml` (`notebook-e2e-native-image`) + runs this job on macOS against Maven Central. + +## 3. Remaining work by phase (per #191 decisions, 2026-10-25) + +### Phase A (in progress) + +1. **#251 (`desktop.yml`)**: `notebook-e2e-native-image` switched to Maven Central with the + `COMPOSE_EXTENDED_VERSION` guard and GitHub Packages check removed. +2. **`:sample` as a native-image output**: `:sample` sets `pythonBindings.nativeImageDirectory` so + `stageNativeImagePythonHome` stages `python-multiplatform-home/` beside `upcall-native-demo`, and + `runNativeUpcallDemo` runs the executable with `JAVA_HOME` and `PYTHONHOME` removed. +3. **Kotlin/Native desktop interpreter targets (`macosArm64`, `linuxX64`)**: add `macosArm64()` and + `linuxX64()` to `:python-multiplatform`, cinterop against the `python-build-standalone` headers + already downloaded by `downloadPython_macos_aarch64` and `downloadPython_linux_x86_64`, link and + load `libpython` dynamically beside the executable (decision 3), and wire `PackagedPythonHome` / + `PythonPayload` for desktop K/N executables. + +### Phase B + +1. **AWT-free GraalVM notebook app**: run the notebook app in an AWT-free window (`windowing = AwtFree`). +2. **Kotlin/Native macOS app**: re-check `KlibScanner` (B-2) coverage for `@Composable` functions, + constructors and properties over the fork's `macosarm64` klibs, add `macosArm64` to + `:python-multiplatform-compose`, and run a K/N macOS app. + +### Phase C + +1. **Windows (`mingwX64`)** once the fork publishes Windows artefacts. +2. **Stub diff guard (`pythonx-compose #54`)** and the final acceptance criterion: the same + `pythonx-compose` app running unchanged on upstream Compose, extended GraalVM and extended K/N + desktop builds with the notebook E2E passing in CI. diff --git a/python-multiplatform-compose/fixtures/notebook-e2e/README.md b/python-multiplatform-compose/fixtures/notebook-e2e/README.md index af55ae43..ef0399bf 100644 --- a/python-multiplatform-compose/fixtures/notebook-e2e/README.md +++ b/python-multiplatform-compose/fixtures/notebook-e2e/README.md @@ -68,11 +68,12 @@ JAVA_HOME= ./gradlew :python-multiplatform-compose:fixtures:notebook-e2e `site-packages/` (the wheel) and `app/`. The image finds them there with no environment. - `notebookE2eNativeRun` removes `JAVA_HOME` and `PYTHONHOME`, requires 20 passed, and fails if `otool -L` shows `libjvm`. -- The image takes `ui`, `ui-text` and `foundation` from compose-multiplatform-core-extended's mavenLocal - build (`composeExtended.*` in the root `gradle.properties`): upstream Compose sends its main-thread +- The image takes `ui`, `ui-text` and `foundation` from compose-multiplatform-core-extended on Maven + Central (`composeExtended.*` in the root `gradle.properties`): upstream Compose sends its main-thread work to skiko's Swing queue, which needs AWT, and AWT needs `libjvm`. The fork sends it to `Dispatchers.Main` under `compose.main.dispatcher=coroutines`, which `NotebookE2eMainDispatcherFactory` supplies (registered by `src/desktopTest/native-image-main/` on the image's classpath only). + CI runs `notebookE2eNativeAgent` and `notebookE2eNativeRun` in `.github/workflows/desktop.yml` (`notebook-e2e-native-image`, #251). ## Scenarios diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 2e8a35dc..2c9f5e32 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -411,10 +411,14 @@ val nativeImageJavaLauncher = javaToolchains.launcherFor { val nativeImageOutputDir = layout.buildDirectory.dir("native/nativeCompile") +pythonBindings { + nativeImageDirectory.set(nativeImageOutputDir) +} + val nativeCompile by tasks.registering(Exec::class) { group = "native" description = "Builds upcall-native-demo with GraalVM native-image from NativeImageMain.kt" - dependsOn(desktopMainCompilation.compileTaskProvider, pythonMultiplatformDesktopJar) + dependsOn(desktopMainCompilation.compileTaskProvider, pythonMultiplatformDesktopJar, "stageNativeImagePythonHome") val classpathProvider = files( desktopMainCompilation.output.allOutputs, @@ -425,7 +429,7 @@ val nativeCompile by tasks.registering(Exec::class) { val launcher = nativeImageJavaLauncher inputs.files(classpathProvider) - outputs.dir(outDir) + outputs.file(outDir.map { it.file("upcall-native-demo") }) doFirst { outDir.get().asFile.mkdirs() @@ -445,8 +449,10 @@ tasks.register("runNativeUpcallDemo") { group = "verification" description = "Runs the native-image binary built by nativeCompile and checks the upcall path" dependsOn(nativeCompile) + workingDir(nativeImageOutputDir) executable = nativeImageOutputDir.get().asFile.resolve("upcall-native-demo").absolutePath - environment("PYTHONHOME", pythonHomeForHost.get().asFile.absolutePath) + environment.remove("JAVA_HOME") + environment.remove("PYTHONHOME") // A zero exit status is not the same as a working upcall path. `PyRun_SimpleString` reports // failure for an exception raised inside the script, but a Python interpreter that never got