crossbind 2.0.0-beta.68 → 2.0.0-beta.72
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/README.md +1 -1
- package/package.json +70 -59
- package/src/actions/addExtension.js +196 -0
- package/src/actions/androidGradle.js +26 -17
- package/src/actions/appInstaller.js +69 -153
- package/src/actions/appSwift.js +66 -0
- package/src/actions/buildApp.js +177 -74
- package/src/actions/buildDependencies.js +3 -3
- package/src/actions/buildNode.js +6 -1
- package/src/actions/buildWasm.js +0 -9
- package/src/actions/createImportedBridges.js +10 -8
- package/src/actions/createInterface.js +40 -29
- package/src/actions/createLib.js +3 -7
- package/src/actions/extensionApi.js +298 -0
- package/src/actions/extensionCommands.js +41 -0
- package/src/actions/extensionsInApp.js +19 -0
- package/src/actions/getAllBridges.js +20 -5
- package/src/actions/getDependLibs.js +5 -0
- package/src/actions/getLinkInputs.js +2 -1
- package/src/actions/gradleProject.js +207 -16
- package/src/actions/iosDevice.js +2 -2
- package/src/actions/iosSimulator.js +3 -3
- package/src/actions/licenses.js +113 -58
- package/src/actions/linuxPackages.js +48 -0
- package/src/actions/mavenVerification.js +37 -0
- package/src/actions/{prepareConanDependencies.js → preparePackageSources.js} +13 -12
- package/src/actions/publish.js +220 -0
- package/src/actions/windowsSigning.js +1 -1
- package/src/actions/writeRuntimeEntry.js +2 -2
- package/src/actions/xcodeProject.js +60 -29
- package/src/actions/xcodeTargets.js +122 -0
- package/src/actions/xcodebuild.js +65 -0
- package/src/app/index.d.ts +144 -132
- package/src/app/index.js +147 -185
- package/src/assets/configure/install.sh +0 -0
- package/src/assets/node-import-hooks.mjs +7 -6
- package/src/assets/toolchain-digests.json +20 -20
- package/src/bin.js +37 -37
- package/src/index.js +4 -3
- package/src/integration/getCrossbindScript.js +3 -2
- package/src/integration/getDependFilePath.js +10 -10
- package/src/program.js +32 -3
- package/src/state/{attachConanDependencies.js → attachPackageSources.js} +20 -16
- package/src/state/index.js +8 -6
- package/src/state/loadConfig.js +26 -9
- package/src/state/refreshPackageSources.js +19 -0
- package/src/utils/appExtensions.js +777 -0
- package/src/utils/appFiles.js +58 -0
- package/src/utils/appLaunches.js +249 -0
- package/src/utils/appOptions.js +142 -112
- package/src/utils/appRustCrates.js +2 -2
- package/src/utils/appTargets.js +168 -0
- package/src/utils/bridgeTools.js +71 -0
- package/src/utils/configChanges.js +30 -0
- package/src/utils/configWriter.js +143 -26
- package/src/utils/cppDts.js +11 -11
- package/src/utils/extensionSteps.js +172 -0
- package/src/utils/nativeImports.js +9 -2
- package/src/utils/nodeDevBuild.js +2 -1
- package/src/utils/openUrl.js +2 -1
- package/src/utils/packageSourceApi.js +87 -0
- package/src/utils/packageSourceImport.js +69 -0
- package/src/utils/packageSourceInstall.js +162 -0
- package/src/utils/{conanStage.js → packageSourceStage.js} +64 -90
- package/src/utils/packageSources.js +108 -0
- package/src/utils/plist.js +5 -0
- package/src/utils/printableText.js +5 -0
- package/src/utils/resolveAppHost.js +7 -0
- package/src/utils/runPackageSource.js +251 -0
- package/src/utils/rustBridgeGen.js +3 -3
- package/src/utils/systemConfig.js +16 -3
- package/src/utils/targetToolchain.js +77 -0
- package/vitest.config.js +6 -1
- package/src/actions/appSetup.js +0 -116
- package/src/actions/appStoreConnect.js +0 -188
- package/src/actions/appUpdate.js +0 -68
- package/src/actions/extensions.js +0 -7
- package/src/actions/googlePlay.js +0 -213
- package/src/actions/googleSignIn.js +0 -78
- package/src/actions/playSetup.js +0 -19
- package/src/actions/publishAndroid.js +0 -102
- package/src/actions/publishIos.js +0 -146
- package/src/state/refreshConanDependencies.js +0 -17
- package/src/utils/conanDependencies.js +0 -66
- package/src/utils/conanImport.js +0 -58
- package/src/utils/conanInstall.js +0 -154
- package/src/utils/conanProfile.js +0 -203
- package/src/utils/runConan.js +0 -257
package/src/actions/buildApp.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { spawnSync } from 'node:child_process';
|
|
5
4
|
import { fileURLToPath } from 'node:url';
|
|
6
5
|
import { rollup } from 'rollup';
|
|
7
6
|
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
@@ -20,8 +19,8 @@ import writeXcodeProject, { HERMES_SLICES, IOS_SDKS } from './xcodeProject.js';
|
|
|
20
19
|
import writeGradleProject, { ANDROID_ABIS } from './gradleProject.js';
|
|
21
20
|
import { attachedDevice } from './androidDevice.js';
|
|
22
21
|
import { packageAndroidRelease } from './androidGradle.js';
|
|
23
|
-
import
|
|
24
|
-
import
|
|
22
|
+
import { compileSwift, swiftLinkArgs } from './appSwift.js';
|
|
23
|
+
import { unpackPackages } from './linuxPackages.js';
|
|
25
24
|
import { APPLE_SIGNING, appleSigning, notarizeApp } from './appleSigning.js';
|
|
26
25
|
import { WINDOWS_SIGNING, signWindows, windowsSigning } from './windowsSigning.js';
|
|
27
26
|
import state from '../state/index.js';
|
|
@@ -30,10 +29,15 @@ import findFiles from '../utils/findFiles.js';
|
|
|
30
29
|
import { getContentHash, getFilesFingerprint } from '../utils/hash.js';
|
|
31
30
|
import { xmlPlist } from '../utils/plist.js';
|
|
32
31
|
import {
|
|
33
|
-
appBackgroundColor, appCategory, appCopyright, appDisplayName, appEncryption, appHeader, appId,
|
|
34
|
-
usageDescriptions,
|
|
32
|
+
androidAppId, appBackgroundColor, appCategory, appCopyright, appDarkBackgroundColor, appDisplayName, appEncryption, appFiles, appHeader, appId,
|
|
33
|
+
appPermissions, appPrivacy, appUrlSchemes, iosAppGroup, iosMultipleWindows, urlSchemeOwners, usageDescriptions,
|
|
35
34
|
} from '../utils/appOptions.js';
|
|
35
|
+
import { carriedFilesOf, copyCarriedFiles } from '../utils/appFiles.js';
|
|
36
|
+
import { activityTypeOwners, fileTypeOwners, linkOwners, macDocumentTypes, shortcutItemOwners } from '../utils/appLaunches.js';
|
|
36
37
|
import { unreasonedApis, unreasonedWarning } from '../utils/requiredReasonApis.js';
|
|
38
|
+
import {
|
|
39
|
+
appExtensionsOf, extensionCmakeArgs, extensionPart, mergedEntitlements, mergedPlist, mergedPrivacy, registrationSource,
|
|
40
|
+
} from '../utils/appExtensions.js';
|
|
37
41
|
import { androidIcons, iosIcon, linuxIcon, macIcon, readAppIcon, windowsIcon } from '../utils/appIcon.js';
|
|
38
42
|
import writeIfChanged from '../utils/writeIfChanged.js';
|
|
39
43
|
import { writeNativeSourceStamp } from '../utils/nativeSourceStamp.js';
|
|
@@ -41,8 +45,10 @@ import resetCMakeCacheForSource from '../utils/resetCMakeCacheForSource.js';
|
|
|
41
45
|
import resolveAppHostRoot, { resolveAppEmbindJsiRoot } from '../utils/resolveAppHost.js';
|
|
42
46
|
import resolveHermes, { HERMES } from '../utils/hermes.js';
|
|
43
47
|
import { getDockerImage, imageRoleFor } from '../utils/pullDockerImage.js';
|
|
48
|
+
import { isUnderBase } from '../utils/replaceBasePathForDocker.js';
|
|
44
49
|
import { chosenRunner } from '../utils/selectRunner.js';
|
|
45
50
|
import { DARWIN_DEPLOYMENT_TARGET } from '../utils/darwinToolchain.js';
|
|
51
|
+
import extensionsInApp from './extensionsInApp.js';
|
|
46
52
|
|
|
47
53
|
const cpuCount = Math.max(1, os.cpus().length - 1);
|
|
48
54
|
const BRIDGE = 'AppBridge';
|
|
@@ -50,6 +56,17 @@ const LINUX_MULTIARCH = { x64: 'x86_64-linux-gnu', arm64: 'aarch64-linux-gnu' };
|
|
|
50
56
|
// Where the android-app, linux and windows images keep the Hermes of the release crossbind pins.
|
|
51
57
|
const IMAGE_HERMES = `/opt/crossbind/hermes/${HERMES.version}`;
|
|
52
58
|
|
|
59
|
+
// What the app's extensions add to an app of this platform.
|
|
60
|
+
const extensionsOf = (platform) => extensionPart(extensionsInApp(platform, (message) => console.warn(`crossbind: ${message}.`)), platform);
|
|
61
|
+
|
|
62
|
+
// The App Group an iOS app shares with the targets of its extensions' that ask for one, or none; app.ios.appGroup is
|
|
63
|
+
// checked all the same.
|
|
64
|
+
function appGroupOf(platform, extensions) {
|
|
65
|
+
if (platform !== 'ios') return null;
|
|
66
|
+
const group = iosAppGroup(state.config);
|
|
67
|
+
return extensions.targets.some(({ appGroup }) => appGroup) ? group : null;
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
// Where an app keeps its executable and its files: a macOS bundle, a Linux or Windows directory, or the app folder of an
|
|
54
71
|
// iOS app's Xcode project or of an Android app's Gradle project, which link the executable themselves.
|
|
55
72
|
export function appLayout(target) {
|
|
@@ -123,24 +140,12 @@ export function appTeam() {
|
|
|
123
140
|
return team;
|
|
124
141
|
}
|
|
125
142
|
|
|
126
|
-
// An Android application id: dot-separated parts of letters, digits and underscores that each start with a letter.
|
|
127
|
-
const ANDROID_APP_ID = /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/;
|
|
128
|
-
|
|
129
|
-
// An Android app's id: the project's app.id, else one made from the project's name.
|
|
130
|
-
export function androidAppId() {
|
|
131
|
-
const id = state.config.app.id ?? `dev.crossbind.${state.config.general.name.replace(/[^A-Za-z0-9.]/g, '_')}`;
|
|
132
|
-
if (!ANDROID_APP_ID.test(id)) {
|
|
133
|
-
throw new Error(`crossbind: ${JSON.stringify(id)} (app.id, else made from the project's name) is no Android application id, which takes dot-separated parts of letters, digits and underscores that each start with a letter, as com.example.app does. Set app.id in crossbind.config.js.`);
|
|
134
|
-
}
|
|
135
|
-
return id;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
143
|
// Apple takes dot-separated integers alone as an app's version.
|
|
139
144
|
export const appVersion = () => (state.config.package?.version ?? '1.0.0').replace(/[-+].*$/, '');
|
|
140
145
|
|
|
141
146
|
const BUILD_NUMBER = 'CROSSBIND_APP_BUILD_NUMBER';
|
|
142
147
|
// Google Play takes no larger version code.
|
|
143
|
-
const MAX_VERSION_CODE = 2100000000;
|
|
148
|
+
export const MAX_VERSION_CODE = 2100000000;
|
|
144
149
|
|
|
145
150
|
// The number CI or a publish gives a build, which iOS takes as its build and Android as its version code:
|
|
146
151
|
// CROSSBIND_APP_BUILD_NUMBER, a whole number both stores take, else none.
|
|
@@ -204,11 +209,16 @@ async function bundle(input, file, plugins) {
|
|
|
204
209
|
await build.close();
|
|
205
210
|
}
|
|
206
211
|
|
|
207
|
-
// A development build claims no scheme: the built app, of the same id, owns them, and
|
|
212
|
+
// A development build claims no scheme, no file type and no user activity: the built app, of the same id, owns them, and
|
|
213
|
+
// a link it opens shows its own page.
|
|
208
214
|
const urlSchemesOf = (target) => (target.buildType === 'release' ? appUrlSchemes(state.config) : []);
|
|
215
|
+
const fileTypesOf = (target) => (target.buildType === 'release' ? fileTypeOwners(state.config) : []);
|
|
216
|
+
const activityTypesOf = (target) => (target.buildType === 'release' ? activityTypeOwners(state.config).map(({ type }) => type) : []);
|
|
209
217
|
|
|
210
|
-
|
|
218
|
+
// The app's Info.plist, with the keys its extensions give.
|
|
219
|
+
const infoPlist = (name, version, hasIcon, urlSchemes, fileTypes, activityTypes) => xmlPlist(mergedPlist({
|
|
211
220
|
CFBundleDisplayName: appDisplayName(state.config),
|
|
221
|
+
...(fileTypes.length > 0 ? { CFBundleDocumentTypes: macDocumentTypes(fileTypes) } : {}),
|
|
212
222
|
CFBundleExecutable: name,
|
|
213
223
|
// Contents/Resources/icon.icns.
|
|
214
224
|
...(hasIcon ? { CFBundleIconFile: 'icon' } : {}),
|
|
@@ -224,8 +234,12 @@ const infoPlist = (name, version, hasIcon, urlSchemes) => xmlPlist({
|
|
|
224
234
|
NSHighResolutionCapable: true,
|
|
225
235
|
// The app menu's About item shows it.
|
|
226
236
|
...(appCopyright(state.config) ? { NSHumanReadableCopyright: appCopyright(state.config) } : {}),
|
|
237
|
+
...(activityTypes.length > 0 ? { NSUserActivityTypes: activityTypes } : {}),
|
|
227
238
|
...usageDescriptions(state.config, 'darwin'),
|
|
228
|
-
});
|
|
239
|
+
}, extensionsOf('darwin').plist));
|
|
240
|
+
|
|
241
|
+
// The app's own files, as app.files lists them, by the project's folder.
|
|
242
|
+
const ownFiles = () => ({ root: state.config.paths.project, paths: appFiles(state.config) });
|
|
229
243
|
|
|
230
244
|
// The host's bytecode and the data the packages ship, beside the page's folder.
|
|
231
245
|
function copyAppFiles(dir, hbc, target) {
|
|
@@ -234,6 +248,7 @@ function copyAppFiles(dir, hbc, target) {
|
|
|
234
248
|
Object.entries(getData('data', target)).forEach(([source, dataName]) => {
|
|
235
249
|
if (fs.existsSync(source)) fs.cpSync(source, `${dir}/data/${dataName}`, { recursive: true });
|
|
236
250
|
});
|
|
251
|
+
copyCarriedFiles(dir, extensionsOf(target.platform).files, ownFiles());
|
|
237
252
|
}
|
|
238
253
|
|
|
239
254
|
// Everything but the page: the host and the app's files, its icon, and on macOS Hermes beside them. A Windows host
|
|
@@ -248,7 +263,7 @@ function assembleApp(target, { executable, hbc, hermes, icon }) {
|
|
|
248
263
|
copyAppFiles(`${app}/${layout.resources}`, hbc, target);
|
|
249
264
|
if (target.platform === 'linux' && icon) fs.writeFileSync(`${app}/${layout.resources}/icon.png`, linuxIcon(icon));
|
|
250
265
|
if (target.platform !== 'darwin') return;
|
|
251
|
-
fs.writeFileSync(`${app}/Contents/Info.plist`, infoPlist(name, appVersion(), Boolean(icon), urlSchemesOf(target)));
|
|
266
|
+
fs.writeFileSync(`${app}/Contents/Info.plist`, infoPlist(name, appVersion(), Boolean(icon), urlSchemesOf(target), fileTypesOf(target), activityTypesOf(target)));
|
|
252
267
|
if (icon) fs.writeFileSync(`${app}/Contents/Resources/icon.icns`, macIcon(icon));
|
|
253
268
|
const framework = `${app}/Contents/Frameworks/hermesvm.framework`;
|
|
254
269
|
fs.cpSync(`${hermes}/Library/Frameworks/macosx/hermesvm.framework`, framework, { recursive: true, verbatimSymlinks: true });
|
|
@@ -301,20 +316,10 @@ const appParts = (target) => state.targets
|
|
|
301
316
|
.filter((t) => t.platform === target.platform && t.runtimeEnv === 'app' && t.buildType === target.buildType && currentApps.has(t.appName))
|
|
302
317
|
.map((t) => ({ arch: t.arch, dir: `${state.config.paths.build}/${t.appName}` }));
|
|
303
318
|
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
assertUnderBase(app, target);
|
|
309
|
-
fs.rmSync(`${app}.zip`, { force: true });
|
|
310
|
-
run(null, ['zip', '-q', '-r', '-X', `${path.basename(app)}.zip`, path.basename(app)], null, target, { workdir: path.dirname(app) });
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
const result = spawnSync('tar', ['-czf', `${app}.tar.gz`, '-C', path.dirname(app), path.basename(app)], {
|
|
314
|
-
encoding: 'utf8',
|
|
315
|
-
env: { ...process.env, COPYFILE_DISABLE: '1' },
|
|
316
|
-
});
|
|
317
|
-
if (result.status !== 0) throw new Error(`crossbind: cannot archive ${app}: ${result.stderr || result.error?.message}`);
|
|
319
|
+
// The container that signs a Windows app sees the project's base alone.
|
|
320
|
+
function assertUnderBase(app, target) {
|
|
321
|
+
if (isUnderBase(app, state.config.paths.base)) return;
|
|
322
|
+
throw new Error(`crossbind: the Windows app ${app} is outside ${state.config.paths.base}, the project's base and all the ${imageRoleFor(target)} container sees. Build the app into a directory under it.`);
|
|
318
323
|
}
|
|
319
324
|
|
|
320
325
|
const ENTITLEMENTS = {
|
|
@@ -329,10 +334,12 @@ const ENTITLEMENTS = {
|
|
|
329
334
|
function signApp(app, target, signing) {
|
|
330
335
|
const sign = signing ? ['--sign', signing.identity, '--options', 'runtime', '--timestamp'] : ['--sign', '-', '--timestamp=none'];
|
|
331
336
|
run(null, ['codesign', '--force', ...sign, `${app}/Contents/Frameworks/hermesvm.framework`], null, target);
|
|
332
|
-
// The hardened runtime keeps the app from the devices of app.permissions unless its signature claims them
|
|
333
|
-
|
|
334
|
-
const
|
|
335
|
-
|
|
337
|
+
// The hardened runtime keeps the app from the devices of app.permissions unless its signature claims them, beside what
|
|
338
|
+
// its extensions claim.
|
|
339
|
+
const devices = Object.fromEntries(Object.keys(appPermissions(state.config)).map((name) => [ENTITLEMENTS[name], true]));
|
|
340
|
+
const claims = mergedEntitlements([['app.permissions', devices], ...extensionsOf('darwin').entitlements]);
|
|
341
|
+
const entitlements = Object.keys(claims).length > 0 ? `${state.config.paths.build}/${path.basename(app)}.entitlements` : null;
|
|
342
|
+
if (entitlements) fs.writeFileSync(entitlements, xmlPlist(claims));
|
|
336
343
|
run(null, ['codesign', '--force', ...sign, ...(entitlements ? ['--entitlements', entitlements] : []), app], null, target);
|
|
337
344
|
}
|
|
338
345
|
|
|
@@ -363,22 +370,56 @@ function warnUnreasonedApis(project, privacy) {
|
|
|
363
370
|
if (warning) console.warn(warning);
|
|
364
371
|
}
|
|
365
372
|
|
|
366
|
-
// Where publishApp last put this project's
|
|
367
|
-
//
|
|
373
|
+
// Where publishApp last put this project's apps, for `crossbind app publish`, which runs Vite and so cannot know the
|
|
374
|
+
// outDir it builds into: an iOS or Android app's project, and each arch's desktop app under its platform's name on the
|
|
375
|
+
// command line.
|
|
368
376
|
const appOutputsFile = () => `${state.config.paths.build}/app-outputs.json`;
|
|
369
377
|
const readAppOutputs = () => (fs.existsSync(appOutputsFile()) ? JSON.parse(fs.readFileSync(appOutputsFile(), 'utf8')) : {});
|
|
378
|
+
// What each desktop app's installers wrote beside it, as builds recorded them.
|
|
379
|
+
const shippedFiles = () => new Set(Object.values(readAppOutputs()).filter(Array.isArray).flat()
|
|
380
|
+
.flatMap(({ installers = [] }) => installers.flatMap(({ files }) => files)));
|
|
381
|
+
// The command line's names of the desktop platforms.
|
|
382
|
+
export const DESKTOP_PLATFORMS = { darwin: 'macos', linux: 'linux', win32: 'windows' };
|
|
383
|
+
|
|
384
|
+
// The platform an app is built for, which its page reads as import.meta.env.CROSSBIND_APP_PLATFORM: CROSSBIND_APP_PLATFORM,
|
|
385
|
+
// else this machine's desktop.
|
|
386
|
+
export const appPlatform = (env = process.env) => env.CROSSBIND_APP_PLATFORM || DESKTOP_PLATFORMS[process.platform];
|
|
387
|
+
|
|
388
|
+
// Forgets what the builds wrote, as a publish's own build writes it anew, but for the installers' files, which a folder the
|
|
389
|
+
// bundler kept holds still, for the page to leave out.
|
|
390
|
+
export function clearAppOutputs() {
|
|
391
|
+
const earlier = [...shippedFiles()];
|
|
392
|
+
if (earlier.length > 0) fs.writeFileSync(appOutputsFile(), JSON.stringify({ earlier: [{ installers: [{ files: earlier }] }] }));
|
|
393
|
+
else fs.rmSync(appOutputsFile(), { force: true });
|
|
394
|
+
}
|
|
370
395
|
|
|
371
|
-
|
|
372
|
-
|
|
396
|
+
// What a publisher's upload gets of the app the build wrote for a platform: a desktop platform's apps, one for each arch.
|
|
373
397
|
export function appOutputOf(platform) {
|
|
374
398
|
const output = readAppOutputs()[platform];
|
|
375
399
|
if (!output) throw new Error(`crossbind: the build wrote no ${platform} app, so there is nothing to publish.`);
|
|
376
|
-
return output;
|
|
400
|
+
return Object.values(DESKTOP_PLATFORMS).includes(platform) ? { apps: output } : output;
|
|
377
401
|
}
|
|
378
402
|
|
|
379
|
-
|
|
403
|
+
const madeFile = (file) => (fs.existsSync(file) ? file : null);
|
|
404
|
+
|
|
405
|
+
// What a publisher's upload takes of an app: an iOS app's project, an Android app's with its APK, bundle and native
|
|
406
|
+
// debug symbols where the build made them, and each arch's desktop app with its installers.
|
|
407
|
+
export function recordAppOutput(target, to, { installers = [] } = {}) {
|
|
408
|
+
const outputs = readAppOutputs();
|
|
409
|
+
const app = path.resolve(to);
|
|
410
|
+
const desktop = DESKTOP_PLATFORMS[target.platform];
|
|
411
|
+
const mobile = target.platform === 'android'
|
|
412
|
+
? { project: app, apk: madeFile(`${app}.apk`), bundle: madeFile(`${app}.aab`), symbols: madeFile(`${app}-symbols.zip`) }
|
|
413
|
+
: { project: app };
|
|
414
|
+
const recorded = desktop
|
|
415
|
+
? { [desktop]: [...(outputs[desktop] ?? []).filter((entry) => entry.app !== app), {
|
|
416
|
+
app,
|
|
417
|
+
arch: target.arch,
|
|
418
|
+
installers: installers.map(({ name, files }) => ({ name, files: files.map((file) => path.resolve(file)) })),
|
|
419
|
+
}] }
|
|
420
|
+
: { [target.platform]: mobile };
|
|
380
421
|
fs.mkdirSync(state.config.paths.build, { recursive: true });
|
|
381
|
-
fs.writeFileSync(appOutputsFile(), `${JSON.stringify({ ...
|
|
422
|
+
fs.writeFileSync(appOutputsFile(), `${JSON.stringify({ ...outputs, ...recorded }, null, 4)}\n`);
|
|
382
423
|
}
|
|
383
424
|
|
|
384
425
|
// Where a build without a bundler puts an app: under its own name, or for iOS and Android as the project of its build
|
|
@@ -396,19 +437,35 @@ export function publishApp(target, { to = appOutputPath(target), page = null } =
|
|
|
396
437
|
if (target.platform === 'ios') {
|
|
397
438
|
const parts = appParts(target).map(({ arch, dir }) => ({ sdk: arch, dir }));
|
|
398
439
|
const icon = readAppIcon(state.config);
|
|
399
|
-
const
|
|
440
|
+
const ownPrivacy = appPrivacy(state.config);
|
|
400
441
|
const build = appBuildNumber();
|
|
401
|
-
|
|
442
|
+
// The app's own options are checked first, then what its extensions add.
|
|
443
|
+
const options = {
|
|
402
444
|
parts, identity: { id: appId(state.config), team: appTeam() }, label: appDisplayName(state.config), version: appVersion(),
|
|
403
445
|
build: build === null ? appVersion() : String(build), icon: icon && iosIcon(icon), urlSchemes: appUrlSchemes(state.config),
|
|
404
|
-
|
|
446
|
+
fileTypes: fileTypeOwners(state.config), links: linkOwners(state.config).map(({ link }) => link),
|
|
447
|
+
activityTypes: activityTypeOwners(state.config).map(({ type }) => type), shortcutItems: shortcutItemOwners(state.config),
|
|
448
|
+
background: appBackgroundColor(state.config), darkBackground: appDarkBackgroundColor(state.config), usage: usageDescriptions(state.config, 'ios'),
|
|
449
|
+
encryption: appEncryption(state.config), multipleWindows: iosMultipleWindows(state.config),
|
|
450
|
+
};
|
|
451
|
+
const extensions = extensionsOf('ios');
|
|
452
|
+
const privacy = mergedPrivacy(ownPrivacy, extensions.privacy);
|
|
453
|
+
writeXcodeProject(to, {
|
|
454
|
+
...options, privacy, entitlements: mergedEntitlements(extensions.entitlements), plist: extensions.plist, targets: extensions.targets,
|
|
455
|
+
appGroup: appGroupOf('ios', extensions),
|
|
405
456
|
});
|
|
406
457
|
if (target.buildType === 'release') warnUnreasonedApis(to, privacy);
|
|
407
458
|
} else if (target.platform === 'android') {
|
|
408
459
|
const icon = readAppIcon(state.config);
|
|
460
|
+
const options = {
|
|
461
|
+
parts: appParts(target), id: androidAppId(state.config), label: appDisplayName(state.config), version: androidVersion(), icons: icon && androidIcons(icon),
|
|
462
|
+
urlSchemes: appUrlSchemes(state.config), background: appBackgroundColor(state.config), darkBackground: appDarkBackgroundColor(state.config),
|
|
463
|
+
permissions: Object.keys(appPermissions(state.config)),
|
|
464
|
+
fileTypes: fileTypeOwners(state.config), links: linkOwners(state.config).map(({ link }) => link), shortcutItems: shortcutItemOwners(state.config),
|
|
465
|
+
};
|
|
466
|
+
const extensions = extensionsOf('android');
|
|
409
467
|
writeGradleProject(to, {
|
|
410
|
-
|
|
411
|
-
urlSchemes: appUrlSchemes(state.config), background: appBackgroundColor(state.config), permissions: Object.keys(appPermissions(state.config)),
|
|
468
|
+
...options, java: extensions.java, extensionPermissions: extensions.permissions, manifests: extensions.manifests, resources: extensions.resources, maven: extensions.maven,
|
|
412
469
|
});
|
|
413
470
|
} else if (path.resolve(to) !== path.resolve(built)) {
|
|
414
471
|
fs.rmSync(to, { recursive: true, force: true });
|
|
@@ -416,11 +473,14 @@ export function publishApp(target, { to = appOutputPath(target), page = null } =
|
|
|
416
473
|
}
|
|
417
474
|
if (page) {
|
|
418
475
|
// The app may land inside the page's own directory, which it then leaves out, with every app built there and what
|
|
419
|
-
// packages it
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
476
|
+
// packages it: what is named after a target of the app, and every installer's files as a build recorded them, which
|
|
477
|
+
// a folder the bundler left as it was holds still.
|
|
478
|
+
const apps = state.targets.filter((t) => t.runtimeEnv === 'app')
|
|
479
|
+
.flatMap((t) => [appOutputName(t), `${state.config.general.name}-${t.platform}-${t.arch}`]).filter((name) => !name.endsWith('.app'));
|
|
480
|
+
const shipped = shippedFiles();
|
|
481
|
+
const isApp = (entry) => entry === path.basename(to) || entry.endsWith('.app')
|
|
482
|
+
|| apps.some((app) => entry === app || entry.startsWith(`${app}.`) || entry.startsWith(`${app}-`))
|
|
483
|
+
|| shipped.has(path.resolve(page, entry));
|
|
424
484
|
fs.readdirSync(page).filter((entry) => !isApp(entry)).forEach((entry) => {
|
|
425
485
|
fs.cpSync(`${page}/${entry}`, `${to}/${appLayout(target).resources}/page/${entry}`, { recursive: true });
|
|
426
486
|
});
|
|
@@ -432,17 +492,8 @@ export function publishApp(target, { to = appOutputPath(target), page = null } =
|
|
|
432
492
|
} else if (target.platform === 'android' && target.buildType === 'release') {
|
|
433
493
|
packageAndroidRelease(to);
|
|
434
494
|
}
|
|
435
|
-
if (['ios', 'android'].includes(target.platform))
|
|
436
|
-
|
|
437
|
-
} else if (target.platform !== 'darwin') {
|
|
438
|
-
if (target.platform === 'win32') signWindowsApp(to, target, page);
|
|
439
|
-
archiveApp(to, target);
|
|
440
|
-
}
|
|
441
|
-
// A desktop app with its page ships with what installs it, and with an updater's key with its update too.
|
|
442
|
-
if (page && ['darwin', 'linux', 'win32'].includes(target.platform)) {
|
|
443
|
-
writeInstaller(to, target);
|
|
444
|
-
writeUpdate(to, target);
|
|
445
|
-
}
|
|
495
|
+
if (['ios', 'android'].includes(target.platform)) recordAppOutput(target, to);
|
|
496
|
+
else if (target.platform === 'win32') signWindowsApp(to, target, page);
|
|
446
497
|
return to;
|
|
447
498
|
}
|
|
448
499
|
|
|
@@ -457,14 +508,32 @@ export default async function buildApp(target, { bridges = [], force = false } =
|
|
|
457
508
|
logger.info(`[${target.path}] app skipped (cargo package - staticlib only)`);
|
|
458
509
|
return false;
|
|
459
510
|
}
|
|
460
|
-
//
|
|
461
|
-
// app section stops the build before anything compiles.
|
|
462
|
-
const header = appHeader(state.config, target.platform);
|
|
511
|
+
// A desktop host's title and window, a Windows one's version, the app's icon and its extensions with their options, as
|
|
512
|
+
// where the page opens files: a bad app section stops the build before anything compiles.
|
|
463
513
|
const icon = readAppIcon(state.config);
|
|
514
|
+
const extensions = extensionsOf(target.platform);
|
|
515
|
+
const header = appHeader(state.config, target.platform, appGroupOf(target.platform, extensions));
|
|
516
|
+
const carried = carriedFilesOf(extensions.files, ownFiles());
|
|
517
|
+
const carriedPaths = new Set(carried);
|
|
518
|
+
// Whether the extension that owns a launch has native code, or for the app's own one that holds the role.
|
|
519
|
+
const hasNative = (extension, role) => extensions.native.some(({ name, roles }) => (extension === null ? roles.includes(role) : name === extension));
|
|
520
|
+
// A desktop's system hands an app no https link: its owners there are those with native code, whose onOpenUrl a
|
|
521
|
+
// phone's code may share.
|
|
522
|
+
const isPhone = ['ios', 'android'].includes(target.platform);
|
|
523
|
+
const links = linkOwners(state.config).filter(({ extension }) => isPhone || hasNative(extension, 'links'));
|
|
524
|
+
// Another system continues no user activity: its owners there are those with native code, whose onUserActivity
|
|
525
|
+
// Apple's code may share.
|
|
526
|
+
const isApple = ['darwin', 'ios'].includes(target.platform);
|
|
527
|
+
const activities = activityTypeOwners(state.config).filter(({ extension }) => isApple || hasNative(extension, 'activities'));
|
|
528
|
+
// A desktop has no quick actions either.
|
|
529
|
+
const shortcuts = shortcutItemOwners(state.config).filter(({ extension }) => isPhone || hasNative(extension, 'shortcuts'));
|
|
530
|
+
const registration = registrationSource(extensions.native, urlSchemeOwners(state.config), fileTypeOwners(state.config), links, activities, shortcuts);
|
|
464
531
|
|
|
465
532
|
const buildType = target.buildType === 'release' ? 'Release' : 'Debug';
|
|
466
533
|
const { build } = state.config.paths;
|
|
467
534
|
const appHostRoot = resolveAppHostRoot();
|
|
535
|
+
appExtensionsOf(state.config.extensions).filter(({ replaces }) => replaces)
|
|
536
|
+
.forEach(({ name, root, replaces }) => logger.info(`[${target.path}] the extension ${name} of ${root} takes the host's ${replaces.join(', ')}`));
|
|
468
537
|
const jsiRoot = resolveAppEmbindJsiRoot();
|
|
469
538
|
const { hermes, cmakeArgs, inputs } = await hostInputs(target);
|
|
470
539
|
|
|
@@ -480,6 +549,9 @@ export default async function buildApp(target, { bridges = [], force = false } =
|
|
|
480
549
|
});
|
|
481
550
|
const { libs, linkArgs, extraSources } = linkInputs(target, { bridge: BRIDGE });
|
|
482
551
|
const sharedLibs = libs.filter((lib) => lib.endsWith('.so'));
|
|
552
|
+
// The extensions' sources compile with the host, which links what they need: a framework on macOS, a library elsewhere.
|
|
553
|
+
// An iOS host is a static library, whose frameworks the app's Xcode project links.
|
|
554
|
+
const hostLinkArgs = [...linkArgs, ...(target.platform === 'darwin' ? extensions.frameworks.map((framework) => `-framework ${framework}`) : extensions.libraries)];
|
|
483
555
|
|
|
484
556
|
const fingerprintFile = `${build}/${target.appName}.fingerprint`;
|
|
485
557
|
const fingerprint = getContentHash(JSON.stringify({
|
|
@@ -488,7 +560,8 @@ export default async function buildApp(target, { bridges = [], force = false } =
|
|
|
488
560
|
// A desktop app carries its icon; an iOS or Android one gets it as its project is written.
|
|
489
561
|
icon: ['ios', 'android'].includes(target.platform) ? null : icon?.hash ?? null,
|
|
490
562
|
// A macOS app's bundle takes its id, name and version from it.
|
|
491
|
-
infoPlist: target.platform === 'darwin'
|
|
563
|
+
infoPlist: target.platform === 'darwin'
|
|
564
|
+
? infoPlist(state.config.general.name, appVersion(), Boolean(icon), urlSchemesOf(target), fileTypesOf(target), activityTypesOf(target)) : null,
|
|
492
565
|
hermes: HERMES.version,
|
|
493
566
|
inputs,
|
|
494
567
|
linkArgs,
|
|
@@ -498,12 +571,25 @@ export default async function buildApp(target, { bridges = [], force = false } =
|
|
|
498
571
|
}),
|
|
499
572
|
env: getEnv(target),
|
|
500
573
|
data: getData('data', target),
|
|
574
|
+
// What the app carries, by its size and time, as a large model would take long to read at each build.
|
|
575
|
+
carried: carried.map((file) => {
|
|
576
|
+
const stat = fs.statSync(file);
|
|
577
|
+
return { file, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
578
|
+
}),
|
|
501
579
|
version: state.config.package?.version,
|
|
502
580
|
runtime: getFilesFingerprint(filesUnder([
|
|
503
581
|
`${appHostRoot}/cpp`, `${appHostRoot}/js`, `${appHostRoot}/third_party`, `${jsiRoot}/cpp/src`, `${jsiRoot}/js`,
|
|
504
582
|
`${state.config.paths.cli}/assets/js-runtime/adapters`,
|
|
505
583
|
])),
|
|
506
584
|
extraSources: getFilesFingerprint(extraSources),
|
|
585
|
+
// An iOS app's frameworks reach its Xcode project through what the host's build writes.
|
|
586
|
+
extensions: {
|
|
587
|
+
registration,
|
|
588
|
+
files: getFilesFingerprint(filesUnder(extensions.roots).filter((file) => !carriedPaths.has(path.resolve(file)))),
|
|
589
|
+
compiled: extensions.compiled,
|
|
590
|
+
link: hostLinkArgs.slice(linkArgs.length),
|
|
591
|
+
frameworks: extensions.frameworks,
|
|
592
|
+
},
|
|
507
593
|
}));
|
|
508
594
|
const isChanged = !fs.existsSync(fingerprintFile) || fs.readFileSync(fingerprintFile, { encoding: 'utf8' }) !== fingerprint;
|
|
509
595
|
if (!force && !isChanged && fs.existsSync(`${build}/${target.appName}`) && fs.existsSync(`${build}/${target.appJsName}`)) {
|
|
@@ -521,11 +607,25 @@ export default async function buildApp(target, { bridges = [], force = false } =
|
|
|
521
607
|
resetCMakeCacheForSource(workDir, `${appHostRoot}/cpp`, target);
|
|
522
608
|
// The host includes it from its build directory; written only when it changed, it recompiles nothing otherwise.
|
|
523
609
|
writeIfChanged(`${workDir}/crossbind_app.h`, header);
|
|
610
|
+
writeIfChanged(`${workDir}/extensions.cpp`, registration);
|
|
524
611
|
// A Windows host's executable carries the app's icon, which its resource script takes from beside the header.
|
|
525
612
|
if (target.platform === 'win32') {
|
|
526
613
|
if (icon) writeIfChanged(`${workDir}/icon.ico`, windowsIcon(icon));
|
|
527
614
|
else fs.rmSync(`${workDir}/icon.ico`, { force: true });
|
|
528
615
|
}
|
|
616
|
+
// An Apple app's Swift, which CMake's Makefiles take none of: each extension's compiles first, its header joins the
|
|
617
|
+
// extension's includes, and its library the host's link, or an iOS app's Xcode project's.
|
|
618
|
+
const swift = ['darwin', 'ios'].includes(target.platform) ? compileSwift(target, extensions.compiled, workDir, buildType === 'Release') : [];
|
|
619
|
+
const swiftLink = swiftLinkArgs(target, swift.map(({ library }) => library));
|
|
620
|
+
// A Linux app's extensions' packages beside crossbind's sysroot, each extension's in a sysroot of its own, whose
|
|
621
|
+
// headers its sources include and whose libraries the host links against.
|
|
622
|
+
const sysroots = target.platform === 'linux' ? await unpackPackages(target, extensions.compiled, {
|
|
623
|
+
cache: `${build}/linux-packages`, workDir, unpack: (deb, root) => run(null, ['dpkg-deb', '-x', deb, root], platformPrefix, target),
|
|
624
|
+
}) : [];
|
|
625
|
+
const compiled = extensions.compiled.map((part) => ({
|
|
626
|
+
...part, includes: [...part.includes, ...[...swift, ...sysroots].filter(({ name: owner }) => owner === part.name).flatMap(({ includes }) => includes)],
|
|
627
|
+
}));
|
|
628
|
+
const ownLink = target.platform === 'darwin' ? swiftLink : sysroots.flatMap(({ libDir }) => [`-L${libDir}`, `-Wl,-rpath-link,${libDir}`]);
|
|
529
629
|
run(null, [
|
|
530
630
|
// An iOS host builds with crossbind's iOS toolchain file rather than as an Xcode project of its own.
|
|
531
631
|
target.platform === 'ios' ? 'ios-cmake' : 'cmake', `${appHostRoot}/cpp`,
|
|
@@ -534,7 +634,9 @@ export default async function buildApp(target, { bridges = [], force = false } =
|
|
|
534
634
|
`-DCROSSBIND_JSI_ROOT=${jsiRoot}`,
|
|
535
635
|
`-DCROSSBIND_HERMES=${hermes}`,
|
|
536
636
|
`-DCROSSBIND_EXTRA_SOURCES=${extraSources.join(';')}`,
|
|
537
|
-
`-
|
|
637
|
+
`-DCROSSBIND_EXTENSION_REGISTRATION=${workDir}/extensions.cpp`,
|
|
638
|
+
...extensionCmakeArgs(compiled),
|
|
639
|
+
`-DCROSSBIND_LINK_ARGS=${[...hostLinkArgs, ...ownLink].join(';')}`,
|
|
538
640
|
`-DCROSSBIND_LINK_DEPENDS=${libs.join(';')}`,
|
|
539
641
|
// An Android release ships the packages' shared libraries stripped as well, and keeps them as they come.
|
|
540
642
|
...(target.platform === 'android' ? [`-DCROSSBIND_SHARED_LIBS=${sharedLibs.join(';')}`] : []),
|
|
@@ -552,7 +654,8 @@ export default async function buildApp(target, { bridges = [], force = false } =
|
|
|
552
654
|
platformPrefix, target);
|
|
553
655
|
await bundle(`${appHostRoot}/js/page.js`, `${build}/${target.appJsName}`, []);
|
|
554
656
|
if (target.platform === 'ios') {
|
|
555
|
-
|
|
657
|
+
const frameworks = extensions.frameworks.flatMap((framework) => ['-framework', framework]);
|
|
658
|
+
assembleIosPart(target, { library: `${workDir}/lib${name}.a`, linkArgs: [...linkArgs, ...frameworks, ...swiftLink], hbc: `${workDir}/host.hbc`, hermes });
|
|
556
659
|
} else if (target.platform === 'android') {
|
|
557
660
|
assembleAndroidPart(target, { libDir: workDir, sharedLibs, hbc: `${workDir}/host.hbc` });
|
|
558
661
|
} else {
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from '../utils/dependencyRebuild.js';
|
|
15
15
|
import withDirLock from '../utils/dirLock.js';
|
|
16
16
|
import { prepareRustSysroot } from '../utils/rustSysroot.js';
|
|
17
|
-
import
|
|
17
|
+
import preparePackageSources from './preparePackageSources.js';
|
|
18
18
|
import getAbsolutePath from '../utils/getAbsolutePath.js';
|
|
19
19
|
import getCMakeListsFilePath from '../utils/getCMakeListsFilePath.js';
|
|
20
20
|
import getParentPath from '../utils/getParentPath.js';
|
|
@@ -26,7 +26,7 @@ export default async function buildDependencies({ targetParams, rebuildOption })
|
|
|
26
26
|
// sync cargo work downstream reads what it resolves. A project with nothing to rebuild still
|
|
27
27
|
// needs its sysroot.
|
|
28
28
|
await prepareRustSysroot(getBuildTargets(targetParams));
|
|
29
|
-
await
|
|
29
|
+
await preparePackageSources(getBuildTargets(targetParams));
|
|
30
30
|
await buildMissingCargoDependencies(getBuildTargets(targetParams), targetParams);
|
|
31
31
|
|
|
32
32
|
const rebuildDeps = getRebuildDeps(state.config.allDependencies, rebuildOption);
|
|
@@ -78,7 +78,7 @@ async function buildMissingCargoDependencies(buildTargets, targetParams) {
|
|
|
78
78
|
const appConfig = state.config;
|
|
79
79
|
const cargoDependencies = (appConfig.allDependencies ?? []).filter((dep) => dep !== appConfig && dep.export?.type === 'cargo');
|
|
80
80
|
if (cargoDependencies.length === 0) return;
|
|
81
|
-
// Bridges read every header for the first wasm target (see
|
|
81
|
+
// Bridges read every header for the first wasm target (see preparePackageSources), which an app configured for
|
|
82
82
|
// another wasm runtime does not build: there a dependency built for the app's runtime alone counts as half built.
|
|
83
83
|
const bridgeTarget = buildTargets.some(isWasm) ? state.targets.find(isWasm) : undefined;
|
|
84
84
|
const extraTarget = bridgeTarget && !buildTargets.some((target) => target.path === bridgeTarget.path) ? bridgeTarget : undefined;
|
package/src/actions/buildNode.js
CHANGED
|
@@ -24,11 +24,16 @@ import toolchainNoticesDir from '../utils/toolchainNotices.js';
|
|
|
24
24
|
const cpuCount = Math.max(1, os.cpus().length - 1);
|
|
25
25
|
const WINDOWS_IMAGE_NOTICES = '/opt/licenses/llvm-mingw';
|
|
26
26
|
|
|
27
|
+
// What a build's output, a package's own packages and a repository's history hold, which no build reads.
|
|
28
|
+
const UNREAD_FOLDERS = ['.crossbind', 'node_modules', '.git'];
|
|
29
|
+
|
|
30
|
+
// The files under these folders a build reads, a link that leads nowhere left out.
|
|
27
31
|
export function filesUnder(dirs) {
|
|
28
32
|
return dirs.flatMap((dir) => (fs.existsSync(dir)
|
|
29
33
|
? fs.readdirSync(dir, { recursive: true })
|
|
34
|
+
.filter((file) => !file.split(/[\\/]/).some((part) => UNREAD_FOLDERS.includes(part)))
|
|
30
35
|
.map((file) => `${dir}/${file}`)
|
|
31
|
-
.filter((file) => fs.statSync(file)
|
|
36
|
+
.filter((file) => fs.statSync(file, { throwIfNoEntry: false })?.isFile())
|
|
32
37
|
: []));
|
|
33
38
|
}
|
|
34
39
|
|
package/src/actions/buildWasm.js
CHANGED
|
@@ -6,7 +6,6 @@ import getLinkInputs from './getLinkInputs.js';
|
|
|
6
6
|
import getData from './getData.js';
|
|
7
7
|
import getEnv from './getEnv.js';
|
|
8
8
|
import buildJs from './buildJs.js';
|
|
9
|
-
import triggerExtensions from './extensions.js';
|
|
10
9
|
import state from '../state/index.js';
|
|
11
10
|
import resolveEmbindRustRoot from '../utils/resolveEmbindRust.js';
|
|
12
11
|
import logger from '../utils/logger.js';
|
|
@@ -128,8 +127,6 @@ export default async function buildWasm(target, options = {}) {
|
|
|
128
127
|
if (!isProd && !/-s\s*STACK_OVERFLOW_CHECK=/.test(set)) emccFlags.push('-sSTACK_OVERFLOW_CHECK=1');
|
|
129
128
|
}
|
|
130
129
|
|
|
131
|
-
triggerExtensions('buildWasm', 'beforeBuild', [emccFlags]);
|
|
132
|
-
|
|
133
130
|
if (target.runtime === 'mt' && !emccFlags.includes('-pthread')) {
|
|
134
131
|
emccFlags.push('-pthread');
|
|
135
132
|
emccFlags.push('-sPTHREAD_POOL_SIZE=Math.min(navigator.hardwareConcurrency || 1, 2)');
|
|
@@ -203,8 +200,6 @@ export default async function buildWasm(target, options = {}) {
|
|
|
203
200
|
logger.startStep(target, 'wasm');
|
|
204
201
|
const t0 = performance.now();
|
|
205
202
|
|
|
206
|
-
triggerExtensions('buildWasm', 'beforeBuildBrowser', [emccFlags]);
|
|
207
|
-
|
|
208
203
|
const data = Object.entries(getData('data', target)).map(([key, value]) => ['--preload-file', `${key.replaceAll('@', '@@')}@/crossbind/${value}`]).flat();
|
|
209
204
|
run('em++', [
|
|
210
205
|
'-lembind',
|
|
@@ -260,8 +255,6 @@ export default async function buildWasm(target, options = {}) {
|
|
|
260
255
|
logger.startStep(target, 'wasm');
|
|
261
256
|
const t0 = performance.now();
|
|
262
257
|
|
|
263
|
-
triggerExtensions('buildWasm', 'beforeBuildEdge', [emccFlags]);
|
|
264
|
-
|
|
265
258
|
const data = Object.entries(getData('data', target)).map(([key, value]) => ['--preload-file', `${key.replaceAll('@', '@@')}@/crossbind/${value}`]).flat();
|
|
266
259
|
run('em++', [
|
|
267
260
|
'-lembind',
|
|
@@ -300,8 +293,6 @@ export default async function buildWasm(target, options = {}) {
|
|
|
300
293
|
if (target.runtimeEnv === 'node') {
|
|
301
294
|
logger.startStep(target, 'wasm');
|
|
302
295
|
|
|
303
|
-
triggerExtensions('buildWasm', 'beforeBuildNodeJS', [emccFlags]);
|
|
304
|
-
|
|
305
296
|
run('em++', [
|
|
306
297
|
'-lembind',
|
|
307
298
|
// The config's emccFlags follow the default level, so its -Oz or -Os wins (the last -O does).
|
|
@@ -5,9 +5,9 @@ import createBridgeFile from './createInterface.js';
|
|
|
5
5
|
import getDependFilePath from '../integration/getDependFilePath.js';
|
|
6
6
|
import resolveNativeImport from '../integration/resolveNativeImport.js';
|
|
7
7
|
import { getRustSymbols } from '../integration/getCrossbindScript.js';
|
|
8
|
-
import { findNativeImportsIn, nativeSpecifierTest } from '../utils/nativeImports.js';
|
|
8
|
+
import { findNativeImportsIn, nativeSpecifierTest, schemeTest } from '../utils/nativeImports.js';
|
|
9
|
+
import { importSchemesOf } from '../utils/packageSources.js';
|
|
9
10
|
|
|
10
|
-
const SCHEME = /^(cargo|conan):/;
|
|
11
11
|
const isBare = (specifier) => !specifier.startsWith('.') && !upath.isAbsolute(specifier);
|
|
12
12
|
|
|
13
13
|
function realPath(file) {
|
|
@@ -18,13 +18,13 @@ function realPath(file) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// The first target whose packages ship the file.
|
|
22
|
-
// what to declare or install, as it does in a bundler.
|
|
23
|
-
function resolveImport(specifier, importer, targets) {
|
|
21
|
+
// The first target whose packages ship the file. An import by a scheme, as cargo: and conan: are, that none resolves
|
|
22
|
+
// fails the build with what to declare or install, as it does in a bundler.
|
|
23
|
+
function resolveImport(specifier, importer, targets, hasScheme) {
|
|
24
24
|
let failure = null;
|
|
25
25
|
for (const target of targets) {
|
|
26
26
|
try {
|
|
27
|
-
const file =
|
|
27
|
+
const file = hasScheme(specifier) ? getDependFilePath(specifier, target) : resolveNativeImport(specifier, importer, target);
|
|
28
28
|
if (file && fs.existsSync(file)) return { target, file };
|
|
29
29
|
} catch (error) {
|
|
30
30
|
failure ??= error;
|
|
@@ -47,8 +47,10 @@ export function findAppNativeImports(targets) {
|
|
|
47
47
|
const projectDir = state.config.paths.project;
|
|
48
48
|
const imports = new Map();
|
|
49
49
|
if (!projectDir || !fs.existsSync(projectDir)) return imports;
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const schemes = importSchemesOf(state.config);
|
|
51
|
+
const hasScheme = schemeTest(schemes);
|
|
52
|
+
for (const { importer, specifier } of findNativeImportsIn(projectDir, nativeSpecifierTest(state.config.ext, schemes))) {
|
|
53
|
+
const resolved = resolveImport(specifier, importer, targets, hasScheme);
|
|
52
54
|
if (!resolved) {
|
|
53
55
|
console.warn(`crossbind: ${upath.relative(projectDir, importer)} imports ${specifier}, which no dependency ships for ${targets.map((t) => t.path).join(', ')}.`);
|
|
54
56
|
continue;
|