crossbind 2.0.0-beta.69 → 2.0.0-beta.73
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/package.json +3 -2
- package/src/actions/addExtension.js +8 -4
- package/src/actions/buildApp.js +38 -9
- package/src/actions/buildDependencies.js +3 -3
- package/src/actions/createImportedBridges.js +10 -8
- package/src/actions/createInterface.js +23 -21
- package/src/actions/createLib.js +3 -3
- package/src/actions/extensionCommands.js +2 -1
- package/src/actions/getAllBridges.js +20 -5
- package/src/actions/getDependLibs.js +6 -0
- package/src/actions/getLinkInputs.js +2 -1
- package/src/actions/gradleProject.js +13 -2
- package/src/actions/licenses.js +30 -24
- package/src/actions/{prepareConanDependencies.js → preparePackageSources.js} +13 -12
- package/src/actions/writeRuntimeEntry.js +2 -2
- package/src/actions/xcodeProject.js +12 -6
- package/src/app/index.d.ts +19 -0
- package/src/app/index.js +32 -7
- package/src/assets/node-import-hooks.mjs +7 -6
- package/src/bin.js +37 -17
- package/src/index.js +4 -3
- package/src/integration/getCrossbindScript.js +3 -2
- package/src/integration/getDependFilePath.js +10 -10
- package/src/state/{attachConanDependencies.js → attachPackageSources.js} +20 -16
- package/src/state/index.js +14 -11
- package/src/state/loadConfig.js +10 -4
- package/src/state/refreshPackageSources.js +19 -0
- package/src/utils/appApiHeader.js +5 -5
- package/src/utils/appBuild.js +8 -0
- package/src/utils/appExtensions.js +44 -28
- package/src/utils/appOptions.js +31 -10
- package/src/utils/appProject.js +24 -0
- package/src/utils/appRustCrates.js +2 -2
- package/src/utils/bridgeTools.js +6 -1
- package/src/utils/cppDts.js +11 -11
- package/src/utils/extensionSteps.js +38 -1
- package/src/utils/nativeImports.js +9 -2
- package/src/utils/nodeDevBuild.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/printableText.js +5 -0
- package/src/utils/resolveAppHost.js +10 -0
- package/src/utils/runPackageSource.js +251 -0
- package/src/utils/rustBridgeGen.js +3 -3
- package/src/utils/targetToolchain.js +77 -0
- package/src/utils/xcframeworkSlice.js +6 -0
- 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
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import state, { setAllDependecyPaths } from '../state/index.js';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import refreshPackageSources from '../state/refreshPackageSources.js';
|
|
4
|
+
import installPackageSources from '../utils/packageSourceInstall.js';
|
|
5
|
+
import { declaredSources } from '../utils/packageSources.js';
|
|
5
6
|
import { releaseTargetOf, HOST_BUILT_PLATFORMS } from '../utils/targets.js';
|
|
6
7
|
import createXCFramework from './createXCFramework.js';
|
|
7
8
|
|
|
@@ -11,11 +12,11 @@ const isBuiltHere = (target) => process.platform === 'darwin' || !HOST_BUILT_PLA
|
|
|
11
12
|
|
|
12
13
|
// iOS headers and archives come out of an xcframework per library with a slice per SDK (state/index.js).
|
|
13
14
|
// One older than an archive it wraps was made before the package was staged again.
|
|
14
|
-
function
|
|
15
|
+
function makeXCFrameworks(iosTargets) {
|
|
15
16
|
const archiveOf = (dependency, target, name) => `${dependency.paths.output}/prebuilt/${target.path}/lib/lib${name}.a`;
|
|
16
17
|
const isFresh = (dependency, name) => mtimeOf(`${dependency.paths.project}/${name}.xcframework/Info.plist`)
|
|
17
18
|
>= Math.max(...iosTargets.map((target) => mtimeOf(archiveOf(dependency, target, name))));
|
|
18
|
-
state.config.allDependencies.filter((dependency) => dependency.general.
|
|
19
|
+
state.config.allDependencies.filter((dependency) => dependency.general.packageSource).forEach((dependency) => {
|
|
19
20
|
// A library its recipe names on another platform only has no slice here.
|
|
20
21
|
const libName = dependency.export.libName.filter((name) => iosTargets.every((target) => fs.existsSync(archiveOf(dependency, target, name))));
|
|
21
22
|
if (libName.every((name) => isFresh(dependency, name))) return;
|
|
@@ -29,11 +30,11 @@ function makeConanXCFrameworks(iosTargets) {
|
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
//
|
|
33
|
-
// interface made without its includes stays that way until the header itself
|
|
34
|
-
// only - a debug build links them like any prebuilt.
|
|
35
|
-
export default async function
|
|
36
|
-
if (
|
|
33
|
+
// The packages of a project's sources are staged before any header is read: SWIG parses headers that
|
|
34
|
+
// include theirs, and an interface made without its includes stays that way until the header itself
|
|
35
|
+
// changes. Release trees only - a debug build links them like any prebuilt.
|
|
36
|
+
export default async function preparePackageSources(targets) {
|
|
37
|
+
if (declaredSources(state.config).length === 0) return;
|
|
37
38
|
// createBridgeFile reads every header for the first wasm target unless it is told otherwise, as it
|
|
38
39
|
// is by Metro, which builds the React Native bridges for the platform's own target, and by a Node
|
|
39
40
|
// build, which reads them for one of its own (nodeBridgeTarget).
|
|
@@ -43,9 +44,9 @@ export default async function prepareConanDependencies(targets) {
|
|
|
43
44
|
const releaseTargets = [...new Map([...targets, bridgeTarget, ...iosTargets].filter(Boolean).map((target) => [
|
|
44
45
|
target.releasePath, releaseTargetOf(target),
|
|
45
46
|
])).values()].filter(isBuiltHere);
|
|
46
|
-
await
|
|
47
|
+
await installPackageSources(state.config, releaseTargets);
|
|
47
48
|
// Also when another process did the staging: this one may have loaded before the manifests existed.
|
|
48
|
-
|
|
49
|
+
refreshPackageSources(state.config);
|
|
49
50
|
setAllDependecyPaths();
|
|
50
|
-
if (iosTargets.length > 0)
|
|
51
|
+
if (iosTargets.length > 0) makeXCFrameworks(releaseTargets.filter((target) => target.platform === 'ios'));
|
|
51
52
|
}
|
|
@@ -50,7 +50,7 @@ function nativesOf(headers, imports, dir) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// The module `node --import` loads ahead of the app, so the app imports the bound headers themselves.
|
|
53
|
-
export function writeImportHooks(entry, headers, imports, extensions) {
|
|
53
|
+
export function writeImportHooks(entry, headers, imports, extensions, schemes) {
|
|
54
54
|
// Node runs the register file from its real path, so the files it names are relative to that.
|
|
55
55
|
const dir = upath.normalize(fs.realpathSync(upath.dirname(entry)));
|
|
56
56
|
const natives = nativesOf(headers, imports, dir);
|
|
@@ -59,7 +59,7 @@ export function writeImportHooks(entry, headers, imports, extensions) {
|
|
|
59
59
|
'// Generated by crossbind - do not edit.',
|
|
60
60
|
"import registerNativeImports from './hooks.mjs';",
|
|
61
61
|
'',
|
|
62
|
-
`registerNativeImports(new URL('./${upath.basename(entry)}', import.meta.url), ${JSON.stringify({ extensions, natives }, null, 4)});`,
|
|
62
|
+
`registerNativeImports(new URL('./${upath.basename(entry)}', import.meta.url), ${JSON.stringify({ extensions, schemes, natives }, null, 4)});`,
|
|
63
63
|
'',
|
|
64
64
|
].join('\n'));
|
|
65
65
|
}
|
|
@@ -151,8 +151,9 @@ function objects(name, targetSettings, hasAssets, hasPrivacy, added) {
|
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
// The asset catalog: the app's icon, from which Xcode makes every size, and the launch screen's color
|
|
155
|
-
|
|
154
|
+
// The asset catalog: the app's icon, from which Xcode makes every size, and the launch screen's color, with the one of
|
|
155
|
+
// the dark appearance where there is one.
|
|
156
|
+
function writeAssets(catalog, icon, background, darkBackground) {
|
|
156
157
|
const info = { author: 'xcode', version: 1 };
|
|
157
158
|
const assetSet = (set, contents) => {
|
|
158
159
|
fs.mkdirSync(`${catalog}/${set}`, { recursive: true });
|
|
@@ -163,9 +164,13 @@ function writeAssets(catalog, icon, background) {
|
|
|
163
164
|
assetSet('AppIcon.appiconset', { images: [{ filename: 'icon.png', idiom: 'universal', platform: 'ios', size: '1024x1024' }] });
|
|
164
165
|
fs.writeFileSync(`${catalog}/AppIcon.appiconset/icon.png`, icon);
|
|
165
166
|
}
|
|
167
|
+
const colorOf = (rgb) => {
|
|
168
|
+
const [red, green, blue] = [1, 3, 5].map((at) => `0x${rgb.slice(at, at + 2).toUpperCase()}`);
|
|
169
|
+
return { 'color-space': 'srgb', components: { alpha: '1.000', red, green, blue } };
|
|
170
|
+
};
|
|
166
171
|
if (background) {
|
|
167
|
-
const
|
|
168
|
-
assetSet(`${LAUNCH_COLOR}.colorset`, { colors: [{ color:
|
|
172
|
+
const dark = darkBackground ? [{ appearances: [{ appearance: 'luminosity', value: 'dark' }], color: colorOf(darkBackground), idiom: 'universal' }] : [];
|
|
173
|
+
assetSet(`${LAUNCH_COLOR}.colorset`, { colors: [{ color: colorOf(background), idiom: 'universal' }, ...dark] });
|
|
169
174
|
}
|
|
170
175
|
}
|
|
171
176
|
|
|
@@ -176,7 +181,8 @@ function writeAssets(catalog, icon, background) {
|
|
|
176
181
|
// where one of them asks for it.
|
|
177
182
|
export default function writeXcodeProject(to, {
|
|
178
183
|
parts, identity, label, version, build = version, icon = null, urlSchemes = [], fileTypes = [], links = [], activityTypes = [], shortcutItems = [],
|
|
179
|
-
background = null, usage = {}, encryption = null, multipleWindows = false, privacy = {}, entitlements = {}, plist = [], targets = [],
|
|
184
|
+
background = null, darkBackground = null, usage = {}, encryption = null, multipleWindows = false, privacy = {}, entitlements = {}, plist = [], targets = [],
|
|
185
|
+
appGroup = null,
|
|
180
186
|
}) {
|
|
181
187
|
const { name } = state.config.general;
|
|
182
188
|
if (parts.length === 0) throw new Error(`crossbind: no part of the iOS app ${name} is built, so there is no Xcode project to write.`);
|
|
@@ -206,7 +212,7 @@ export default function writeXcodeProject(to, {
|
|
|
206
212
|
|
|
207
213
|
fs.writeFileSync(`${to}/main.m`, MAIN);
|
|
208
214
|
fs.writeFileSync(`${to}/Info.plist`, info);
|
|
209
|
-
writeAssets(`${to}/Assets.xcassets`, icon, background);
|
|
215
|
+
writeAssets(`${to}/Assets.xcassets`, icon, background, darkBackground);
|
|
210
216
|
const hasPrivacy = Object.keys(privacy).length > 0;
|
|
211
217
|
if (hasPrivacy) fs.writeFileSync(`${to}/PrivacyInfo.xcprivacy`, privacyManifest(privacy));
|
|
212
218
|
const hasEntitlements = Object.keys(signed).length > 0;
|
package/src/app/index.d.ts
CHANGED
|
@@ -24,6 +24,23 @@ export declare const app: {
|
|
|
24
24
|
launchArgs(): Promise<Launch>;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
/** Where the app runs, as its host tells the page before the page's own scripts run; the same through the app's run. */
|
|
28
|
+
export interface Device {
|
|
29
|
+
/** The platform the app is built for. */
|
|
30
|
+
readonly platform: 'macos' | 'windows' | 'linux' | 'ios' | 'android';
|
|
31
|
+
/** The arch the app's process runs as. */
|
|
32
|
+
readonly arch: 'arm64' | 'x64';
|
|
33
|
+
/** What the device is to its user as the app starts: a phone; a tablet, an iPad or an Android device whose screen's shorter side has 600dp; or a desktop, a computer, a Chromebook or a Mac that runs an iOS app. */
|
|
34
|
+
readonly formFactor: 'phone' | 'tablet' | 'desktop';
|
|
35
|
+
/** The system's name and version, as its maker numbers it: iOS or iPadOS 18.4, Android 15 with its API level, macOS 26.1, Windows 10.0.26100, or a Linux distribution's, as Ubuntu 24.04, else its kernel's release. */
|
|
36
|
+
readonly os: { readonly name: string; readonly version: string; readonly apiLevel?: number };
|
|
37
|
+
/** Whether the iOS Simulator or an Android emulator runs the app. */
|
|
38
|
+
readonly isVirtual: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Where the app runs, read at once, as the page starts; reading it outside an app throws. */
|
|
42
|
+
export declare const device: Device;
|
|
43
|
+
|
|
27
44
|
/** What windows.set() changes of an open window: sizes and places in logical pixels, a page's size without the frame, and x and y the frame's place from the top left of the main screen, given together. A limit of 0 takes it away. */
|
|
28
45
|
export interface WindowChanges {
|
|
29
46
|
title?: string;
|
|
@@ -57,6 +74,8 @@ export interface WindowOptions extends Omit<WindowChanges, 'center' | 'minWidth'
|
|
|
57
74
|
backgroundColor?: string;
|
|
58
75
|
/** false opens a window without a title bar and its buttons, which the page moves with data-crossbind-drag or windows.startDrag(). */
|
|
59
76
|
frame?: boolean;
|
|
77
|
+
/** 'hidden' lays a macOS window's page under its title bar, whose buttons stay at its top left, as app.window.titleBar does; Windows and Linux keep their title bar and list it in ignored. */
|
|
78
|
+
titleBar?: 'visible' | 'hidden';
|
|
60
79
|
/** The id of the window it stays above, and closes with. */
|
|
61
80
|
parent?: number;
|
|
62
81
|
/** With a parent: a sheet of the parent's on macOS, and on Windows and Linux a window the parent takes no input under until it closes. */
|
package/src/app/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
// What an app's host does for its page: the app's folders, quitting, its windows, the events of the app's native code and
|
|
2
|
-
// the functions of its extensions, by their names. Each call goes to the host of an app
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// the functions of its extensions, by their names. Each call goes to the host of an app, which crossbind app builds; in a
|
|
3
|
+
// browser it rejects, or events.on throws. A page imports these from crossbind/app, which an app's bundler plugin takes
|
|
4
|
+
// from the app host, with what crossbind's own extensions give the page, as crossbind/app/core alone holds these.
|
|
5
5
|
|
|
6
|
-
// The app's crossbind.js, which initNative() loads, hands the calls to the host.
|
|
6
|
+
// The app's crossbind.js, which initNative() loads, hands the calls to the host. A browser has crossbind's glue as well,
|
|
7
|
+
// but no token of a host, and none of its functions.
|
|
7
8
|
function hostOf(member, name) {
|
|
8
9
|
const host = globalThis.Crossbind?.[member];
|
|
9
10
|
if (host) return host;
|
|
11
|
+
if (!globalThis.__crossbind_token && !globalThis.Crossbind?.app) throw new Error(`crossbind: ${name} works in an app alone, which crossbind app builds`);
|
|
10
12
|
if (globalThis.Crossbind) throw new Error(`crossbind: this app's host is older than crossbind/app; update @crossbind/core-app-host to call ${name}`);
|
|
11
|
-
|
|
12
|
-
throw new Error(`crossbind: ${name} works in an app alone, whose crossbind.config.js sets target.runtimeEnv: 'app'`);
|
|
13
|
+
throw new Error(`crossbind: ${name} works once the page has called initNative()`);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
const call = (name, options) => hostOf('app', name)(name, options);
|
|
@@ -39,8 +40,9 @@ const WINDOW_SHARED = {
|
|
|
39
40
|
};
|
|
40
41
|
const WINDOW_SET = { ...WINDOW_SHARED, center: 'boolean' };
|
|
41
42
|
const WINDOW_OPEN = {
|
|
42
|
-
...WINDOW_SHARED, path: 'string', name: 'string', backgroundColor: 'string', frame: 'boolean', parent: 'number', modal: 'boolean',
|
|
43
|
+
...WINDOW_SHARED, path: 'string', name: 'string', backgroundColor: 'string', frame: 'boolean', titleBar: 'string', parent: 'number', modal: 'boolean',
|
|
43
44
|
};
|
|
45
|
+
const TITLE_BARS = ['visible', 'hidden'];
|
|
44
46
|
const MAX_WINDOW_SIZE = 16384;
|
|
45
47
|
const MAX_WINDOW_PLACE = 1000000;
|
|
46
48
|
// The size a window opens at without one.
|
|
@@ -70,6 +72,10 @@ function checkedWindow(name, options, kinds) {
|
|
|
70
72
|
throw new TypeError(`crossbind: ${name}'s path ${JSON.stringify(path)} is no path of the app's own pages, as /settings.html is.`);
|
|
71
73
|
}
|
|
72
74
|
if (backgroundColor !== undefined && !COLOR.test(backgroundColor)) throw new TypeError(`crossbind: ${name}'s backgroundColor is no color of the form #rrggbb.`);
|
|
75
|
+
if (options.titleBar !== undefined && !TITLE_BARS.includes(options.titleBar)) throw new TypeError(`crossbind: ${name}'s titleBar is neither 'visible' nor 'hidden'.`);
|
|
76
|
+
if (options.frame === false && options.titleBar === 'hidden') {
|
|
77
|
+
throw new TypeError(`crossbind: ${name} hides the title bar of a window with a frame alone: drop titleBar, or frame: false.`);
|
|
78
|
+
}
|
|
73
79
|
if (parent !== undefined && !isWindowId(parent)) throw new TypeError(`crossbind: ${name}'s parent is no window's id.`);
|
|
74
80
|
if (options.modal && parent === undefined) throw new TypeError(`crossbind: ${name} opens a modal window above a parent: give it parent, the id of a window.`);
|
|
75
81
|
if (name === 'windows.open') checkedLimits(options);
|
|
@@ -96,6 +102,25 @@ function windowOf(name, id) {
|
|
|
96
102
|
return { id };
|
|
97
103
|
}
|
|
98
104
|
|
|
105
|
+
// What the app's host tells the page of where the app runs, from before the page's own scripts on.
|
|
106
|
+
function deviceOf(name) {
|
|
107
|
+
const device = globalThis.__crossbind_device;
|
|
108
|
+
if (device) return device[name];
|
|
109
|
+
if (globalThis.__crossbind_token) throw new Error(`crossbind: this app's host is older than crossbind/app; update @crossbind/core-app-host to read device.${name}`);
|
|
110
|
+
throw new Error(`crossbind: device.${name} works in an app alone, which crossbind app builds`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Where the app runs, the same through its run, which the page reads at once, as it starts: the platform and the arch
|
|
114
|
+
// the app is built for, what the device is to its user, its system's name and version, and whether a simulator or an
|
|
115
|
+
// emulator runs the app.
|
|
116
|
+
export const device = Object.freeze({
|
|
117
|
+
get platform() { return deviceOf('platform'); },
|
|
118
|
+
get arch() { return deviceOf('arch'); },
|
|
119
|
+
get formFactor() { return deviceOf('formFactor'); },
|
|
120
|
+
get os() { return deviceOf('os'); },
|
|
121
|
+
get isVirtual() { return deviceOf('isVirtual'); },
|
|
122
|
+
});
|
|
123
|
+
|
|
99
124
|
export const app = {
|
|
100
125
|
// The app's own folders, made when first asked for: { data, cache, logs }.
|
|
101
126
|
paths: async () => call('app.paths', {}),
|
|
@@ -5,7 +5,6 @@ import { registerHooks } from 'node:module';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
7
|
|
|
8
|
-
const SCHEME = /^(cargo|conan):/;
|
|
9
8
|
const isBare = (specifier) => !/^(\.{1,2}\/|\/|[A-Za-z]:[\\/]|file:)/.test(specifier);
|
|
10
9
|
|
|
11
10
|
function notBound(what, parentURL, cause) {
|
|
@@ -17,23 +16,25 @@ const exportOf = (name) => (typeof name === 'string' ? name : `${name.wire} as $
|
|
|
17
16
|
const proxyOf = (entry, names) => `export { ${['initNative', 'AllSymbols', ...names.map(exportOf)].join(', ')} } from ${JSON.stringify(entry)};\n`;
|
|
18
17
|
|
|
19
18
|
// Another hook may still serve it; Node's own resolver hands an unknown scheme on unresolved.
|
|
20
|
-
function resolveUnbound(specifier, context, nextResolve) {
|
|
19
|
+
function resolveUnbound(specifier, context, nextResolve, scheme) {
|
|
21
20
|
let resolved;
|
|
22
21
|
try {
|
|
23
22
|
resolved = nextResolve(specifier, context);
|
|
24
23
|
} catch (error) {
|
|
25
24
|
throw notBound(specifier, context.parentURL, error);
|
|
26
25
|
}
|
|
27
|
-
if (
|
|
26
|
+
if (scheme.test(resolved.url)) throw notBound(specifier, context.parentURL);
|
|
28
27
|
return resolved;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
// `natives` lists each bound file, relative to the entry, with the bare specifiers that name it and the names it
|
|
32
|
-
// exports, a name the entry exports under another as { local, wire }.
|
|
33
|
-
|
|
31
|
+
// exports, a name the entry exports under another as { local, wire }. `schemes` are those an import names a store
|
|
32
|
+
// by: the two every build has, for a register file an older build wrote, which names none.
|
|
33
|
+
export default function registerNativeImports(entry, { extensions, schemes = ['cargo', 'conan'], natives }) {
|
|
34
34
|
const entryUrl = new URL(entry).href;
|
|
35
35
|
const dir = path.dirname(fileURLToPath(entryUrl));
|
|
36
36
|
const isNative = new RegExp(`\\.(${extensions.join('|')})$`);
|
|
37
|
+
const scheme = new RegExp(`^(${schemes.join('|')}):`);
|
|
37
38
|
const byFile = new Map();
|
|
38
39
|
const bySpecifier = new Map();
|
|
39
40
|
natives.forEach(({ file, specifiers, names }) => {
|
|
@@ -46,7 +47,7 @@ export default function registerNativeImports(entry, { extensions, natives }) {
|
|
|
46
47
|
resolve(specifier, context, nextResolve) {
|
|
47
48
|
const url = bySpecifier.get(specifier);
|
|
48
49
|
if (url) return { url, format: 'module', shortCircuit: true };
|
|
49
|
-
if (isBare(specifier) && (
|
|
50
|
+
if (isBare(specifier) && (scheme.test(specifier) || isNative.test(specifier))) return resolveUnbound(specifier, context, nextResolve, scheme);
|
|
50
51
|
return nextResolve(specifier, context);
|
|
51
52
|
},
|
|
52
53
|
load(url, context, nextLoad) {
|
package/src/bin.js
CHANGED
|
@@ -15,6 +15,7 @@ import buildWasiCommand from './actions/buildWasiCommand.js';
|
|
|
15
15
|
import buildNode, { addonsInPackages, bundleNodeLoader, publishNodeData } from './actions/buildNode.js';
|
|
16
16
|
import buildNativeCommand from './actions/buildNativeCommand.js';
|
|
17
17
|
import buildApp, { appOutputPath, assertAppRunner, publishApp } from './actions/buildApp.js';
|
|
18
|
+
import { assertAppProject } from './utils/appProject.js';
|
|
18
19
|
import {
|
|
19
20
|
makeUploadKey, releaseSigningEnv, showUploadKeyPassword, useUploadKey,
|
|
20
21
|
} from './actions/uploadKey.js';
|
|
@@ -44,7 +45,8 @@ import {
|
|
|
44
45
|
RUNNER_PLATFORMS, RUNNER_ROLES, connectCommands, deleteRunner, deploySteps, initRunner, newRunnerToken, runnerHostPort, startRunner,
|
|
45
46
|
} from './actions/runnerCommands.js';
|
|
46
47
|
import { cargoHome } from './utils/runCargo.js';
|
|
47
|
-
import {
|
|
48
|
+
import { containerRootOf, sourceRoot } from './utils/runPackageSource.js';
|
|
49
|
+
import { importSchemesOf, sourceNamesOf } from './utils/packageSources.js';
|
|
48
50
|
import { cleanDepsCache } from './utils/dependencyRebuild.js';
|
|
49
51
|
import collectLicenseRows from './actions/licenses.js';
|
|
50
52
|
import { formatNoticesMarkdown, formatCycloneDxSbom, validateSpdx } from './utils/licenseReport.js';
|
|
@@ -112,13 +114,34 @@ function declaredSbomPath(projectDir) {
|
|
|
112
114
|
// An app's page is the project's Vite build, which @crossbind/plugin-vite makes into the app of the platform named.
|
|
113
115
|
const APP_PLATFORMS = ['android', 'ios', 'linux', 'macos', 'windows'];
|
|
114
116
|
|
|
117
|
+
// Vite's CLI beside the entry of the package vite resolves to, where Vite keeps it and vite-plus-core too, whose vp runs
|
|
118
|
+
// it from there. An entry outside the package is another one's, which a lookup past it found.
|
|
119
|
+
function viteCliBesideEntry(require, packageDir) {
|
|
120
|
+
try {
|
|
121
|
+
const entry = require.resolve('vite');
|
|
122
|
+
const cli = path.join(path.dirname(entry), 'cli.js');
|
|
123
|
+
return entry.startsWith(`${packageDir}${path.sep}`) && fs.existsSync(cli) ? cli : null;
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
115
129
|
function projectVite() {
|
|
130
|
+
const require = createRequire(`${state.config.paths.project}/package.json`);
|
|
131
|
+
let manifestPath;
|
|
116
132
|
try {
|
|
117
|
-
|
|
118
|
-
return path.join(path.dirname(manifest), JSON.parse(fs.readFileSync(manifest, 'utf8')).bin.vite);
|
|
133
|
+
manifestPath = require.resolve('vite/package.json');
|
|
119
134
|
} catch {
|
|
120
135
|
throw new Error("crossbind: crossbind app builds the app's page with the project's Vite, which the project does not have. Add vite and @crossbind/plugin-vite to it, as the desktop-app template has them.");
|
|
121
136
|
}
|
|
137
|
+
// A workspace can map vite to a package of Vite's API alone, as vite-plus maps it to vite-plus-core.
|
|
138
|
+
const { name, version, bin } = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
139
|
+
if (bin?.vite) return path.join(path.dirname(manifestPath), bin.vite);
|
|
140
|
+
const cli = viteCliBesideEntry(require, path.dirname(manifestPath));
|
|
141
|
+
if (!cli) {
|
|
142
|
+
throw new Error(`crossbind: crossbind app runs the project's Vite, and its vite resolves to ${name} ${version}, which has neither a vite command nor Vite's cli.js beside its entry. Have vite resolve to Vite itself for the project, with an override where its workspace maps vite to another package.`);
|
|
143
|
+
}
|
|
144
|
+
return cli;
|
|
122
145
|
}
|
|
123
146
|
|
|
124
147
|
// The project's Vite, as `crossbind app dev`, `build` and `publish` run it, and its exit code once it ends. A signal sent
|
|
@@ -136,11 +159,7 @@ function runVite(args, env) {
|
|
|
136
159
|
});
|
|
137
160
|
}
|
|
138
161
|
|
|
139
|
-
|
|
140
|
-
if (state.config.target.runtimeEnv !== 'app') {
|
|
141
|
-
throw new Error("crossbind: crossbind app builds an app, which this project is not: set target: { runtimeEnv: 'app' } in crossbind.config.js, as the desktop-app template does.");
|
|
142
|
-
}
|
|
143
|
-
}
|
|
162
|
+
const assertAppCommand = () => assertAppProject(state.config, 'crossbind app builds an app');
|
|
144
163
|
|
|
145
164
|
const commandApp = program.command('app').description('build the project as an app, or open it on the dev server');
|
|
146
165
|
[
|
|
@@ -157,12 +176,12 @@ const commandApp = program.command('app').description('build the project as an a
|
|
|
157
176
|
.allowUnknownOption()
|
|
158
177
|
.allowExcessArguments()
|
|
159
178
|
.action(async ({ platform, device }, command) => {
|
|
160
|
-
|
|
179
|
+
assertAppCommand();
|
|
161
180
|
// An Android build checks its upload key before the native build, not after it.
|
|
162
181
|
const isAndroidBuild = name === 'build' && (platform ?? process.env.CROSSBIND_APP_PLATFORM) === 'android';
|
|
163
182
|
const { env: signing } = isAndroidBuild ? await releaseSigningEnv() : { env: {} };
|
|
164
183
|
const onDevice = device === undefined ? {} : { CROSSBIND_APP_DEVICE: iosDeviceOption(device, platform ?? process.env.CROSSBIND_APP_PLATFORM) };
|
|
165
|
-
const env = { ...process.env, ...signing, ...(platform ? { CROSSBIND_APP_PLATFORM: platform } : {}), ...onDevice };
|
|
184
|
+
const env = { ...process.env, ...signing, CROSSBIND_APP_BUILD: '1', ...(platform ? { CROSSBIND_APP_PLATFORM: platform } : {}), ...onDevice };
|
|
166
185
|
process.exit(await runVite([...(name === 'build' ? ['build'] : []), ...command.args], env));
|
|
167
186
|
});
|
|
168
187
|
});
|
|
@@ -188,7 +207,7 @@ commandApp.command('publish')
|
|
|
188
207
|
.allowUnknownOption()
|
|
189
208
|
.allowExcessArguments()
|
|
190
209
|
.action(async ({ platform }, command) => {
|
|
191
|
-
|
|
210
|
+
assertAppCommand();
|
|
192
211
|
await publish(command.args, { platform, envPlatform: process.env.CROSSBIND_APP_PLATFORM, runVite });
|
|
193
212
|
});
|
|
194
213
|
|
|
@@ -310,17 +329,18 @@ imageArgument(commandDocker.command('create').description('create docker contain
|
|
|
310
329
|
.action((image) => {
|
|
311
330
|
// Google ships the linux NDK for x86_64 only, so an android or android-app container is always amd64.
|
|
312
331
|
const platform = ['android', 'android-app'].includes(image) ? 'linux/amd64' : undefined;
|
|
332
|
+
const sources = sourceNamesOf(state.config);
|
|
313
333
|
// Docker on Linux makes a missing mount source owned by root, which the user's builds cannot write.
|
|
314
|
-
[cargoHome(),
|
|
334
|
+
[cargoHome(), ...sources.map((name) => sourceRoot(name))].forEach((dir) => fs.mkdirSync(dir, { recursive: true }));
|
|
315
335
|
dockerExec([
|
|
316
336
|
'run', '-dit',
|
|
317
337
|
...(platform ? ['--platform', platform] : []),
|
|
318
338
|
'--name', dockerContainerName(image),
|
|
319
339
|
'-v', `${state.config.paths.base}:/tmp/crossbind/live`,
|
|
320
|
-
// Cargo's registry cache, shared with host builds and with every other project, and
|
|
321
|
-
//
|
|
340
|
+
// Cargo's registry cache, shared with host builds and with every other project, and each package
|
|
341
|
+
// source's store and work directories, which every other container build of it uses.
|
|
322
342
|
'-v', `${cargoHome()}:/var/cache/crossbind/cargo`,
|
|
323
|
-
'-v', `${
|
|
343
|
+
...sources.flatMap((name) => ['-v', `${sourceRoot(name)}:${containerRootOf(name)}`]),
|
|
324
344
|
getDockerImage(image, platform),
|
|
325
345
|
'bash',
|
|
326
346
|
]);
|
|
@@ -527,7 +547,7 @@ async function createNodeAddons(targetParams) {
|
|
|
527
547
|
if (headers.length && fs.existsSync(`${state.config.paths.output}/${loaderTarget.jsName}`)) {
|
|
528
548
|
const entry = `${state.config.paths.output}/node/napi.mjs`;
|
|
529
549
|
writeRuntimeEntry(entry, headers, ENTRY_RUNTIMES.node(loaderTarget.jsName));
|
|
530
|
-
if (imports.size) writeImportHooks(entry, headers, imports, nativeExtensions(state.config.ext));
|
|
550
|
+
if (imports.size) writeImportHooks(entry, headers, imports, nativeExtensions(state.config.ext), importSchemesOf(state.config));
|
|
531
551
|
else removeImportHooks(entry);
|
|
532
552
|
}
|
|
533
553
|
}
|
|
@@ -603,7 +623,7 @@ async function createWasmJs(targetParams, wasmOptions = {}) {
|
|
|
603
623
|
if (appHeaders.length && nodeTarget) {
|
|
604
624
|
const entry = `${state.config.paths.output}/node/wasm.mjs`;
|
|
605
625
|
writeRuntimeEntry(entry, appHeaders, ENTRY_RUNTIMES.node(nodeTarget.jsName));
|
|
606
|
-
if (imports.size) writeImportHooks(entry, appHeaders, imports, nativeExtensions(state.config.ext));
|
|
626
|
+
if (imports.size) writeImportHooks(entry, appHeaders, imports, nativeExtensions(state.config.ext), importSchemesOf(state.config));
|
|
607
627
|
else removeImportHooks(entry);
|
|
608
628
|
}
|
|
609
629
|
if (headers.length && edgeTarget) {
|
package/src/index.js
CHANGED
|
@@ -4,10 +4,11 @@ export { default as createLib } from './actions/createLib.js';
|
|
|
4
4
|
export { default as createBridgeFile } from './actions/createInterface.js';
|
|
5
5
|
export { default as buildWasm } from './actions/buildWasm.js';
|
|
6
6
|
export {
|
|
7
|
-
default as buildApp, buildProjectApp, publishApp, appLayout, appOutputName, appTargetParams,
|
|
7
|
+
default as buildApp, buildProjectApp, publishApp, appLayout, appOutputName, appPlatform, appTargetParams, emptyAppOutDir,
|
|
8
8
|
} from './actions/buildApp.js';
|
|
9
9
|
export { checkInstaller, shipApp } from './actions/appInstaller.js';
|
|
10
|
-
export { resolveAppPageApi } from './utils/resolveAppHost.js';
|
|
10
|
+
export { findAppPageApi, resolveAppPageApi } from './utils/resolveAppHost.js';
|
|
11
|
+
export { default as isAppBuild } from './utils/appBuild.js';
|
|
11
12
|
export { default as openInSimulator, buildForSimulator } from './actions/iosSimulator.js';
|
|
12
13
|
export {
|
|
13
14
|
default as openOnIosDevice, buildForIosDevice, connectedIosDevice, iosDeviceAddress, iosDevServer,
|
|
@@ -24,7 +25,7 @@ export { getTargetParams, getBuildTargets, getFilteredBuildTargets, getFilteredT
|
|
|
24
25
|
export { default as getCrossbindScript, getRustJsScript } from './integration/getCrossbindScript.js';
|
|
25
26
|
export { default as getDependFilePath } from './integration/getDependFilePath.js';
|
|
26
27
|
export { default as buildDependencies, getDependenciesStamp } from './actions/buildDependencies.js';
|
|
27
|
-
export { default as
|
|
28
|
+
export { default as preparePackageSources } from './actions/preparePackageSources.js';
|
|
28
29
|
export { default as findFiles } from './utils/findFiles.js';
|
|
29
30
|
export { getContentHash, getFileHash } from './utils/hash.js';
|
|
30
31
|
export {
|
|
@@ -5,6 +5,7 @@ import loadJson from '../utils/loadJson.js';
|
|
|
5
5
|
import state from '../state/index.js';
|
|
6
6
|
import { parseSurface, createRustBridgeCrate, createCrateImportBridge } from '../utils/rustBridgeGen.js';
|
|
7
7
|
import { parseCargoMarkerName } from '../utils/cargoImport.js';
|
|
8
|
+
import { cratesOf } from '../utils/packageSourceImport.js';
|
|
8
9
|
import { UNBOUND_MESSAGE } from '../assets/js-runtime/adapters/unboundMessage.js';
|
|
9
10
|
|
|
10
11
|
// `base` is where the bundler serves the app from, as Vite's `base` names it; webpack's public path wins at run time.
|
|
@@ -62,7 +63,7 @@ export function getRustSymbols(rsFile) {
|
|
|
62
63
|
// Marker under <cache>/rust-crates/: a direct crate import - the model comes from the
|
|
63
64
|
// upstream crate's own source, and the bridge is synthesized against the cargo dependency.
|
|
64
65
|
const { crateName, modulePath } = parseCargoMarkerName(path.basename(rsReal, '.rs'));
|
|
65
|
-
const cargoDeps = state.config
|
|
66
|
+
const cargoDeps = cratesOf(state.config);
|
|
66
67
|
if (Object.hasOwn(cargoDeps, crateName)
|
|
67
68
|
&& path.dirname(rsReal) === fs.realpathSync(path.join(state.config.paths.cache, 'rust-crates'))) {
|
|
68
69
|
const { exports } = createCrateImportBridge({
|
|
@@ -92,7 +93,7 @@ export function getRustSymbols(rsFile) {
|
|
|
92
93
|
projectPath: state.config.paths.project,
|
|
93
94
|
dtsMode: state.config.dts,
|
|
94
95
|
vectors,
|
|
95
|
-
cargoDependencies: state.config
|
|
96
|
+
cargoDependencies: cratesOf(state.config),
|
|
96
97
|
log: () => {},
|
|
97
98
|
});
|
|
98
99
|
}
|
|
@@ -2,8 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import state from '../state/index.js';
|
|
4
4
|
import { parseCargoImport, writeCargoMarker } from '../utils/cargoImport.js';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { cratesOf, sourceOfImport, resolveSourceHeader } from '../utils/packageSourceImport.js';
|
|
6
|
+
import refreshPackageSources from '../state/refreshPackageSources.js';
|
|
7
7
|
|
|
8
8
|
const LAYOUT = { header: 'include', module: 'swig', source: '' };
|
|
9
9
|
|
|
@@ -85,20 +85,20 @@ export default function getDependFilePath(source, target) {
|
|
|
85
85
|
// an undeclared crate is a hard, actionable error instead of a silent npm fallthrough.
|
|
86
86
|
if (source.startsWith('cargo:')) {
|
|
87
87
|
const cargoImport = parseCargoImport(source);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
throw new Error(`crossbind: '${source}' is not declared - add '${cargoImport.crateName}' to cargoDependencies in crossbind.config.`);
|
|
88
|
+
if (!Object.hasOwn(cratesOf(state.config), cargoImport.crateName)) {
|
|
89
|
+
throw new Error(`crossbind: '${source}' is not declared - add '${cargoImport.crateName}' to packages.cargo in crossbind.config.`);
|
|
91
90
|
}
|
|
92
91
|
const marker = writeCargoMarker(state.config.paths.cache, cargoImport);
|
|
93
92
|
existsCache.set(marker, true);
|
|
94
93
|
return marker;
|
|
95
94
|
}
|
|
96
95
|
|
|
97
|
-
//
|
|
98
|
-
// port's prebuilt; from there the header takes the same path as any other.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
// `<source>:<package>/<header>`, as conan:zlib/zlib.h, names a header of a declared package of a package
|
|
97
|
+
// source, staged per target like a port's prebuilt; from there the header takes the same path as any other.
|
|
98
|
+
const packageSource = sourceOfImport(state.config, source);
|
|
99
|
+
if (packageSource) {
|
|
100
|
+
refreshPackageSources(state.config);
|
|
101
|
+
return resolveSourceHeader(state.config, packageSource.name, source, target ?? state.targets.find((t) => t.platform === 'wasm'));
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const matches = getMatchingPackages(source);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getFilledConfig, flattenDependencies } from './loadConfig.js';
|
|
2
2
|
import calculateDependencyParameters from './calculateDependencyParameters.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { sourceStageDir, sourcePackageDir } from '../utils/packageSourceImport.js';
|
|
4
|
+
import { declaredSources, dependenciesKey, dependenciesOf } from '../utils/packageSources.js';
|
|
5
|
+
import { readSourceManifest, sourceTargetName } from '../utils/packageSourceStage.js';
|
|
6
6
|
import { TARGETS, targetPathOf } from '../utils/targets.js';
|
|
7
7
|
|
|
8
8
|
// What a recipe declares for its consumers, per staged target since it declares other things on another
|
|
@@ -32,25 +32,29 @@ function targetSpecsOf(name, targets) {
|
|
|
32
32
|
// staged for it, so a name it lacks drops out there.
|
|
33
33
|
const libsOf = (name, targets) => [...new Set(Object.values(targets).flat().filter((entry) => entry.name === name).flatMap((entry) => entry.libs))];
|
|
34
34
|
|
|
35
|
-
export function
|
|
35
|
+
export function sourceDependencyConfigs(source, manifest, stageDir) {
|
|
36
36
|
return manifest.packages.map((pkg) => ({
|
|
37
|
-
general: { name:
|
|
37
|
+
general: { name: sourceTargetName(source, pkg.name), packageSource: { name: source, package: pkg } },
|
|
38
38
|
export: { type: 'cmake', libName: libsOf(pkg.name, manifest.targets) },
|
|
39
|
-
paths: { project:
|
|
39
|
+
paths: { project: sourcePackageDir(stageDir, pkg.name), output: `${sourcePackageDir(stageDir, pkg.name)}/dist` },
|
|
40
40
|
targetSpecs: targetSpecsOf(pkg.name, manifest.targets),
|
|
41
41
|
}));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
function stagedDependencies(config, source) {
|
|
45
|
+
const stageDir = sourceStageDir(config.paths.cache, source);
|
|
46
|
+
const manifest = readSourceManifest(source, stageDir, dependenciesKey(dependenciesOf(config, source)));
|
|
47
|
+
return manifest ? sourceDependencyConfigs(source, manifest, stageDir).map((d) => getFilledConfig(d, { isDepend: true })) : [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The staged packages of each source join the config as dependencies, so header resolution, the link, the
|
|
51
|
+
// cmake graph and the license rows see them like any port. Only packages staged for what the config declares
|
|
52
|
+
// now join; reading a manifest runs nothing, so state can do it while it loads.
|
|
53
|
+
export default function attachPackageSources(config) {
|
|
54
|
+
const declared = declaredSources(config);
|
|
55
|
+
if (declared.length === 0 && !config.dependencies?.some((d) => d.general.packageSource)) return;
|
|
56
|
+
const staged = declared.flatMap(({ name }) => stagedDependencies(config, name));
|
|
57
|
+
config.dependencies = [...config.dependencies.filter((d) => !d.general.packageSource), ...staged];
|
|
54
58
|
config.allDependencies = flattenDependencies(config.dependencies);
|
|
55
59
|
config.dependencyParameters = calculateDependencyParameters(config);
|
|
56
60
|
}
|
package/src/state/index.js
CHANGED
|
@@ -5,10 +5,12 @@ import {
|
|
|
5
5
|
TARGETS, targetPathOf, nodeAddonNamesOf, nativeCommandNamesOf, wasiNamesOf, appNamesOf,
|
|
6
6
|
} from '../utils/targets.js';
|
|
7
7
|
import { findCargoModuleImportsIn, writeCargoMarker } from '../utils/cargoImport.js';
|
|
8
|
+
import { cratesOf } from '../utils/packageSourceImport.js';
|
|
8
9
|
import { exportWasiSdkPath } from '../utils/wasiToolchain.js';
|
|
9
10
|
import loadConfig, { assertBinarySelection } from './loadConfig.js';
|
|
10
11
|
import { configChangesOf } from '../utils/configChanges.js';
|
|
11
|
-
import
|
|
12
|
+
import refreshPackageSources from './refreshPackageSources.js';
|
|
13
|
+
import xcframeworkSliceOf from '../utils/xcframeworkSlice.js';
|
|
12
14
|
|
|
13
15
|
const cacheDir = `${process.cwd()}/.crossbind`;
|
|
14
16
|
|
|
@@ -28,7 +30,7 @@ async function initProcessState() {
|
|
|
28
30
|
state.cache = loadCacheState();
|
|
29
31
|
state.config = await loadConfig(undefined, undefined, { changes: configChangesOf() });
|
|
30
32
|
assertBinarySelection(state.config);
|
|
31
|
-
|
|
33
|
+
refreshPackageSources(state.config);
|
|
32
34
|
exportWasiSdkPath(state.config.system);
|
|
33
35
|
// Same idea for the docker image overrides: pullDockerImage.js resolves them from the
|
|
34
36
|
// environment alone, so it stays free of a state import (and of the cycle that would create).
|
|
@@ -74,7 +76,7 @@ async function initProcessState() {
|
|
|
74
76
|
// Cargo-crate import markers must exist BEFORE any bundler starts: metro resolves against
|
|
75
77
|
// its startup file map, so a marker first created mid-resolution is invisible to that very
|
|
76
78
|
// build. Bare crates come from the config, module imports from the app's own sources.
|
|
77
|
-
const cargoDeps = state.config
|
|
79
|
+
const cargoDeps = cratesOf(state.config);
|
|
78
80
|
const crateNames = Object.keys(cargoDeps);
|
|
79
81
|
const moduleImports = crateNames.length && state.config.paths.project
|
|
80
82
|
? findCargoModuleImportsIn(state.config.paths.project).filter((i) => Object.hasOwn(cargoDeps, i.crateName))
|
|
@@ -96,14 +98,20 @@ export function setAllDependecyPaths() {
|
|
|
96
98
|
state.config.allDependencyPaths = {};
|
|
97
99
|
state.targets.forEach((target) => {
|
|
98
100
|
state.config.allDependencyPaths[target.path] = { cmake: {} };
|
|
101
|
+
const owners = new Map();
|
|
99
102
|
state.config.allDependencies.forEach((d) => {
|
|
100
103
|
state.config.allDependencyPaths[target.path].cmake[d.general.name] = `${d.paths.output}/prebuilt`;
|
|
101
|
-
// A
|
|
102
|
-
const libNames = d.general.
|
|
104
|
+
// A package source's recipe can name a library on another platform only (libpng is png16 on Windows).
|
|
105
|
+
const libNames = d.general.packageSource
|
|
103
106
|
? d.export.libName.filter((name) => [target.path, target.releasePath]
|
|
104
107
|
.some((targetPath) => fs.existsSync(`${d.paths.output}/prebuilt/${targetPath}/lib/lib${name}.a`)))
|
|
105
108
|
: d.export.libName;
|
|
106
109
|
libNames.forEach((name) => {
|
|
110
|
+
// A project can list a port's packages for several platforms, which share the library's name: the one
|
|
111
|
+
// that serves the target keeps the entry.
|
|
112
|
+
const owner = owners.get(name);
|
|
113
|
+
if (owner && owner !== d && d.functions?.isEnabled?.(target) === false) return;
|
|
114
|
+
owners.set(name, d);
|
|
107
115
|
state.config.allDependencyPaths[target.path][name] = {
|
|
108
116
|
root: `${d.paths.output}/prebuilt/${target.path}`,
|
|
109
117
|
};
|
|
@@ -117,12 +125,7 @@ export function setAllDependecyPaths() {
|
|
|
117
125
|
const isDynamicLib = target.platform === 'android' && platformConfig.libType !== 'static';
|
|
118
126
|
const dep = state.config.allDependencyPaths[target.path][name];
|
|
119
127
|
if (target.platform === 'ios') {
|
|
120
|
-
|
|
121
|
-
if (target.arch === 'iphoneos') {
|
|
122
|
-
xcRoot = `${d.paths.project}/${name}.xcframework/ios-arm64`;
|
|
123
|
-
} else if (target.arch === 'iphonesimulator') {
|
|
124
|
-
xcRoot = `${d.paths.project}/${name}.xcframework/ios-arm64-simulator`;
|
|
125
|
-
}
|
|
128
|
+
const xcRoot = xcframeworkSliceOf(d.paths.project, name, target);
|
|
126
129
|
dep.header = `${xcRoot}/Headers`;
|
|
127
130
|
dep.libPath = xcRoot;
|
|
128
131
|
dep.lib = `${dep.libPath}/lib${name}.a`;
|