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
26 changes: 19 additions & 7 deletions package-lock.json

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

17 changes: 17 additions & 0 deletions packages/cmake-rn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,28 @@ To link against `weak-node-api` just include the CMake config exposed through `W
cmake_minimum_required(VERSION 3.15...3.31)
project(tests-buffers)

# Defines the "weak-node-api" target
include(${WEAK_NODE_API_CONFIG})

add_library(addon SHARED addon.c)
target_link_libraries(addon PRIVATE weak-node-api)
target_compile_features(addon PRIVATE cxx_std_20)

if(APPLE)
# Build frameworks when building for Apple (optional)
set_target_properties(addon PROPERTIES
FRAMEWORK TRUE
MACOSX_FRAMEWORK_IDENTIFIER async_test.addon
MACOSX_FRAMEWORK_SHORT_VERSION_STRING 1.0
MACOSX_FRAMEWORK_BUNDLE_VERSION 1.0
XCODE_ATTRIBUTE_SKIP_INSTALL NO
)
else()
set_target_properties(addon PROPERTIES
PREFIX ""
SUFFIX .node
)
endif()
```

This is different from how `cmake-js` "injects" the Node-API for linking (via `${CMAKE_JS_INC}`, `${CMAKE_JS_SRC}` and `${CMAKE_JS_LIB}`). To allow for interoperability between these tools, we inject these when you pass `--cmake-js` to `cmake-rn`.
3 changes: 2 additions & 1 deletion packages/cmake-rn/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"dependencies": {
"@react-native-node-api/cli-utils": "0.1.0",
"cmake-file-api": "0.1.0",
"react-native-node-api": "0.5.2"
"react-native-node-api": "0.5.2",
"zod": "^4.1.11"
},
"peerDependencies": {
"node-addon-api": "^8.3.1",
Expand Down
163 changes: 43 additions & 120 deletions packages/cmake-rn/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,14 @@ import {
assertFixable,
wrapAction,
} from "@react-native-node-api/cli-utils";
import { isSupportedTriplet } from "react-native-node-api";
import * as cmakeFileApi from "cmake-file-api";

import {
getCmakeJSVariables,
getWeakNodeApiVariables,
} from "./weak-node-api.js";
import {
platforms,
allTriplets as allTriplets,
findPlatformForTriplet,
platformHasTriplet,
} from "./platforms.js";
import { BaseOpts, TripletContext, Platform } from "./platforms/types.js";
import { Platform } from "./platforms/types.js";

// We're attaching a lot of listeners when spawning in parallel
EventEmitter.defaultMaxListeners = 100;
Expand Down Expand Up @@ -170,17 +164,17 @@ program = program.action(
process.cwd(),
expandTemplate(baseOptions.out, baseOptions),
);
const { out, build: buildPath } = baseOptions;
const { verbose, clean, source, out, build: buildPath } = baseOptions;

assertFixable(
fs.existsSync(path.join(baseOptions.source, "CMakeLists.txt")),
`No CMakeLists.txt found in source directory: ${chalk.dim(baseOptions.source)}`,
fs.existsSync(path.join(source, "CMakeLists.txt")),
`No CMakeLists.txt found in source directory: ${chalk.dim(source)}`,
{
instructions: `Change working directory into a directory with a CMakeLists.txt, create one or specify the correct source directory using --source`,
},
);

if (baseOptions.clean) {
if (clean) {
await fs.promises.rm(buildPath, { recursive: true, force: true });
}
const triplets = new Set<string>(requestedTriplets);
Expand Down Expand Up @@ -217,25 +211,47 @@ program = program.action(

const tripletContexts = [...triplets].map((triplet) => {
const platform = findPlatformForTriplet(triplet);
const tripletBuildPath = getTripletBuildPath(buildPath, triplet);

return {
triplet,
platform,
buildPath: tripletBuildPath,
outputPath: path.join(tripletBuildPath, "out"),
options: baseOptions,
async spawn(command: string, args: string[], cwd?: string) {
await spawn(command, args, {
outputMode: verbose ? "inherit" : "buffered",
outputPrefix: verbose ? chalk.dim(`[${triplet}] `) : undefined,
cwd,
});
},
};
});

// Configure every triplet project
const tripletsSummary = chalk.dim(
`(${getTripletsSummary(tripletContexts)})`,
);

// Perform configure steps for each platform in sequence
await oraPromise(
Promise.all(
tripletContexts.map(({ platform, ...context }) =>
configureProject(platform, context, baseOptions),
),
platforms.map(async (platform) => {
const relevantTriplets = tripletContexts.filter(({ triplet }) =>
platformHasTriplet(platform, triplet),
);
if (relevantTriplets.length > 0) {
await platform.configure(
relevantTriplets,
baseOptions,
(command, args, cwd) =>
spawn(command, args, {
outputMode: verbose ? "inherit" : "buffered",
outputPrefix: verbose
? chalk.dim(`[${platform.name}] `)
: undefined,
cwd,
}),
);
}
}),
),
{
text: `Configuring projects ${tripletsSummary}`,
Expand All @@ -249,13 +265,14 @@ program = program.action(
await oraPromise(
Promise.all(
tripletContexts.map(async ({ platform, ...context }) => {
// Delete any stale build artifacts before building
// This is important, since we might rename the output files
await fs.promises.rm(context.outputPath, {
recursive: true,
force: true,
});
await buildProject(platform, context, baseOptions);
// TODO: Consider if this is still important 😬
// // Delete any stale build artifacts before building
// // This is important, since we might rename the output files
// await fs.promises.rm(context.outputPath, {
// recursive: true,
// force: true,
// });
await platform.build(context, baseOptions);
}),
),
{
Expand All @@ -274,13 +291,7 @@ program = program.action(
if (relevantTriplets.length == 0) {
continue;
}
await platform.postBuild(
{
outputPath: out,
triplets: relevantTriplets,
},
baseOptions,
);
await platform.postBuild(out, relevantTriplets, baseOptions);
}
}),
);
Expand All @@ -302,92 +313,4 @@ function getTripletsSummary(
.join(" / ");
}

/**
* Namespaces the output path with a triplet name
*/
function getTripletBuildPath(buildPath: string, triplet: unknown) {
assert(typeof triplet === "string", "Expected triplet to be a string");
return path.join(buildPath, triplet.replace(/;/g, "_"));
}

async function configureProject<T extends string>(
platform: Platform<T[], Record<string, unknown>>,
context: TripletContext<T>,
options: BaseOpts,
) {
const { triplet, buildPath, outputPath } = context;
const { verbose, source, weakNodeApiLinkage, cmakeJs } = options;

// TODO: Make the two following definitions a part of the platform definition

const nodeApiDefinitions =
weakNodeApiLinkage && isSupportedTriplet(triplet)
? [getWeakNodeApiVariables(triplet)]
: [];

const cmakeJsDefinitions =
cmakeJs && isSupportedTriplet(triplet)
? [getCmakeJSVariables(triplet)]
: [];

const definitions = [
...nodeApiDefinitions,
...cmakeJsDefinitions,
...options.define,
{ CMAKE_LIBRARY_OUTPUT_DIRECTORY: outputPath },
];

await cmakeFileApi.createSharedStatelessQuery(buildPath, "codemodel", "2");

await spawn(
"cmake",
[
"-S",
source,
"-B",
buildPath,
...platform.configureArgs(context, options),
...toDefineArguments(definitions),
],
{
outputMode: verbose ? "inherit" : "buffered",
outputPrefix: verbose ? chalk.dim(`[${triplet}] `) : undefined,
},
);
}

async function buildProject<T extends string>(
platform: Platform<T[], Record<string, unknown>>,
context: TripletContext<T>,
options: BaseOpts,
) {
const { triplet, buildPath } = context;
const { verbose, configuration } = options;
await spawn(
"cmake",
[
"--build",
buildPath,
"--config",
configuration,
...(options.target.length > 0 ? ["--target", ...options.target] : []),
"--",
...platform.buildArgs(context, options),
],
{
outputMode: verbose ? "inherit" : "buffered",
outputPrefix: verbose ? chalk.dim(`[${triplet}] `) : undefined,
},
);
}

function toDefineArguments(declarations: Array<Record<string, string>>) {
return declarations.flatMap((values) =>
Object.entries(values).flatMap(([key, definition]) => [
"-D",
`${key}=${definition}`,
]),
);
}

export { program };
8 changes: 8 additions & 0 deletions packages/cmake-rn/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function toDefineArguments(declarations: Array<Record<string, string>>) {
return declarations.flatMap((values) =>
Object.entries(values).flatMap(([key, definition]) => [
"-D",
`${key}=${definition}`,
]),
);
}
Loading