@invarn/cibuild 2.9.4 → 2.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,18 +46,8 @@
46
46
  */
47
47
  /** The share of the machine's memory the two daemon heaps may take together. */
48
48
  export const GRADLE_HEAP_SHARE_PERCENT = 75;
49
- export const GRADLE_MEMORY_FUNCTIONS = `# This machine's memory in MiB, or nothing when it cannot be read.
50
- cibuild_mem_mib() {
51
- if [ -r /proc/meminfo ]; then
52
- awk '/^MemTotal:/ { print int($2 / 1024); exit }' /proc/meminfo
53
- else
54
- local bytes
55
- bytes=$(sysctl -n hw.memsize 2>/dev/null || true)
56
- if [ -n "$bytes" ]; then echo $(( bytes / 1048576 )); fi
57
- fi
58
- }
59
-
60
- # $1 a .properties file, $2 a key. Prints "<line number><TAB><value>" for the
49
+ /** Reads one key of a .properties file, as Java does: the last assignment wins. */
50
+ const GRADLE_PROP_LINE_FUNCTION = `# $1 a .properties file, $2 a key. Prints "<line number><TAB><value>" for the
61
51
  # last assignment of the key, or nothing.
62
52
  cibuild_prop_line() {
63
53
  [ -f "$1" ] || return 0
@@ -70,8 +60,53 @@ cibuild_prop_line() {
70
60
  }
71
61
  END { if (found != "") print found }
72
62
  ' "$1"
63
+ }`;
64
+ /**
65
+ * Sends a Kotlin Multiplatform project's metadata compiles
66
+ * (compile*KotlinMetadata, the KotlinCompileCommon tasks) to the Kotlin daemon
67
+ * while every other Kotlin compile stays in-process. With
68
+ * -Pkotlin.compiler.execution.strategy=in-process, Kotlin 2.2.0 compiled
69
+ * DroidKaigi/conference-app-2025's common metadata in K1 mode and rejected its
70
+ * context parameters; the same task on the daemon passed (nightly-baseline 95).
71
+ *
72
+ * The Kotlin plugin's classes are not on an init script's classpath, so the
73
+ * task is matched by class name and the strategy enum is loaded through the
74
+ * task's own classloader. A Kotlin plugin without the property (older than the
75
+ * strategy API) is left alone: this script must never fail a build. Lazy
76
+ * (configureEach where Gradle has it), so it is configuration-cache safe and
77
+ * realizes no task. It runs once per build, included builds as well; they
78
+ * have no metadata compiles to touch.
79
+ */
80
+ export const KOTLIN_METADATA_DAEMON_INIT_SCRIPT = `// cibuild: Kotlin metadata compiles run in the Kotlin daemon (nightly-baseline 95).
81
+ allprojects {
82
+ def hook = { t ->
83
+ if (!t.class.name.startsWith('org.jetbrains.kotlin.gradle.tasks.KotlinCompileCommon')) return
84
+ try {
85
+ def strategy = t.class.classLoader.loadClass('org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy')
86
+ t.compilerExecutionStrategy.set(strategy.enumConstants.find { it.name() == 'DAEMON' })
87
+ } catch (Throwable ignored) {
88
+ }
89
+ }
90
+ try {
91
+ tasks.configureEach(hook)
92
+ } catch (MissingMethodException ignored) {
93
+ tasks.all(hook)
94
+ }
95
+ }
96
+ `;
97
+ export const GRADLE_MEMORY_FUNCTIONS = `# This machine's memory in MiB, or nothing when it cannot be read.
98
+ cibuild_mem_mib() {
99
+ if [ -r /proc/meminfo ]; then
100
+ awk '/^MemTotal:/ { print int($2 / 1024); exit }' /proc/meminfo
101
+ else
102
+ local bytes
103
+ bytes=$(sysctl -n hw.memsize 2>/dev/null || true)
104
+ if [ -n "$bytes" ]; then echo $(( bytes / 1048576 )); fi
105
+ fi
73
106
  }
74
107
 
108
+ ${GRADLE_PROP_LINE_FUNCTION}
109
+
75
110
  # The -Xmx of a JVM argument string in MiB (the last one wins, as in the JVM),
76
111
  # or nothing when it has none.
77
112
  cibuild_xmx_mib() {
@@ -91,6 +126,40 @@ cibuild_xmx_mib() {
91
126
  END { if (r != "") print r }'
92
127
  }
93
128
 
129
+ # The -XX:MaxMetaspaceSize of a JVM argument string in MiB (the last one
130
+ # wins), or nothing when it has none.
131
+ cibuild_metaspace_mib() {
132
+ printf '%s\\n' "$1" | tr ' \\t,' '\\n\\n\\n' | awk '
133
+ /^-XX:MaxMetaspaceSize=[0-9]+[kKmMgGtT]?$/ {
134
+ n = substr($0, 22); u = substr(n, length(n))
135
+ if (u ~ /[0-9]/) { v = n / 1048576 }
136
+ else {
137
+ n = substr(n, 1, length(n) - 1)
138
+ if (u ~ /[kK]/) v = n / 1024
139
+ else if (u ~ /[mM]/) v = n
140
+ else if (u ~ /[gG]/) v = n * 1024
141
+ else v = n * 1048576
142
+ }
143
+ r = int(v)
144
+ }
145
+ END { if (r != "") print r }'
146
+ }
147
+
148
+ # $1 a JVM argument string, $2 a size in MiB: the same arguments with every
149
+ # -XX:MaxMetaspaceSize replaced by that one.
150
+ cibuild_with_metaspace() (
151
+ set -f
152
+ out=""
153
+ replaced=0
154
+ for arg in $1; do
155
+ case "$arg" in
156
+ -XX:MaxMetaspaceSize=*) if [ "$replaced" -eq 0 ]; then out="$out -XX:MaxMetaspaceSize=\${2}m"; replaced=1; fi ;;
157
+ *) out="$out $arg" ;;
158
+ esac
159
+ done
160
+ printf '%s' "\${out# }"
161
+ )
162
+
94
163
  # $1 a JVM argument string, $2 a heap in MiB: the same arguments with every
