Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build-package-artifact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ jobs:
set -e
missing=0
echo "simulator-server inventory:"
for plat in darwin linux; do
for plat in darwin linux linux-arm64; do
p="packages/argent/bin/${plat}/simulator-server"
if [[ -x "$p" ]]; then
size=$(wc -c <"$p")
echo " ok ${plat}: ${p} (${size} bytes)"
echo " ok ${plat}: ${p} (${size} bytes; $(file -b "$p"))"
else
echo " missing ${plat}: ${p}"
if [[ "$plat" == "darwin" ]]; then missing=1; fi
if [[ "$plat" == "linux" && "${ARGENT_REQUIRE_LINUX_BINARY}" == "1" ]]; then missing=1; fi
if [[ "$plat" == linux* && "${ARGENT_REQUIRE_LINUX_BINARY}" == "1" ]]; then missing=1; fi
fi
done
[[ "$missing" == "0" ]] || { echo "::error::required simulator-server binary missing"; exit 1; }
Expand Down
15 changes: 8 additions & 7 deletions .github/workflows/publish-next.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,27 @@ jobs:
- run: npm run build -w @swmansion/argent

# Per-platform bundle layout (commit 80e724a). darwin is always required;
# Linux is gated by ARGENT_REQUIRE_LINUX_BINARY so the existing release
# pipeline keeps working until radon's Linux artifact is reliably present
# — flip the env var to "1" once that lands. The inventory always logs
# what shipped so a silently-missing Linux binary shows up in the run.
# both Linux keys (x86_64 "linux" and arm64 "linux-arm64") are gated by
# ARGENT_REQUIRE_LINUX_BINARY so the existing release pipeline keeps
# working until radon's Linux artifacts are reliably present — flip the
# env var to "1" once that lands. The inventory always logs what shipped
# so a silently-missing Linux binary shows up in the run.
- name: Verify per-platform simulator-server binaries
env:
ARGENT_REQUIRE_LINUX_BINARY: "0"
run: |
set -e
missing=0
echo "simulator-server inventory:"
for plat in darwin linux; do
for plat in darwin linux linux-arm64; do
p="packages/argent/bin/${plat}/simulator-server"
if [[ -x "$p" ]]; then
size=$(wc -c <"$p")
echo " ✓ ${plat}: ${p} (${size} bytes)"
echo " ✓ ${plat}: ${p} (${size} bytes; $(file -b "$p"))"
else
echo " ✗ ${plat}: ${p} — missing"
if [[ "$plat" == "darwin" ]]; then missing=1; fi
if [[ "$plat" == "linux" && "${ARGENT_REQUIRE_LINUX_BINARY}" == "1" ]]; then missing=1; fi
if [[ "$plat" == linux* && "${ARGENT_REQUIRE_LINUX_BINARY}" == "1" ]]; then missing=1; fi
fi
done
[[ "$missing" == "0" ]] || { echo "::error::required simulator-server binary missing"; exit 1; }
Expand Down
15 changes: 8 additions & 7 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,27 @@ jobs:
- run: npm run build -w @swmansion/argent

# Per-platform bundle layout (commit 80e724a). darwin is always required;
# Linux is gated by ARGENT_REQUIRE_LINUX_BINARY so the existing release
# pipeline keeps working until radon's Linux artifact is reliably present
# — flip the env var to "1" once that lands. The inventory always logs
# what shipped so a silently-missing Linux binary shows up in the run.
# both Linux keys (x86_64 "linux" and arm64 "linux-arm64") are gated by
# ARGENT_REQUIRE_LINUX_BINARY so the existing release pipeline keeps
# working until radon's Linux artifacts are reliably present — flip the
# env var to "1" once that lands. The inventory always logs what shipped
# so a silently-missing Linux binary shows up in the run.
- name: Verify per-platform simulator-server binaries
env:
ARGENT_REQUIRE_LINUX_BINARY: "0"
run: |
set -e
missing=0
echo "simulator-server inventory:"
for plat in darwin linux; do
for plat in darwin linux linux-arm64; do
p="packages/argent/bin/${plat}/simulator-server"
if [[ -x "$p" ]]; then
size=$(wc -c <"$p")
echo " ✓ ${plat}: ${p} (${size} bytes)"
echo " ✓ ${plat}: ${p} (${size} bytes; $(file -b "$p"))"
else
echo " ✗ ${plat}: ${p} — missing"
if [[ "$plat" == "darwin" ]]; then missing=1; fi
if [[ "$plat" == "linux" && "${ARGENT_REQUIRE_LINUX_BINARY}" == "1" ]]; then missing=1; fi
if [[ "$plat" == linux* && "${ARGENT_REQUIRE_LINUX_BINARY}" == "1" ]]; then missing=1; fi
fi
done
[[ "$missing" == "0" ]] || { echo "::error::required simulator-server binary missing"; exit 1; }
Expand Down
14 changes: 11 additions & 3 deletions packages/argent/scripts/argent-simulator-server.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@ const { spawn } = require("node:child_process");
const path = require("node:path");
const fs = require("node:fs");

