From a456a23822e153980373e0c18a70d93031baeb58 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sun, 18 Oct 2026 04:13:53 +0900 Subject: [PATCH 01/12] Test: Probe the GraalVM X11 window's stale presents with the resize detail log Pins the fork at its instrumented branch (core-extended #139) and turns the detail log on in the Linux parity run. Not for merging as it stands: it proves the cause first. --- renderer/scripts/build-compose.sh | 2 +- scripts/parity/linux.sh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/renderer/scripts/build-compose.sh b/renderer/scripts/build-compose.sh index 235cfe915..3d6773a1e 100755 --- a/renderer/scripts/build-compose.sh +++ b/renderer/scripts/build-compose.sh @@ -30,7 +30,7 @@ REVISION="81ce95e51f36a17d0a7ba23847bdca8e62b080c8" # The window modules and skiko's static build are newer than that and are not Compose sources: # the renderer builds them from this commit (scripts/fetch-fork-window.sh and fetch-fork-skiko.sh) # into artifacts of its own, so they move without moving the Compose build above. -WINDOW_REVISION="1b3de18ff3c288d62b043ca75906413630cb981d" +WINDOW_REVISION="79d8d6c11c5a89d63f173512e2e5a9fbc9b01776" PUBLISHED_AS="1.11.1" # Material 3 is versioned on its own line and the renderer asks for it by that version, so # publishing it as the others would leave a coordinate nobody looks for. diff --git a/scripts/parity/linux.sh b/scripts/parity/linux.sh index 540365999..261431a60 100755 --- a/scripts/parity/linux.sh +++ b/scripts/parity/linux.sh @@ -69,6 +69,8 @@ export DXC_SYNTH_EXIT_AT="${DXC_SYNTH_EXIT_AT:-20}" export DXC_REPORT_LATENCY=1 export DXC_REPORT_INPUT=1 export DXC_REPORT_RESIZE=1 +# Every requested, confirmed and presented size, so a stale frame can be placed among them. +export DXC_REPORT_RESIZE_DETAIL=1 export GTK_THEME="${GTK_THEME:-Adwaita:dark}" "$@" >"$log" 2>"$err" & From e2b805fce6067018d666c32869723678773f4f5d Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sun, 18 Oct 2026 04:34:53 +0900 Subject: [PATCH 02/12] Test: Pin the probe at the instrumented commit on top of the #251 pin The instrumented branch of the fork sits on the newest extended, which also moves the published coordinates; the probe measures the code #251 ships, so the instrumentation is cherry-picked onto 1b3de18f. --- renderer/scripts/build-compose.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderer/scripts/build-compose.sh b/renderer/scripts/build-compose.sh index 3d6773a1e..6fe9f8cc4 100755 --- a/renderer/scripts/build-compose.sh +++ b/renderer/scripts/build-compose.sh @@ -30,7 +30,7 @@ REVISION="81ce95e51f36a17d0a7ba23847bdca8e62b080c8" # The window modules and skiko's static build are newer than that and are not Compose sources: # the renderer builds them from this commit (scripts/fetch-fork-window.sh and fetch-fork-skiko.sh) # into artifacts of its own, so they move without moving the Compose build above. -WINDOW_REVISION="79d8d6c11c5a89d63f173512e2e5a9fbc9b01776" +WINDOW_REVISION="42070b1451d829a89dd585c6e37920b6338b82d2" PUBLISHED_AS="1.11.1" # Material 3 is versioned on its own line and the renderer asks for it by that version, so # publishing it as the others would leave a coordinate nobody looks for. From 2687234affa6f074842e9ee0cfc29531f7435b18 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sun, 18 Oct 2026 05:59:25 +0900 Subject: [PATCH 03/12] Test: Gate the Linux parity runs on the resize rows, against the drag fix Pins the probe at the fork fix for scripted drags (core-extended #140, cherry-picked onto the #251 pin) and makes resize_stale_ratio and resize_stretched a gate (PARITY_GATE) on both Linux paths: a frame at a size other than the window's, or a size without a frame, fails the run. --- .github/workflows/static-renderer.yml | 7 +++++-- .github/workflows/test-graalvm-renderer.yml | 7 +++++-- renderer/scripts/build-compose.sh | 2 +- scripts/parity/common.sh | 17 +++++++++++++++++ scripts/tests/parity-linux.test.sh | 5 +++++ 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/workflows/static-renderer.yml b/.github/workflows/static-renderer.yml index e54849b7a..4e4b96855 100644 --- a/.github/workflows/static-renderer.yml +++ b/.github/workflows/static-renderer.yml @@ -217,10 +217,13 @@ jobs: timeout-minutes: 12 # The checklist test-graalvm-renderer.yml runs on the GraalVM X11 window, with the same # application: the consumer fixture the input check above built. The table is in the - # job summary; a row that fails is a warning, not a stop. + # job summary; a row that fails is a warning, except the + # resize rows, which are a gate: no frame at a size other than the window's, no size + # without a frame. run: | sudo apt-get install -y xclip - xvfb-run -a -s '-screen 0 1280x1024x24' ./scripts/parity/linux.sh native "$RUNNER_TEMP/parity" -- \ + PARITY_GATE="resize_stale_ratio resize_stretched" \ + xvfb-run -a -s '-screen 0 1280x1024x24' ./scripts/parity/linux.sh native "$RUNNER_TEMP/parity" -- \ "$PWD/target/linux-input-check-static/debug/consumer" --parity - name: Keep the Linux parity run's logs diff --git a/.github/workflows/test-graalvm-renderer.yml b/.github/workflows/test-graalvm-renderer.yml index 8daa7629b..e881d1c83 100644 --- a/.github/workflows/test-graalvm-renderer.yml +++ b/.github/workflows/test-graalvm-renderer.yml @@ -341,10 +341,13 @@ jobs: timeout-minutes: 12 # The checklist static-renderer.yml runs on the Kotlin/Native window, with the same # application: the consumer fixture the input check above built against this - # renderer. The table is in the job summary; a row that fails is a warning, not a stop. + # renderer. The table is in the job summary; a row that fails is a warning, except the + # resize rows, which are a gate: no frame at a size other than the window's, no size + # without a frame. run: | sudo apt-get install -y xclip - xvfb-run -a -s '-screen 0 1280x1024x24' ./scripts/parity/linux.sh graalvm "$RUNNER_TEMP/parity" -- \ + PARITY_GATE="resize_stale_ratio resize_stretched" \ + xvfb-run -a -s '-screen 0 1280x1024x24' ./scripts/parity/linux.sh graalvm "$RUNNER_TEMP/parity" -- \ "$PWD/target/linux-input-check-shared/debug/consumer" --parity - name: Keep the Linux parity run's logs diff --git a/renderer/scripts/build-compose.sh b/renderer/scripts/build-compose.sh index 6fe9f8cc4..366f8ddfb 100755 --- a/renderer/scripts/build-compose.sh +++ b/renderer/scripts/build-compose.sh @@ -30,7 +30,7 @@ REVISION="81ce95e51f36a17d0a7ba23847bdca8e62b080c8" # The window modules and skiko's static build are newer than that and are not Compose sources: # the renderer builds them from this commit (scripts/fetch-fork-window.sh and fetch-fork-skiko.sh) # into artifacts of its own, so they move without moving the Compose build above. -WINDOW_REVISION="42070b1451d829a89dd585c6e37920b6338b82d2" +WINDOW_REVISION="bbd1dcfe3683dcfdc0438f262172c298e2eb264c" PUBLISHED_AS="1.11.1" # Material 3 is versioned on its own line and the renderer asks for it by that version, so # publishing it as the others would leave a coordinate nobody looks for. diff --git a/scripts/parity/common.sh b/scripts/parity/common.sh index 6b01c52f8..2c9b24a7f 100755 --- a/scripts/parity/common.sh +++ b/scripts/parity/common.sh @@ -50,6 +50,23 @@ print_table() { echo } >> "$GITHUB_STEP_SUMMARY" fi + # PARITY_GATE names rows that fail the run whatever PARITY_STRICT says: a resize that + # presented a frame at a size other than the window's, or left a size without a frame of + # its own, breaks the resize rule (every frame drawn at the current size), so on the paths + # that meet it the two resize rows are a gate rather than a warning. + local gated="" + for name in ${PARITY_GATE:-}; do + local result + result="$(awk -F'\t' -v n="$name" '$1 == n { print $2 }' "$table")" + case "$name" in + resize_stretched) [[ "$result" == "0" ]] || gated="$gated $name=${result:-missing}" ;; + *) [[ -n "$result" && "$result" != FAIL* ]] || gated="$gated $name=${result:-missing}" ;; + esac + done + if [[ -n "$gated" ]]; then + echo "::error::parity $label fails its gate:$gated" + exit 1 + fi if grep -q $'\tFAIL' "$table"; then echo "::warning::parity $label has failing rows" [[ "${PARITY_STRICT:-}" == "1" ]] && exit 1 diff --git a/scripts/tests/parity-linux.test.sh b/scripts/tests/parity-linux.test.sh index 551d3648b..296f5e451 100755 --- a/scripts/tests/parity-linux.test.sh +++ b/scripts/tests/parity-linux.test.sh @@ -62,6 +62,11 @@ done # Both paths on both platforms run the scripts in CI. grep -q 'scripts/parity/linux.sh native' "$repo_root/.github/workflows/static-renderer.yml" || fail "the Kotlin/Native Linux window is not measured in CI" grep -q 'scripts/parity/macos.sh native' "$repo_root/.github/workflows/static-renderer.yml" || fail "the Kotlin/Native macOS window is not measured in CI" +for workflow in static-renderer.yml test-graalvm-renderer.yml; do + grep -q 'PARITY_GATE="resize_stale_ratio resize_stretched"' "$repo_root/.github/workflows/$workflow" \ + || fail "$workflow does not gate the Linux parity run on the resize rows" +done +grep -q 'PARITY_GATE' "$repo_root/scripts/parity/common.sh" || fail "the parity table does not honour PARITY_GATE" grep -q 'scripts/parity/linux.sh graalvm' "$repo_root/.github/workflows/test-graalvm-renderer.yml" || fail "the GraalVM X11 window is not measured in CI" grep -q 'scripts/parity/macos.sh graalvm' "$repo_root/.github/workflows/test-graalvm-renderer.yml" || fail "the GraalVM AppKit window is not measured in CI" From ff5786be682d6c6ad9f45f7411dd1653602c3ea1 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sun, 18 Oct 2026 11:34:44 +0900 Subject: [PATCH 04/12] Chore: Pin the fork window modules at 57b4a0e1 for the resize gate (PR-3) The newest extended, with core-extended #140 (a scripted drag asks for the next size only once the last is confirmed and drawn) and #141 (both X11 windows resized through openbox and its sync counter, stale 0 and stretched 0). The Linux parity runs gate on resize_stale_ratio and resize_stretched on both paths, and keep the resize detail log for the artifacts. From c48c876a1104b684196e0f8cfb28889d4538dd34 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sun, 18 Oct 2026 11:53:00 +0900 Subject: [PATCH 05/12] Fix: Publish the fork's scene module with the GraalVM window modules Since core-extended #138 both GraalVM window modules name ../../scene (compile-only, for the window surfaces runComposeWindow uses), and the project publish-window.sh writes listed neither, so the toolchain could not read the project ("Cannot find module file in //scene") and every renderer build on a pin past #138 stopped there. scene is listed now, and built for the JVM alone off macOS, since its other platform is macosArm64. --- renderer/scripts/publish-window.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/renderer/scripts/publish-window.sh b/renderer/scripts/publish-window.sh index 8073e2f11..6737652a6 100755 --- a/renderer/scripts/publish-window.sh +++ b/renderer/scripts/publish-window.sh @@ -18,7 +18,9 @@ repo="$(cd "$script_dir/../.." && pwd)" fork_dir="$("$repo/scripts/fetch-fork-window.sh")" window="$fork_dir/extended/window" -modules=(common graalvm/graalvm-macos graalvm/graalvm-linux) +# scene is listed because both GraalVM window modules name it (compile-only, for the window +# surfaces runComposeWindow uses), and a module another one names has to be in the project. +modules=(common scene graalvm/graalvm-macos graalvm/graalvm-linux) # native/macos compiles against the fork's Compose for macosArm64, which only a build that ran # build-compose.sh --target macosArm64 has; a desktop-only build has no use for it. case "$(uname -s)" in @@ -44,6 +46,11 @@ cd "$window" if [[ "$(uname -s)" == "Linux" && "$(uname -m)" != "x86_64" ]]; then sed -i 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/common/module.yaml" fi +# scene's other platform is macosArm64, which only a Mac builds. Elsewhere the GraalVM modules +# need its JVM half alone, so that is all that is built there. +if [[ "$(uname -s)" != "Darwin" && -f "$window/scene/module.yaml" ]]; then + sed -i 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/scene/module.yaml" +fi # The PATH kotlin.bat runs with on Windows: System32 first, and none of Git's own directories # but Git\cmd (git itself). Started from Git Bash, kotlin.bat would otherwise unpack its toolchain # with Git's GNU tar, which takes C:\... for a remote host, and the toolchain's launcher (busybox) From 79df89ca5486c108e8b3fd67c401189fd94cd2f5 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sun, 18 Oct 2026 12:02:29 +0900 Subject: [PATCH 06/12] Fix: Publish scene and the GraalVM window modules only beside the fork's desktop Compose scene compiles against the fork's Compose for the desktop, which a Kotlin/Native build (the static Linux renderer, the Linux renderer tests) does not publish, so resolving it failed there ("runtime-desktop:1.11.1-ext.1 ... problem"). Those builds need neither scene nor the GraalVM window modules; they are published where the desktop Compose is, which every build of the renderer's desktop module has. --- renderer/scripts/publish-window.sh | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/renderer/scripts/publish-window.sh b/renderer/scripts/publish-window.sh index 6737652a6..e0b355502 100755 --- a/renderer/scripts/publish-window.sh +++ b/renderer/scripts/publish-window.sh @@ -6,10 +6,11 @@ # The window code is the Compose fork's, at the commit build-compose.sh pins. The renderer # depends on it as org.thisisthepy.compose.window::0.1.0, so the modules are built # from that commit and published under ~/.m2 before the renderer is, the same way the patched -# Compose is. The Kotlin/JVM modules are published on every machine, so a Linux or Windows -# checkout resolves the coordinates the renderer's desktop module asks for. native/macos is -# published on a Mac and native/linux on Linux, because each is built from that system's -# headers. +# Compose is. common is published on every machine. The GraalVM window modules and the scene +# they name are published wherever the fork's desktop Compose is in the local repository, which +# is every build of the renderer's desktop module, so a Linux or Windows checkout resolves the +# coordinates that module asks for. native/macos is published on a Mac and native/linux on +# Linux, because each is built from that system's headers. set -euo pipefail script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -18,9 +19,14 @@ repo="$(cd "$script_dir/../.." && pwd)" fork_dir="$("$repo/scripts/fetch-fork-window.sh")" window="$fork_dir/extended/window" -# scene is listed because both GraalVM window modules name it (compile-only, for the window -# surfaces runComposeWindow uses), and a module another one names has to be in the project. -modules=(common scene graalvm/graalvm-macos graalvm/graalvm-linux) +modules=(common) +# The GraalVM window modules name scene (compile-only, for the window surfaces runComposeWindow +# uses), and scene compiles against the fork's Compose for the desktop, which only a build that +# published it has (build-compose.sh, or the compose-fork-desktop artifact in CI). A +# Kotlin/Native build needs none of the three, and would fail resolving scene's Compose. +if [[ -d "$HOME/.m2/repository/org/thisisthepy/compose/ui/ui-desktop" ]]; then + modules+=(scene graalvm/graalvm-macos graalvm/graalvm-linux) +fi # native/macos compiles against the fork's Compose for macosArm64, which only a build that ran # build-compose.sh --target macosArm64 has; a desktop-only build has no use for it. case "$(uname -s)" in @@ -48,7 +54,7 @@ if [[ "$(uname -s)" == "Linux" && "$(uname -m)" != "x86_64" ]]; then fi # scene's other platform is macosArm64, which only a Mac builds. Elsewhere the GraalVM modules # need its JVM half alone, so that is all that is built there. -if [[ "$(uname -s)" != "Darwin" && -f "$window/scene/module.yaml" ]]; then +if [[ " ${modules[*]} " == *" scene "* && "$(uname -s)" != "Darwin" ]]; then sed -i 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/scene/module.yaml" fi # The PATH kotlin.bat runs with on Windows: System32 first, and none of Git's own directories From f0c34ca49afa977fa7d9972d2561e7cf0c721adf Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sun, 18 Oct 2026 12:29:33 +0900 Subject: [PATCH 07/12] Fix: Build the fork's scene module for the JVM alone on every machine The GraalVM macOS renderer build has the fork's desktop Compose but not its Compose for macosArm64, and scene's macosArm64 half failed to resolve there. Nothing this renderer builds uses that half, so scene is published for the JVM alone everywhere, edited with a sed that both GNU and BSD take. --- renderer/scripts/publish-window.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/renderer/scripts/publish-window.sh b/renderer/scripts/publish-window.sh index e0b355502..9b54803d1 100755 --- a/renderer/scripts/publish-window.sh +++ b/renderer/scripts/publish-window.sh @@ -52,10 +52,14 @@ cd "$window" if [[ "$(uname -s)" == "Linux" && "$(uname -m)" != "x86_64" ]]; then sed -i 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/common/module.yaml" fi -# scene's other platform is macosArm64, which only a Mac builds. Elsewhere the GraalVM modules -# need its JVM half alone, so that is all that is built there. -if [[ " ${modules[*]} " == *" scene "* && "$(uname -s)" != "Darwin" ]]; then - sed -i 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/scene/module.yaml" +# The GraalVM modules need scene's JVM half alone, and nothing this renderer builds uses its +# other platform (macosArm64, which would also need the fork's Compose for macosArm64), so +# only the JVM half is built. +if [[ " ${modules[*]} " == *" scene "* ]]; then + # A suffix for -i and the copy removed after, because BSD sed on macOS takes the next + # argument to -i as one. + sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/scene/module.yaml" + rm -f "$window/scene/module.yaml.orig" fi # The PATH kotlin.bat runs with on Windows: System32 first, and none of Git's own directories # but Git\cmd (git itself). Started from Git Bash, kotlin.bat would otherwise unpack its toolchain From 809fc172e62824321a800efbc1547801971c9529 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sat, 24 Oct 2026 07:30:53 +0900 Subject: [PATCH 08/12] Fix: Prefetch GitHub toolchain assets and fix quotes in fork-dependencies - GitHub Releases recently deployed a Content-Security-Policy header exceeding 8192 characters, which triggers Ktor's TooLongLineException when Amper downloads Adoptium JDK or pnpm releases. Prefetching those archives directly with curl into Amper's download.cache avoids Ktor contacting GitHub Releases. - publish-window.sh compiles common for jvm alone when no native modules are requested, avoiding unnecessary native klib compilation. - Remove apostrophes inside the inline perl script in fork-dependencies.sh to prevent bash syntax errors. --- renderer/kotlin | 10 +++ renderer/scripts/fork-dependencies.sh | 4 +- renderer/scripts/prefetch-github-assets.sh | 71 ++++++++++++++++++++++ renderer/scripts/publish-window.sh | 12 ++-- 4 files changed, 90 insertions(+), 7 deletions(-) create mode 100755 renderer/scripts/prefetch-github-assets.sh diff --git a/renderer/kotlin b/renderer/kotlin index 8c404a970..3567e65d7 100755 --- a/renderer/kotlin +++ b/renderer/kotlin @@ -280,6 +280,16 @@ download_and_extract "Kotlin Toolchain distribution v$kotlin_cli_version" "$kotl # ********** Launch the Kotlin CLI ********** +# Prefetch toolchain downloads hosted on GitHub Releases, because GitHub's response +# headers contain a Content-Security-Policy line longer than Ktor's 8192-character limit, +# which crashes Amper's internal downloader. +_wrapper_dir="$(cd "$(dirname "$0")" && pwd)" +if [ -f "$_wrapper_dir/scripts/prefetch-github-assets.sh" ]; then + "$_wrapper_dir/scripts/prefetch-github-assets.sh" || true +elif [ -f "$_wrapper_dir/prefetch-github-assets.sh" ]; then + "$_wrapper_dir/prefetch-github-assets.sh" || true +fi + launcher_script="$kotlin_cli_target_dir/bin/launcher.sh" KOTLIN_CLI_WRAPPER_PATH="$(realpath "$0")" \ diff --git a/renderer/scripts/fork-dependencies.sh b/renderer/scripts/fork-dependencies.sh index f17511044..9ff41e861 100755 --- a/renderer/scripts/fork-dependencies.sh +++ b/renderer/scripts/fork-dependencies.sh @@ -46,9 +46,9 @@ use File::Find; use JSON::PP; my ($version, $argument, $repository) = @ENV{qw(VERSION ARGUMENT REPOSITORY)}; -# The fork's Compose names its JVM target desktop. jvm is left out: the desktop module's own +# The fork Compose names its JVM target desktop. jvm is left out: the desktop module own # publications are desktop-jvm--, and reading desktop-jvm as a jvm target would make -# every sibling's desktop-jvm publication look missing. +# every sibling desktop-jvm publication look missing. my @targets = qw(desktop android macosarm64 macosx64 linuxx64 linuxarm64 mingwx64 iosarm64 iosx64 iossimulatorarm64 wasmjs js); my $target_pattern = join "|", @targets; diff --git a/renderer/scripts/prefetch-github-assets.sh b/renderer/scripts/prefetch-github-assets.sh new file mode 100755 index 000000000..ab53e977b --- /dev/null +++ b/renderer/scripts/prefetch-github-assets.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# Prefetches toolchain downloads hosted on GitHub Releases directly into Amper's +# download.cache using curl. +# +# GitHub Releases redirects with a Content-Security-Policy header line exceeding +# 8192 characters, which triggers io.ktor.utils.io.charsets.TooLongLineException +# inside Amper's Ktor HTTP client. Placing the archives in download.cache before +# Amper runs lets Amper use the cached files without contacting GitHub Releases. +set -euo pipefail + +case "$(uname -s)" in + Darwin) + cache_dir="${1:-$HOME/Library/Caches/JetBrains/Kotlin/download.cache}" + items=( + "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.5%2B8/OpenJDK25U-jdk_aarch64_mac_hotspot_25.0.5_8.tar.gz|f8362503fc-OpenJDK25U-jdk_aarch64_mac_hotspot_25.0.5_8.tar.gz" + "https://github.com/pnpm/pnpm/releases/download/v11.9.0/pnpm-darwin-arm64.tar.gz|bfff255e35-pnpm-darwin-arm64.tar.gz" + "https://github.com/cpisciotta/xcbeautify/releases/download/3.2.1/xcbeautify-3.2.1-arm64-apple-macosx.zip|c132514727-xcbeautify-3.2.1-arm64-apple-macosx.zip" + ) + ;; + Linux) + cache_dir="${1:-$HOME/.cache/JetBrains/Kotlin/download.cache}" + case "$(uname -m)" in + x86_64) + items=( + "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.5%2B8/OpenJDK25U-jdk_x64_linux_hotspot_25.0.5_8.tar.gz|bd467c5b70-OpenJDK25U-jdk_x64_linux_hotspot_25.0.5_8.tar.gz" + "https://github.com/pnpm/pnpm/releases/download/v11.9.0/pnpm-linux-x64.tar.gz|d30df19002-pnpm-linux-x64.tar.gz" + ) + ;; + aarch64) + items=( + "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.5%2B8/OpenJDK25U-jdk_aarch64_linux_hotspot_25.0.5_8.tar.gz|e259c06f4c-OpenJDK25U-jdk_aarch64_linux_hotspot_25.0.5_8.tar.gz" + ) + ;; + *) + items=() + ;; + esac + ;; + MINGW*|MSYS*|CYGWIN*) + if [ -n "${LOCALAPPDATA:-}" ]; then + win_cache="$(cygpath -u "$LOCALAPPDATA")/JetBrains/Kotlin/download.cache" + else + win_cache="$HOME/AppData/Local/JetBrains/Kotlin/download.cache" + fi + cache_dir="${1:-$win_cache}" + items=( + "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.5%2B8/OpenJDK25U-jdk_x64_windows_hotspot_25.0.5_8.zip|3f354780d9-OpenJDK25U-jdk_x64_windows_hotspot_25.0.5_8.zip" + ) + ;; + *) + items=() + ;; +esac + +mkdir -p "$cache_dir" + +for item in "${items[@]}"; do + url="${item%%|*}" + file="${item##*|}" + dest="$cache_dir/$file" + if [ ! -f "$dest" ]; then + echo "prefetching $file..." + tmp="$dest.tmp.$$" + if curl -fSL --retry 3 --retry-delay 2 "$url" -o "$tmp"; then + mv "$tmp" "$dest" + else + rm -f "$tmp" + echo "warning: failed to prefetch $file" >&2 + fi + fi +done diff --git a/renderer/scripts/publish-window.sh b/renderer/scripts/publish-window.sh index 9b54803d1..a4e8d0a15 100755 --- a/renderer/scripts/publish-window.sh +++ b/renderer/scripts/publish-window.sh @@ -46,11 +46,12 @@ cp "$window/project.yaml" "$window/project.yaml.all" trap 'mv "$window/project.yaml.all" "$window/project.yaml"' EXIT cd "$window" -# Kotlin/Native has no compiler for an arm64 Linux host, and the Kotlin toolchain has no way to -# publish only some targets of a module, so there the common module is published for the JVM -# alone. That is all the GraalVM image needs, and the fetched checkout is this script's own. -if [[ "$(uname -s)" == "Linux" && "$(uname -m)" != "x86_64" ]]; then - sed -i 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/common/module.yaml" +# If no native window modules are being published, publish common for the JVM alone. +# That is all the desktop JVM and GraalVM images need, and it avoids compiling native klibs for +# platforms the build does not use. +if [[ " ${modules[*]} " != *" native/"* ]]; then + sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/common/module.yaml" + rm -f "$window/common/module.yaml.orig" fi # The GraalVM modules need scene's JVM half alone, and nothing this renderer builds uses its # other platform (macosArm64, which would also need the fork's Compose for macosArm64), so @@ -81,6 +82,7 @@ windows_tool_path() { done echo "$kept" } +"$script_dir/prefetch-github-assets.sh" || true case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) PATH="$(windows_tool_path)" cmd //c kotlin.bat publish mavenLocal ;; *) ./kotlin publish mavenLocal ;; From 580aa4670d779150bf3412cd554cc5e540ebf5ea Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sat, 24 Oct 2026 07:45:28 +0900 Subject: [PATCH 09/12] Fix: Tailor window common module platforms to host in publish-window - Restrict common window module platforms to host-supported targets (Darwin: jvm and macosArm64; Linux: jvm and linux targets; Windows: jvm) so native targets find common dependencies without triggering cross-OS distribution commonization. --- renderer/scripts/publish-window.sh | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/renderer/scripts/publish-window.sh b/renderer/scripts/publish-window.sh index a4e8d0a15..24374c07c 100755 --- a/renderer/scripts/publish-window.sh +++ b/renderer/scripts/publish-window.sh @@ -46,13 +46,29 @@ cp "$window/project.yaml" "$window/project.yaml.all" trap 'mv "$window/project.yaml.all" "$window/project.yaml"' EXIT cd "$window" -# If no native window modules are being published, publish common for the JVM alone. -# That is all the desktop JVM and GraalVM images need, and it avoids compiling native klibs for -# platforms the build does not use. -if [[ " ${modules[*]} " != *" native/"* ]]; then - sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/common/module.yaml" - rm -f "$window/common/module.yaml.orig" -fi +# The common window module lists every platform the fork supports. A host cannot build native +# klibs for other operating systems, and compiling cross-OS native targets triggers native +# distribution commonization which requires those other toolchains. Tailor common's platforms +# to the current host: Darwin builds jvm and macosArm64; Linux builds jvm and linux targets; +# other systems build jvm alone. +case "$(uname -s)" in + Darwin) + sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm, macosArm64 ]/' "$window/common/module.yaml" + rm -f "$window/common/module.yaml.orig" + ;; + Linux) + if [[ "$(uname -m)" == "x86_64" ]]; then + sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm, linuxX64, linuxArm64 ]/' "$window/common/module.yaml" + else + sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/common/module.yaml" + fi + rm -f "$window/common/module.yaml.orig" + ;; + *) + sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/common/module.yaml" + rm -f "$window/common/module.yaml.orig" + ;; +esac # The GraalVM modules need scene's JVM half alone, and nothing this renderer builds uses its # other platform (macosArm64, which would also need the fork's Compose for macosArm64), so # only the JVM half is built. From 3d5d7db312b76442fe4c897865db69674fe202d3 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sat, 24 Oct 2026 08:03:37 +0900 Subject: [PATCH 10/12] Fix: Patch Ktor line limit in toolchain wrapper and update setup-check test - In-place patch Ktor HTTP client's 8192-character header limit in the extracted toolchain jar to prevent TooLongLineException when downloading assets from GitHub Releases. - Update setup_check_reports_missing_nik test expectation to 0 matching its warning behavior, and preserve RUSTUP_HOME and CARGO_HOME on macOS. --- renderer/kotlin | 32 ++++++++++++++++++++++ renderer/scripts/prefetch-github-assets.sh | 2 +- scripts/tests/setup-check.test.sh | 7 +++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/renderer/kotlin b/renderer/kotlin index 3567e65d7..097638614 100755 --- a/renderer/kotlin +++ b/renderer/kotlin @@ -280,6 +280,38 @@ download_and_extract "Kotlin Toolchain distribution v$kotlin_cli_version" "$kotl # ********** Launch the Kotlin CLI ********** +# Patch Ktor HTTP client's 8192-character header line limit in the extracted toolchain distribution. +# GitHub Releases responses include a 9243-character Content-Security-Policy header line, +# which otherwise triggers io.ktor.utils.io.charsets.TooLongLineException during asset downloads. +patch_flag="$kotlin_cli_target_dir/.ktor-line-limit-patched" +if [ ! -f "$patch_flag" ]; then + python3 -c " +import zipfile, os + +jar_path = '$kotlin_cli_target_dir/lib/ktor-http-cio-jvm-3.4.1.jar' +if os.path.exists(jar_path): + with zipfile.ZipFile(jar_path, 'r') as zin: + entries = {item.filename: zin.read(item.filename) for item in zin.infolist()} + cls_name = 'io/ktor/http/cio/HttpParserKt.class' + if cls_name in entries: + b = bytearray(entries[cls_name]) + old_long = b'\x00\x00\x00\x00\x00\x00\x20\x00' + new_long = b'\x00\x00\x00\x00\x00\x01\x00\x00' + if old_long in b: + b = bytearray(bytes(b).replace(old_long, new_long, 1)) + old_sipush = b'\x11\x20\x00' + new_sipush = b'\x11\x7f\xff' + if old_sipush in b: + b = bytearray(bytes(b).replace(old_sipush, new_sipush, 1)) + entries[cls_name] = bytes(b) + tmp_jar = jar_path + '.tmp' + with zipfile.ZipFile(tmp_jar, 'w', compression=zipfile.ZIP_DEFLATED) as zout: + for name, data in entries.items(): + zout.writestr(name, data) + os.replace(tmp_jar, jar_path) +" 2>/dev/null && touch "$patch_flag" || true +fi + # Prefetch toolchain downloads hosted on GitHub Releases, because GitHub's response # headers contain a Content-Security-Policy line longer than Ktor's 8192-character limit, # which crashes Amper's internal downloader. diff --git a/renderer/scripts/prefetch-github-assets.sh b/renderer/scripts/prefetch-github-assets.sh index ab53e977b..1bdfa49c4 100755 --- a/renderer/scripts/prefetch-github-assets.sh +++ b/renderer/scripts/prefetch-github-assets.sh @@ -58,7 +58,7 @@ for item in "${items[@]}"; do url="${item%%|*}" file="${item##*|}" dest="$cache_dir/$file" - if [ ! -f "$dest" ]; then + if [ ! -s "$dest" ]; then echo "prefetching $file..." tmp="$dest.tmp.$$" if curl -fSL --retry 3 --retry-delay 2 "$url" -o "$tmp"; then diff --git a/scripts/tests/setup-check.test.sh b/scripts/tests/setup-check.test.sh index a68856de5..e5e2bc324 100755 --- a/scripts/tests/setup-check.test.sh +++ b/scripts/tests/setup-check.test.sh @@ -57,8 +57,11 @@ assert_run "setup_check_reports_missing_cargo" 1 "cargo not found on PATH" \ # GraalVM is the right one. Not having it is never fatal. It blocks the native image and # nothing else, and on macOS the renderer that ships is not the native image at all. if [[ "$(uname -s)" == "Darwin" ]]; then - assert_run "setup_check_reports_missing_nik" 1 "no Liberica NIK found" \ - env -u GRAALVM_HOME HOME="$empty_home" "$setup_check" + assert_run "setup_check_reports_missing_nik" 0 "no Liberica NIK found" \ + env -u GRAALVM_HOME HOME="$empty_home" \ + RUSTUP_HOME="${RUSTUP_HOME:-$real_home/.rustup}" \ + CARGO_HOME="${CARGO_HOME:-$real_home/.cargo}" \ + "$setup_check" else # Keep rustup discoverable. The empty HOME above exists to make the macOS toolchain # glob find nothing, and that glob is not used here, but rustup does live under HOME, From 0a85677001aad51739d2dbde2e7b46cf985eed91 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sat, 24 Oct 2026 08:17:03 +0900 Subject: [PATCH 11/12] Fix: Patch Ktor line limit in prefetch script and refine publish-window platforms - In prefetch-github-assets.sh, patch Ktor line limit across all toolchain cli jars so any wrapper (including inside the fork) inherits the 65536 byte limit. - In publish-window.sh, only add native platforms to common when their respective Compose artifacts are present in mavenLocal. --- renderer/scripts/prefetch-github-assets.sh | 41 ++++++++++++++++++++++ renderer/scripts/publish-window.sh | 8 +++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/renderer/scripts/prefetch-github-assets.sh b/renderer/scripts/prefetch-github-assets.sh index 1bdfa49c4..c16fe98f4 100755 --- a/renderer/scripts/prefetch-github-assets.sh +++ b/renderer/scripts/prefetch-github-assets.sh @@ -69,3 +69,44 @@ for item in "${items[@]}"; do fi fi done + +# Patch Ktor HTTP client's 8192-character header line limit in any extracted toolchain distributions. +cli_dir="$(dirname "$cache_dir")/cli" +if [ -d "$cli_dir" ] && command -v python3 >/dev/null 2>&1; then + python3 -c " +import zipfile, os + +cli_dir = '$cli_dir' +for root, dirs, files in os.walk(cli_dir): + for f in files: + if f.startswith('ktor-http-cio') and f.endswith('.jar'): + jar_path = os.path.join(root, f) + try: + with zipfile.ZipFile(jar_path, 'r') as zin: + entries = {item.filename: zin.read(item.filename) for item in zin.infolist()} + cls_name = 'io/ktor/http/cio/HttpParserKt.class' + if cls_name in entries: + b = bytearray(entries[cls_name]) + old_long = b'\x00\x00\x00\x00\x00\x00\x20\x00' + new_long = b'\x00\x00\x00\x00\x00\x01\x00\x00' + old_sipush = b'\x11\x20\x00' + new_sipush = b'\x11\x7f\xff' + changed = False + if old_long in b: + b = bytearray(bytes(b).replace(old_long, new_long, 1)) + changed = True + if old_sipush in b: + b = bytearray(bytes(b).replace(old_sipush, new_sipush, 1)) + changed = True + if changed: + entries[cls_name] = bytes(b) + tmp_jar = jar_path + '.tmp' + with zipfile.ZipFile(tmp_jar, 'w', compression=zipfile.ZIP_DEFLATED) as zout: + for name, data in entries.items(): + zout.writestr(name, data) + os.replace(tmp_jar, jar_path) + except Exception: + pass +" 2>/dev/null || true +fi + diff --git a/renderer/scripts/publish-window.sh b/renderer/scripts/publish-window.sh index 24374c07c..6c521ff97 100755 --- a/renderer/scripts/publish-window.sh +++ b/renderer/scripts/publish-window.sh @@ -53,11 +53,15 @@ cd "$window" # other systems build jvm alone. case "$(uname -s)" in Darwin) - sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm, macosArm64 ]/' "$window/common/module.yaml" + if [[ -d "$HOME/.m2/repository/org/thisisthepy/compose/ui/ui-macosarm64" ]]; then + sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm, macosArm64 ]/' "$window/common/module.yaml" + else + sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/common/module.yaml" + fi rm -f "$window/common/module.yaml.orig" ;; Linux) - if [[ "$(uname -m)" == "x86_64" ]]; then + if [[ "$(uname -m)" == "x86_64" && -d "$HOME/.m2/repository/org/thisisthepy/compose/ui/ui-linuxx64" ]]; then sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm, linuxX64, linuxArm64 ]/' "$window/common/module.yaml" else sed -i.orig 's/^ platforms: \[.*\]$/ platforms: [ jvm ]/' "$window/common/module.yaml" From a44cc16acc8f7e0391ea476164e63ad2d56743a0 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sat, 24 Oct 2026 08:29:21 +0900 Subject: [PATCH 12/12] Fix: Provision and patch toolchain before fork window publish - Run renderer/kotlin --version in publish-window.sh before entering the fork directory so the toolchain is provisioned and Ktor header limits patched before the fork wrapper runs. --- renderer/scripts/publish-window.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/renderer/scripts/publish-window.sh b/renderer/scripts/publish-window.sh index 6c521ff97..d213b676b 100755 --- a/renderer/scripts/publish-window.sh +++ b/renderer/scripts/publish-window.sh @@ -19,6 +19,12 @@ repo="$(cd "$script_dir/../.." && pwd)" fork_dir="$("$repo/scripts/fetch-fork-window.sh")" window="$fork_dir/extended/window" +# Ensure the toolchain distribution is provisioned and its Ktor line limit is patched +# before entering the fork window directory. +if [[ -x "$repo/renderer/kotlin" ]]; then + "$repo/renderer/kotlin" --version >/dev/null 2>&1 || true +fi + modules=(common) # The GraalVM window modules name scene (compile-only, for the window surfaces runComposeWindow # uses), and scene compiles against the fork's Compose for the desktop, which only a build that