95
164
  # -Xmx replaced by that one (added in front when there was none).
96
165
  cibuild_with_xmx() (
@@ -119,10 +188,32 @@ cibuild_scaled_heap() {
119
188
  echo "$v"
120
189
  }
121
190
 
191
+ # The Kotlin daemon heap a multiplatform project's metadata compiles get out
192
+ # of a budget in MiB: an eighth, rounded down to 64 MiB, from 512 MiB to 1 GiB.
193
+ cibuild_metadata_daemon_heap() {
194
+ local v=$(( $1 / 8 / 64 * 64 ))
195
+ [ "$v" -ge 512 ] || v=512
196
+ [ "$v" -le 1024 ] || v=1024
197
+ echo "$v"
198
+ }
199
+
200
+ # Whether the Gradle project here applies Kotlin Multiplatform: a build script
201
+ # or version catalog that names the plugin. Convention plugins in buildSrc or
202
+ # an included build sit a few directories down; build outputs are not read.
203
+ cibuild_applies_kotlin_multiplatform() {
204
+ local f
205
+ while IFS= read -r -d '' f; do
206
+ if grep -q -E 'kotlin\\("multiplatform"\\)|org\\.jetbrains\\.kotlin\\.multiplatform|kotlin-multiplatform|kotlinMultiplatform' "$f" 2>/dev/null; then return 0; fi
207
+ done < <(find . -maxdepth 6 \\( -name .git -o -name node_modules -o -name build -o -name .gradle \\) -prune -o -type f \\( -name '*.gradle' -o -name '*.gradle.kts' -o -name '*.versions.toml' \\) -print0 2>/dev/null)
208
+ return 1
209
+ }
210
+
122
211
  # The plan, from its inputs only, so a test can run it:
123
212
  # $1 this machine's memory in MiB (empty when unknown)
124
213
  # $2 the project's gradle.properties $3 the Gradle user home's
125
214
  # $4 JVM arguments the pipeline asks for instead (may be empty)
215
+ # $5 the metadata init script, when the project applies Kotlin
216
+ # Multiplatform (empty otherwise)
126
217
  # Sets CIBUILD_GRADLE_MEMORY_ARGS (extra Gradle arguments),
127
218
  # CIBUILD_GRADLE_MEMORY_LINE (what to print before Gradle runs; empty when
128
219
  # nothing changes) and CIBUILD_GRADLE_MEMORY_SENTENCE (what to print if a
@@ -133,7 +224,7 @@ cibuild_scaled_heap() {
133
224
  # its own inherits the Gradle daemon's, and in-process compilation has no
134
225
  # Kotlin daemon at all.
135
226
  cibuild_gradle_memory_plan() {
136
- local mem="$1" project="$2" user="$3" preferred="$4" l
227
+ local mem="$1" project="$2" user="$3" preferred="$4" init="\${5:-}" l
137
228
  local jvm="" jvm_at="" kd="" kd_at="" strategy=""
138
229
  CIBUILD_GRADLE_MEMORY_ARGS=()
139
230
  CIBUILD_GRADLE_MEMORY_LINE=""
@@ -203,18 +294,66 @@ cibuild_gradle_memory_plan() {
203
294
  # One heap, the whole budget. Two did not fit: ReFra's Kotlin daemon was
204
295
  # lost mid-compile at a 60/40 split, and mifos-mobile's Gradle daemon ran out
205
296
  # of its 40 %. In-process, Kotlin compiles in the Gradle daemon's heap.
206
- local ng now
207
- ng=$(cibuild_scaled_heap "$budget" 1 1)
208
- CIBUILD_GRADLE_MEMORY_ARGS=("-Dorg.gradle.jvmargs=$(cibuild_with_xmx "$jvm" "$ng")")
297
+ # A multiplatform project's metadata compiles are the exception: they go to
298
+ # a small Kotlin daemon (the init script), carved out of the same budget.
299
+ local ng now newjvm mg mk md=""
300
+ if [ "$k" -gt 0 ] && [ -n "$init" ]; then md=$(cibuild_metadata_daemon_heap "$budget"); fi
301
+ ng=$(cibuild_scaled_heap $(( budget - \${md:-0} )) 1 1)
302
+ newjvm=$(cibuild_with_xmx "$jvm" "$ng")
209
303
  now="Gradle -Xmx\${ng}m"
210
304
  if [ "$k" -gt 0 ]; then
211
- CIBUILD_GRADLE_MEMORY_ARGS+=("-Pkotlin.compiler.execution.strategy=in-process")
305
+ # One JVM now holds what two did, so a Metaspace cap Gradle declared must
306
+ # hold Kotlin's too: the Kotlin daemon's own (kotlin.daemon.jvmargs, or
307
+ # -Dkotlin.daemon.jvm.options in Gradle's), else the Gradle one it
308
+ # inherits. GetStream/whatsApp-clone-compose capped Gradle at 512m and gave
309
+ # Kotlin 1g, and in-process compileDebugKotlin ran out of Metaspace
310
+ # (nightly-baseline 79). No Gradle cap, nothing to raise.
311
+ mg=$(cibuild_metaspace_mib "$jvm")
312
+ if [ -n "$mg" ]; then
313
+ mk=$(cibuild_metaspace_mib "$kd")
314
+ [ -n "$mk" ] || mk=$(cibuild_metaspace_mib "$(printf '%s\\n' "$jvm" | tr ' \\t' '\\n\\n' | sed -n 's/^-Dkotlin\\.daemon\\.jvm\\.options=//p')")
315
+ [ -n "$mk" ] || mk="$mg"
316
+ newjvm=$(cibuild_with_metaspace "$newjvm" $(( mg + mk )))
317
+ now="$now with -XX:MaxMetaspaceSize=$(( mg + mk ))m (Gradle's \${mg}m and Kotlin's \${mk}m, in one JVM now)"
318
+ fi
212
319
  now="$now and Kotlin compiled in-process in it (kotlin.compiler.execution.strategy=in-process: one heap, not a Gradle and a Kotlin daemon)"
213
320
  fi
321
+ if [ -n "$md" ]; then
322
+ now="$now, and Kotlin metadata compiles (compile*KotlinMetadata) in a Kotlin daemon of -Xmx\${md}m beside it (in-process, Kotlin 2.2 compiled them in K1 mode)"
323
+ fi
324
+ CIBUILD_GRADLE_MEMORY_ARGS=("-Dorg.gradle.jvmargs=$newjvm")
325
+ if [ "$k" -gt 0 ]; then
326
+ CIBUILD_GRADLE_MEMORY_ARGS+=("-Pkotlin.compiler.execution.strategy=in-process")
327
+ fi
328
+ if [ -n "$md" ]; then
329
+ CIBUILD_GRADLE_MEMORY_ARGS+=("-Pkotlin.daemon.jvmargs=-Xmx\${md}m" --init-script "$init")
330
+ fi
214
331
  CIBUILD_GRADLE_MEMORY_LINE="Gradle memory: this machine has $(cibuild_gib "$mem") GiB and the declared heaps ask for $(cibuild_gib "$total") GiB ($asked), past the $(cibuild_gib "$budget") GiB this build allows them. Running with $now, set on the command line."
215
332
  CIBUILD_GRADLE_MEMORY_SENTENCE="$CIBUILD_GRADLE_MEMORY_SENTENCE This build ran them capped: $now."
216
333
  }
217
334
 
335
+ # $1 the project's gradle.properties, $2 the Gradle user home's. A JDK path the
336
+ # project pins with org.gradle.java.home that is not on this machine — a
337
+ # developer's own, like SEAbdulbasit/MusicApp-KMP's
338
+ # /Users/abdulbasit/Library/Java/... (nightly-baseline 83) — stops Gradle
339
+ # before it starts: "given for org.gradle.java.home Gradle property is
340
+ # invalid". On the command line, -Dorg.gradle.java.home beats both files
341
+ # (measured, Gradle 8.14.3), so the build's own JDK is passed instead. A path
342
+ # that exists is left alone. Sets CIBUILD_GRADLE_JAVA_HOME_LINE.
343
+ cibuild_gradle_java_home_plan() {
344
+ local l v="" at=""
345
+ CIBUILD_GRADLE_JAVA_HOME_LINE=""
346
+ for f in "$2" "$1"; do
347
+ l=$(cibuild_prop_line "$f" org.gradle.java.home)
348
+ if [ -n "$l" ]; then v="\${l#* }"; at="$f:\${l%% *}"; break; fi
349
+ done
350
+ [ -n "$v" ] || return 0
351
+ [ -d "$v" ] && return 0
352
+ [ -n "\${JAVA_HOME:-}" ] && [ -d "$JAVA_HOME" ] || return 0
353
+ CIBUILD_GRADLE_MEMORY_ARGS+=("-Dorg.gradle.java.home=$JAVA_HOME")
354
+ CIBUILD_GRADLE_JAVA_HOME_LINE="Gradle: org.gradle.java.home=$v at $at is not on this machine; running with this build's JDK, $JAVA_HOME (-Dorg.gradle.java.home on the command line)."
355
+ }
356
+
218
357
  # $1 the number of Gradle workers the pipeline asks for (may be empty). Adds
219
358
  # --max-workers to the plan's arguments; run after cibuild_gradle_memory_plan.
220
359
  # Sets CIBUILD_GRADLE_WORKERS_LINE (what to print; empty when nothing asked).
@@ -282,11 +421,19 @@ cibuild_gradle() {
282
421
  # (either may be empty). Run in the Gradle project directory, after GRADLE_CMD
283
422
  # is chosen.
284
423
  cibuild_gradle_memory_cap() {
424
+ local metadata_init=""
425
+ if cibuild_applies_kotlin_multiplatform; then
426
+ metadata_init="$(mktemp -d)/cibuild-kotlin-metadata-daemon.init.gradle"
427
+ cat > "$metadata_init" <<'CIBUILD_KOTLIN_METADATA_INIT'
428
+ ${KOTLIN_METADATA_DAEMON_INIT_SCRIPT}CIBUILD_KOTLIN_METADATA_INIT
429
+ fi
285
430
  cibuild_gradle_memory_plan "$(cibuild_mem_mib || true)" "gradle.properties" \\
286
- "\${GRADLE_USER_HOME:-$HOME/.gradle}/gradle.properties" "$1"
431
+ "\${GRADLE_USER_HOME:-$HOME/.gradle}/gradle.properties" "$1" "$metadata_init"
287
432
  cibuild_gradle_workers_plan "$2"
433
+ cibuild_gradle_java_home_plan "gradle.properties" "\${GRADLE_USER_HOME:-$HOME/.gradle}/gradle.properties"
288
434
  if [ -n "$CIBUILD_GRADLE_MEMORY_LINE" ]; then echo "$CIBUILD_GRADLE_MEMORY_LINE"; fi
289
435
  if [ -n "$CIBUILD_GRADLE_WORKERS_LINE" ]; then echo "$CIBUILD_GRADLE_WORKERS_LINE"; fi
436
+ if [ -n "$CIBUILD_GRADLE_JAVA_HOME_LINE" ]; then echo "$CIBUILD_GRADLE_JAVA_HOME_LINE"; fi
290
437
  CIBUILD_GRADLE_BIN="$GRADLE_CMD"
291
438
  GRADLE_CMD=cibuild_gradle
292
439
  }`;
@@ -308,4 +455,41 @@ export function gradleMemoryCommands(jvmargs, escape, maxWorkers = '') {
308
455
  `cibuild_gradle_memory_cap '${escape(jvmargs)}' '${escape(maxWorkers)}'`,
309
456
  ];
310
457
  }
458
+ /**
459
+ * A pinned org.gradle.java.home that is not on this machine, made to name the
460
+ * build's JDK for every Gradle run in the build. The Gradle steps already pass
461
+ * -Dorg.gradle.java.home on their own command line; a script step's
462
+ * ./gradlew and a build phase Xcode runs do not, and they read the checkout's
463
+ * gradle.properties. SEAbdulbasit/MusicApp-KMP's Android step went on with the
464
+ * override and its KMM "Prepare the iOS build" script stopped on the same
465
+ * "given for org.gradle.java.home Gradle property is invalid"
466
+ * (nightly-baseline 92). So the line itself is rewritten, in every
467
+ * gradle.properties of the checkout, once the build's JDK is chosen. A path
468
+ * that exists, an empty value or a commented-out line is left alone.
469
+ */
470
+ export const GRADLE_JAVA_HOME_PIN_FUNCTIONS = `${GRADLE_PROP_LINE_FUNCTION}
471
+
472
+ # Run once JAVA_HOME is the build's JDK. Searches the checkout
473
+ # (CIBUILD_SOURCE_DIR, else the current directory) to the depth a nested
474
+ # Gradle project sits at, and prints one line per file it rewrites.
475
+ cibuild_gradle_java_home_pin() {
476
+ [ -n "\${JAVA_HOME:-}" ] && [ -d "$JAVA_HOME" ] || return 0
477
+ local root f l v tmp
478
+ root="\${CIBUILD_SOURCE_DIR:-.}"
479
+ while IFS= read -r f; do
480
+ l=$(cibuild_prop_line "$f" org.gradle.java.home)
481
+ [ -n "$l" ] || continue
482
+ v="\${l#* }"
483
+ [ -n "$v" ] || continue
484
+ [ -d "$v" ] && continue
485
+ tmp=$(mktemp)
486
+ awk -v key=org.gradle.java.home -v home="$JAVA_HOME" '
487
+ { line = $0; sub(/^[ \\t]+/, "", line) }
488
+ line !~ /^[#!]/ { k = line; sub(/[ \\t]*[=:].*$/, "", k); if (k == key) { print key "=" home; next } }
489
+ { print }
490
+ ' "$f" > "$tmp" && cat "$tmp" > "$f"
491
+ rm -f "$tmp"
492
+ echo "Gradle: org.gradle.java.home=$v at \${f#"$root"/}:\${l%% *} is not on this machine; that line now names this build's JDK, $JAVA_HOME, so every Gradle run in this build uses it (a script's ./gradlew and Xcode's build phases as well as the Gradle steps)."
493
+ done < <(find "$root" -maxdepth 4 \\( -name .git -o -name node_modules -o -name build -o -name .gradle \\) -prune -o -type f -name gradle.properties -print 2>/dev/null)
494
+ }`;
311
495
  //# sourceMappingURL=gradle-memory.js.map
@@ -67,10 +67,27 @@ describe("cibuild_gradle_memory_plan", () => {
67
67
  test("mifos-mobile on its 6 GiB mac guest gets the one heap too", () => {
68
68
  writeFileSync(join(dir, "gradle.properties"), "org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g\n");
69
69
  expect(plan("6144").args).toEqual([
70
- "-Dorg.gradle.jvmargs=-Xmx4608m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g",
70
+ // Its Kotlin daemon inherited the 1g Metaspace cap; in one JVM the cap holds both.
71
+ "-Dorg.gradle.jvmargs=-Xmx4608m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=2048m",
71
72
  "-Pkotlin.compiler.execution.strategy=in-process",
72
73
  ]);
73
74
  });
75
+ // GetStream/whatsApp-clone-compose (nightly-baseline 79): Gradle capped at
76
+ // 512m Metaspace and Kotlin given 1g of its own; in-process, the one JVM
77
+ // ran out of Metaspace in compileDebugKotlin.
78
+ test("whatsApp-clone-compose: a Metaspace cap holds Kotlin's too once Kotlin is in-process", () => {
79
+ writeFileSync(join(dir, "gradle.properties"), "org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -XX:MaxMetaspaceSize=512m -Dkotlin.daemon.jvm.options=-XX:MaxMetaspaceSize=1g -Dlint.nullness.ignore-deprecated=true\n");
80
+ const p = plan("9933");
81
+ expect(p.args).toEqual([
82
+ "-Dorg.gradle.jvmargs=-Xmx7424m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -XX:MaxMetaspaceSize=1536m -Dkotlin.daemon.jvm.options=-XX:MaxMetaspaceSize=1g -Dlint.nullness.ignore-deprecated=true",
83
+ "-Pkotlin.compiler.execution.strategy=in-process",
84
+ ]);
85
+ expect(p.line).toContain("with -XX:MaxMetaspaceSize=1536m (Gradle's 512m and Kotlin's 1024m, in one JVM now)");
86
+ });
87
+ test("a Kotlin daemon with no Metaspace of its own inherits Gradle's, and the cap doubles", () => {
88
+ writeFileSync(join(dir, "gradle.properties"), "org.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=1g\n");
89
+ expect(plan(TEN_GIB).args[0]).toBe("-Dorg.gradle.jvmargs=-Xmx7680m -XX:MaxMetaspaceSize=2048m");
90
+ });
74
91
  test("the one heap never goes below 512 MiB", () => {
75
92
  writeFileSync(join(dir, "gradle.properties"), "org.gradle.jvmargs=-Xmx4g\n");
76
93
  expect(plan("600").args).toEqual(["-Dorg.gradle.jvmargs=-Xmx512m", "-Pkotlin.compiler.execution.strategy=in-process"]);
@@ -157,6 +174,36 @@ printf 'SENTENCE %s\\n' "$CIBUILD_GRADLE_MEMORY_SENTENCE"`;
157
174
  // ReFra was killed again with its heap capped: off-heap memory and parallel
158
175
  // native compilers are past what a heap cap bounds. Its CI also sets
159
176
  // org.gradle.workers.max=2, which the pipeline carries as max_workers.
177
+ // nightly-baseline 83: SEAbdulbasit/MusicApp-KMP pins its developer's JDK in
178
+ // gradle.properties, and Gradle refused to start.
179
+ describe("cibuild_gradle_java_home_plan", () => {
180
+ const javaHomePlan = (env) => {
181
+ const out = execFileSync("bash", ["-c", `${GRADLE_MEMORY_FUNCTIONS}
182
+ CIBUILD_GRADLE_MEMORY_ARGS=()
183
+ cibuild_gradle_java_home_plan gradle.properties "$1/gradle.properties"
184
+ for a in "\${CIBUILD_GRADLE_MEMORY_ARGS[@]}"; do printf 'ARG %s\\n' "$a"; done
185
+ printf 'LINE %s\\n' "$CIBUILD_GRADLE_JAVA_HOME_LINE"`, "plan", join(dir, "no-user-home")], { cwd: dir, encoding: "utf-8", env: { PATH: process.env.PATH ?? "", ...env } });
186
+ const lines = out.split("\n");
187
+ return {
188
+ args: lines.filter((l) => l.startsWith("ARG ")).map((l) => l.slice(4)),
189
+ line: (lines.find((l) => l.startsWith("LINE ")) ?? "").slice(5),
190
+ };
191
+ };
192
+ test("a pinned JDK that is not on this machine is replaced by the build's", () => {
193
+ writeFileSync(join(dir, "gradle.properties"), "org.gradle.jvmargs=-Xmx2g\norg.gradle.java.home=/Users/abdulbasit/Library/Java/JavaVirtualMachines/corretto-21.0.3/Contents/Home\n");
194
+ const p = javaHomePlan({ JAVA_HOME: dir });
195
+ expect(p.args).toEqual([`-Dorg.gradle.java.home=${dir}`]);
196
+ expect(p.line).toContain("org.gradle.java.home=/Users/abdulbasit/Library/Java/JavaVirtualMachines/corretto-21.0.3/Contents/Home at gradle.properties:2 is not on this machine");
197
+ });
198
+ test("a pinned JDK that exists is left alone", () => {
199
+ writeFileSync(join(dir, "gradle.properties"), `org.gradle.java.home=${dir}\n`);
200
+ expect(javaHomePlan({ JAVA_HOME: "/elsewhere" }).args).toEqual([]);
201
+ });
202
+ test("no pin, nothing", () => {
203
+ writeFileSync(join(dir, "gradle.properties"), "org.gradle.jvmargs=-Xmx2g\n");
204
+ expect(javaHomePlan({ JAVA_HOME: dir })).toEqual({ args: [], line: "" });
205
+ });
206
+ });
160
207
  describe("cibuild_gradle_workers_plan", () => {
161
208
  test("ReFra: the worker count goes after the heap cap, and the sentence names it", () => {
162
209
  copyFileSync(join(FIXTURES, "refra.gradle.properties"), join(dir, "gradle.properties"));
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=kotlin-metadata-compiles-on-a-daemon.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kotlin-metadata-compiles-on-a-daemon.test.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/kotlin-metadata-compiles-on-a-daemon.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Kotlin metadata compiles run in a Kotlin daemon when the memory plan puts
3
+ * every other Kotlin compile in-process (nightly-baseline 95).
4
+ *
5
+ * DroidKaigi/conference-app-2025 asks for -Xmx6g; on a 6 GiB guest the plan
6
+ * (nightly-baseline 79) runs one heap and passes
7
+ * -Pkotlin.compiler.execution.strategy=in-process. With Kotlin 2.2.0, the
8
+ * in-process :core:common:compileCommonMainKotlinMetadata then failed with
9
+ * "Context parameters are not supported in K1 mode", while the same task on
10
+ * the Kotlin daemon passed (measured at the pin, five runs each). So a project
11
+ * that applies Kotlin Multiplatform gets an init script that sends only its
12
+ * metadata compiles to a small Kotlin daemon, carved out of the same budget.
13
+ *
14
+ * The shell under test is the one that ships: it is executed here.
15
+ */
16
+ import { execFileSync } from "node:child_process";
17
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
18
+ import { tmpdir } from "node:os";
19
+ import { dirname, join } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+ import { GRADLE_MEMORY_FUNCTIONS, KOTLIN_METADATA_DAEMON_INIT_SCRIPT } from "./gradle-memory.js";
22
+ const FIXTURES = join(dirname(fileURLToPath(import.meta.url)), "../../../test/fixtures/kotlin-metadata-daemon");
23
+ const SIX_GIB = "6144";
24
+ let dir;
25
+ beforeEach(() => {
26
+ dir = mkdtempSync(join(tmpdir(), "cibuild-kotlin-metadata-"));
27
+ });
28
+ afterEach(() => {
29
+ rmSync(dir, { recursive: true, force: true });
30
+ });
31
+ function bash(script, args = []) {
32
+ return execFileSync("bash", ["-c", `${GRADLE_MEMORY_FUNCTIONS}\n${script}`, "t", ...args], {
33
+ cwd: dir,
34
+ encoding: "utf-8",
35
+ });
36
+ }
37
+ /** The shipped plan, as the step runs it, with an init script path for a multiplatform project. */
38
+ function plan(memMib, initScript) {
39
+ const out = bash(`cibuild_gradle_memory_plan "$1" gradle.properties "$2/gradle.properties" "" "$3"
40
+ for a in "\${CIBUILD_GRADLE_MEMORY_ARGS[@]}"; do printf 'ARG %s\\n' "$a"; done
41
+ printf 'LINE %s\\n' "$CIBUILD_GRADLE_MEMORY_LINE"`, [memMib, join(dir, "no-user-home"), initScript]);
42
+ const lines = out.split("\n");
43
+ return {
44
+ args: lines.filter((l) => l.startsWith("ARG ")).map((l) => l.slice(4)),
45
+ line: (lines.find((l) => l.startsWith("LINE ")) ?? "").slice(5),
46
+ };
47
+ }
48
+ describe("a multiplatform project's metadata compiles leave the in-process plan", () => {
49
+ beforeEach(() => {
50
+ cpSync(join(FIXTURES, "droidkaigi"), dir, { recursive: true });
51
+ });
52
+ test("DroidKaigi on a 6 GiB guest: one Gradle heap, less a small Kotlin daemon for metadata", () => {
53
+ const p = plan(SIX_GIB, "/x/metadata.init.gradle");
54
+ // 75 % of 6 GiB is 4608 MiB: 576 of it for the metadata daemon, the rest Gradle's.
55
+ expect(p.args).toEqual([
56
+ "-Dorg.gradle.jvmargs=-Xmx4032m -XX:MaxMetaspaceSize=6144m -Dfile.encoding=UTF-8",
57
+ "-Pkotlin.compiler.execution.strategy=in-process",
58
+ "-Pkotlin.daemon.jvmargs=-Xmx576m",
59
+ "--init-script",
60
+ "/x/metadata.init.gradle",
61
+ ]);
62
+ expect(p.line).toContain("Kotlin metadata compiles (compile*KotlinMetadata) in a Kotlin daemon of -Xmx576m");
63
+ });
64
+ test("a project that is not multiplatform keeps 79's plan exactly", () => {
65
+ expect(plan(SIX_GIB, "").args).toEqual([
66
+ "-Dorg.gradle.jvmargs=-Xmx4608m -XX:MaxMetaspaceSize=6144m -Dfile.encoding=UTF-8",
67
+ "-Pkotlin.compiler.execution.strategy=in-process",
68
+ ]);
69
+ });
70
+ test("when the heaps fit, nothing is in-process and no init script is passed", () => {
71
+ expect(plan("32768", "/x/metadata.init.gradle").args).toEqual([]);
72
+ });
73
+ test("the metadata daemon's heap grows with the budget, to 1 GiB", () => {
74
+ writeFileSync(join(dir, "gradle.properties"), "org.gradle.jvmargs=-Xmx16g\n");
75
+ const p = plan("16384", "/x/metadata.init.gradle");
76
+ expect(p.args).toContain("-Pkotlin.daemon.jvmargs=-Xmx1024m");
77
+ expect(p.args[0]).toBe("-Dorg.gradle.jvmargs=-Xmx11264m");
78
+ });
79
+ });
80
+ describe("cibuild_applies_kotlin_multiplatform", () => {
81
+ test("DroidKaigi's version catalog names the multiplatform plugin", () => {
82
+ cpSync(join(FIXTURES, "droidkaigi"), dir, { recursive: true });
83
+ expect(bash("cibuild_applies_kotlin_multiplatform && echo yes || echo no").trim()).toBe("yes");
84
+ });
85
+ test('kotlin("multiplatform") in a build script', () => {
86
+ mkdirSync(join(dir, "shared"));
87
+ writeFileSync(join(dir, "shared/build.gradle.kts"), 'plugins {\n kotlin("multiplatform")\n}\n');
88
+ expect(bash("cibuild_applies_kotlin_multiplatform && echo yes || echo no").trim()).toBe("yes");
89
+ });
90
+ test("an Android-only project is not", () => {
91
+ mkdirSync(join(dir, "app"));
92
+ writeFileSync(join(dir, "app/build.gradle.kts"), 'plugins {\n id("com.android.application")\n kotlin("android")\n}\n');
93
+ writeFileSync(join(dir, "settings.gradle.kts"), 'include(":app")\n');
94
+ expect(bash("cibuild_applies_kotlin_multiplatform && echo yes || echo no").trim()).toBe("no");
95
+ });
96
+ test("a build directory is not read", () => {
97
+ mkdirSync(join(dir, "build/tmp"), { recursive: true });
98
+ writeFileSync(join(dir, "build/tmp/x.gradle.kts"), 'kotlin("multiplatform")\n');
99
+ expect(bash("cibuild_applies_kotlin_multiplatform && echo yes || echo no").trim()).toBe("no");
100
+ });
101
+ });
102
+ describe("cibuild_gradle_memory_cap on a multiplatform project", () => {
103
+ test("writes the init script and runs Gradle with it", () => {
104
+ cpSync(join(FIXTURES, "droidkaigi"), dir, { recursive: true });
105
+ const out = bash(`cibuild_mem_mib() { echo ${SIX_GIB}; }
106
+ GRADLE_CMD=./gradlew
107
+ cibuild_gradle_memory_cap '' ''
108
+ for a in "\${CIBUILD_GRADLE_MEMORY_ARGS[@]}"; do printf 'ARG %s\\n' "$a"; done`);
109
+ const args = out.split("\n").filter((l) => l.startsWith("ARG ")).map((l) => l.slice(4));
110
+ const at = args.indexOf("--init-script");
111
+ expect(at).toBeGreaterThan(0);
112
+ const script = args[at + 1];
113
+ expect(existsSync(script)).toBe(true);
114
+ expect(readFileSync(script, "utf-8")).toBe(KOTLIN_METADATA_DAEMON_INIT_SCRIPT);
115
+ expect(out).toContain("Kotlin metadata compiles (compile*KotlinMetadata) in a Kotlin daemon");
116
+ rmSync(dirname(script), { recursive: true, force: true });
117
+ });
118
+ test("an Android-only project gets no init script", () => {
119
+ writeFileSync(join(dir, "gradle.properties"), "org.gradle.jvmargs=-Xmx8g\n");
120
+ const out = bash(`cibuild_mem_mib() { echo ${SIX_GIB}; }
121
+ GRADLE_CMD=./gradlew
122
+ cibuild_gradle_memory_cap '' ''
123
+ for a in "\${CIBUILD_GRADLE_MEMORY_ARGS[@]}"; do printf 'ARG %s\\n' "$a"; done`);
124
+ expect(out).not.toContain("--init-script");
125
+ expect(out).toContain("ARG -Pkotlin.compiler.execution.strategy=in-process");
126
+ });
127
+ });
128
+ describe("the init script", () => {
129
+ test("touches only the metadata compile task class, through the task's own classloader", () => {
130
+ expect(KOTLIN_METADATA_DAEMON_INIT_SCRIPT).toContain("org.jetbrains.kotlin.gradle.tasks.KotlinCompileCommon");
131
+ expect(KOTLIN_METADATA_DAEMON_INIT_SCRIPT).toContain("classLoader.loadClass('org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy')");
132
+ expect(KOTLIN_METADATA_DAEMON_INIT_SCRIPT).toContain("'DAEMON'");
133
+ });
134
+ test("never fails a build: an older Kotlin plugin without the strategy property is left alone", () => {
135
+ expect(KOTLIN_METADATA_DAEMON_INIT_SCRIPT).toMatch(/catch \(Throwable/);
136
+ expect(KOTLIN_METADATA_DAEMON_INIT_SCRIPT).not.toContain("${");
137
+ });
138
+ });
139
+ //# sourceMappingURL=kotlin-metadata-compiles-on-a-daemon.test.js.map
@@ -181,10 +181,14 @@ describe('the output_dir input still steers where everything goes', () => {
181
181
  * repository has a reason to exclude it.
182
182
  */
183
183
  describe('derived data is outside the source tree', () => {
184
+ // The lines that choose the path: the root, then the definition.
184
185
  const definitionOf = (script) => {
185
- const line = script.split('\n').find((l) => l.startsWith('DERIVED_DATA_PATH='));
186
- expect(line).toBeDefined();
187
- return line;
186
+ const lines = script.split('\n');
187
+ const from = lines.findIndex((l) => l.startsWith('cibuild_dd_root='));
188
+ const to = lines.findIndex((l) => l.startsWith('DERIVED_DATA_PATH='));
189
+ expect(from).toBeGreaterThan(-1);
190
+ expect(to).toBeGreaterThan(from);
191
+ return lines.slice(from, to + 1).join('\n');
188
192
  };
189
193
  // Runs the step's own definition line, the way the step would.
190
194
  const resolve = (script, env) => execFileSync('bash', ['-c', `${definitionOf(script)}\nprintf '%s' "$DERIVED_DATA_PATH"`], {
@@ -238,11 +242,33 @@ describe('derived data is outside the source tree', () => {
238
242
  symlinkSync(real, link);
239
243
  const script = await scriptFor(new XcodeBuildStepExecutor(), {});
240
244
  const lines = script.split('\n');
241
- const from = lines.findIndex((l) => l.startsWith('DERIVED_DATA_PATH='));
245
+ const from = lines.findIndex((l) => l.startsWith('cibuild_dd_root='));
242
246
  const to = lines.findIndex((l) => l.startsWith('echo "Derived data:'));
243
247
  const path = execFileSync('bash', ['-c', `set -e\n${lines.slice(from, to).join('\n')}\nprintf '%s' "$DERIVED_DATA_PATH"`], { env: { PATH: process.env.PATH ?? '', TMPDIR: link, OUTPUT_DIR: '/work/repo/build' } }).toString();
244
248
  expect(path.startsWith(real + '/')).toBe(true);
245
249
  });
250
+ // nightly-baseline 58, measured on runner-mac-01: xcodebuild folds
251
+ // /private/tmp back to /tmp and FileManager does not, so LNPopupController's
252
+ // text-built header paths came out eight characters short under the guest's
253
+ // /tmp/cibuild-work-<id> root, and built under the user's home.
254
+ const tmpIsALink = (() => {
255
+ try {
256
+ return realpathSync('/tmp') !== '/tmp';
257
+ }
258
+ catch {
259
+ return false;
260
+ }
261
+ })();
262
+ (tmpIsALink ? test : test.skip)('a scratch root under /tmp is traded for the build user\'s home', async () => {
263
+ const scratch = mkdtempSync('/tmp/cibuild-work-');
264
+ const home = realpathSync(mkdtempSync(join(tmpdir(), 'home-')));
265
+ const path = resolve(await scriptFor(new XcodeBuildStepExecutor(), {}), {
266
+ CIBUILD_SCRATCH: scratch,
267
+ CIBUILD_USER_HOME: home,
268
+ OUTPUT_DIR: '/work/repo/build',
269
+ });
270
+ expect(path.startsWith(`${home}/Library/Developer/cibuild/cibuild-derived-data/`)).toBe(true);
271
+ });
246
272
  test('the product listing and the xctestrun search follow it', async () => {
247
273
  const build = await scriptFor(new XcodeBuildStepExecutor(), {});
248
274
  expect(build).toContain('ls -lhR "$DERIVED_DATA_PATH/Build/Products"');
@@ -1 +1 @@
1
- {"version":3,"file":"xcode.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/xcode.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"xcode.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/xcode.ts"],"names":[],"mappings":"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"}
@@ -25,16 +25,26 @@ import { refusedFetchHelpers, retryOnRefusedFetch } from './git-fetch-retry.js';
25
25
  * on that to avoid compiling the app twice) and two that name different ones
26
26
  * stay apart, as they did before.
27
27
  *
28
- * The path is resolved to its real one. On macOS /tmp and TMPDIR's /var are
29
- * symlinks into /private, and analogcode/Swift-Radio-Pro's LNPopupController
30
- * lists header directories with FileManager (real paths) but strips
31
- * packageBase.path (the symlinked one) by text replacement: every header
32
- * search path came out eight characters short.
28
+ * **Never under /tmp** (nightly-baseline 58). analogcode/Swift-Radio-Pro's
29
+ * LNPopupController builds its header search paths by deleting
30
+ * `Context.packageDirectory` from the paths FileManager lists, and under /tmp
31
+ * the two disagree by `/private`: xcodebuild folds `/private/tmp` back to
32
+ * `/tmp` however the path is given, and FileManager does not, so every header
33
+ * path came out eight characters short ("troller/…"). Resolving the path did
34
+ * not help, for that reason. The guest's scratch root is
35
+ * `/tmp/cibuild-work-<id>`, so there derived data goes to the build user's
36
+ * real home instead. Measured on runner-mac-01 2026-10-03: the same build
37
+ * fails under /tmp and succeeds under the home, in a fresh VM each. Not
38
+ * /var/folders (TMPDIR): it built correctly there too. SwiftPM caches a
39
+ * manifest's evaluation, relative paths and all, so the first evaluation in a
40
+ * VM decides — another reason the root must be right from the first build.
33
41
  */
34
42
  function derivedDataPathLines() {
35
43
  return [
36
- '# Derived data outside the source tree, keyed by the output dir',
37
- 'DERIVED_DATA_PATH="${CIBUILD_SCRATCH:-${TMPDIR:-/tmp}}/cibuild-derived-data/$(printf \'%s\' "$OUTPUT_DIR" | cksum | cut -d\' \' -f1)"',
44
+ '# Derived data outside the source tree, keyed by the output dir, and never under /tmp',
45
+ 'cibuild_dd_root="${CIBUILD_SCRATCH:-${TMPDIR:-/tmp}}"',
46
+ 'case "$(cd "$cibuild_dd_root" 2>/dev/null && pwd -P)" in /private/tmp|/private/tmp/*) cibuild_dd_root="${CIBUILD_USER_HOME:-$HOME}/Library/Developer/cibuild" ;; esac',
47
+ 'DERIVED_DATA_PATH="$cibuild_dd_root/cibuild-derived-data/$(printf \'%s\' "$OUTPUT_DIR" | cksum | cut -d\' \' -f1)"',
38
48
  'mkdir -p "$DERIVED_DATA_PATH"',
39
49
  '# Its real path: /tmp and TMPDIR are symlinks on macOS, and a package that builds paths by text arithmetic breaks on them',
40
50
  'DERIVED_DATA_PATH="$(cd "$DERIVED_DATA_PATH" && pwd -P)"',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invarn/cibuild",
3
- "version": "2.9.4",
3
+ "version": "2.9.6",
4
4
  "description": "CI Build CLI — local pipeline orchestration and validation",
5
5
  "type": "module",
6
6
  "main": "dist/cli.cjs",