const binary = path.join(__dirname, process.platform, "simulator-server");
// Mirrors hostPlatformKey() in @argent/native-devtools-ios: darwin ships a
// universal (lipo) binary, but Linux binaries are single-arch ELFs, so arm64
// Linux resolves to its own "linux-arm64" directory next to the x86_64 one
// ("linux"). Duplicated here because the dispatcher must stay a standalone
// file — it ships verbatim as the npm `bin` entry.
const platformKey =
process.platform === "linux" && process.arch === "arm64" ? "linux-arm64" : process.platform;

const binary = path.join(__dirname, platformKey, "simulator-server");
if (!fs.existsSync(binary)) {
console.error(
`argent-simulator-server: no binary for platform "${process.platform}" at ${binary}.\n` +
`Supported hosts today: darwin, linux.`
`argent-simulator-server: no binary for platform "${platformKey}" at ${binary}.\n` +
`Supported hosts today: darwin, linux (x86_64 and arm64).`
);
process.exit(1);
}
Expand Down
4 changes: 3 additions & 1 deletion packages/argent/scripts/bundle-tools.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ const AX_TCP_BIN_SRC = path.resolve(BIN_SRC_ROOT, "darwin/tcp/ax-service");
const BIN_DIR = path.resolve(__dirname, "../bin");
const AX_BIN_DEST = path.resolve(BIN_DIR, "darwin/ax-service");
const AX_TCP_BIN_DEST = path.resolve(BIN_DIR, "darwin/tcp/ax-service");
const SUPPORTED_HOST_PLATFORMS = ["darwin", "linux"];
// Host platform keys (see hostPlatformKey() in @argent/native-devtools-ios):
// darwin is a universal binary; Linux ships one single-arch ELF per key.
const SUPPORTED_HOST_PLATFORMS = ["darwin", "linux", "linux-arm64"];
// Generated module pinning the Perfetto version that stamps the bundled
// trace_processor.wasm. esbuild inlines it into every bundle via the
// @argent/native-devtools-android alias.
Expand Down
41 changes: 39 additions & 2 deletions packages/argent/test/dispatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,26 @@ beforeEach(() => {
}
});

// Mirrors the dispatcher's host platform key: process.platform, except
// "linux-arm64" on arm64 Linux. Keeps the suite green on every host the
// dispatcher itself supports.
const HOST_PLATFORM_KEY =
process.platform === "linux" && process.arch === "arm64" ? "linux-arm64" : process.platform;

