Skip to content

Commit 441dcc4

Browse files
authored
Ferric --verbose --concurrency and --clean (#332)
* Add re-export of "p-limit" to cli-utils * Add --verbose, --concurrency, --clean options * Show notice when --verbose and --concurrency > 1
1 parent d1294d5 commit 441dcc4

File tree

7 files changed

+154
-56
lines changed

7 files changed

+154
-56
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

.changeset/evil-pens-shop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-native-node-api/cli-utils": patch
3+
---
4+
5+
Add re-export of "p-limit"

package-lock.json

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

packages/cli-utils/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"bufout": "^0.3.2",
1515
"chalk": "^5.4.1",
1616
"commander": "^14.0.1",
17-
"ora": "^8.2.0"
17+
"ora": "^8.2.0",
18+
"p-limit": "^7.2.0"
1819
}
1920
}

packages/cli-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from "@commander-js/extra-typings";
22
export { default as chalk } from "chalk";
33
export * from "ora";
44
export * from "bufout";
5+
export { default as pLimit } from "p-limit";
56

67
export * from "./actions.js";
78
export * from "./errors.js";

packages/ferric/src/build.ts

Lines changed: 102 additions & 34 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,69 @@ 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+
137+
function logNotice(message: string, ...params: string[]) {
138+
console.log(`${chalk.yellow("ℹ︎")} ${message}`, ...params);
139+
}
140+
115141
export const buildCommand = new Command("build")
116142
.description("Build Rust Node-API module")
117143
.addOption(targetOption)
144+
.addOption(cleanOption)
118145
.addOption(appleTarget)
119146
.addOption(androidTarget)
120147
.addOption(ndkVersionOption)
121148
.addOption(outputPathOption)
122149
.addOption(configurationOption)
123150
.addOption(xcframeworkExtensionOption)
124151
.addOption(appleBundleIdentifierOption)
152+
.addOption(concurrencyOption)
153+
.addOption(verboseOption)
125154
.action(
126155
wrapAction(
127156
async ({
128157
target: targetArg,
158+
clean,
129159
apple,
130160
android,
131161
ndkVersion,
132162
output: outputPath,
133163
configuration,
134164
xcframeworkExtension,
135165
appleBundleIdentifier,
166+
concurrency,
167+
verbose,
136168
}) => {
169+
if (clean) {
170+
await oraPromise(
171+
() => spawn("cargo", ["clean"], { outputMode: "buffered" }),
172+
{
173+
text: "Cleaning target directory",
174+
successText: "Cleaned target directory",
175+
failText: (error) => `Failed to clean target directory: ${error}`,
176+
},
177+
);
178+
}
179+
if (verbose && concurrency > 1) {
180+
logNotice(
181+
`Consider passing ${chalk.blue("--concurrency")} 1 when running in verbose mode`,
182+
);
183+
}
184+
const limit = pLimit(concurrency);
137185
const targets = new Set([...targetArg]);
138186
if (apple) {
139187
for (const target of APPLE_TARGETS) {
@@ -159,15 +207,12 @@ export const buildCommand = new Command("build")
159207
targets.add("aarch64-apple-ios-sim");
160208
}
161209
}
162-
console.error(
163-
chalk.yellowBright("ℹ"),
164-
chalk.dim(
165-
`Using default targets, pass ${chalk.italic(
166-
"--android",
167-
)}, ${chalk.italic("--apple")} or individual ${chalk.italic(
168-
"--target",
169-
)} options, to avoid this.`,
170-
),
210+
logNotice(
211+
`Using default targets, pass ${chalk.blue(
212+
"--android",
213+
)}, ${chalk.blue("--apple")} or individual ${chalk.blue(
214+
"--target",
215+
)} options, choose exactly what to target`,
171216
);
172217
}
173218
ensureCargo();
@@ -180,30 +225,40 @@ export const buildCommand = new Command("build")
180225
targets.size +
181226
(targets.size === 1 ? " target" : " targets") +
182227
chalk.dim(" (" + [...targets].join(", ") + ")");
228+
183229
const [appleLibraries, androidLibraries] = await oraPromise(
184230
Promise.all([
185231
Promise.all(
186-
appleTargets.map(
187-
async (target) =>
188-
[target, await build({ configuration, target })] as const,
232+
appleTargets.map((target) =>
233+
limit(
234+
async () =>
235+
[
236+
target,
237+
await build({ configuration, target, verbose }),
238+
] as const,
239+
),
189240
),
190241
),
191242
Promise.all(
192-
androidTargets.map(
193-
async (target) =>
194-
[
195-
target,
196-
await build({
197-
configuration,
243+
androidTargets.map((target) =>
244+
limit(
245+
async () =>
246+
[
198247
target,
199-
ndkVersion,
200-
androidApiLevel: ANDROID_API_LEVEL,
201-
}),
202-
] as const,
248+
await build({
249+
configuration,
250+
target,
251+
verbose,
252+
ndkVersion,
253+
androidApiLevel: ANDROID_API_LEVEL,
254+
}),
255+
] as const,
256+
),
203257
),
204258
),
205259
]),
206260
{
261+
isSilent: verbose,
207262
text: `Building ${targetsDescription}`,
208263
successText: `Built ${targetsDescription}`,
209264
failText: (error: Error) => `Failed to build: ${error.message}`,
@@ -225,11 +280,13 @@ export const buildCommand = new Command("build")
225280
);
226281

227282
await oraPromise(
228-
createAndroidLibsDirectory({
229-
outputPath: androidLibsOutputPath,
230-
libraries,
231-
autoLink: true,
232-
}),
283+
limit(() =>
284+
createAndroidLibsDirectory({
285+
outputPath: androidLibsOutputPath,
286+
libraries,
287+
autoLink: true,
288+
}),
289+
),
233290
{
234291
text: "Assembling Android libs directory",
235292
successText: `Android libs directory assembled into ${prettyPath(
@@ -243,14 +300,25 @@ export const buildCommand = new Command("build")
243300

244301
if (appleLibraries.length > 0) {
245302
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-
}),
303+
304+
const frameworkPaths = await oraPromise(
305+
Promise.all(
306+
libraryPaths.map((libraryPath) =>
307+
limit(() =>
308+
// TODO: Pass true as `versioned` argument for -darwin targets
309+
createAppleFramework({
310+
libraryPath,
311+
bundleIdentifier: appleBundleIdentifier,
312+
}),
313+
),
314+
),
253315
),
316+
{
317+
text: "Creating Apple frameworks",
318+
successText: `Created Apple frameworks`,
319+
failText: ({ message }) =>
320+
`Failed to create Apple frameworks: ${message}`,
321+
},
254322
);
255323
const xcframeworkFilename = determineXCFrameworkFilename(
256324
frameworkPaths,

0 commit comments

Comments
 (0)