Skip to content

Commit ad66739

Browse files
committed
Use Xcode to build frameworks
1 parent c222839 commit ad66739

File tree

9 files changed

+388
-210
lines changed

9 files changed

+388
-210
lines changed

package-lock.json

Lines changed: 19 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cmake-rn/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"dependencies": {
2727
"@react-native-node-api/cli-utils": "0.1.0",
2828
"cmake-file-api": "0.1.0",
29-
"react-native-node-api": "0.5.2"
29+
"react-native-node-api": "0.5.2",
30+
"zod": "^4.1.11"
3031
},
3132
"peerDependencies": {
3233
"node-addon-api": "^8.3.1",

packages/cmake-rn/src/cli.ts

Lines changed: 63 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,14 @@ import {
1212
assertFixable,
1313
wrapAction,
1414
} from "@react-native-node-api/cli-utils";
15-
import { isSupportedTriplet } from "react-native-node-api";
16-
import * as cmakeFileApi from "cmake-file-api";
1715

18-
import {
19-
getCmakeJSVariables,
20-
getWeakNodeApiVariables,
21-
} from "./weak-node-api.js";
2216
import {
2317
platforms,
2418
allTriplets as allTriplets,
2519
findPlatformForTriplet,
2620
platformHasTriplet,
2721
} from "./platforms.js";
28-
import { BaseOpts, TripletContext, Platform } from "./platforms/types.js";
22+
import { BaseOpts, Platform } from "./platforms/types.js";
2923

3024
// We're attaching a lot of listeners when spawning in parallel
3125
EventEmitter.defaultMaxListeners = 100;
@@ -74,7 +68,9 @@ const tripletOption = new Option(
7468
const buildPathOption = new Option(
7569
"--build <path>",
7670
"Specify the build directory to store the configured CMake project",
77-
);
71+
)
72+
.argParser((input) => path.resolve(process.cwd(), input))
73+
.default(path.resolve(process.cwd(), "build"), "./build");
7874

7975
const cleanOption = new Option(
8076
"--clean",
@@ -84,7 +80,7 @@ const cleanOption = new Option(
8480
const outPathOption = new Option(
8581
"--out <path>",
8682
"Specify the output directory to store the final build artifacts",
87-
).default(false, "./{build}/{configuration}");
83+
).default("{build}/{configuration}");
8884

8985
const defineOption = new Option(
9086
"-D,--define <entry...>",
@@ -151,18 +147,33 @@ for (const platform of platforms) {
151147
program = platform.amendCommand(program);
152148
}
153149

150+
function expandTemplate(
151+
input: string,
152+
values: Record<string, unknown>,
153+
): string {
154+
return input.replaceAll(/{([^}]+)}/g, (_, key: string) =>
155+
typeof values[key] === "string" ? values[key] : "",
156+
);
157+
}
158+
154159
program = program.action(
155160
wrapAction(async ({ triplet: requestedTriplets, ...baseOptions }) => {
161+
const buildPath = getBuildPath(baseOptions);
162+
baseOptions.out = path.resolve(
163+
process.cwd(),
164+
expandTemplate(baseOptions.out, baseOptions),
165+
);
166+
const { verbose, clean, source, out } = baseOptions;
167+
156168
assertFixable(
157-
fs.existsSync(path.join(baseOptions.source, "CMakeLists.txt")),
158-
`No CMakeLists.txt found in source directory: ${chalk.dim(baseOptions.source)}`,
169+
fs.existsSync(path.join(source, "CMakeLists.txt")),
170+
`No CMakeLists.txt found in source directory: ${chalk.dim(source)}`,
159171
{
160172
instructions: `Change working directory into a directory with a CMakeLists.txt, create one or specify the correct source directory using --source`,
161173
},
162174
);
163175

164-
const buildPath = getBuildPath(baseOptions);
165-
if (baseOptions.clean) {
176+
if (clean) {
166177
await fs.promises.rm(buildPath, { recursive: true, force: true });
167178
}
168179
const triplets = new Set<string>(requestedTriplets);
@@ -197,31 +208,49 @@ program = program.action(
197208
}
198209
}
199210

200-
if (!baseOptions.out) {
201-
baseOptions.out = path.join(buildPath, baseOptions.configuration);
202-
}
203-
204211
const tripletContexts = [...triplets].map((triplet) => {
205212
const platform = findPlatformForTriplet(triplet);
206-
const tripletBuildPath = getTripletBuildPath(buildPath, triplet);
213+
207214
return {
208215
triplet,
209216
platform,
210-
buildPath: tripletBuildPath,
211-
outputPath: path.join(tripletBuildPath, "out"),
212-
options: baseOptions,
217+
async spawn(command: string, args: string[], cwd?: string) {
218+
await spawn(command, args, {
219+
outputMode: verbose ? "inherit" : "buffered",
220+
outputPrefix: verbose ? chalk.dim(`[${triplet}] `) : undefined,
221+
cwd,
222+
});
223+
},
213224
};
214225
});
215226

216227
// Configure every triplet project
217228
const tripletsSummary = chalk.dim(
218229
`(${getTripletsSummary(tripletContexts)})`,
219230
);
231+
232+
// Perform configure steps for each platform in sequence
220233
await oraPromise(
221234
Promise.all(
222-
tripletContexts.map(({ platform, ...context }) =>
223-
configureProject(platform, context, baseOptions),
224-
),
235+
platforms.map(async (platform) => {
236+
const relevantTriplets = tripletContexts.filter(({ triplet }) =>
237+
platformHasTriplet(platform, triplet),
238+
);
239+
if (relevantTriplets.length > 0) {
240+
await platform.configure(
241+
relevantTriplets,
242+
baseOptions,
243+
(command, args, cwd) =>
244+
spawn(command, args, {
245+
outputMode: verbose ? "inherit" : "buffered",
246+
outputPrefix: verbose
247+
? chalk.dim(`[${platform.name}] `)
248+
: undefined,
249+
cwd,
250+
}),
251+
);
252+
}
253+
}),
225254
),
226255
{
227256
text: `Configuring projects ${tripletsSummary}`,
@@ -235,13 +264,14 @@ program = program.action(
235264
await oraPromise(
236265
Promise.all(
237266
tripletContexts.map(async ({ platform, ...context }) => {
238-
// Delete any stale build artifacts before building
239-
// This is important, since we might rename the output files
240-
await fs.promises.rm(context.outputPath, {
241-
recursive: true,
242-
force: true,
243-
});
244-
await buildProject(platform, context, baseOptions);
267+
// TODO: Consider if this is still important 😬
268+
// // Delete any stale build artifacts before building
269+
// // This is important, since we might rename the output files
270+
// await fs.promises.rm(context.outputPath, {
271+
// recursive: true,
272+
// force: true,
273+
// });
274+
await platform.build(context, baseOptions);
245275
}),
246276
),
247277
{
@@ -253,20 +283,15 @@ program = program.action(
253283
);
254284

255285
// Perform post-build steps for each platform in sequence
286+
// console.log("📦 Writing prebuilds to:", prettyPath(out));
256287
for (const platform of platforms) {
257288
const relevantTriplets = tripletContexts.filter(({ triplet }) =>
258289
platformHasTriplet(platform, triplet),
259290
);
260291
if (relevantTriplets.length == 0) {
261292
continue;
262293
}
263-
await platform.postBuild(
264-
{
265-
outputPath: baseOptions.out || baseOptions.source,
266-
triplets: relevantTriplets,
267-
},
268-
baseOptions,
269-
);
294+
await platform.postBuild(out, relevantTriplets, baseOptions);
270295
}
271296
}),
272297
);
@@ -293,92 +318,4 @@ function getBuildPath({ build, source }: BaseOpts) {
293318
return path.resolve(process.cwd(), build || path.join(source, "build"));
294319
}
295320

296-
/**
297-
* Namespaces the output path with a triplet name
298-
*/
299-
function getTripletBuildPath(buildPath: string, triplet: unknown) {
300-
assert(typeof triplet === "string", "Expected triplet to be a string");
301-
return path.join(buildPath, triplet.replace(/;/g, "_"));
302-
}
303-
304-
async function configureProject<T extends string>(
305-
platform: Platform<T[], Record<string, unknown>>,
306-
context: TripletContext<T>,
307-
options: BaseOpts,
308-
) {
309-
const { triplet, buildPath, outputPath } = context;
310-
const { verbose, source, weakNodeApiLinkage, cmakeJs } = options;
311-
312-
// TODO: Make the two following definitions a part of the platform definition
313-
314-
const nodeApiDefinitions =
315-
weakNodeApiLinkage && isSupportedTriplet(triplet)
316-
? [getWeakNodeApiVariables(triplet)]
317-
: [];
318-
319-
const cmakeJsDefinitions =
320-
cmakeJs && isSupportedTriplet(triplet)
321-
? [getCmakeJSVariables(triplet)]
322-
: [];
323-
324-
const definitions = [
325-
...nodeApiDefinitions,
326-
...cmakeJsDefinitions,
327-
...options.define,
328-
{ CMAKE_LIBRARY_OUTPUT_DIRECTORY: outputPath },
329-
];
330-
331-
await cmakeFileApi.createSharedStatelessQuery(buildPath, "codemodel", "2");
332-
333-
await spawn(
334-
"cmake",
335-
[
336-
"-S",
337-
source,
338-
"-B",
339-
buildPath,
340-
...platform.configureArgs(context, options),
341-
...toDefineArguments(definitions),
342-
],
343-
{
344-
outputMode: verbose ? "inherit" : "buffered",
345-
outputPrefix: verbose ? chalk.dim(`[${triplet}] `) : undefined,
346-
},
347-
);
348-
}
349-
350-
async function buildProject<T extends string>(
351-
platform: Platform<T[], Record<string, unknown>>,
352-
context: TripletContext<T>,
353-
options: BaseOpts,
354-
) {
355-
const { triplet, buildPath } = context;
356-
const { verbose, configuration } = options;
357-
await spawn(
358-
"cmake",
359-
[
360-
"--build",
361-
buildPath,
362-
"--config",
363-
configuration,
364-
...(options.target.length > 0 ? ["--target", ...options.target] : []),
365-
"--",
366-
...platform.buildArgs(context, options),
367-
],
368-
{
369-
outputMode: verbose ? "inherit" : "buffered",
370-
outputPrefix: verbose ? chalk.dim(`[${triplet}] `) : undefined,
371-
},
372-
);
373-
}
374-
375-
function toDefineArguments(declarations: Array<Record<string, string>>) {
376-
return declarations.flatMap((values) =>
377-
Object.entries(values).flatMap(([key, definition]) => [
378-
"-D",
379-
`${key}=${definition}`,
380-
]),
381-
);
382-
}
383-
384321
export { program };

packages/cmake-rn/src/helpers.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function toDefineArguments(declarations: Array<Record<string, string>>) {
2+
return declarations.flatMap((values) =>
3+
Object.entries(values).flatMap(([key, definition]) => [
4+
"-D",
5+
`${key}=${definition}`,
6+
]),
7+
);
8+
}

0 commit comments

Comments
 (0)