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
13 changes: 12 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

99 changes: 73 additions & 26 deletions packages/react-native-node-api-cmake/src/android.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import assert from "node:assert";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";

import { type SupportedTriplet } from "./triplets.js";

/**
* https://developer.android.com/ndk/guides/other_build_systems
*/
export const ANDROID_TRIPLETS = [
"aarch64-linux-android",
"armv7a-linux-androideabi",
"i686-linux-android",
"x86_64-linux-android",
] as const;

export type AndroidTriplet = (typeof ANDROID_TRIPLETS)[number];
import { AndroidTriplet } from "./triplets.js";

export const DEFAULT_ANDROID_TRIPLETS = [
"aarch64-linux-android",
Expand All @@ -23,15 +11,9 @@ export const DEFAULT_ANDROID_TRIPLETS = [
"x86_64-linux-android",
] as const satisfies AndroidTriplet[];

export function isAndroidTriplet(
triplet: SupportedTriplet
): triplet is AndroidTriplet {
return ANDROID_TRIPLETS.includes(triplet as AndroidTriplet);
}

type AndroidArchitecture = "armeabi-v7a" | "arm64-v8a" | "x86" | "x86_64";

export const ARCHITECTURES = {
export const ANDROID_ARCHITECTURES = {
"armv7a-linux-androideabi": "armeabi-v7a",
"aarch64-linux-android": "arm64-v8a",
"i686-linux-android": "x86",
Expand Down Expand Up @@ -64,7 +46,14 @@ export function getAndroidConfigureCmakeArgs({
ndkPath,
"build/cmake/android.toolchain.cmake"
);
const architecture = ARCHITECTURES[triplet];
const architecture = ANDROID_ARCHITECTURES[triplet];

const linkerFlags: string[] = [
// `--no-version-undefined`,
// `--whole-archive`,
// `--no-whole-archive`,
];

return [
// Use the XCode as generator for Apple platforms
"-G",
Expand All @@ -73,8 +62,8 @@ export function getAndroidConfigureCmakeArgs({
toolchainPath,
"-D",
"CMAKE_SYSTEM_NAME=Android",
"-D",
`CPACK_SYSTEM_NAME=Android-${architecture}`,
// "-D",
// `CPACK_SYSTEM_NAME=Android-${architecture}`,
// "-D",
// `CMAKE_INSTALL_PREFIX=${installPath}`,
// "-D",
Expand All @@ -96,8 +85,66 @@ export function getAndroidConfigureCmakeArgs({
"-D",
"ANDROID_STL=c++_shared",
// Pass linker flags to avoid errors from undefined symbols
// TODO: Link against a fake libhermes to avoid this (or whatever other lib which will be providing the symbols)
// TODO: Link against a weak-node-api to avoid this (or whatever other lib which will be providing the symbols)
// "-D",
// `CMAKE_SHARED_LINKER_FLAGS="-Wl,--allow-shlib-undefined"`,
"-D",
`CMAKE_SHARED_LINKER_FLAGS="-Wl,--allow-shlib-undefined"`,
`CMAKE_SHARED_LINKER_FLAGS=${linkerFlags
.map((flag) => `-Wl,${flag}`)
.join(" ")}`,
];
}

/**
* Determine the filename of the Android libs directory based on the framework paths.
* Ensuring that all framework paths have the same base name.
*/
export function determineAndroidLibsFilename(frameworkPaths: string[]) {
const frameworkNames = frameworkPaths.map((p) =>
path.basename(p, path.extname(p))
);
const candidates = new Set<string>(frameworkNames);
assert(
candidates.size === 1,
"Expected all frameworks to have the same name"
);
const [name] = candidates;
return `${name}.android.node`;
}

type AndroidLibsDirectoryOptions = {
outputPath: string;
libraryPathByTriplet: Record<AndroidTriplet, string>;
autoLink: boolean;
};

export async function createAndroidLibsDirectory({
outputPath,
libraryPathByTriplet,
autoLink,
}: AndroidLibsDirectoryOptions) {
// Delete and recreate any existing output directory
await fs.promises.rm(outputPath, { recursive: true, force: true });
await fs.promises.mkdir(outputPath, { recursive: true });
for (const [triplet] of Object.entries(libraryPathByTriplet)) {
const libraryPath = libraryPathByTriplet[triplet as AndroidTriplet];
assert(
fs.existsSync(libraryPath),
`Library not found: ${libraryPath} for triplet ${triplet}`
);
const arch = ANDROID_ARCHITECTURES[triplet as AndroidTriplet];
const archOutputPath = path.join(outputPath, arch);
await fs.promises.mkdir(archOutputPath, { recursive: true });
const libraryName = path.basename(libraryPath, path.extname(libraryPath));
const libraryOutputPath = path.join(archOutputPath, libraryName);
await fs.promises.copyFile(libraryPath, libraryOutputPath);
}
if (autoLink) {
// Write a file to mark the Android libs directory is a Node-API module
await fs.promises.writeFile(
path.join(outputPath, "react-native-node-api-module"),
"",
"utf8"
);
}
}
81 changes: 44 additions & 37 deletions packages/react-native-node-api-cmake/src/apple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,9 @@ import cp from "node:child_process";
import fs from "node:fs";
import path from "node:path";

import type { SupportedTriplet } from "./triplets.js";
import { spawn } from "bufout";

export const APPLE_TRIPLETS = [
"arm64;x86_64-apple-darwin",
"x86_64-apple-darwin",
"arm64-apple-darwin",
"arm64-apple-ios",
"arm64-apple-ios-sim",
"arm64-apple-tvos",
"arm64-apple-tvos-sim",
// "x86_64-apple-tvos",
"arm64-apple-visionos",
"arm64-apple-visionos-sim",
] as const;

export type AppleTriplet = (typeof APPLE_TRIPLETS)[number];
import { AppleTriplet, isAppleTriplet } from "./triplets.js";

export const DEFAULT_APPLE_TRIPLETS = [
"arm64;x86_64-apple-darwin",
Expand All @@ -41,7 +27,7 @@ type XcodeSDKName =
| "appletvsimulator"
| "macosx";

const SDK_NAMES = {
const XCODE_SDK_NAMES = {
"x86_64-apple-darwin": "macosx",
"arm64-apple-darwin": "macosx",
"arm64;x86_64-apple-darwin": "macosx",
Expand All @@ -54,9 +40,24 @@ const SDK_NAMES = {
"arm64-apple-visionos-sim": "xrsimulator",
} satisfies Record<AppleTriplet, XcodeSDKName>;

type CMakeSystemName = "Darwin" | "iOS" | "tvOS" | "watchOS" | "visionOS";

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-tvos": "appletvos",
"arm64-apple-tvos-sim": "tvOS",
"arm64-apple-visionos": "visionOS",
"arm64-apple-visionos-sim": "visionOS",
} satisfies Record<AppleTriplet, CMakeSystemName>;

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

export const ARCHITECTURES = {
export const APPLE_ARCHITECTURES = {
"x86_64-apple-darwin": "x86_64",
"arm64-apple-darwin": "arm64",
"arm64;x86_64-apple-darwin": "arm64;x86_64",
Expand All @@ -69,17 +70,15 @@ export const ARCHITECTURES = {
"arm64-apple-visionos-sim": "arm64",
} satisfies Record<AppleTriplet, AppleArchitecture>;

export function isAppleTriplet(
triplet: SupportedTriplet
): triplet is AppleTriplet {
return APPLE_TRIPLETS.includes(triplet as AppleTriplet);
}

export function getAppleSDKPath(triplet: AppleTriplet) {
return cp
.spawnSync("xcrun", ["--sdk", SDK_NAMES[triplet], "--show-sdk-path"], {
encoding: "utf-8",
})
.spawnSync(
"xcrun",
["--sdk", XCODE_SDK_NAMES[triplet], "--show-sdk-path"],
{
encoding: "utf-8",
}
)
.stdout.trim();
}

Expand All @@ -98,23 +97,27 @@ export function createPlistContent(values: Record<string, string>) {
].join("\n");
}

export function getAppleConfigureCmakeArgs(triplet: AppleTriplet) {
type AppleConfigureOptions = {
triplet: AppleTriplet;
};

export function getAppleConfigureCmakeArgs({ triplet }: AppleConfigureOptions) {
assert(isAppleTriplet(triplet));
const sdkPath = getAppleSDKPath(triplet);
const systemName = CMAKE_SYSTEM_NAMES[triplet];

return [
// Use the XCode as generator for Apple platforms
"-G",
"Xcode",
// Pass linker flags to avoid errors from undefined symbols
"-D",
`CMAKE_SHARED_LINKER_FLAGS="-Wl,-undefined,dynamic_lookup"`,
`CMAKE_SYSTEM_NAME=${systemName}`,
// Set the SDK path for the target platform
"-D",
`CMAKE_OSX_SYSROOT=${sdkPath}`,
// Set the target architecture
"-D",
`CMAKE_OSX_ARCHITECTURES=${ARCHITECTURES[triplet]}`,
`CMAKE_OSX_ARCHITECTURES=${APPLE_ARCHITECTURES[triplet]}`,
];
}

Expand All @@ -126,6 +129,7 @@ export function getAppleBuildArgs() {
type XCframeworkOptions = {
frameworkPaths: string[];
outputPath: string;
autoLink: boolean;
};

export function createFramework(libraryPath: string) {
Expand Down Expand Up @@ -171,6 +175,7 @@ export function createFramework(libraryPath: string) {
export async function createXCframework({
frameworkPaths,
outputPath,
autoLink,
}: XCframeworkOptions) {
// Delete any existing xcframework to prevent the error:
// - A library with the identifier 'macos-arm64' already exists.
Expand All @@ -189,13 +194,15 @@ export async function createXCframework({
outputMode: "buffered",
}
);
// Write a file to mark the xcframework is a Node-API module
// TODO: Consider including this in the Info.plist file instead
fs.writeFileSync(
path.join(outputPath, "react-native-node-api-module"),
"",
"utf8"
);
if (autoLink) {
// Write a file to mark the xcframework is a Node-API module
// TODO: Consider including this in the Info.plist file instead
fs.writeFileSync(
path.join(outputPath, "react-native-node-api-module"),
"",
"utf8"
);
}
}

/**
Expand Down
Loading