crossbind 2.0.0-beta.63 → 2.0.0-beta.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/package.json +2 -2
- package/src/actions/buildBinTools.js +0 -0
- package/src/actions/buildJs.js +2 -9
- package/src/actions/buildNode.js +4 -6
- package/src/actions/buildWasiCommand.js +10 -5
- package/src/actions/buildWasm.js +24 -3
- package/src/actions/createImportedBridges.js +7 -0
- package/src/actions/createInterface.js +3 -3
- package/src/actions/createLib.js +14 -2
- package/src/actions/getData.js +2 -1
- package/src/actions/getEnv.js +7 -0
- package/src/actions/runnerCommands.js +16 -4
- package/src/actions/writeRuntimeEntry.js +18 -7
- package/src/assets/cpp-runtime/node.cpp +18 -1
- package/src/assets/js-runtime/adapters/worker-comlink.js +51 -0
- package/src/assets/js-runtime/core.js +5 -4
- package/src/assets/toolchain-digests.json +19 -19
- package/src/assets/wasi-runtime/http/README.md +17 -0
- package/src/assets/wasi-runtime/http/bindings/crossbind_http.c +2537 -0
- package/src/assets/wasi-runtime/http/bindings/crossbind_http.h +826 -0
- package/src/assets/wasi-runtime/http/bindings/crossbind_http_component_type.o +0 -0
- package/src/assets/wasi-runtime/http/handler.cpp +298 -0
- package/src/assets/wasi-runtime/http/include/crossbind/http.h +31 -0
- package/src/assets/wasi-runtime/http/wit/deps/LICENSE.md +8 -0
- package/src/assets/wasi-runtime/http/wit/deps/cli.wit +28 -0
- package/src/assets/wasi-runtime/http/wit/deps/clocks.wit +43 -0
- package/src/assets/wasi-runtime/http/wit/deps/http.wit +509 -0
- package/src/assets/wasi-runtime/http/wit/deps/random.wit +18 -0
- package/src/assets/wasi-runtime/http/wit/world.wit +6 -0
- package/src/bin.js +29 -179
- package/src/cli.js +14 -0
- package/src/integration/getCrossbindScript.js +4 -3
- package/src/program.js +169 -0
- package/src/state/index.js +4 -3
- package/src/state/loadConfig.js +4 -11
- package/src/utils/appRustCrates.js +4 -0
- package/src/utils/bridgeExtras.js +74 -6
- package/src/utils/cargoLock.js +19 -0
- package/src/utils/cppDts.js +2 -0
- package/src/utils/embindObjectLifetime.js +35 -0
- package/src/utils/packageLicense.js +4 -2
- package/src/utils/resetCMakeCacheForSource.js +26 -0
- package/src/utils/rustBridgeGen.js +13 -4
- package/src/utils/rustMt.js +5 -0
- package/src/utils/rustSysroot.js +7 -5
- package/src/utils/swigInterface.js +5 -4
- package/src/utils/systemConfig.js +13 -0
- package/src/utils/targets.js +11 -0
- package/src/utils/wasiHttp.js +50 -0
package/src/bin.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
|
-
import {
|
|
5
|
+
import { Option } from 'commander';
|
|
6
6
|
|
|
7
7
|
import { state } from './index.js';
|
|
8
|
+
import { addSystemCommands, createProgram, handleFatal } from './program.js';
|
|
8
9
|
import createBridgeFile from './actions/createInterface.js';
|
|
9
10
|
import createLib from './actions/createLib.js';
|
|
10
11
|
import buildWasm from './actions/buildWasm.js';
|
|
@@ -15,8 +16,8 @@ import buildExternal from './actions/buildExternal.js';
|
|
|
15
16
|
import buildPackageTypes from './actions/buildTypes.js';
|
|
16
17
|
import buildLib from './actions/buildLib.js';
|
|
17
18
|
import buildDependencies from './actions/buildDependencies.js';
|
|
18
|
-
import writeRuntimeEntry, { removeImportHooks, writeImportHooks } from './actions/writeRuntimeEntry.js';
|
|
19
|
-
import { createImportedBridges, findAppNativeImports } from './actions/createImportedBridges.js';
|
|
19
|
+
import writeRuntimeEntry, { removeImportHooks, writeImportHooks, writeImportManifest } from './actions/writeRuntimeEntry.js';
|
|
20
|
+
import { bindsAppImports, createImportedBridges, findAppNativeImports } from './actions/createImportedBridges.js';
|
|
20
21
|
import { nativeExtensions } from './utils/nativeImports.js';
|
|
21
22
|
import getDependFilePath from './integration/getDependFilePath.js';
|
|
22
23
|
import { boundHeaderSpecifiers, bridgeTargets, nodeBridgeTarget, resolveBoundHeaders } from './utils/boundHeaders.js';
|
|
@@ -28,18 +29,11 @@ import { OPT_IN_PLATFORMS, selectRuntimeEnvs } from './utils/targets.js';
|
|
|
28
29
|
import { resolveHostPlatform } from './utils/hostPlatform.js';
|
|
29
30
|
import resolveEmbindNapiRoot, { resolveEmbindJsiRoot } from './utils/resolveEmbindNapi.js';
|
|
30
31
|
|
|
31
|
-
import writeJson from './utils/writeJson.js';
|
|
32
|
-
import flattenConfigForTable from './utils/flattenConfigForTable.js';
|
|
33
|
-
import systemKeys from './utils/systemKeys.js';
|
|
34
32
|
import logger from './utils/logger.js';
|
|
35
33
|
import { getDockerImage, getDockerContainerName } from './utils/pullDockerImage.js';
|
|
36
34
|
import {
|
|
37
|
-
RUNNER_PLATFORMS, RUNNER_ROLES, connectCommands, deploySteps, initRunner, newRunnerToken, runnerHostPort, startRunner,
|
|
35
|
+
RUNNER_PLATFORMS, RUNNER_ROLES, connectCommands, deleteRunner, deploySteps, initRunner, newRunnerToken, runnerHostPort, startRunner,
|
|
38
36
|
} from './actions/runnerCommands.js';
|
|
39
|
-
import {
|
|
40
|
-
deleteAccount as deleteCloudAccount, fetchUsage, login as loginToCloud, logout as logoutOfCloud, usageLines,
|
|
41
|
-
} from './actions/cloudCommands.js';
|
|
42
|
-
import { cloudUrl } from './utils/cloudAccount.js';
|
|
43
37
|
import { cargoHome } from './utils/runCargo.js';
|
|
44
38
|
import { conanRoot } from './utils/runConan.js';
|
|
45
39
|
import { cleanDepsCache } from './utils/dependencyRebuild.js';
|
|
@@ -48,36 +42,16 @@ import { formatNoticesMarkdown, formatCycloneDxSbom, validateSpdx } from './util
|
|
|
48
42
|
import { SBOM_FILE, nodePackageMeta, writeNodePackageLicense } from './utils/packageLicense.js';
|
|
49
43
|
import findFiles from './utils/findFiles.js';
|
|
50
44
|
|
|
51
|
-
const packageJSON = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url)));
|
|
52
|
-
|
|
53
45
|
const platforms = [...new Set(state.targets.map(target => target.platform).filter(t => t))];
|
|
54
46
|
const archs = [...new Set(state.targets.map(target => target.arch).filter(t => t))];
|
|
55
47
|
const runtimes = [...new Set(state.targets.map(target => target.runtime).filter(t => t))];
|
|
56
48
|
const buildTypes = [...new Set(state.targets.map(target => target.buildType).filter(t => t))];
|
|
57
49
|
const runtimeEnvs = [...new Set(state.targets.map(target => target.runtimeEnv).filter(t => t))];
|
|
58
50
|
|
|
59
|
-
// Build failures bubble up as exceptions with a `cause` chain (see actions/run.js);
|
|
60
|
-
// show the operator a single line — DEBUG=1 for the full stack — and exit non-zero.
|
|
61
|
-
const handleFatal = (e) => {
|
|
62
|
-
if (process.env.DEBUG) {
|
|
63
|
-
console.error(e);
|
|
64
|
-
} else {
|
|
65
|
-
let message = e?.message || String(e);
|
|
66
|
-
if (e?.cause?.message) message += `\n caused by: ${e.cause.message.split('\n')[0]}`;
|
|
67
|
-
console.error(message);
|
|
68
|
-
}
|
|
69
|
-
process.exit(1);
|
|
70
|
-
};
|
|
71
51
|
process.on('uncaughtException', handleFatal);
|
|
72
52
|
process.on('unhandledRejection', handleFatal);
|
|
73
53
|
|
|
74
|
-
const program =
|
|
75
|
-
|
|
76
|
-
program
|
|
77
|
-
.name('crossbind')
|
|
78
|
-
.description('Compile C++ files to WebAssembly and native platforms.')
|
|
79
|
-
.version(packageJSON.version)
|
|
80
|
-
.showHelpAfterError();
|
|
54
|
+
const program = createProgram();
|
|
81
55
|
|
|
82
56
|
program.command('build')
|
|
83
57
|
.description('compile the project that was set up using crossbind')
|
|
@@ -87,8 +61,9 @@ program.command('build')
|
|
|
87
61
|
.addOption(new Option('-b, --build-type <buildType>', 'target build type').argParser(createListParser(buildTypes)))
|
|
88
62
|
.addOption(new Option('-e, --runtime-env <runtimeEnv>', 'binaries to make, by runtime environment (without it, only the archives are built)').argParser(createListParser(runtimeEnvs)))
|
|
89
63
|
.option('--rebuild-deps [list]', 'rebuild dependencies from source instead of using prebuilt (all, or a comma-separated list of names)')
|
|
64
|
+
.option('--bind-imports', "also bind what the app's JavaScript imports into browser binaries (node binaries always do), for a page that resolves those imports itself")
|
|
90
65
|
.action((options) => {
|
|
91
|
-
const { rebuildDeps, ...rest } = options;
|
|
66
|
+
const { rebuildDeps, bindImports, ...rest } = options;
|
|
92
67
|
const targetParams = { ...rest };
|
|
93
68
|
|
|
94
69
|
if (!targetParams.arch) {
|
|
@@ -109,7 +84,7 @@ program.command('build')
|
|
|
109
84
|
if (state.config.build.withBuildConfig) {
|
|
110
85
|
buildExternal(targetParams);
|
|
111
86
|
} else {
|
|
112
|
-
build(targetParams, rebuildDeps, binaryRuntimeEnvs);
|
|
87
|
+
build(targetParams, rebuildDeps, binaryRuntimeEnvs, { bindImports });
|
|
113
88
|
}
|
|
114
89
|
});
|
|
115
90
|
|
|
@@ -284,10 +259,10 @@ commandRunner.command('start')
|
|
|
284
259
|
connectCommands(options.role, runner.url, runner.displayToken).forEach((line) => console.log(` ${line}`));
|
|
285
260
|
}));
|
|
286
261
|
|
|
287
|
-
commandRunner.command('
|
|
288
|
-
.description('stop and
|
|
262
|
+
commandRunner.command('delete')
|
|
263
|
+
.description('stop and delete the local runner')
|
|
289
264
|
.addOption(roleOption())
|
|
290
|
-
.action(runnerAction((options) =>
|
|
265
|
+
.action(runnerAction((options) => deleteRunner({ role: options.role })));
|
|
291
266
|
|
|
292
267
|
commandRunner.command('init')
|
|
293
268
|
.description('write a folder that deploys a runner to a hosting platform')
|
|
@@ -306,76 +281,7 @@ commandRunner.command('init')
|
|
|
306
281
|
deploySteps(platform, dir, newRunnerToken(), options.role, url).forEach((step) => console.log(` ${step}`));
|
|
307
282
|
}));
|
|
308
283
|
|
|
309
|
-
program
|
|
310
|
-
.description('sign in to crossbind cloud with your GitHub account')
|
|
311
|
-
.action(async () => {
|
|
312
|
-
const url = cloudUrl(state.config.system);
|
|
313
|
-
const { login } = await loginToCloud({ url });
|
|
314
|
-
console.log(`crossbind: signed in to ${url} as ${login}.`);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
program.command('logout')
|
|
318
|
-
.description('sign out of crossbind cloud on this machine')
|
|
319
|
-
.option('--all', 'sign out every machine signed in to this account')
|
|
320
|
-
.action(async (options) => {
|
|
321
|
-
const url = cloudUrl(state.config.system);
|
|
322
|
-
const result = await logoutOfCloud({ url, all: Boolean(options.all) });
|
|
323
|
-
if (!result) {
|
|
324
|
-
console.log(`crossbind: not signed in to ${url}.`);
|
|
325
|
-
} else if (result.revoked) {
|
|
326
|
-
console.log(`crossbind: signed ${result.login} out${options.all ? ' on every machine' : ''}.`);
|
|
327
|
-
} else {
|
|
328
|
-
console.warn(`crossbind: signed out here, but crossbind cloud did not revoke the token (${result.reason.replace(/^crossbind: /, '')}). Sign in again and run crossbind logout --all to revoke it.`);
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
program.command('usage')
|
|
333
|
-
.description('show what you used of crossbind cloud this month and what is left')
|
|
334
|
-
.action(async () => {
|
|
335
|
-
usageLines(await fetchUsage({ url: cloudUrl(state.config.system) })).forEach((line) => console.log(line));
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
program.command('account')
|
|
339
|
-
.description('manage your crossbind cloud account')
|
|
340
|
-
.command('delete')
|
|
341
|
-
.description('delete your crossbind cloud account, with the sign-in of every machine (https://crossbind.dev/privacy/)')
|
|
342
|
-
.option('--yes', 'delete it; without this, nothing is deleted')
|
|
343
|
-
.action(async (options) => {
|
|
344
|
-
const url = cloudUrl(state.config.system);
|
|
345
|
-
if (!options.yes) {
|
|
346
|
-
throw new Error(`crossbind: this deletes your crossbind cloud account at ${url}, with the sign-in of every machine. Run crossbind account delete --yes to go ahead.`);
|
|
347
|
-
}
|
|
348
|
-
const { login } = await deleteCloudAccount({ url });
|
|
349
|
-
console.log(`crossbind: deleted the crossbind cloud account of ${login}.`);
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
const commandConfig = program.command('config')
|
|
353
|
-
.description('manage the crossbind configuration files');
|
|
354
|
-
|
|
355
|
-
commandConfig.command('get')
|
|
356
|
-
.description('get the crossbind system configuration')
|
|
357
|
-
.argument('<key>', 'configuration key to read')
|
|
358
|
-
.action((key) => getSystemConfig(key));
|
|
359
|
-
|
|
360
|
-
commandConfig.command('set')
|
|
361
|
-
.description('set the crossbind system configuration')
|
|
362
|
-
.argument('<key>', 'configuration key to set')
|
|
363
|
-
.argument('<value>', 'value to assign')
|
|
364
|
-
.action((key, value) => setSystemConfig(key, value));
|
|
365
|
-
|
|
366
|
-
commandConfig.command('delete')
|
|
367
|
-
.description('delete the crossbind system configuration')
|
|
368
|
-
.argument('<key>', 'configuration key to remove')
|
|
369
|
-
.action((key) => deleteSystemConfig(key));
|
|
370
|
-
|
|
371
|
-
commandConfig.command('list')
|
|
372
|
-
.description('list the crossbind configurations')
|
|
373
|
-
.addOption(new Option('-t, --type <type>', 'config type').default('system').choices(['all', 'system', 'project']))
|
|
374
|
-
.action((options) => listSystemConfig(options.type));
|
|
375
|
-
|
|
376
|
-
commandConfig.command('keys')
|
|
377
|
-
.description('list all available system configuration keys for crossbind')
|
|
378
|
-
.action(() => listSystemKeys());
|
|
284
|
+
addSystemCommands(program);
|
|
379
285
|
|
|
380
286
|
// `pnpm run <script> -- --flag` forwards the `--` literally and commander would then
|
|
381
287
|
// treat the flags as positionals. Strip the first separator for commands where every
|
|
@@ -388,67 +294,6 @@ if (['build', 'clean-deps', 'licenses'].includes(argv[2])) {
|
|
|
388
294
|
|
|
389
295
|
program.parse(argv);
|
|
390
296
|
|
|
391
|
-
function listSystemKeys() {
|
|
392
|
-
console.info('Available configurations:');
|
|
393
|
-
console.table(systemKeys);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function getSystemConfig(systemKey) {
|
|
397
|
-
if (!systemKey) {
|
|
398
|
-
listSystemConfig('system');
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
const key = systemKey.toUpperCase();
|
|
402
|
-
const output = {};
|
|
403
|
-
if (key in state.config.system) {
|
|
404
|
-
output[key] = { value: state.config.system[key] || 'undefined', default: systemKeys[key].default };
|
|
405
|
-
}
|
|
406
|
-
Object.keys(systemKeys).filter((k) => k.includes(key)).forEach((k) => {
|
|
407
|
-
if (k in state.config.system) {
|
|
408
|
-
output[k] = { value: state.config.system[k] || 'undefined', default: systemKeys[k].default };
|
|
409
|
-
}
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
console.table(output);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
function setSystemConfig(key, value) {
|
|
416
|
-
if (!systemKeys[key]) {
|
|
417
|
-
throw new Error(`Configuration ${key} is not available. Please choose from the following available configurations: ${Object.keys(systemKeys).join(', ')}`);
|
|
418
|
-
}
|
|
419
|
-
if (systemKeys[key].options && !systemKeys[key].options.includes(value)) {
|
|
420
|
-
throw new Error(`Value ${value} is not available. Please choose from the following available values: ${systemKeys[key].options.join(', ')}`);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
state.config.system[key] = value;
|
|
424
|
-
writeJson(state.config.paths.systemConfig, state.config.system);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function deleteSystemConfig(key) {
|
|
428
|
-
if (!systemKeys[key]) {
|
|
429
|
-
throw new Error(`Configuration ${key} is not available. Please choose from the following available configurations: ${Object.keys(systemKeys).join(', ')}`);
|
|
430
|
-
}
|
|
431
|
-
delete state.config.system[key];
|
|
432
|
-
writeJson(state.config.paths.systemConfig, state.config.system);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function listSystemConfig(type) {
|
|
436
|
-
const { system: systemConfig, ...projectConfig } = state.config;
|
|
437
|
-
if (type === 'all' || type === 'system') {
|
|
438
|
-
console.log('System Configuration');
|
|
439
|
-
console.table(systemConfig);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if (type === 'all') {
|
|
443
|
-
console.log('');
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
if (type === 'all' || type === 'project') {
|
|
447
|
-
console.log(`Project Configuration (paths relative to ${projectConfig.paths.base})`);
|
|
448
|
-
console.table(flattenConfigForTable(projectConfig, { base: projectConfig.paths.base }));
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
297
|
function run(programName, params) {
|
|
453
298
|
runCrossbindApp(programName, params, null, null, { console: true });
|
|
454
299
|
}
|
|
@@ -461,7 +306,7 @@ function onlyLoadsPackagedAddons(binaryParams) {
|
|
|
461
306
|
return targets.every((target) => target.runtimeEnv === 'node' && target.platform !== 'wasm') && addonsInPackages();
|
|
462
307
|
}
|
|
463
308
|
|
|
464
|
-
async function build(targetParams, rebuildOption, binaryRuntimeEnvs) {
|
|
309
|
+
async function build(targetParams, rebuildOption, binaryRuntimeEnvs, wasmOptions = {}) {
|
|
465
310
|
await buildDependencies({ targetParams, rebuildOption });
|
|
466
311
|
const binaryParams = { ...targetParams, runtimeEnv: binaryRuntimeEnvs };
|
|
467
312
|
if (!onlyLoadsPackagedAddons(binaryParams)) {
|
|
@@ -470,7 +315,7 @@ async function build(targetParams, rebuildOption, binaryRuntimeEnvs) {
|
|
|
470
315
|
if (binaryRuntimeEnvs.length === 0) {
|
|
471
316
|
logger.info(`crossbind: no runtime environment was selected, so only the archives were built - pass -e (${runtimeEnvs.join(', ')}) for a binary.`);
|
|
472
317
|
} else {
|
|
473
|
-
await createWasmJs(binaryParams);
|
|
318
|
+
await createWasmJs(binaryParams, wasmOptions);
|
|
474
319
|
await createNodeAddons(binaryParams);
|
|
475
320
|
await createNativeCommands(binaryParams);
|
|
476
321
|
await createWasiCommands(binaryParams);
|
|
@@ -593,16 +438,15 @@ function scopeOutputToCommonJs() {
|
|
|
593
438
|
}
|
|
594
439
|
}
|
|
595
440
|
|
|
596
|
-
async function createWasmJs(targetParams) {
|
|
441
|
+
async function createWasmJs(targetParams, wasmOptions = {}) {
|
|
597
442
|
const targets = getFilteredBuildTargets(targetParams, { platform: 'wasm' });
|
|
598
443
|
if (targets.length === 0) {
|
|
599
444
|
return;
|
|
600
445
|
}
|
|
601
446
|
const headers = createBridges();
|
|
602
|
-
|
|
603
|
-
const
|
|
604
|
-
const
|
|
605
|
-
const nodeHeaders = [...headers, ...createImportedBridges(headers, imports)];
|
|
447
|
+
const bindsImports = (target) => bindsAppImports(target, wasmOptions);
|
|
448
|
+
const imports = targets.some(bindsImports) ? findAppNativeImports([state.targets.find((t) => t.platform === 'wasm')]) : new Map();
|
|
449
|
+
const appHeaders = [...headers, ...createImportedBridges(headers, imports)];
|
|
606
450
|
const optOf = (bridged) => ({
|
|
607
451
|
buildSource: false,
|
|
608
452
|
nativeGlob: [
|
|
@@ -619,7 +463,7 @@ async function createWasmJs(targetParams) {
|
|
|
619
463
|
// link and the copy step below moved build/data away on the last run.
|
|
620
464
|
const distComplete = fs.existsSync(`${state.config.paths.output}/${target.jsName}`)
|
|
621
465
|
&& fs.existsSync(`${state.config.paths.output}/${target.wasmName}`);
|
|
622
|
-
createLib(target, 'Bridge', optOf(
|
|
466
|
+
createLib(target, 'Bridge', optOf(bindsImports(target) ? appHeaders : headers));
|
|
623
467
|
const built = await buildWasm(target, { force: !distComplete });
|
|
624
468
|
if (!built) {
|
|
625
469
|
continue;
|
|
@@ -640,15 +484,21 @@ async function createWasmJs(targetParams) {
|
|
|
640
484
|
if (targets.some((target) => ['node', 'edge'].includes(target.runtimeEnv))) {
|
|
641
485
|
scopeOutputToCommonJs();
|
|
642
486
|
}
|
|
487
|
+
// The list a page that maps the app's imports itself reads; a build without --bind-imports is not for one.
|
|
488
|
+
if (targets.some((target) => target.runtimeEnv === 'browser')) {
|
|
489
|
+
const manifest = `${state.config.paths.output}/browser/imports.json`;
|
|
490
|
+
if (wasmOptions.bindImports) writeImportManifest(manifest, appHeaders, imports, state.config.paths.project);
|
|
491
|
+
else fs.rmSync(manifest, { force: true });
|
|
492
|
+
}
|
|
643
493
|
// A runtime's entry loads its single-thread wasm32 release build.
|
|
644
494
|
const entryTargetOf = (runtimeEnv) => targets.find((target) => target.runtimeEnv === runtimeEnv && target.arch === 'wasm32'
|
|
645
495
|
&& target.runtime === 'st' && target.buildType === 'release' && fs.existsSync(`${state.config.paths.output}/${target.jsName}`));
|
|
646
496
|
const nodeTarget = entryTargetOf('node');
|
|
647
497
|
const edgeTarget = entryTargetOf('edge');
|
|
648
|
-
if (
|
|
498
|
+
if (appHeaders.length && nodeTarget) {
|
|
649
499
|
const entry = `${state.config.paths.output}/node/wasm.mjs`;
|
|
650
|
-
writeRuntimeEntry(entry,
|
|
651
|
-
if (imports.size) writeImportHooks(entry,
|
|
500
|
+
writeRuntimeEntry(entry, appHeaders, ENTRY_RUNTIMES.node(nodeTarget.jsName));
|
|
501
|
+
if (imports.size) writeImportHooks(entry, appHeaders, imports, nativeExtensions(state.config.ext));
|
|
652
502
|
else removeImportHooks(entry);
|
|
653
503
|
}
|
|
654
504
|
if (headers.length && edgeTarget) {
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { addSystemCommands, createProgram, handleFatal } from './program.js';
|
|
4
|
+
|
|
5
|
+
// bin.js loads the project before it reads the command line, which fails outside a project; these commands need none.
|
|
6
|
+
const program = createProgram();
|
|
7
|
+
addSystemCommands(program);
|
|
8
|
+
const withoutProject = [...program.commands.map((command) => command.name()), '-V', '--version'];
|
|
9
|
+
|
|
10
|
+
if (withoutProject.includes(process.argv[2])) {
|
|
11
|
+
await program.parseAsync().catch(handleFatal);
|
|
12
|
+
} else {
|
|
13
|
+
await import('./bin.js');
|
|
14
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import
|
|
3
|
+
import getEnv from '../actions/getEnv.js';
|
|
4
4
|
import loadJson from '../utils/loadJson.js';
|
|
5
5
|
import state from '../state/index.js';
|
|
6
6
|
import { parseSurface, createRustBridgeCrate, createCrateImportBridge } from '../utils/rustBridgeGen.js';
|
|
@@ -70,6 +70,7 @@ export function getRustSymbols(rsFile) {
|
|
|
70
70
|
modulePath,
|
|
71
71
|
spec: cargoDeps[crateName],
|
|
72
72
|
cacheDir: state.config.paths.cache,
|
|
73
|
+
projectDir: state.config.paths.project,
|
|
73
74
|
dtsMode: state.config.dts,
|
|
74
75
|
log: () => {},
|
|
75
76
|
});
|
|
@@ -110,7 +111,7 @@ function buildScript(target, symbols, base) {
|
|
|
110
111
|
if (!target) {
|
|
111
112
|
throw new Error('The target is not available!');
|
|
112
113
|
}
|
|
113
|
-
const env = JSON.stringify(
|
|
114
|
+
const env = JSON.stringify(getEnv(target));
|
|
114
115
|
const getPlatformScript = target.platform === 'wasm' ? getWebScript : getReactNativeScript;
|
|
115
116
|
|
|
116
117
|
let symbolExportDefineString = '';
|
|
@@ -178,7 +179,7 @@ function getReactNativeScript(env) {
|
|
|
178
179
|
const RNJsiLib = TurboModuleRegistry.get('RNJsiLib');
|
|
179
180
|
|
|
180
181
|
function setEnv() {
|
|
181
|
-
const env =
|
|
182
|
+
const env = ${env};
|
|
182
183
|
const CROSSBIND_DATA_PATH = Module.Crossbind.getEnv('CROSSBIND_DATA_PATH');
|
|
183
184
|
|
|
184
185
|
Object.entries(env).forEach(([key, value]) => {
|
package/src/program.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { Command, Option } from 'commander';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
deleteAccount as deleteCloudAccount, fetchUsage, login as loginToCloud, logout as logoutOfCloud, usageLines,
|
|
6
|
+
} from './actions/cloudCommands.js';
|
|
7
|
+
import { cloudUrl } from './utils/cloudAccount.js';
|
|
8
|
+
import flattenConfigForTable from './utils/flattenConfigForTable.js';
|
|
9
|
+
import loadSystemConfig, { systemConfigPath } from './utils/systemConfig.js';
|
|
10
|
+
import systemKeys from './utils/systemKeys.js';
|
|
11
|
+
import writeJson from './utils/writeJson.js';
|
|
12
|
+
|
|
13
|
+
const packageJSON = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url)));
|
|
14
|
+
|
|
15
|
+
// Build failures bubble up as exceptions with a `cause` chain (see actions/run.js);
|
|
16
|
+
// show the operator a single line — DEBUG=1 for the full stack — and exit non-zero.
|
|
17
|
+
export const handleFatal = (e) => {
|
|
18
|
+
if (process.env.DEBUG) {
|
|
19
|
+
console.error(e);
|
|
20
|
+
} else {
|
|
21
|
+
let message = e?.message || String(e);
|
|
22
|
+
if (e?.cause?.message) message += `\n caused by: ${e.cause.message.split('\n')[0]}`;
|
|
23
|
+
console.error(message);
|
|
24
|
+
}
|
|
25
|
+
process.exit(1);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const createProgram = () => new Command()
|
|
29
|
+
.name('crossbind')
|
|
30
|
+
.description('Compile C++ files to WebAssembly and native platforms.')
|
|
31
|
+
.version(packageJSON.version)
|
|
32
|
+
.showHelpAfterError();
|
|
33
|
+
|
|
34
|
+
export function addSystemCommands(program) {
|
|
35
|
+
program.command('login')
|
|
36
|
+
.description('sign in to crossbind cloud with your GitHub account')
|
|
37
|
+
.action(async () => {
|
|
38
|
+
const url = cloudUrl(loadSystemConfig());
|
|
39
|
+
const { login } = await loginToCloud({ url });
|
|
40
|
+
console.log(`crossbind: signed in to ${url} as ${login}.`);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
program.command('logout')
|
|
44
|
+
.description('sign out of crossbind cloud on this machine')
|
|
45
|
+
.option('--all', 'sign out every machine signed in to this account')
|
|
46
|
+
.action(async (options) => {
|
|
47
|
+
const url = cloudUrl(loadSystemConfig());
|
|
48
|
+
const result = await logoutOfCloud({ url, all: Boolean(options.all) });
|
|
49
|
+
if (!result) {
|
|
50
|
+
console.log(`crossbind: not signed in to ${url}.`);
|
|
51
|
+
} else if (result.revoked) {
|
|
52
|
+
console.log(`crossbind: signed ${result.login} out${options.all ? ' on every machine' : ''}.`);
|
|
53
|
+
} else {
|
|
54
|
+
console.warn(`crossbind: signed out here, but crossbind cloud did not revoke the token (${result.reason.replace(/^crossbind: /, '')}). Sign in again and run crossbind logout --all to revoke it.`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
program.command('usage')
|
|
59
|
+
.description('show what you used of crossbind cloud this month and what is left')
|
|
60
|
+
.action(async () => {
|
|
61
|
+
usageLines(await fetchUsage({ url: cloudUrl(loadSystemConfig()) })).forEach((line) => console.log(line));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
program.command('account')
|
|
65
|
+
.description('manage your crossbind cloud account')
|
|
66
|
+
.command('delete')
|
|
67
|
+
.description('delete your crossbind cloud account, with the sign-in of every machine (https://crossbind.dev/privacy/)')
|
|
68
|
+
.option('--yes', 'delete it; without this, nothing is deleted')
|
|
69
|
+
.action(async (options) => {
|
|
70
|
+
const url = cloudUrl(loadSystemConfig());
|
|
71
|
+
if (!options.yes) {
|
|
72
|
+
throw new Error(`crossbind: this deletes your crossbind cloud account at ${url}, with the sign-in of every machine. Run crossbind account delete --yes to go ahead.`);
|
|
73
|
+
}
|
|
74
|
+
const { login } = await deleteCloudAccount({ url });
|
|
75
|
+
console.log(`crossbind: deleted the crossbind cloud account of ${login}.`);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const commandConfig = program.command('config')
|
|
79
|
+
.description('manage the crossbind configuration files');
|
|
80
|
+
|
|
81
|
+
commandConfig.command('get')
|
|
82
|
+
.description('get the crossbind system configuration')
|
|
83
|
+
.argument('<key>', 'configuration key to read')
|
|
84
|
+
.action((key) => getSystemConfig(key));
|
|
85
|
+
|
|
86
|
+
commandConfig.command('set')
|
|
87
|
+
.description('set the crossbind system configuration')
|
|
88
|
+
.argument('<key>', 'configuration key to set')
|
|
89
|
+
.argument('<value>', 'value to assign')
|
|
90
|
+
.action((key, value) => setSystemConfig(key, value));
|
|
91
|
+
|
|
92
|
+
commandConfig.command('delete')
|
|
93
|
+
.description('delete the crossbind system configuration')
|
|
94
|
+
.argument('<key>', 'configuration key to remove')
|
|
95
|
+
.action((key) => deleteSystemConfig(key));
|
|
96
|
+
|
|
97
|
+
commandConfig.command('list')
|
|
98
|
+
.description('list the crossbind configurations')
|
|
99
|
+
.addOption(new Option('-t, --type <type>', 'config type').default('system').choices(['all', 'system', 'project']))
|
|
100
|
+
.action((options) => listSystemConfig(options.type));
|
|
101
|
+
|
|
102
|
+
commandConfig.command('keys')
|
|
103
|
+
.description('list all available system configuration keys for crossbind')
|
|
104
|
+
.action(() => listSystemKeys());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function listSystemKeys() {
|
|
108
|
+
console.info('Available configurations:');
|
|
109
|
+
console.table(systemKeys);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function getSystemConfig(systemKey) {
|
|
113
|
+
if (!systemKey) {
|
|
114
|
+
await listSystemConfig('system');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const system = loadSystemConfig();
|
|
118
|
+
const key = systemKey.toUpperCase();
|
|
119
|
+
const output = {};
|
|
120
|
+
if (key in system) {
|
|
121
|
+
output[key] = { value: system[key] || 'undefined', default: systemKeys[key].default };
|
|
122
|
+
}
|
|
123
|
+
Object.keys(systemKeys).filter((k) => k.includes(key)).forEach((k) => {
|
|
124
|
+
if (k in system) {
|
|
125
|
+
output[k] = { value: system[k] || 'undefined', default: systemKeys[k].default };
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
console.table(output);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function setSystemConfig(key, value) {
|
|
133
|
+
if (!systemKeys[key]) {
|
|
134
|
+
throw new Error(`Configuration ${key} is not available. Please choose from the following available configurations: ${Object.keys(systemKeys).join(', ')}`);
|
|
135
|
+
}
|
|
136
|
+
if (systemKeys[key].options && !systemKeys[key].options.includes(value)) {
|
|
137
|
+
throw new Error(`Value ${value} is not available. Please choose from the following available values: ${systemKeys[key].options.join(', ')}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
writeJson(systemConfigPath(), { ...loadSystemConfig(), [key]: value });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function deleteSystemConfig(key) {
|
|
144
|
+
if (!systemKeys[key]) {
|
|
145
|
+
throw new Error(`Configuration ${key} is not available. Please choose from the following available configurations: ${Object.keys(systemKeys).join(', ')}`);
|
|
146
|
+
}
|
|
147
|
+
const { [key]: deleted, ...system } = loadSystemConfig();
|
|
148
|
+
writeJson(systemConfigPath(), system);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function listSystemConfig(type) {
|
|
152
|
+
// Importing the state loads the project, which fails outside one, so only a listing of the project does.
|
|
153
|
+
const { system: systemConfig, ...projectConfig } = type === 'system'
|
|
154
|
+
? { system: loadSystemConfig() }
|
|
155
|
+
: (await import('./state/index.js')).default.config;
|
|
156
|
+
if (type === 'all' || type === 'system') {
|
|
157
|
+
console.log('System Configuration');
|
|
158
|
+
console.table(systemConfig);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (type === 'all') {
|
|
162
|
+
console.log('');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (type === 'all' || type === 'project') {
|
|
166
|
+
console.log(`Project Configuration (paths relative to ${projectConfig.paths.base})`);
|
|
167
|
+
console.table(flattenConfigForTable(projectConfig, { base: projectConfig.paths.base }));
|
|
168
|
+
}
|
|
169
|
+
}
|
package/src/state/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import loadJson from '../utils/loadJson.js';
|
|
3
3
|
import writeJson from '../utils/writeJson.js';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
TARGETS, targetPathOf, nodeAddonNamesOf, nativeCommandNamesOf, wasiNamesOf,
|
|
6
|
+
} from '../utils/targets.js';
|
|
5
7
|
import { findCargoModuleImportsIn, writeCargoMarker } from '../utils/cargoImport.js';
|
|
6
8
|
import { exportWasiSdkPath } from '../utils/wasiToolchain.js';
|
|
7
9
|
import loadConfig, { assertBinarySelection } from './loadConfig.js';
|
|
@@ -52,8 +54,7 @@ async function initProcessState() {
|
|
|
52
54
|
target.dataTxtName = `${state.config.general.name}-${target.path}.${target.runtimeEnv}.data.txt`;
|
|
53
55
|
}
|
|
54
56
|
if (target.platform === 'wasi') {
|
|
55
|
-
|
|
56
|
-
target.wasmName = `${state.config.general.name}-${target.path}.wasm`;
|
|
57
|
+
Object.assign(target, wasiNamesOf(target, state.config.general.name));
|
|
57
58
|
}
|
|
58
59
|
if (target.runtimeEnv === 'node' && target.platform !== 'wasm') {
|
|
59
60
|
Object.assign(target, nodeAddonNamesOf(target, state.config.general.name));
|
package/src/state/loadConfig.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
1
|
import fs from 'node:fs';
|
|
3
|
-
import
|
|
2
|
+
import loadSystemConfig, { systemConfigPath } from '../utils/systemConfig.js';
|
|
4
3
|
import loadJs from '../utils/loadJs.js';
|
|
5
4
|
import {
|
|
6
5
|
resolveDependencyOverride, resolveDependencyReplace, restampIdentity, resolveExcludedNames, getOverrideKey,
|
|
@@ -32,7 +31,7 @@ export function assertBinarySelection(config) {
|
|
|
32
31
|
export default async function loadConfig(configDir = process.cwd(), configName = 'crossbind.config') {
|
|
33
32
|
const config = await loadJs(configDir, configName) || {};
|
|
34
33
|
if (config.excludedDependencies?.length) {
|
|
35
|
-
logger.info('crossbind: `excludedDependencies` in crossbind.config.js is no longer read — declare exclusions in crossbind.overrides.js as { <name>:
|
|
34
|
+
logger.info('crossbind: `excludedDependencies` in crossbind.config.js is no longer read — declare exclusions in crossbind.overrides.js as { <name>: { exclude: true } }.');
|
|
36
35
|
}
|
|
37
36
|
const overrides = await loadJs(configDir, 'crossbind.overrides');
|
|
38
37
|
const exclude = resolveExcludedNames(overrides);
|
|
@@ -83,14 +82,8 @@ export default async function loadConfig(configDir = process.cwd(), configName =
|
|
|
83
82
|
}
|
|
84
83
|
});
|
|
85
84
|
|
|
86
|
-
output.paths.systemConfig =
|
|
87
|
-
output.system =
|
|
88
|
-
|
|
89
|
-
Object.entries(systemKeys).forEach(([key, value]) => {
|
|
90
|
-
if (!(key in output.system)) {
|
|
91
|
-
output.system[key] = value.default;
|
|
92
|
-
}
|
|
93
|
-
});
|
|
85
|
+
output.paths.systemConfig = systemConfigPath();
|
|
86
|
+
output.system = loadSystemConfig();
|
|
94
87
|
|
|
95
88
|
return output;
|
|
96
89
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import state from '../state/index.js';
|
|
4
|
+
import { followCargoLock, recordCargoLock } from './cargoLock.js';
|
|
3
5
|
import { cargoTripleFor } from './cargoTarget.js';
|
|
4
6
|
import { isMtWasm, assertMtRustToolchain, cargoTargetDirFor, cargoBuildInvocation } from './rustMt.js';
|
|
5
7
|
import runCargo from './runCargo.js';
|
|
@@ -110,6 +112,7 @@ export default function buildAppRustCrates(target, cacheDir, cargoDependencies =
|
|
|
110
112
|
manifestPath: `${superDir}/Cargo.toml`,
|
|
111
113
|
sysroot,
|
|
112
114
|
});
|
|
115
|
+
followCargoLock(state.config.paths.project, superDir);
|
|
113
116
|
const build = runCargo(args, {
|
|
114
117
|
rustflags, panic, allowUnstable, target,
|
|
115
118
|
});
|
|
@@ -118,5 +121,6 @@ export default function buildAppRustCrates(target, cacheDir, cargoDependencies =
|
|
|
118
121
|
}
|
|
119
122
|
const libFile = `${targetDir}/${triple}/release/libcrossbind_app_super.a`;
|
|
120
123
|
if (!fs.existsSync(libFile)) throw new Error(`crossbind: expected cargo output ${libFile} not found`);
|
|
124
|
+
recordCargoLock(state.config.paths.project, superDir);
|
|
121
125
|
return [libFile];
|
|
122
126
|
}
|