Skip to content

Commit 5bf562a

Browse files
committed
Add --verbose, --concurrency, --clean options
1 parent 7aaabe5 commit 5bf562a

File tree

3 files changed

+97
-27
lines changed

3 files changed

+97
-27
lines changed

.changeset/big-plums-write.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"ferric-cli": patch
3+
---
4+
5+
Add --verbose, --concurrency, --clean options

packages/ferric/src/build.ts

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import path from "node:path";
22
import fs from "node:fs";
3+
import os from "node:os";
34

45
import {
56
chalk,
@@ -9,6 +10,8 @@ import {
910
assertFixable,
1011
wrapAction,
1112
prettyPath,
13+
pLimit,
14+
spawn,
1215
} from "@react-native-node-api/cli-utils";
1316

1417
import {
@@ -85,6 +88,10 @@ function getDefaultTargets() {
8588
const targetOption = new Option("--target <target...>", "Target triple")
8689
.choices(ALL_TARGETS)
8790
.default(getDefaultTargets());
91+
const cleanOption = new Option(
92+
"--clean",
93+
"Delete the target directory before building",
94+
).default(false);
8895
const appleTarget = new Option("--apple", "Use all Apple targets");
8996
const androidTarget = new Option("--android", "Use all Android targets");
9097
const ndkVersionOption = new Option(
@@ -112,28 +119,61 @@ const appleBundleIdentifierOption = new Option(
112119
"Unique CFBundleIdentifier used for Apple framework artifacts",
113120
).default(undefined, "com.callstackincubator.node-api.{libraryName}");
114121

122+
const concurrencyOption = new Option(
123+
"--concurrency <limit>",
124+
"Limit the number of concurrent tasks",
125+
)
126+
.argParser((value) => parseInt(value, 10))
127+
.default(
128+
os.availableParallelism(),
129+
`${os.availableParallelism()} or 1 when verbose is enabled`,
130+
);
131+
132+
const verboseOption = new Option(
133+
"--verbose",
134+
"Print more output from underlying compiler & tools",
135+
).default(process.env.CI ? true : false, `false in general and true on CI`);
136+
115137
export const buildCommand = new Command("build")
116138
.description("Build Rust Node-API module")
117139
.addOption(targetOption)
140+
.addOption(cleanOption)
118141
.addOption(appleTarget)
119142
.addOption(androidTarget)
120143
.addOption(ndkVersionOption)
121144
.addOption(outputPathOption)
122145
.addOption(configurationOption)
123146
.addOption(xcframeworkExtensionOption)
124147
.addOption(appleBundleIdentifierOption)
148+
.addOption(concurrencyOption)
149+
.addOption(verboseOption)
125150
.action(
126151
wrapAction(
127152
async ({
128153
target: targetArg,
154+
clean,
129155
apple,
130156
android,
131157
ndkVersion,
132158
output: outputPath,
133159
configuration,
134160
xcframeworkExtension,
135161
appleBundleIdentifier,
162+
concurrency,
163+
verbose,
136164
}) => {
165+
if (clean) {
166+
await oraPromise(
167+
() => spawn("cargo", ["clean"], { outputMode: "buffered" }),
168+
{
169+
text: "Cleaning target directory",
170+
successText: "Cleaned target directory",
171+
failText: (error) => `Failed to clean target directory: ${error}`,
172+
},
173+
);
174+
}
175+
// Force a limit of 1 concurrent task to avoid interleaving output
176+
const limit = pLimit(verbose ? 1 : concurrency);
137177
const targets = new Set([...targetArg]);
138178
if (apple) {
139179
for (const target of APPLE_TARGETS) {
@@ -180,30 +220,40 @@ export const buildCommand = new Command("build")
180220
targets.size +
181221
(targets.size === 1 ? " target" : " targets") +
182222
chalk.dim(" (" + [...targets].join(", ") + ")");
223+
183224
const [appleLibraries, androidLibraries] = await oraPromise(
184225
Promise.all([
185226
Promise.all(
186-
appleTargets.map(
187-
async (target) =>
188-
[target, await build({ configuration, target })] as const,
227+
appleTargets.map((target) =>
228+
limit(
229+
async () =>
230+
[
231+
target,
232+
await build({ configuration, target, verbose }),
233+
] as const,
234+
),
189235
),
190236
),
191237
Promise.all(
192-
androidTargets.map(
193-
async (target) =>
194-
[
195-
target,
196-
await build({
197-
configuration,
238+
androidTargets.map((target) =>
239+
limit(
240+
async () =>
241+
[
198242
target,
199-
ndkVersion,
200-
androidApiLevel: ANDROID_API_LEVEL,
201-
}),
202-
] as const,
243+
await build({
244+
configuration,
245+
target,
246+
verbose,
247+
ndkVersion,
248+
androidApiLevel: ANDROID_API_LEVEL,
249+
}),
250+
] as const,
251+
),
203252
),
204253
),
205254
]),
206255
{
256+
isSilent: verbose,
207257
text: `Building ${targetsDescription}`,
208258
successText: `Built ${targetsDescription}`,
209259
failText: (error: Error) => `Failed to build: ${error.message}`,
@@ -225,11 +275,13 @@ export const buildCommand = new Command("build")
225275
);
226276

227277
await oraPromise(
228-
createAndroidLibsDirectory({
229-
outputPath: androidLibsOutputPath,
230-
libraries,
231-
autoLink: true,
232-
}),
278+
limit(() =>
279+
createAndroidLibsDirectory({
280+
outputPath: androidLibsOutputPath,
281+
libraries,
282+
autoLink: true,
283+
}),
284+
),
233285
{
234286
text: "Assembling Android libs directory",
235287
successText: `Android libs directory assembled into ${prettyPath(
@@ -243,14 +295,25 @@ export const buildCommand = new Command("build")
243295

244296
if (appleLibraries.length > 0) {
245297
const libraryPaths = await combineLibraries(appleLibraries);
246-
const frameworkPaths = await Promise.all(
247-
libraryPaths.map((libraryPath) =>
248-
// TODO: Pass true as `versioned` argument for -darwin targets
249-
createAppleFramework({
250-
libraryPath,
251-
bundleIdentifier: appleBundleIdentifier,
252-
}),
298+
299+
const frameworkPaths = await oraPromise(
300+
Promise.all(
301+
libraryPaths.map((libraryPath) =>
302+
limit(() =>
303+
// TODO: Pass true as `versioned` argument for -darwin targets
304+
createAppleFramework({
305+
libraryPath,
306+
bundleIdentifier: appleBundleIdentifier,
307+
}),
308+
),
309+
),
253310
),
311+
{
312+
text: "Creating Apple frameworks",
313+
successText: `Created Apple frameworks`,
314+
failText: ({ message }) =>
315+
`Failed to create Apple frameworks: ${message}`,
316+
},
254317
);
255318
const xcframeworkFilename = determineXCFrameworkFilename(
256319
frameworkPaths,

packages/ferric/src/cargo.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export function ensureCargo() {
9595

9696
type BuildOptions = {
9797
configuration: "debug" | "release";
98+
verbose: boolean;
9899
} & (
99100
| {
100101
target: AndroidTargetName;
@@ -109,7 +110,7 @@ type BuildOptions = {
109110
);
110111

111112
export async function build(options: BuildOptions) {
112-
const { target, configuration } = options;
113+
const { target, configuration, verbose } = options;
113114
const args = ["build", "--target", target];
114115
if (configuration.toLowerCase() === "release") {
115116
args.push("--release");
@@ -123,7 +124,8 @@ export async function build(options: BuildOptions) {
123124
args.push("-Z", "build-std=std,panic_abort");
124125
}
125126
await spawn("cargo", args, {
126-
outputMode: "buffered",
127+
outputMode: verbose ? "inherit" : "buffered",
128+
outputPrefix: verbose ? chalk.dim(`[${target}]`) : undefined,
127129
env: {
128130
...process.env,
129131
...getTargetEnvironmentVariables(options),

0 commit comments

Comments
 (0)