From 47a51b08f4d79f808c1b74de79c3852aa853df53 Mon Sep 17 00:00:00 2001 From: Charan Rathore <180254320+charan-rathore@users.noreply.github.com> Date: Mon, 12 Oct 2026 23:56:16 +0530 Subject: [PATCH] fix(process): add an opt-in Swift module cache (#2252) * fix(process): add an opt-in Swift module cache Signed-off-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com> * fix(process): preserve caller-owned Swift module caches Fold the complementary implementation from #2260 into the earlier #2252 proposal, retaining the original contribution history. Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com> * fix(process): bypass xcrun lookup for explicit Swift toolchains --------- Signed-off-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com> --- docs/process-capture.md | 49 ++++ src/process/DarwinProcessRunTokenReader.ts | 76 +++-- src/process/ProcessOwnershipObservation.ts | 31 +- .../darwinProcessRunTokenReader.test.ts | 274 +++++++++++++++++- 4 files changed, 392 insertions(+), 38 deletions(-) diff --git a/docs/process-capture.md b/docs/process-capture.md index 35e818f311..cc163f7159 100644 --- a/docs/process-capture.md +++ b/docs/process-capture.md @@ -416,3 +416,52 @@ while its token became unavailable after this change; npm changes its title as well. A newly started unreadable process can prevent verified cleanup even after the selected command exits. REA preserves this uncertainty and leaves that process untouched. + +Reading those tokens requires compiling a small Swift helper with `xcrun swiftc`. +By default that compilation reuses Swift's shared module cache, while the +compiler's intermediate objects stay inside the per-run temporary root that REA +removes afterwards. Set `REA_DARWIN_MODULE_CACHE_PATH` to a writable directory +when a shared cache is unavailable, for example under an enforced write +boundary. REA creates the directory, passes it to the compiler as +`-module-cache-path`, and exports it as `CLANG_MODULE_CACHE_PATH` so the +Objective-C header modules the helper builds land there too. REA never removes +that directory: it outlives each run, so the first compilation into a new +directory pays a cold-cache build and later runs reuse it. An absent or empty +value keeps the shared default. + +`xcrun` also maintains a separate tool-lookup cache. Its `--no-cache` option +refreshes entries and does not guarantee a write-free lookup. To avoid that +lookup while compiling REA's helper, set `REA_DARWIN_SWIFTC_PATH` to the absolute +path of an installed `swiftc` and `SDKROOT` to the absolute path of a compatible +macOS SDK. REA invokes that compiler directly with `-sdk` and the selected SDK; +an unset or empty compiler setting keeps the normal `xcrun swiftc` path. A +missing or relative SDK/compiler path fails with an actionable prerequisite +error before compilation. An incompatible installation reports the compiler's +bounded diagnostics; REA does not repair or replace the toolchain. + +For example, select paths from your existing Xcode or Command Line Tools +installation **before** entering an enforced write boundary: + +```sh +export REA_DARWIN_SWIFTC_PATH="$(xcrun --find swiftc)" +export SDKROOT="$(xcrun --show-sdk-path)" +export REA_DARWIN_MODULE_CACHE_PATH="$PWD/rea-cache/modules" +export TMPDIR="$PWD/rea-cache/tmp" +mkdir -p "$TMPDIR" +rea analyze-javascript-application ./fixture --artifact-format directory +``` + +Keep the selected compiler and SDK paired. Permit REA's evidence/output paths +and its temporary directory as well as the caller-owned module cache. REA does +not create a sandbox or claim that these settings contain every write made by +arbitrary toolchains or analysis providers. They bypass REA's `xcrun` lookup +and select the helper's Swift/Clang cache without disabling ownership checks. +The caller-owned cache survives cancellation and cleanup; per-run compiler +intermediates and the compiled helper remain REA-owned temporary files. + +The boundary must also permit process-table inspection. On macOS, `/bin/ps` +is setuid and a blanket sandbox may reject its launch with `spawn EPERM`. +REA then preserves ownership uncertainty and can report `cleanup_incomplete`; +it does not disable verification to continue. A sandbox that explicitly allows +this read-only process inspector is a separate prerequisite from the compiler +and cache settings. diff --git a/src/process/DarwinProcessRunTokenReader.ts b/src/process/DarwinProcessRunTokenReader.ts index 3fbb44a6ae..5aafcd7875 100644 --- a/src/process/DarwinProcessRunTokenReader.ts +++ b/src/process/DarwinProcessRunTokenReader.ts @@ -1,7 +1,7 @@ import { mkdir, mkdtemp, rm } from "node:fs/promises"; import { rmSync } from "node:fs"; import { tmpdir } from "node:os"; -import { join } from "node:path"; +import { isAbsolute, join } from "node:path"; import { fileURLToPath } from "node:url"; import { z } from "zod"; @@ -163,14 +163,19 @@ const compilerOutcomeDescription = ( const compilerInspectionError = ( cause: unknown, timeoutMs: number, + compiler: DarwinProcessReaderCompiler, ): DarwinProcessOwnershipInspectionError => { const outcome = compilerOutcome(cause, timeoutMs); const code = outcome?.code ?? "unknown"; const description = compilerOutcomeDescription(outcome, code); + const launcher = + compiler.kind === "xcrun" + ? "xcrun" + : "the selected Swift compiler (REA_DARWIN_SWIFTC_PATH)"; const prefix = code === "ENOENT" - ? "macOS process ownership inspection requires the Apple Swift compiler via xcrun" - : "macOS process ownership helper compilation failed via xcrun"; + ? `macOS process ownership inspection requires the Apple Swift compiler via ${launcher}` + : `macOS process ownership helper compilation failed via ${launcher}`; const stderr = boundedUtf8Text(outcome?.stderr ?? "", STDERR_EXCERPT_LIMIT); const diagnostics = stderr.text.trim(); const truncated = @@ -235,15 +240,35 @@ const waitForCompilation = async ( }); }; +/** Explicit tool selection; direct Swift compilation also needs its matching SDK. */ +export type DarwinProcessReaderCompiler = + | { readonly kind: "xcrun"; readonly executable: string } + | { + readonly kind: "swiftc"; + readonly executable: string; + readonly sdkRoot: string; + }; + /** Compile and retain the narrow Darwin sysctl reader for this REA process. */ export const createDarwinProcessRunTokenReader = ( options: { - readonly xcrun?: string; + readonly compiler?: DarwinProcessReaderCompiler; /** Deadline for the helper compilation; the default keeps verification fast. */ readonly compileTimeoutMs?: number; + /** + * Caller-owned directory holding the compiler's module cache. Unset keeps + * Swift's reusable default cache; a supplied directory outlives this + * reader because cleanup only removes the per-run helper root. + */ + readonly moduleCachePath?: string; } = {}, ) => { + const compiler = options.compiler ?? { + kind: "xcrun", + executable: "/usr/bin/xcrun", + }; const compilerTimeoutMs = options.compileTimeoutMs ?? COMPILER_TIMEOUT_MS; + const moduleCachePath = options.moduleCachePath ?? ""; let root: string | undefined; let executable: string | undefined; let compilation: ProcessReaderCompilation | undefined; @@ -266,6 +291,14 @@ export const createDarwinProcessRunTokenReader = ( const runCompilation = async (signal: AbortSignal): Promise => { let operationRoot: string | undefined; try { + if ( + compiler.kind === "swiftc" && + (!isAbsolute(compiler.executable) || !isAbsolute(compiler.sdkRoot)) + ) { + throw new DarwinProcessOwnershipInspectionError( + "Direct Darwin helper compilation requires absolute REA_DARWIN_SWIFTC_PATH and SDKROOT paths selecting a compatible installed Swift compiler and macOS SDK.", + ); + } operationRoot = await mkdtemp(join(tmpdir(), "rea-process-token-")); root = operationRoot; process.once("exit", cleanupAtExit); @@ -280,23 +313,30 @@ export const createDarwinProcessRunTokenReader = ( ...process.env, TMPDIR: compilerTemporary, }; - // Use Swift's reusable default cache for REA's internal helper compilation. - delete compilerEnvironment.CLANG_MODULE_CACHE_PATH; + const compilerArguments = + compiler.kind === "xcrun" ? ["swiftc"] : ["-sdk", compiler.sdkRoot]; + if (compiler.kind === "swiftc") + compilerEnvironment.SDKROOT = compiler.sdkRoot; + if (moduleCachePath === "") { + // Use Swift's reusable default cache for REA's internal helper compilation. + delete compilerEnvironment.CLANG_MODULE_CACHE_PATH; + } else { + await mkdir(moduleCachePath, { recursive: true }); + compilerArguments.push("-module-cache-path", moduleCachePath); + compilerEnvironment.CLANG_MODULE_CACHE_PATH = moduleCachePath; + } + compilerArguments.push(...sourceFiles, "-o", output); try { - await execFileOutput( - options.xcrun ?? "/usr/bin/xcrun", - ["swiftc", ...sourceFiles, "-o", output], - { - timeout: compilerTimeoutMs, - maxBuffer: 1024 * 1024, - signal, - stopSignal: SWIFTC_INTERRUPT, - env: compilerEnvironment, - }, - ); + await execFileOutput(compiler.executable, compilerArguments, { + timeout: compilerTimeoutMs, + maxBuffer: 1024 * 1024, + signal, + stopSignal: SWIFTC_INTERRUPT, + env: compilerEnvironment, + }); } catch (cause: unknown) { if (isExpectedAbort(cause, signal)) throw cause; - throw compilerInspectionError(cause, compilerTimeoutMs); + throw compilerInspectionError(cause, compilerTimeoutMs, compiler); } if (signal.aborted || closed) throw abortReason(signal); executable = output; diff --git a/src/process/ProcessOwnershipObservation.ts b/src/process/ProcessOwnershipObservation.ts index f44feb960e..834335d57e 100644 --- a/src/process/ProcessOwnershipObservation.ts +++ b/src/process/ProcessOwnershipObservation.ts @@ -4,7 +4,10 @@ import { AnalysisCapabilityUnavailableError } from "../domain/analysisErrorCore. import { launcherIdentityFailure } from "./ProcessOwnershipIdentity.js"; import { descendantsOf, liveProcesses } from "./ProcessOwnershipProcessTree.js"; import { execFileOutput } from "./ExecFileOutput.js"; -import { createDarwinProcessRunTokenReader } from "./DarwinProcessRunTokenReader.js"; +import { + createDarwinProcessRunTokenReader, + type DarwinProcessReaderCompiler, +} from "./DarwinProcessRunTokenReader.js"; import { readProcessRunTokens } from "./ProcessRunTokenObservations.js"; import type { OwnedProcessGroup, @@ -219,16 +222,32 @@ export const createSystemProcessOwnershipHost = ( hostEnvironment: NodeJS.ProcessEnv = process.env, options: { /** Override the compiler executable used by the Darwin native reader. */ - readonly darwinXcrun?: string; + readonly darwinCompiler?: DarwinProcessReaderCompiler; + /** Override the caller-owned module cache used compiling that helper. */ + readonly darwinModuleCachePath?: string; } = {}, ): ProcessOwnershipHost => { + const darwinModuleCachePath = + options.darwinModuleCachePath ?? + hostEnvironment.REA_DARWIN_MODULE_CACHE_PATH; + const swiftcPath = hostEnvironment.REA_DARWIN_SWIFTC_PATH; + const darwinCompiler = + options.darwinCompiler ?? + (swiftcPath === undefined || swiftcPath === "" + ? undefined + : { + kind: "swiftc" as const, + executable: swiftcPath, + sdkRoot: hostEnvironment.SDKROOT ?? "", + }); const darwinTokens = platform === "darwin" - ? createDarwinProcessRunTokenReader( - options.darwinXcrun === undefined + ? createDarwinProcessRunTokenReader({ + ...(darwinCompiler === undefined ? {} : { compiler: darwinCompiler }), + ...(darwinModuleCachePath === undefined ? {} - : { xcrun: options.darwinXcrun }, - ) + : { moduleCachePath: darwinModuleCachePath }), + }) : undefined; const listProcesses = async (signal?: AbortSignal) => { if (platform === "win32") return []; diff --git a/tests/boundary/process/darwinProcessRunTokenReader.test.ts b/tests/boundary/process/darwinProcessRunTokenReader.test.ts index caae8b2bec..8acafbd443 100644 --- a/tests/boundary/process/darwinProcessRunTokenReader.test.ts +++ b/tests/boundary/process/darwinProcessRunTokenReader.test.ts @@ -5,6 +5,7 @@ import { mkdir, mkdtemp, readFile, + readdir, rm, symlink, writeFile, @@ -156,7 +157,10 @@ type ElectronCaptureOutcome = it("reports an actionable missing Swift compiler without installing it", async () => { const reader = createDarwinProcessRunTokenReader({ - xcrun: join(tmpdir(), "rea-no-such-xcrun"), + compiler: { + kind: "xcrun", + executable: join(tmpdir(), "rea-no-such-xcrun"), + }, }); try { await expect( @@ -198,7 +202,9 @@ it.skipIf(process.platform === "win32")( '#!/usr/bin/env node\nprocess.stderr.write("module cache is not writable\\n" + "x".repeat(12000)); process.exitCode = 1;\n', ); await chmod(executable, 0o755); - const reader = createDarwinProcessRunTokenReader({ xcrun: executable }); + const reader = createDarwinProcessRunTokenReader({ + compiler: { kind: "xcrun", executable: executable }, + }); try { const failure = await preparationFailure(reader); expect(failure.compilerOutcome).toMatchObject({ @@ -248,7 +254,7 @@ it.skipIf(process.platform === "win32")( ); await chmod(executable, 0o755); const reader = createDarwinProcessRunTokenReader({ - xcrun: executable, + compiler: { kind: "xcrun", executable: executable }, // Headroom for the child interpreter's cold start under suite load. compileTimeoutMs: 3_000, }); @@ -280,7 +286,9 @@ it.skipIf(process.platform === "win32")( '#!/usr/bin/env node\nprocess.stderr.write("fatal: segmentation fault during type checking\\n");\nprocess.kill(process.pid, "SIGSEGV");\n', ); await chmod(executable, 0o755); - const reader = createDarwinProcessRunTokenReader({ xcrun: executable }); + const reader = createDarwinProcessRunTokenReader({ + compiler: { kind: "xcrun", executable: executable }, + }); try { await expect(preparationFailure(reader)).resolves.toMatchObject({ message: @@ -310,7 +318,9 @@ it.skipIf(process.platform === "win32")( `#!/usr/bin/env node\nprocess.stderr.write(${JSON.stringify(`${diagnostic}\n`)}); process.exitCode = 1;\n`, ); await chmod(executable, 0o755); - const reader = createDarwinProcessRunTokenReader({ xcrun: executable }); + const reader = createDarwinProcessRunTokenReader({ + compiler: { kind: "xcrun", executable: executable }, + }); try { const failure = await preparationFailure(reader); const outcome = failure.compilerOutcome; @@ -376,7 +386,7 @@ it.skipIf(process.platform === "win32")( ); const fakeCompiler = await writeBlockingCompiler(directory); const reader = createDarwinProcessRunTokenReader({ - xcrun: fakeCompiler.executable, + compiler: { kind: "xcrun", executable: fakeCompiler.executable }, }); const preparation = reader.prepare(); try { @@ -390,6 +400,242 @@ it.skipIf(process.platform === "win32")( }, ); +const writeCacheRecordingCompiler = async (directory: string) => { + const executable = join(directory, "fake-cache-xcrun"); + const statePath = join(directory, "cache-compile-state.json"); + const source = `#!/usr/bin/env node +const fs = process.getBuiltinModule("node:fs"); +const path = process.getBuiltinModule("node:path"); +const flagIndex = process.argv.indexOf("-module-cache-path"); +const cache = flagIndex === -1 ? undefined : process.argv[flagIndex + 1]; +fs.writeFileSync(${JSON.stringify(statePath)}, JSON.stringify({ + arguments: process.argv.slice(2), + moduleCacheEnvironment: process.env.CLANG_MODULE_CACHE_PATH ?? null, +})); +if (cache !== undefined) { + fs.mkdirSync(cache, { recursive: true }); + fs.writeFileSync(path.join(cache, "written-into-cache"), "kept"); +} +const output = process.argv[process.argv.indexOf("-o") + 1]; +fs.writeFileSync(output, "#!/bin/sh\\nexit 0\\n"); +fs.chmodSync(output, 0o755); +`; + await writeFile(executable, source); + await chmod(executable, 0o755); + return { executable, statePath }; +}; + +const readCacheCompileState = async (statePath: string) => + JSON.parse(await readFile(statePath, "utf8")) as { + arguments: string[]; + moduleCacheEnvironment: string | null; + }; + +const existsPath = async (target: string) => + access(target).then( + () => true, + () => false, + ); + +it.skipIf(process.platform === "win32")( + "forwards a caller-owned module cache and keeps it after the helper root is removed", + async () => { + const directory = await createTestTempDirectory( + "rea-process-token-caller-cache-test-", + ); + const cachePath = join(directory, "caller", " modules "); + const fakeCompiler = await writeCacheRecordingCompiler(directory); + const reader = createDarwinProcessRunTokenReader({ + compiler: { kind: "xcrun", executable: fakeCompiler.executable }, + moduleCachePath: cachePath, + }); + let executable: string; + try { + executable = await reader.prepare(); + } finally { + await reader.close(); + } + const state = await readCacheCompileState(fakeCompiler.statePath); + const flagIndex = state.arguments.indexOf("-module-cache-path"); + expect(state.arguments[0]).toBe("swiftc"); + expect(flagIndex).toBe(1); + expect(state.arguments[flagIndex + 1]).toBe(cachePath); + expect(state.moduleCacheEnvironment).toBe(cachePath); + expect(await existsPath(join(cachePath, "written-into-cache"))).toBe(true); + expect(await existsPath(executable)).toBe(false); + }, +); + +it.skipIf(process.platform === "win32")( + "selects the caller module cache through REA_DARWIN_MODULE_CACHE_PATH", + async () => { + const directory = await createTestTempDirectory( + "rea-process-token-host-cache-test-", + ); + const cachePath = join(directory, "from-environment", "modules"); + const fakeCompiler = await writeCacheRecordingCompiler(directory); + const host = createSystemProcessOwnershipHost( + "darwin", + { ...process.env, REA_DARWIN_MODULE_CACHE_PATH: cachePath }, + { + darwinCompiler: { kind: "xcrun", executable: fakeCompiler.executable }, + }, + ); + try { + await host.prepare?.(AbortSignal.timeout(15_000)); + const state = await readCacheCompileState(fakeCompiler.statePath); + const flagIndex = state.arguments.indexOf("-module-cache-path"); + expect(state.arguments[flagIndex + 1]).toBe(cachePath); + } finally { + await host.close?.(); + } + }, +); + +it.skipIf(process.platform === "win32")( + "keeps the shared module cache for the system host without the setting", + async () => { + const directory = await createTestTempDirectory( + "rea-process-token-host-default-test-", + ); + vi.stubEnv("CLANG_MODULE_CACHE_PATH", join(directory, "ambient-cache")); + const hostEnvironment = { ...process.env }; + delete hostEnvironment.REA_DARWIN_MODULE_CACHE_PATH; + const fakeCompiler = await writeCacheRecordingCompiler(directory); + const host = createSystemProcessOwnershipHost("darwin", hostEnvironment, { + darwinCompiler: { kind: "xcrun", executable: fakeCompiler.executable }, + }); + try { + await host.prepare?.(AbortSignal.timeout(15_000)); + const state = await readCacheCompileState(fakeCompiler.statePath); + expect(state.arguments).not.toContain("-module-cache-path"); + expect(state.moduleCacheEnvironment).toBeNull(); + } finally { + await host.close?.(); + } + }, +); + +it.skipIf(process.platform === "win32")( + "bypasses xcrun for an explicitly selected Swift compiler and SDK", + async () => { + const directory = await createTestTempDirectory( + "rea-process-token-direct-compiler-", + ); + const fakeCompiler = await writeCacheRecordingCompiler(directory); + const sdkRoot = join(directory, "MacOSX.sdk"); + const cachePath = join(directory, "modules"); + const host = createSystemProcessOwnershipHost("darwin", { + ...process.env, + REA_DARWIN_SWIFTC_PATH: fakeCompiler.executable, + SDKROOT: sdkRoot, + REA_DARWIN_MODULE_CACHE_PATH: cachePath, + }); + try { + await host.prepare?.(AbortSignal.timeout(15_000)); + const state = await readCacheCompileState(fakeCompiler.statePath); + expect(state.arguments.slice(0, 2)).toEqual(["-sdk", sdkRoot]); + expect(state.arguments).not.toContain("swiftc"); + expect(state.arguments).toContain(cachePath); + } finally { + await host.close?.(); + } + }, +); + +it.skipIf(process.platform === "win32")( + "reports the missing SDK prerequisite before starting a direct compiler", + async () => { + const directory = await createTestTempDirectory( + "rea-process-token-direct-no-sdk-", + ); + const fakeCompiler = await writeCacheRecordingCompiler(directory); + const host = createSystemProcessOwnershipHost("darwin", { + ...process.env, + REA_DARWIN_SWIFTC_PATH: fakeCompiler.executable, + SDKROOT: "", + }); + try { + await expect( + host.prepare?.(AbortSignal.timeout(15_000)), + ).rejects.toMatchObject({ + name: "DarwinProcessOwnershipInspectionError", + message: expect.stringContaining("REA_DARWIN_SWIFTC_PATH and SDKROOT"), + }); + expect(await existsPath(fakeCompiler.statePath)).toBe(false); + } finally { + await host.close?.(); + } + }, +); + +it.skipIf(process.platform === "win32")( + "identifies a missing selected Swift compiler rather than xcrun", + async () => { + const directory = await createTestTempDirectory( + "rea-process-token-direct-missing-", + ); + const host = createSystemProcessOwnershipHost("darwin", { + ...process.env, + REA_DARWIN_SWIFTC_PATH: join(directory, "missing-swiftc"), + SDKROOT: join(directory, "MacOSX.sdk"), + }); + try { + await expect( + host.prepare?.(AbortSignal.timeout(15_000)), + ).rejects.toMatchObject({ + message: expect.stringContaining( + "selected Swift compiler (REA_DARWIN_SWIFTC_PATH)", + ), + compilerOutcome: expect.objectContaining({ + outcome: "spawn_failed", + code: "ENOENT", + }), + }); + } finally { + await host.close?.(); + } + }, +); + +it.skipIf(!onDarwin)( + "compiles the helper against the caller cache with the host compiler", + async () => { + const directory = await createTestTempDirectory( + "rea-process-token-real-cache-test-", + ); + const cachePath = join(directory, "caller", "modules"); + const { stdout: compilerPath } = await execFileAsync("/usr/bin/xcrun", [ + "--find", + "swiftc", + ]); + const { stdout: sdkRoot } = await execFileAsync("/usr/bin/xcrun", [ + "--show-sdk-path", + ]); + const host = createSystemProcessOwnershipHost("darwin", { + ...process.env, + REA_DARWIN_MODULE_CACHE_PATH: cachePath, + REA_DARWIN_SWIFTC_PATH: compilerPath.trim(), + SDKROOT: sdkRoot.trim(), + }); + try { + await host.prepare?.(AbortSignal.timeout(180_000)); + const entries = await readdir(cachePath); + expect( + entries.some( + (name) => + name.endsWith(".swiftmodule") || + name.endsWith(".pcm") || + name === "Modules", + ), + ).toBe(true); + } finally { + await host.close?.(); + } + expect(await existsPath(join(directory, "caller"))).toBe(true); + }, +); + it.skipIf(process.platform === "win32")( "rethrows compile cancellation, removes its temporary root, and permits retry", async () => { @@ -398,7 +644,7 @@ it.skipIf(process.platform === "win32")( ); const fakeCompiler = await writeBlockingCompiler(directory); const reader = createDarwinProcessRunTokenReader({ - xcrun: fakeCompiler.executable, + compiler: { kind: "xcrun", executable: fakeCompiler.executable }, }); const controller = new AbortController(); const compilation = reader.prepare(controller.signal).then( @@ -483,7 +729,7 @@ it.skipIf(process.platform === "win32")( ); const compiler = await writeTemporaryObjectCompiler(directory); const reader = createDarwinProcessRunTokenReader({ - xcrun: compiler.executable, + compiler: { kind: "xcrun", executable: compiler.executable }, }); const controller = new AbortController(); const preparation = reader.prepare(controller.signal).then( @@ -532,7 +778,7 @@ it.skipIf(process.platform === "win32").each(["first", "second"] as const)( ); const fakeCompiler = await writeBlockingCompiler(directory); const reader = createDarwinProcessRunTokenReader({ - xcrun: fakeCompiler.executable, + compiler: { kind: "xcrun", executable: fakeCompiler.executable }, }); const firstController = new AbortController(); const secondController = new AbortController(); @@ -590,7 +836,7 @@ it.skipIf(process.platform === "win32" || process.getuid?.() === 0)( ); const fakeCompiler = await writeBlockingCompiler(directory); const reader = createDarwinProcessRunTokenReader({ - xcrun: fakeCompiler.executable, + compiler: { kind: "xcrun", executable: fakeCompiler.executable }, }); const controller = new AbortController(); const preparation = reader.prepare(controller.signal).then( @@ -631,7 +877,7 @@ it.skipIf(process.platform === "win32")( ); const fakeCompiler = await writeBlockingCompiler(directory); const reader = createDarwinProcessRunTokenReader({ - xcrun: fakeCompiler.executable, + compiler: { kind: "xcrun", executable: fakeCompiler.executable }, }); const preparation = reader.prepare().then( () => ({ state: "fulfilled" as const }), @@ -664,7 +910,7 @@ it.skipIf(process.platform === "win32")( ); const fakeCompiler = await writeBlockingCompiler(directory); const reader = createDarwinProcessRunTokenReader({ - xcrun: fakeCompiler.executable, + compiler: { kind: "xcrun", executable: fakeCompiler.executable }, }); const prepareSpy = vi .spyOn(systemProcessOwnershipHost, "prepare") @@ -716,7 +962,7 @@ it.skipIf(process.platform === "win32").each(["baseline", "tokens"] as const)( ); const fakeCompiler = await writeBlockingIdentityCompiler(directory); const host = createSystemProcessOwnershipHost("darwin", process.env, { - darwinXcrun: fakeCompiler.executable, + darwinCompiler: { kind: "xcrun", executable: fakeCompiler.executable }, }); const controller = new AbortController(); let helperRoot: string | undefined; @@ -775,7 +1021,7 @@ it.skipIf(process.platform === "win32")( ); const fakeCompiler = await writeBlockingIdentityCompiler(directory); const reader = createDarwinProcessRunTokenReader({ - xcrun: fakeCompiler.executable, + compiler: { kind: "xcrun", executable: fakeCompiler.executable }, }); const prepareSpy = vi .spyOn(systemProcessOwnershipHost, "prepare")