/**
* Stage a tmp dispatcher dir. Returns the path to the copied dispatcher .cjs
* (suitable to pass to `node`) and the per-platform bin dir where the caller
* should drop a fake simulator-server.
*/
function stageDispatcher(): { dispatcher: string; platformDir: string } {
function stageDispatcher(platformKey: string = HOST_PLATFORM_KEY): {
dispatcher: string;
platformDir: string;
} {
const dir = fs.mkdtempSync(path.join(tmpRoot, "case-"));
sessionTmpDirs.push(dir);
const dispatcher = path.join(dir, "argent-simulator-server.cjs");
fs.copyFileSync(DISPATCHER_SRC, dispatcher);
const platformDir = path.join(dir, process.platform);
const platformDir = path.join(dir, platformKey);
fs.mkdirSync(platformDir, { recursive: true });
return { dispatcher, platformDir };
}
Expand Down Expand Up @@ -107,6 +116,34 @@ describe("argent-simulator-server dispatcher", () => {
expect(result.stderr).toContain("Supported hosts today: darwin, linux");
});

it("resolves the linux-arm64 binary when running on arm64 Linux", async () => {
// The dispatcher reads process.platform / process.arch at require time,
// so the test fakes both inside a child process (via `node -e`) before
// requiring the dispatcher — exercising the real resolution logic on any
// host. The fake binary is a bash script, so executing it works even
// though the staged directory is named for a different platform.
const { dispatcher, platformDir } = stageDispatcher("linux-arm64");
writeFakeBinary(platformDir, "#!/usr/bin/env bash\necho arm64-ok\nexit 0\n");
const stub =
'Object.defineProperty(process, "platform", { value: "linux" });' +
'Object.defineProperty(process, "arch", { value: "arm64" });' +
"require(process.argv[1]);";
const result = await new Promise<RunResult>((resolve, reject) => {
const child = spawn(process.execPath, ["-e", stub, dispatcher], {
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.on("data", (c) => (stdout += c.toString()));
child.stderr.on("data", (c) => (stderr += c.toString()));
child.on("error", reject);
child.on("exit", (code, signal) => resolve({ exitCode: code, signal, stdout, stderr }));
});
expect(result.stderr).toBe("");
expect(result.stdout).toContain("arm64-ok");
expect(result.exitCode).toBe(0);
});

it("propagates the child's exit code on a clean exit", async () => {
// Fake binary exits 0 — dispatcher must mirror that exactly. A
// `process.exit(code ?? 1)` regression that defaults 0→1 would surface
Expand Down
23 changes: 18 additions & 5 deletions packages/native-devtools-ios/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,23 @@ export const nativeDevtoolsDylibPathTcp = () => {

// simulator-server is a host-side binary that talks to both iOS Simulators
// (macOS) and Android emulators (any host with `adb`). Each platform's
// binary lives in its own subdirectory of bin/ — keyed by `process.platform`
// — so a single npm package can ship both without colliding filenames.
// binary lives in its own subdirectory of bin/ — keyed by the host platform
// key below — so a single npm package can ship all of them without colliding
// filenames.
//
// The key is `process.platform`, except on arm64 Linux where it is
// "linux-arm64": darwin ships a universal (lipo) binary so one "darwin" dir
// serves both arches, but Linux binaries are single-arch ELFs, so arm64 gets
// its own directory next to the x86_64 one ("linux", the pre-arm64 name kept
// for backward compatibility).
export function hostPlatformKey(): string {
if (process.platform === "linux" && process.arch === "arm64") {
return "linux-arm64";
}
return process.platform;
}
function platformBinDir(): string {
return path.join(BIN_DIR, process.platform);
return path.join(BIN_DIR, hostPlatformKey());
}
// TCP dir: <platform>/tcp by default; ARGENT_SIMULATOR_SERVER_TCP_DIR overrides the whole path.
function platformTcpBinDir(): string {
Expand All @@ -74,8 +87,8 @@ export function simulatorServerBinaryPath(): string {
? ` Found a binary at the old flat path ${flat}; move it to ${p} or update ARGENT_SIMULATOR_SERVER_DIR to point at the parent of the platform subdirectory.`
: "";
throw new Error(
`simulator-server binary not found for platform "${process.platform}" at ${p}. ` +
`Supported hosts today: darwin, linux.${migrationHint}`
`simulator-server binary not found for platform "${hostPlatformKey()}" at ${p}. ` +
`Supported hosts today: darwin, linux (x86_64 and arm64).${migrationHint}`
);
}
return p;
Expand Down
69 changes: 69 additions & 0 deletions packages/native-devtools-ios/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,26 @@ import type * as ResolverModule from "../src/index";

let tmpRoot = "";
const originalPlatform = process.platform;
const originalArch = process.arch;
const originalDevtoolsDir = process.env.ARGENT_NATIVE_DEVTOOLS_DIR;
const originalSimulatorDir = process.env.ARGENT_SIMULATOR_SERVER_DIR;

function setPlatform(value: NodeJS.Platform) {
Object.defineProperty(process, "platform", { value, configurable: true });
}

function setArch(value: NodeJS.Architecture) {
Object.defineProperty(process, "arch", { value, configurable: true });
}

beforeAll(() => {
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "argent-resolver-"));
});

afterAll(() => {
if (tmpRoot) fs.rmSync(tmpRoot, { recursive: true, force: true });
setPlatform(originalPlatform);
setArch(originalArch);
if (originalDevtoolsDir === undefined) delete process.env.ARGENT_NATIVE_DEVTOOLS_DIR;
else process.env.ARGENT_NATIVE_DEVTOOLS_DIR = originalDevtoolsDir;
if (originalSimulatorDir === undefined) delete process.env.ARGENT_SIMULATOR_SERVER_DIR;
Expand All @@ -39,6 +45,7 @@ afterAll(() => {

afterEach(() => {
setPlatform(originalPlatform);
setArch(originalArch);
});

/**
Expand Down Expand Up @@ -138,6 +145,68 @@ describe("simulator-server path resolution", () => {
});
});

describe("host platform key (arch-aware Linux bin dirs)", () => {
// Linux binaries are single-arch ELFs (unlike the universal macOS binary),
// so arm64 Linux resolves to its own "linux-arm64" directory while x86_64
// keeps the pre-arm64 "linux" name. Without this split, an arm64 host would
// silently pick up the x86_64 ELF and fail with ENOEXEC at spawn time.
it("resolves bin/linux-arm64 on arm64 Linux", async () => {
setPlatform("linux");
setArch("arm64");
const dir = fs.mkdtempSync(path.join(tmpRoot, "linux-arm64-"));
const platDir = path.join(dir, "linux-arm64");
fs.mkdirSync(platDir, { recursive: true });
const binPath = path.join(platDir, "simulator-server");
fs.writeFileSync(binPath, "", { mode: 0o755 });
process.env.ARGENT_SIMULATOR_SERVER_DIR = dir;
const r = await loadResolver();
expect(r.hostPlatformKey()).toBe("linux-arm64");
expect(r.simulatorServerBinaryPath()).toBe(binPath);
expect(r.simulatorServerBinaryDir()).toBe(platDir);
});

it("keeps resolving bin/linux on x86_64 Linux", async () => {
setPlatform("linux");
setArch("x64");
const dir = fs.mkdtempSync(path.join(tmpRoot, "linux-x64-"));
const platDir = path.join(dir, "linux");
fs.mkdirSync(platDir, { recursive: true });
const binPath = path.join(platDir, "simulator-server");
fs.writeFileSync(binPath, "", { mode: 0o755 });
process.env.ARGENT_SIMULATOR_SERVER_DIR = dir;
const r = await loadResolver();
expect(r.hostPlatformKey()).toBe("linux");
expect(r.simulatorServerBinaryPath()).toBe(binPath);
});

it("keeps resolving bin/darwin on arm64 macOS (universal binary)", async () => {
setPlatform("darwin");
setArch("arm64");
const dir = fs.mkdtempSync(path.join(tmpRoot, "darwin-arm64-"));
const platDir = path.join(dir, "darwin");
fs.mkdirSync(platDir, { recursive: true });
const binPath = path.join(platDir, "simulator-server");
fs.writeFileSync(binPath, "", { mode: 0o755 });
process.env.ARGENT_SIMULATOR_SERVER_DIR = dir;
const r = await loadResolver();
expect(r.hostPlatformKey()).toBe("darwin");
expect(r.simulatorServerBinaryPath()).toBe(binPath);
});

it("names the linux-arm64 key in the missing-binary error", async () => {
// The error must name the directory actually searched ("linux-arm64"),
// not bare process.platform, so an arm64 user re-running the download
// script can tell which asset is absent.
setPlatform("linux");
setArch("arm64");
const dir = fs.mkdtempSync(path.join(tmpRoot, "linux-arm64-missing-"));
fs.mkdirSync(path.join(dir, "linux-arm64"), { recursive: true });
process.env.ARGENT_SIMULATOR_SERVER_DIR = dir;
const r = await loadResolver();
expect(() => r.simulatorServerBinaryPath()).toThrow(/linux-arm64/);
});
});

describe("ax-service path resolution", () => {
it("joins process.platform into the ax-service bin path", async () => {
if (originalPlatform !== "darwin") return;
Expand Down
14 changes: 9 additions & 5 deletions scripts/dev.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,20 @@ console.log("✓ Dispatcher TypeScript built\n");

// Copy the simulator-server binary for the current host platform into the
// platform-keyed subdir the runtime resolver looks at. Mirrors bundle-tools.cjs
// but only for `process.platform` — dev iterations don't need every host's
// binary alongside.
const BIN_DIR = path.join(ARGENT_PKG, "bin", process.platform);
const BIN_SRC = path.join(NATIVE_DEVTOOLS_PKG, "bin", process.platform, "simulator-server");
// but only for the current host's key — dev iterations don't need every
// host's binary alongside. The key mirrors hostPlatformKey() in
// @argent/native-devtools-ios (process.platform, except "linux-arm64" on
// arm64 Linux).
const HOST_PLATFORM_KEY =
process.platform === "linux" && process.arch === "arm64" ? "linux-arm64" : process.platform;
const BIN_DIR = path.join(ARGENT_PKG, "bin", HOST_PLATFORM_KEY);
const BIN_SRC = path.join(NATIVE_DEVTOOLS_PKG, "bin", HOST_PLATFORM_KEY, "simulator-server");
const BIN_DEST = path.join(BIN_DIR, "simulator-server");
fs.mkdirSync(BIN_DIR, { recursive: true });
if (fs.existsSync(BIN_SRC)) {
fs.copyFileSync(BIN_SRC, BIN_DEST);
fs.chmodSync(BIN_DEST, 0o755);
console.log(`✓ Copied simulator-server binary (${process.platform})`);
console.log(`✓ Copied simulator-server binary (${HOST_PLATFORM_KEY})`);
} else {
console.warn(
`⚠ simulator-server binary not found at ${BIN_SRC} — gestures won't work. ` +
Expand Down
Loading