Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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: 6 additions & 0 deletions .changeset/bright-parts-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"cmake-rn": patch
"react-native-node-api": patch
---

Add x86_64 and universal simulator triplets
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ jobs:
- run: npm ci
- run: npm run bootstrap
env:
CMAKE_RN_TRIPLETS: arm64-apple-ios-sim
CMAKE_RN_TRIPLETS: arm64;x86_64-apple-ios-sim
FERRIC_TARGETS: aarch64-apple-ios-sim
- run: npm run pod-install
working-directory: apps/test-app
Expand Down
4 changes: 2 additions & 2 deletions packages/cmake-rn/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ program = program.action(
for (const platform of Object.values(platforms)) {
// Forcing the types a bit here, since the platform id option is dynamically added
if ((baseOptions as Record<string, unknown>)[platform.id]) {
for (const triplet of platform.triplets) {
for (const triplet of await platform.defaultTriplets("release")) {
triplets.add(triplet);
}
}
Expand All @@ -196,7 +196,7 @@ program = program.action(
if (triplets.size === 0) {
for (const platform of Object.values(platforms)) {
if (platform.isSupportedByHost()) {
for (const triplet of await platform.defaultTriplets()) {
for (const triplet of await platform.defaultTriplets("development")) {
triplets.add(triplet);
}
}
Expand Down
6 changes: 4 additions & 2 deletions packages/cmake-rn/src/platforms/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,10 @@ export const platform: Platform<Triplet[], AndroidOpts> = {
"i686-linux-android",
"x86_64-linux-android",
],
defaultTriplets() {
if (process.arch === "arm64") {
defaultTriplets(purpose) {
if (purpose === "release") {
return [...this.triplets];
} else if (process.arch === "arm64") {
return ["aarch64-linux-android"];
} else if (process.arch === "x64") {
return ["x86_64-linux-android"];
Expand Down
94 changes: 70 additions & 24 deletions packages/cmake-rn/src/platforms/apple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,22 @@ const XCODE_SDK_NAMES = {
"x86_64-apple-darwin": "macosx",
"arm64-apple-darwin": "macosx",
"arm64;x86_64-apple-darwin": "macosx",

"arm64-apple-ios": "iphoneos",
"arm64-apple-ios-sim": "iphonesimulator",
"arm64-apple-tvos": "appletvos",
"x86_64-apple-ios-sim": "iphonesimulator",
"arm64;x86_64-apple-ios-sim": "iphonesimulator",

// "x86_64-apple-tvos": "appletvos",
"arm64-apple-tvos": "appletvos",
"x86_64-apple-tvos-sim": "appletvsimulator",
"arm64-apple-tvos-sim": "appletvsimulator",
"arm64;x86_64-apple-tvos-sim": "appletvsimulator",

"arm64-apple-visionos": "xros",
"arm64-apple-visionos-sim": "xrsimulator",
"x86_64-apple-visionos-sim": "xrsimulator",
"arm64;x86_64-apple-visionos-sim": "xrsimulator",
} satisfies Record<Triplet, XcodeSDKName>;

type CMakeSystemName = "Darwin" | "iOS" | "tvOS" | "watchOS" | "visionOS";
Expand All @@ -77,27 +86,44 @@ const CMAKE_SYSTEM_NAMES = {
"x86_64-apple-darwin": "Darwin",
"arm64-apple-darwin": "Darwin",
"arm64;x86_64-apple-darwin": "Darwin",

"arm64-apple-ios": "iOS",
"arm64-apple-ios-sim": "iOS",
"arm64-apple-tvos": "tvOS",
"x86_64-apple-ios-sim": "iOS",
"arm64;x86_64-apple-ios-sim": "iOS",

// "x86_64-apple-tvos": "appletvos",
"arm64-apple-tvos": "tvOS",
"arm64-apple-tvos-sim": "tvOS",
"x86_64-apple-tvos-sim": "tvOS",
"arm64;x86_64-apple-tvos-sim": "tvOS",

"arm64-apple-visionos": "visionOS",
"x86_64-apple-visionos-sim": "visionOS",
"arm64-apple-visionos-sim": "visionOS",
"arm64;x86_64-apple-visionos-sim": "visionOS",
} satisfies Record<Triplet, CMakeSystemName>;

const DESTINATION_BY_TRIPLET = {
"x86_64-apple-darwin": "generic/platform=macOS",
"arm64-apple-darwin": "generic/platform=macOS",
"arm64;x86_64-apple-darwin": "generic/platform=macOS",

"arm64-apple-ios": "generic/platform=iOS",
"arm64-apple-ios-sim": "generic/platform=iOS Simulator",
"x86_64-apple-ios-sim": "generic/platform=iOS Simulator",
"arm64;x86_64-apple-ios-sim": "generic/platform=iOS Simulator",

"arm64-apple-tvos": "generic/platform=tvOS",
// "x86_64-apple-tvos": "generic/platform=tvOS",
"x86_64-apple-tvos-sim": "generic/platform=tvOS Simulator",
"arm64-apple-tvos-sim": "generic/platform=tvOS Simulator",
"arm64;x86_64-apple-tvos-sim": "generic/platform=tvOS Simulator",

"arm64-apple-visionos": "generic/platform=visionOS",
"arm64-apple-visionos-sim": "generic/platform=visionOS Simulator",
// TODO: Verify that the three following destinations are correct and actually work
"x86_64-apple-darwin": "generic/platform=macOS,arch=x86_64",
"arm64-apple-darwin": "generic/platform=macOS,arch=arm64",
"arm64;x86_64-apple-darwin": "generic/platform=macOS",
"x86_64-apple-visionos-sim": "generic/platform=visionOS Simulator",
"arm64;x86_64-apple-visionos-sim": "generic/platform=visionOS Simulator",
} satisfies Record<Triplet, string>;

type AppleArchitecture = "arm64" | "x86_64" | "arm64;x86_64";
Expand All @@ -106,30 +132,24 @@ export const APPLE_ARCHITECTURES = {
"x86_64-apple-darwin": "x86_64",
"arm64-apple-darwin": "arm64",
"arm64;x86_64-apple-darwin": "arm64;x86_64",

"arm64-apple-ios": "arm64",
"arm64-apple-ios-sim": "arm64",
"arm64-apple-tvos": "arm64",
"x86_64-apple-ios-sim": "x86_64",
"arm64;x86_64-apple-ios-sim": "arm64;x86_64",

// "x86_64-apple-tvos": "x86_64",
"arm64-apple-tvos": "arm64",
"arm64-apple-tvos-sim": "arm64",
"x86_64-apple-tvos-sim": "x86_64",
"arm64;x86_64-apple-tvos-sim": "arm64;x86_64",

"arm64-apple-visionos": "arm64",
"x86_64-apple-visionos-sim": "x86_64",
"arm64-apple-visionos-sim": "arm64",
"arm64;x86_64-apple-visionos-sim": "arm64;x86_64",
} satisfies Record<Triplet, AppleArchitecture>;

export function createPlistContent(values: Record<string, string>) {
return [
'<?xml version="1.0" encoding="UTF-8"?>',
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
'<plist version="1.0">',
"<dict>",
...Object.entries(values).flatMap(([key, value]) => [
`<key>${key}</key>`,
`<string>${value}</string>`,
]),
"</dict>",
"</plist>",
].join("\n");
}

const xcframeworkExtensionOption = new Option(
"--xcframework-extension",
"Don't rename the xcframework to .apple.node",
Expand Down Expand Up @@ -171,16 +191,42 @@ export const platform: Platform<Triplet[], AppleOpts> = {
id: "apple",
name: "Apple",
triplets: [
"arm64-apple-darwin",
"x86_64-apple-darwin",
"arm64;x86_64-apple-darwin",

"arm64-apple-ios",
"arm64-apple-ios-sim",
"x86_64-apple-ios-sim",
"arm64;x86_64-apple-ios-sim",

"arm64-apple-tvos",
"x86_64-apple-tvos-sim",
"arm64-apple-tvos-sim",
"arm64;x86_64-apple-tvos-sim",

"arm64-apple-visionos",
"x86_64-apple-visionos-sim",
"arm64-apple-visionos-sim",
"arm64;x86_64-apple-visionos-sim",
],
defaultTriplets() {
return process.arch === "arm64" ? ["arm64-apple-ios-sim"] : [];
defaultTriplets(purpose) {
if (purpose === "release") {
return [
"arm64;x86_64-apple-darwin",

"arm64-apple-ios",
"arm64;x86_64-apple-ios-sim",

"arm64-apple-tvos",
"arm64;x86_64-apple-tvos-sim",

"arm64-apple-visionos",
"arm64;x86_64-apple-visionos-sim",
];
} else {
return ["arm64;x86_64-apple-ios-sim"];
}
},
amendCommand(command) {
return command.addOption(xcframeworkExtensionOption);
Expand Down
14 changes: 9 additions & 5 deletions packages/cmake-rn/src/platforms/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export type Platform<
Triplets extends string[] = string[],
Opts extends cli.OptionValues = Record<string, unknown>,
Command = ExtendedCommand<Opts>,
Triplet extends string = Triplets[number],
> = {
/**
* Used to identify the platform in the CLI.
Expand All @@ -47,9 +48,12 @@ export type Platform<
*/
triplets: Readonly<Triplets>;
/**
* Get the limited subset of triplets that should be built by default for this platform, to support a development workflow.
* Get the limited subset of triplets that should be built by default for this platform.
*
*/
defaultTriplets(): Triplets[number][] | Promise<Triplets[number][]>;
defaultTriplets(
purpose: "development" | "release",
Copy link
Collaborator

@shirakaba shirakaba Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ bikeshedding - feel free to solve more important problems instead!

Xcode uses the terminology build configuration (which you can name "orange" | "banana" if you wish, but defaults to Debug and Release when creating new projects).

They confusingly use the term Development for codesigning for local deployment.

From an Apple point of view, I'd go with:

- purpose: "development" | "release",
+ configuration: "Debug" | "Release",

On the other hand, Android uses build variants:

- purpose: "development" | "release",
+ variant: "debug" | "release",

So uhhh I guess we can't win. 🤔

But either way, "debug" is probably more consistent than "development"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This value isn't encoding the build configuration "Release" vs "Debug" that you're referring to. What I'm trying to capture here (which might be total overkill BTW) is that a library want to build only for some triplets when they're in the process of iterating their library, to match simulators of a specific architecture for example. To speed things up while iterating locally, the don't want to produce pre-builds for all possible triplets.

Copy link
Collaborator

@shirakaba shirakaba Oct 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right. Then surely this should be changed to onlyBuildActiveArchitecture: boolean or something? As "active architecture" I believe is terminology commonly used across various IDEs and build systems.

Or buildAllArchitectures: boolean.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree - it's the same concept as the activeArchOnly option passable to the React Native CLI: src/commands/buildAndroid/index.ts#L58-L77 where it's actually probing ADB. We might be able to do the same or something similar to figure out what simulators and emulators are actively booted 🤔

That being said, I don't know if it's worth it though investing too much into this feature: We're running all of the builds of triplets in parallel so the increase in build-time doesn't scale linearly with the amount of triplets.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave it to you! Just driving by 😄

Copy link
Collaborator Author

@kraenhansen kraenhansen Oct 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot what do you think? What would be a good name for this parameter and supported values?

(don't know if tagging it here will work the same way it does in agent tasks 🙈)

): Triplet[] | Promise<Triplet[]>;
/**
* Implement this to add any platform specific options to the command.
*/
Expand All @@ -62,15 +66,15 @@ export type Platform<
* Configure all projects for this platform.
*/
configure(
triplets: TripletContext<Triplets[number]>[],
triplets: TripletContext<Triplet>[],
options: BaseOpts & Opts,
spawn: Spawn,
): Promise<void>;
/**
* Platform specific command to build a triplet project.
*/
build(
context: TripletContext<Triplets[number]>,
context: TripletContext<Triplet>,
options: BaseOpts & Opts,
): Promise<void>;
/**
Expand All @@ -81,7 +85,7 @@ export type Platform<
* Location of the final prebuilt artefact.
*/
outputPath: string,
triplets: TripletContext<Triplets[number]>[],
triplets: TripletContext<Triplet>[],
options: BaseOpts & Opts,
): Promise<void>;
};
2 changes: 1 addition & 1 deletion packages/ferric/src/cargo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const APPLE_XCFRAMEWORK_CHILDS_PER_TARGET: Record<AppleTargetName, string> = {
"aarch64-apple-darwin": "macos-arm64_x86_64", // Universal
"x86_64-apple-darwin": "macos-arm64_x86_64", // Universal
"aarch64-apple-ios": "ios-arm64",
"aarch64-apple-ios-sim": "ios-arm64-simulator",
"aarch64-apple-ios-sim": "ios-arm64_x86_64-simulator", // Universal
// "aarch64-apple-ios-macabi": "", // Catalyst
// "x86_64-apple-ios": "ios-x86_64",
// "x86_64-apple-ios-macabi": "ios-x86_64-simulator",
Expand Down
16 changes: 0 additions & 16 deletions packages/host/src/node/prebuilds/apple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,8 @@ import os from "node:os";
import plist from "@expo/plist";
import { spawn } from "@react-native-node-api/cli-utils";

import { AppleTriplet } from "./triplets.js";
import { determineLibraryBasename } from "../path-utils.js";

type AppleArchitecture = "arm64" | "x86_64" | "arm64;x86_64";

export const APPLE_ARCHITECTURES = {
"x86_64-apple-darwin": "x86_64",
"arm64-apple-darwin": "arm64",
"arm64;x86_64-apple-darwin": "arm64;x86_64",
"arm64-apple-ios": "arm64",
"arm64-apple-ios-sim": "arm64",
"arm64-apple-tvos": "arm64",
// "x86_64-apple-tvos": "x86_64",
"arm64-apple-tvos-sim": "arm64",
"arm64-apple-visionos": "arm64",
"arm64-apple-visionos-sim": "arm64",
} satisfies Record<AppleTriplet, AppleArchitecture>;

type XCframeworkOptions = {
frameworkPaths: string[];
outputPath: string;
Expand Down
13 changes: 11 additions & 2 deletions packages/host/src/node/prebuilds/triplets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,25 @@ export const ANDROID_TRIPLETS = [
export type AndroidTriplet = (typeof ANDROID_TRIPLETS)[number];

export const APPLE_TRIPLETS = [
"arm64;x86_64-apple-darwin",
"x86_64-apple-darwin",
"arm64-apple-darwin",
"arm64;x86_64-apple-darwin",

"arm64-apple-ios",
"x86_64-apple-ios-sim",
"arm64-apple-ios-sim",
"arm64;x86_64-apple-ios-sim",

"arm64-apple-tvos",
"arm64-apple-tvos-sim",
// "x86_64-apple-tvos",
"x86_64-apple-tvos-sim",
"arm64-apple-tvos-sim",
"arm64;x86_64-apple-tvos-sim",

"arm64-apple-visionos",
"x86_64-apple-visionos-sim",
"arm64-apple-visionos-sim",
"arm64;x86_64-apple-visionos-sim",
] as const;

export type AppleTriplet = (typeof APPLE_TRIPLETS)[number];
Expand Down
Loading