crossbind 2.0.0-beta.62 → 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/README.md +2 -2
- package/package.json +7 -2
- package/src/actions/buildBinTools.js +0 -0
- package/src/actions/buildDependencies.js +15 -2
- package/src/actions/buildExternal.js +9 -1
- package/src/actions/buildJs.js +2 -9
- package/src/actions/buildLib.js +25 -8
- package/src/actions/buildNativeCommand.js +84 -0
- package/src/actions/buildNode.js +49 -10
- package/src/actions/buildTypes.js +8 -2
- package/src/actions/buildWasiCommand.js +10 -9
- package/src/actions/buildWasm.js +72 -31
- package/src/actions/cloudCommands.js +133 -0
- package/src/actions/createImportedBridges.js +71 -0
- package/src/actions/createInterface.js +247 -68
- package/src/actions/createLib.js +70 -42
- package/src/actions/getData.js +6 -2
- package/src/actions/getEnv.js +7 -0
- package/src/actions/getLinkInputs.js +12 -5
- package/src/actions/isSourceNewer.js +2 -0
- package/src/actions/licenses.js +143 -19
- package/src/actions/prepareConanDependencies.js +51 -0
- package/src/actions/publishBinary.js +32 -0
- package/src/actions/run.js +63 -66
- package/src/actions/runnerCommands.js +433 -0
- package/src/actions/writeRuntimeEntry.js +80 -0
- package/src/assets/cmake/command/CMakeLists.txt +21 -0
- package/src/assets/cmake/command/command.cpp +1 -0
- package/src/assets/cmake/dist.cmake +2 -1
- package/src/assets/configure/install.sh +22 -0
- package/src/assets/cpp-runtime/node.cpp +18 -1
- package/src/assets/js-runtime/adapters/path-url.js +6 -3
- package/src/assets/js-runtime/adapters/unboundMessage.js +2 -0
- package/src/assets/js-runtime/adapters/worker-comlink.js +138 -60
- package/src/assets/js-runtime/core.js +12 -0
- package/src/assets/node-import-hooks.mjs +61 -0
- 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/assets/wasi-runtime/stubs.c +29 -0
- package/src/bin.js +218 -161
- package/src/cli.js +14 -0
- package/src/index.js +3 -0
- package/src/integration/bindHeaderImports.js +18 -0
- package/src/integration/getCrossbindScript.js +50 -13
- package/src/integration/getDependFilePath.js +9 -0
- package/src/integration/resolveNativeImport.js +14 -0
- package/src/node/dev.mjs +19 -0
- package/src/program.js +169 -0
- package/src/runner/blobs.js +135 -0
- package/src/runner/files.js +193 -0
- package/src/runner/server.js +320 -0
- package/src/state/attachConanDependencies.js +56 -0
- package/src/state/index.js +20 -11
- package/src/state/loadConfig.js +37 -35
- package/src/state/refreshBuiltDependencies.js +20 -0
- package/src/state/refreshConanDependencies.js +17 -0
- package/src/utils/androidToolchain.js +4 -0
- package/src/utils/appRustCrates.js +19 -1
- package/src/utils/appSources.js +97 -6
- package/src/utils/archiveFlags.js +12 -0
- package/src/utils/boundHeaders.js +79 -0
- package/src/utils/bridgeExtras.js +554 -0
- package/src/utils/bridgeRuntimeFixes.js +48 -0
- package/src/utils/bundledLicenses.js +26 -0
- package/src/utils/cargoLock.js +19 -0
- package/src/utils/cargoTarget.js +21 -3
- package/src/utils/cloudAccount.js +55 -0
- package/src/utils/conanDependencies.js +66 -0
- package/src/utils/conanImport.js +58 -0
- package/src/utils/conanInstall.js +154 -0
- package/src/utils/conanProfile.js +203 -0
- package/src/utils/conanStage.js +292 -0
- package/src/utils/copyToDistSource.js +13 -0
- package/src/utils/cppDts.js +60 -15
- package/src/utils/darwinToolchain.js +11 -0
- package/src/utils/dirLock.js +137 -28
- package/src/utils/downloadAndExtractFile.js +34 -7
- package/src/utils/embindArgumentGuards.js +41 -2
- package/src/utils/embindCallArguments.js +19 -0
- package/src/utils/embindJspiQueue.js +25 -0
- package/src/utils/embindObjectLifetime.js +35 -0
- package/src/utils/execContainer.js +49 -0
- package/src/utils/headerImports.js +16 -5
- package/src/utils/hostPlatform.js +21 -0
- package/src/utils/iosToolchain.js +6 -0
- package/src/utils/isSourceCmakePackage.js +10 -0
- package/src/utils/licenseReport.js +11 -2
- package/src/utils/linuxToolchain.js +24 -0
- package/src/utils/makeTreeWritable.js +12 -0
- package/src/utils/nativeImports.js +23 -0
- package/src/utils/nativeSourceStamp.js +36 -0
- package/src/utils/nodeDevBuild.js +92 -0
- package/src/utils/packageLicense.js +103 -0
- package/src/utils/pthreadMainScript.js +14 -0
- package/src/utils/pullDockerImage.js +3 -1
- package/src/utils/relocatePrebuilt.js +119 -0
- package/src/utils/remoteClient.js +127 -0
- package/src/utils/remoteOutputs.js +39 -0
- package/src/utils/remoteRunner.js +97 -0
- package/src/utils/remoteUpload.js +134 -0
- package/src/utils/replaceFile.js +8 -0
- package/src/utils/resetCMakeCacheForSource.js +26 -0
- package/src/utils/runCargo.js +48 -51
- package/src/utils/runConan.js +257 -0
- package/src/utils/runtimeEntries.js +56 -0
- package/src/utils/rustBridgeGen.js +22 -8
- package/src/utils/rustMt.js +9 -0
- package/src/utils/rustSysroot.js +7 -5
- package/src/utils/scopedEmbind.js +22 -0
- package/src/utils/selectRunner.js +81 -0
- package/src/utils/sourceFingerprint.js +4 -3
- package/src/utils/swigInterface.js +70 -4
- package/src/utils/systemConfig.js +13 -0
- package/src/utils/systemKeys.js +35 -3
- package/src/utils/targets.js +60 -4
- package/src/utils/toolchainNotices.js +5 -0
- package/src/utils/wasiHttp.js +50 -0
- package/src/utils/wasiToolchain.js +14 -1
- package/src/utils/windowsToolchain.js +18 -0
- package/vitest.config.js +11 -0
package/src/actions/createLib.js
CHANGED
|
@@ -10,8 +10,13 @@ import triggerExtensions from './extensions.js';
|
|
|
10
10
|
import state from '../state/index.js';
|
|
11
11
|
import logger from '../utils/logger.js';
|
|
12
12
|
import { WASI_EMULATION_LIBS, WASI_LINK_LIBS, wasiCFlags, wasiCxxFlags } from '../utils/wasiToolchain.js';
|
|
13
|
+
import { stageWasiHttpInclude, wasiHttpHeader } from '../utils/wasiHttp.js';
|
|
13
14
|
import { getFilesFingerprint, getContentHash } from '../utils/hash.js';
|
|
14
15
|
import { withDependencyBridges } from '../utils/dependencyBridges.js';
|
|
16
|
+
import { targetArchiveFlags } from '../utils/archiveFlags.js';
|
|
17
|
+
import { conanInputsOf } from '../utils/conanDependencies.js';
|
|
18
|
+
import { nativeSourceStampChanged, writeNativeSourceStamp } from '../utils/nativeSourceStamp.js';
|
|
19
|
+
import resetCMakeCacheForSource from '../utils/resetCMakeCacheForSource.js';
|
|
15
20
|
|
|
16
21
|
const cpuCount = Math.max(1, os.cpus().length - 1);
|
|
17
22
|
const sharedPlatforms = ['android'];
|
|
@@ -29,6 +34,11 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
29
34
|
const platformPrefix = `${fileType ? `${fileType}-` : ''}${buildType}`;
|
|
30
35
|
const libdir = `${state.config.paths.build}/${platformPrefix}/prebuilt/${target.path}`;
|
|
31
36
|
const buildPath = `${state.config.paths.build}/${platformPrefix}/${target.path}`;
|
|
37
|
+
const sourceDirs = options.buildSource ? state.config.paths.native : [];
|
|
38
|
+
const sourcesChanged = nativeSourceStampChanged(`${libdir}/lib`, sourceDirs);
|
|
39
|
+
// Automatically loaded cargo packages have no CMake dependency paths.
|
|
40
|
+
const depPaths = state.config.allDependencyPaths?.[target.path];
|
|
41
|
+
const sourceReplacements = state.config.build?.sourceReplaceList?.(target, depPaths) ?? [];
|
|
32
42
|
// wasi configure tool links need the runtime stubs; compiled below, referenced from LIBS.
|
|
33
43
|
const wasiStubObj = `${buildPath}/crossbind-wasi-stubs.o`;
|
|
34
44
|
|
|
@@ -40,10 +50,20 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
40
50
|
const fingerprintChanged = fingerprint !== null
|
|
41
51
|
&& (!fs.existsSync(fingerprintFile) || fs.readFileSync(fingerprintFile, { encoding: 'utf8' }) !== fingerprint);
|
|
42
52
|
|
|
43
|
-
// Config emccFlags feed compile-time state too (CROSSBIND_JSPI below), so a flag change must miss this cache
|
|
53
|
+
// Config emccFlags feed compile-time state too (CROSSBIND_JSPI below), so a flag change must miss this cache, and so
|
|
54
|
+
// does a compile option a dependency declares (Lerc's LERC_STATIC on Windows). Without one the hash stays as it was.
|
|
44
55
|
const configEmccFlags = getData('binary', target)?.emccFlags || [];
|
|
56
|
+
const compileOptions = getData('cmake', target)?.compileOptions || [];
|
|
45
57
|
const flagsFingerprintFile = `${libdir}/crossbind-emccflags.fingerprint`;
|
|
46
|
-
const
|
|
58
|
+
const conanInputs = conanInputsOf(state.config);
|
|
59
|
+
const compileInputs = compileOptions.length ? { emccFlags: configEmccFlags, compileOptions } : configEmccFlags;
|
|
60
|
+
const flagsInputs = sourceReplacements.length ? { compileInputs, sourceReplacements } : compileInputs;
|
|
61
|
+
const keyInputs = conanInputs ? [flagsInputs, conanInputs] : flagsInputs;
|
|
62
|
+
// An app's wasi source compiles against <crossbind/http.h>, so a new one misses this cache too.
|
|
63
|
+
const httpHeader = target.platform === 'wasi' && !state.config.build.withBuildConfig
|
|
64
|
+
? getFilesFingerprint([wasiHttpHeader(state.config.paths.cli)])
|
|
65
|
+
: null;
|
|
66
|
+
const flagsFingerprint = getContentHash(JSON.stringify(httpHeader ? [keyInputs, httpHeader] : keyInputs));
|
|
47
67
|
const flagsChanged = !fs.existsSync(flagsFingerprintFile)
|
|
48
68
|
|| fs.readFileSync(flagsFingerprintFile, { encoding: 'utf8' }) !== flagsFingerprint;
|
|
49
69
|
|
|
@@ -65,7 +85,7 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
65
85
|
return changed;
|
|
66
86
|
}
|
|
67
87
|
|
|
68
|
-
if (!options.force && !fingerprintChanged && !flagsChanged && fs.existsSync(`${libdir}/lib`)) {
|
|
88
|
+
if (!options.force && !sourcesChanged && !fingerprintChanged && !flagsChanged && fs.existsSync(`${libdir}/lib`)) {
|
|
69
89
|
logger.cachedStep(target, fileType);
|
|
70
90
|
return false;
|
|
71
91
|
}
|
|
@@ -79,7 +99,6 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
79
99
|
|
|
80
100
|
const buildEnv = { params: [] };
|
|
81
101
|
let buildParams;
|
|
82
|
-
const depPaths = state.config.allDependencyPaths[target.path];
|
|
83
102
|
if (state.config.build.withBuildConfig) {
|
|
84
103
|
const { getBuildParams, getExtraLibs } = state.config.build;
|
|
85
104
|
buildEnv.console = true;
|
|
@@ -106,31 +125,20 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
106
125
|
const extraLibs = getExtraLibs ? getExtraLibs(target) : [];
|
|
107
126
|
|
|
108
127
|
triggerExtensions('createLib', 'setFlagWithBuildConfig', [buildEnv, cFlags, ldFlags]);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (target.platform === 'wasm') {
|
|
115
|
-
cFlags.push('-msimd128');
|
|
116
|
-
ldFlags.push('-msimd128');
|
|
117
|
-
}
|
|
128
|
+
const archiveFlags = targetArchiveFlags(target);
|
|
129
|
+
cFlags.push(...archiveFlags);
|
|
130
|
+
ldFlags.push(...archiveFlags);
|
|
118
131
|
|
|
119
132
|
if (isJspiTarget) {
|
|
120
133
|
cFlags.push('-DCROSSBIND_JSPI');
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
if (target.platform === 'wasm' && target.arch === 'wasm64') {
|
|
124
|
-
cFlags.push('-sMEMORY64=1');
|
|
125
|
-
ldFlags.push('-sMEMORY64=1');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
136
|
buildEnv.params.push('-e', `CFLAGS=${cFlags.join(' ')}`);
|
|
129
137
|
buildEnv.params.push('-e', `CXXFLAGS=${cFlags.join(' ')}`);
|
|
130
138
|
buildEnv.params.push('-e', `LDFLAGS=${ldFlags.join(' ')} ${extraLibs.join(' ')}`);
|
|
131
139
|
// Emulation archives must land in LIBS (after the objects), not LDFLAGS.
|
|
132
140
|
const wasiStubLib = state.config.build?.buildType === 'configure' ? `${wasiStubObj} ` : '';
|
|
133
|
-
const wasiLibs = target.platform === 'wasi' ? ` ${wasiStubLib}${WASI_EMULATION_LIBS.join(' ')}` : '';
|
|
141
|
+
const wasiLibs = target.platform === 'wasi' ? ` ${wasiStubLib}${WASI_EMULATION_LIBS.join(' ')} -Wl,--wrap=lseek` : '';
|
|
134
142
|
buildEnv.params.push('-e', `LIBS=${dependLibs} ${extraLibs.join(' ')}${wasiLibs}`);
|
|
135
143
|
|
|
136
144
|
let configBuildEnv = state.config.build.env;
|
|
@@ -141,26 +149,19 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
141
149
|
buildEnv.params.push('-e', e);
|
|
142
150
|
});
|
|
143
151
|
} else {
|
|
144
|
-
|
|
152
|
+
// The source of an http build includes <crossbind/http.h>.
|
|
153
|
+
const headerDirs = target.platform === 'wasi'
|
|
154
|
+
? [...(options.headerDirs ?? []), stageWasiHttpInclude(state.config.paths.cli, `${buildPath}/crossbind-wasi-http`)]
|
|
155
|
+
: options.headerDirs;
|
|
156
|
+
buildParams = getCmakeParameters(target, { ...options, nativeGlob, headerDirs });
|
|
145
157
|
|
|
146
158
|
triggerExtensions('createLib', 'setFlagWithoutBuildConfig', [buildEnv]);
|
|
147
159
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
buildEnv.params.push('-e', `
|
|
151
|
-
buildEnv.params.push('-e', `
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (target.platform === 'wasm') {
|
|
155
|
-
buildEnv.params.push('-e', `CFLAGS=-msimd128`);
|
|
156
|
-
buildEnv.params.push('-e', `CXXFLAGS=-msimd128`);
|
|
157
|
-
buildEnv.params.push('-e', `LDFLAGS=-msimd128`);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (target.platform === 'wasm' && target.arch === 'wasm64') {
|
|
161
|
-
buildEnv.params.push('-e', `CFLAGS=-sMEMORY64=1`);
|
|
162
|
-
buildEnv.params.push('-e', `CXXFLAGS=-sMEMORY64=1`);
|
|
163
|
-
buildEnv.params.push('-e', `LDFLAGS=-sMEMORY64=1`);
|
|
160
|
+
const archiveFlags = targetArchiveFlags(target).join(' ');
|
|
161
|
+
if (archiveFlags) {
|
|
162
|
+
buildEnv.params.push('-e', `CFLAGS=${archiveFlags}`);
|
|
163
|
+
buildEnv.params.push('-e', `CXXFLAGS=${archiveFlags}`);
|
|
164
|
+
buildEnv.params.push('-e', `LDFLAGS=${archiveFlags}`);
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
if (isJspiTarget) {
|
|
@@ -171,7 +172,17 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
171
172
|
|
|
172
173
|
logger.startStep(target, fileType);
|
|
173
174
|
const t0 = performance.now();
|
|
174
|
-
|
|
175
|
+
let cmakeDir = state.config.build.withBuildConfig ? `${state.config.paths.build}/source` : state.config.paths.cmakeDir;
|
|
176
|
+
if (state.config.build?.buildType !== 'configure' && sourceReplacements.length) {
|
|
177
|
+
// Patches belong to a target's copy: a platform-specific change must not alter another target's inputs.
|
|
178
|
+
const patchedSource = `${buildPath}/crossbind-source`;
|
|
179
|
+
fs.mkdirSync(buildPath, { recursive: true });
|
|
180
|
+
fs.cpSync(cmakeDir, patchedSource, { recursive: true, preserveTimestamps: true });
|
|
181
|
+
sourceReplacements.forEach(({ regex, replacement, paths }) => {
|
|
182
|
+
replace({ regex, replacement, paths: paths.map((p) => `${patchedSource}/${p}`), recursive: false, silent: true });
|
|
183
|
+
});
|
|
184
|
+
cmakeDir = patchedSource;
|
|
185
|
+
}
|
|
175
186
|
|
|
176
187
|
if (state.config.build?.beforeRun) {
|
|
177
188
|
const dataList = state.config.build?.beforeRun(cmakeDir);
|
|
@@ -191,11 +202,24 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
191
202
|
], platformPrefix, target, { console: buildEnv.console });
|
|
192
203
|
};
|
|
193
204
|
|
|
194
|
-
|
|
205
|
+
// A configure build's make install runs coreutils install, whose chmod on a file it has just written
|
|
206
|
+
// Docker Desktop's file sharing now and then refuses; the wrapper finishes it by path. Staged like stubs.c.
|
|
207
|
+
const stageInstall = () => {
|
|
208
|
+
const staged = `${buildPath}/crossbind-install.sh`;
|
|
209
|
+
fs.mkdirSync(buildPath, { recursive: true });
|
|
210
|
+
fs.copyFileSync(`${state.config.paths.cli}/assets/configure/install.sh`, staged);
|
|
211
|
+
fs.chmodSync(staged, 0o755);
|
|
212
|
+
return staged;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
if (!options.bypassCmake || sourcesChanged) {
|
|
195
216
|
if (state.config.build?.buildType === 'configure') {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
217
|
+
// make compares times, so the tree starts afresh with the extracted times: no source then looks newer
|
|
218
|
+
// than what it generates, and no object of an earlier build looks newer than its source.
|
|
219
|
+
fs.rmSync(buildPath, { recursive: true, force: true });
|
|
220
|
+
fs.cpSync(cmakeDir, buildPath, { recursive: true, preserveTimestamps: true });
|
|
221
|
+
if (sourceReplacements.length) {
|
|
222
|
+
sourceReplacements.forEach(({ regex, replacement, paths }) => {
|
|
199
223
|
replace({
|
|
200
224
|
regex, replacement, paths: paths.map((p) => `${buildPath}/${p}`), recursive: false, silent: true,
|
|
201
225
|
});
|
|
@@ -216,6 +240,7 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
216
240
|
stageWasiStubs();
|
|
217
241
|
buildParams.push(`-DCMAKE_EXE_LINKER_FLAGS=${[...wasiCxxFlags(), wasiStubObj, ...WASI_LINK_LIBS].join(' ')}`);
|
|
218
242
|
}
|
|
243
|
+
resetCMakeCacheForSource(buildPath, cmakeDir, target);
|
|
219
244
|
run(null, [
|
|
220
245
|
target.platform === 'ios' && state.config.build?.useIOSCMake ? 'ios-cmake' : 'cmake', cmakeDir,
|
|
221
246
|
`-DCMAKE_BUILD_TYPE=${buildType}`,
|
|
@@ -232,8 +257,10 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
232
257
|
// Some upstream makefiles race when one -j invocation carries multiple goals (openssl's
|
|
233
258
|
// install builds apps twice); recipes can split phases via build.makePhases.
|
|
234
259
|
const makePhases = state.config.build?.makePhases || [['install']];
|
|
260
|
+
// A make command-line variable beats the Makefile's own INSTALL, autosetup's (sqlite3) included.
|
|
261
|
+
const installParams = state.config.build?.buildType === 'configure' ? [`INSTALL=${stageInstall()}`] : [];
|
|
235
262
|
makePhases.forEach((phase) => {
|
|
236
|
-
run(null, ['make', `-j${cpuCount}`, ...phase], platformPrefix, target, { console: buildEnv.console });
|
|
263
|
+
run(null, ['make', `-j${cpuCount}`, ...installParams, ...phase], platformPrefix, target, { console: buildEnv.console, exclusive: true });
|
|
237
264
|
});
|
|
238
265
|
}
|
|
239
266
|
const t2 = performance.now();
|
|
@@ -247,6 +274,7 @@ export default function createLib(target, fileType, options = {}) {
|
|
|
247
274
|
fs.writeFileSync(fingerprintFile, fingerprint);
|
|
248
275
|
}
|
|
249
276
|
fs.writeFileSync(flagsFingerprintFile, flagsFingerprint);
|
|
277
|
+
writeNativeSourceStamp(`${libdir}/lib`, sourceDirs);
|
|
250
278
|
// Callers can force the final link when a lib actually rebuilt.
|
|
251
279
|
return true;
|
|
252
280
|
}
|
package/src/actions/getData.js
CHANGED
|
@@ -3,7 +3,10 @@ import state from '../state/index.js';
|
|
|
3
3
|
import { getFilteredTargetSpec, getBuildTargets } from './target.js';
|
|
4
4
|
|
|
5
5
|
function getRecursiveData(obj, config, dependency, field, target) {
|
|
6
|
-
|
|
6
|
+
// An app may list a port's wasm, Android and iOS packages together. One with no prebuilt for the target
|
|
7
|
+
// drops out of the link, and its data stays out with it.
|
|
8
|
+
const skipsTarget = field === 'data' && dependency !== config && dependency.functions?.isEnabled && !dependency.functions.isEnabled(target);
|
|
9
|
+
const entryArray = skipsTarget ? [] : getFilteredTargetSpec(dependency?.targetSpecs, target).map(s => s[field]);
|
|
7
10
|
const entries = Object.assign({}, ...entryArray);
|
|
8
11
|
Object.entries(entries).forEach(([dKey, value]) => {
|
|
9
12
|
if (field === 'data') {
|
|
@@ -27,7 +30,8 @@ function getRecursiveData(obj, config, dependency, field, target) {
|
|
|
27
30
|
obj[dKey] = [...(obj[dKey] || []), ...value];
|
|
28
31
|
} else if (typeof value === 'object') {
|
|
29
32
|
obj[dKey] = { ...(obj[dKey] || {}), ...value };
|
|
30
|
-
} else {
|
|
33
|
+
} else if (!Object.hasOwn(obj, dKey)) {
|
|
34
|
+
// The app comes before its dependencies and a package before its own, so the nearest value stays.
|
|
31
35
|
obj[dKey] = value;
|
|
32
36
|
}
|
|
33
37
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import state from '../state/index.js';
|
|
2
|
+
import getData from './getData.js';
|
|
3
|
+
|
|
4
|
+
export default function getEnv(target) {
|
|
5
|
+
return Object.fromEntries(Object.entries(getData('env', target))
|
|
6
|
+
.map(([key, value]) => [key, typeof value === 'function' ? value(state, target) : value]));
|
|
7
|
+
}
|
|
@@ -3,9 +3,10 @@ import getDependLibs from './getDependLibs.js';
|
|
|
3
3
|
import state from '../state/index.js';
|
|
4
4
|
import buildAppRustCrates from '../utils/appRustCrates.js';
|
|
5
5
|
|
|
6
|
-
// The archives of a final link (wasm
|
|
7
|
-
// keepFlag spells "keep this symbol" for the target's linker
|
|
8
|
-
|
|
6
|
+
// The archives of a final link (wasm, Node-API addon or native executable) and which of them must
|
|
7
|
+
// survive whole. keepFlag spells "keep this symbol" for the target's linker; a native executable
|
|
8
|
+
// binds nothing, so it links no bridge.
|
|
9
|
+
export default function getLinkInputs(target, { keepFlag, withBridge = true }) {
|
|
9
10
|
const buildType = target.buildType === 'release' ? 'Release' : 'Debug';
|
|
10
11
|
const { build, output, cache } = state.config.paths;
|
|
11
12
|
const { name } = state.config.general;
|
|
@@ -23,7 +24,7 @@ export default function getLinkInputs(target, { keepFlag }) {
|
|
|
23
24
|
...getDependLibs(target),
|
|
24
25
|
...appRustLibs,
|
|
25
26
|
sourceLibCandidates.find((lib) => fs.existsSync(lib)) ?? sourceLibCandidates[0],
|
|
26
|
-
`${build}/Bridge-${buildType}/${target.path}/lib${name}.a
|
|
27
|
+
...(withBridge ? [`${build}/Bridge-${buildType}/${target.path}/lib${name}.a`] : []),
|
|
27
28
|
];
|
|
28
29
|
|
|
29
30
|
// By default only the Bridge archive is kept whole (see linkLayout.js) so the linker
|
|
@@ -38,8 +39,14 @@ export default function getLinkInputs(target, { keepFlag }) {
|
|
|
38
39
|
// with their keep symbol pinned (keepFlag pulls just the registration object); manual-bindings
|
|
39
40
|
// crates have no keep symbol and fall back to whole-archive - safe only while they are the
|
|
40
41
|
// single loaded Rust archive.
|
|
42
|
+
// A COFF link cannot load the super whole either: two crates in it may import one DLL through raw-dylib (std
|
|
43
|
+
// and getrandom both take ProcessPrng from bcryptprimitives.dll), and lld fails on the repeated import tables.
|
|
44
|
+
// There it links like a package bridge, through its keep symbol.
|
|
41
45
|
const rustKeepFlags = [];
|
|
42
|
-
if (appRustLibs.length > 0)
|
|
46
|
+
if (appRustLibs.length > 0) {
|
|
47
|
+
if (target.platform === 'win32') rustKeepFlags.push(keepFlag('crossbind_app_super'));
|
|
48
|
+
else wholeArchiveNames.add('crossbind_app_super');
|
|
49
|
+
}
|
|
43
50
|
const depends = state.config.dependencyParameters.getCmakeDepends(target);
|
|
44
51
|
depends.forEach((dep) => {
|
|
45
52
|
if (dep.export.wholeArchive === true) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import p from 'node:path';
|
|
3
3
|
import state from '../state/index.js';
|
|
4
|
+
import { nativeSourceStampChanged } from '../utils/nativeSourceStamp.js';
|
|
4
5
|
|
|
5
6
|
function newestMtimeInDir(dir) {
|
|
6
7
|
let newest = 0;
|
|
@@ -22,6 +23,7 @@ function newestMtimeInDir(dir) {
|
|
|
22
23
|
// (bundler plugins: the built js) or a directory (CLI lib cache: the staged prebuilt dir).
|
|
23
24
|
export function isNativeSourceNewerThan(artifactPath) {
|
|
24
25
|
if (!fs.existsSync(artifactPath)) return false;
|
|
26
|
+
if (nativeSourceStampChanged(artifactPath, state.config.paths.native)) return true;
|
|
25
27
|
const stat = fs.statSync(artifactPath);
|
|
26
28
|
const artifactMtime = stat.isDirectory() ? newestMtimeInDir(artifactPath) : stat.mtimeMs;
|
|
27
29
|
for (const dir of state.config.paths.native) {
|
package/src/actions/licenses.js
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
3
4
|
import state from '../state/index.js';
|
|
4
5
|
import loadJs from '../utils/loadJs.js';
|
|
5
6
|
import { isCopyleft } from '../utils/licenseReport.js';
|
|
6
7
|
import familyManifestOf from '../utils/familyManifest.js';
|
|
7
8
|
import { wasiToolchainIdentity } from '../utils/provenance.js';
|
|
8
9
|
import { resolveWasiSdkPath } from '../utils/wasiToolchain.js';
|
|
10
|
+
import resolveEmbindNapiRoot, { resolveEmbindJsiRoot } from '../utils/resolveEmbindNapi.js';
|
|
11
|
+
import { shippedBundledLicense } from '../utils/bundledLicenses.js';
|
|
12
|
+
import toolchainNoticesDir from '../utils/toolchainNotices.js';
|
|
9
13
|
|
|
10
14
|
const LEGACY_LICENSE_FILES = ['LICENSE', 'LICENSE.md', 'LICENSE.txt', 'COPYING'];
|
|
11
15
|
const WASI_LIBC_LICENSE = 'Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT';
|
|
16
|
+
const CROSSBIND_ROOT = fileURLToPath(new URL('../..', import.meta.url));
|
|
17
|
+
const MISSING_TEXT = '(missing: build the package once to extract the upstream source)';
|
|
18
|
+
|
|
19
|
+
function packageLicenseFile(dir) {
|
|
20
|
+
return LEGACY_LICENSE_FILES.map((name) => path.join(dir, name)).find((file) => fs.existsSync(file));
|
|
21
|
+
}
|
|
12
22
|
|
|
13
23
|
// License texts come from any extracted upstream source tree of the family (variants share the tarball).
|
|
14
24
|
function findSourceDir(familyDir) {
|
|
@@ -20,20 +30,21 @@ function findSourceDir(familyDir) {
|
|
|
20
30
|
return null;
|
|
21
31
|
}
|
|
22
32
|
|
|
23
|
-
function readLicenseTexts(
|
|
33
|
+
function readLicenseTexts(files, locate) {
|
|
24
34
|
const texts = (files || []).map((file) => {
|
|
25
|
-
const filePath =
|
|
35
|
+
const filePath = locate(file);
|
|
26
36
|
const body = filePath && fs.existsSync(filePath)
|
|
27
37
|
? fs.readFileSync(filePath, 'utf8').trim()
|
|
28
|
-
:
|
|
38
|
+
: MISSING_TEXT;
|
|
29
39
|
return `=== ${file} ===\n\n${body}`;
|
|
30
40
|
});
|
|
31
41
|
return texts.length > 0 ? texts.join('\n\n') : null;
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
// Vendored copies compiled into the artifact (recipe `bundled` map, keyed by platform)
|
|
35
|
-
// become first-class notice/SBOM rows with texts from the family source tree
|
|
36
|
-
|
|
45
|
+
// become first-class notice/SBOM rows with texts from the family source tree, or from the dist
|
|
46
|
+
// of whichever package of the family shipped them.
|
|
47
|
+
async function bundledRowsOf(node, platform, familyProjects) {
|
|
37
48
|
const recipe = await loadJs(node.paths.project, 'crossbind.build');
|
|
38
49
|
const entries = recipe?.bundled?.[platform];
|
|
39
50
|
if (!entries || entries.length === 0) return [];
|
|
@@ -50,7 +61,9 @@ async function bundledRowsOf(node, platform) {
|
|
|
50
61
|
licenseNotes: `${entry.notes ? `${entry.notes}; ` : ''}vendored inside ${node.general.name} ${node.package?.nativeVersion || ''}`.trim(),
|
|
51
62
|
sha256: null,
|
|
52
63
|
sourceUrl: null,
|
|
53
|
-
licenseText: readLicenseTexts(
|
|
64
|
+
licenseText: readLicenseTexts(entry.files, (file) => (sourceDir
|
|
65
|
+
? path.join(sourceDir, file)
|
|
66
|
+
: shippedBundledLicense(familyProjects, entry.name, file))),
|
|
54
67
|
isCopyleft: isCopyleft(entry.license),
|
|
55
68
|
}));
|
|
56
69
|
}
|
|
@@ -90,6 +103,16 @@ function wasiToolchainRows() {
|
|
|
90
103
|
];
|
|
91
104
|
}
|
|
92
105
|
|
|
106
|
+
// The notices a build copied out of its toolchain image (buildNode does it for Windows addons).
|
|
107
|
+
function toolchainNoticeTexts(platform) {
|
|
108
|
+
const build = state.config?.paths?.build;
|
|
109
|
+
const dir = build ? path.join(build, toolchainNoticesDir(platform)) : null;
|
|
110
|
+
if (!dir || !fs.existsSync(dir)) return null;
|
|
111
|
+
const texts = fs.readdirSync(dir).sort()
|
|
112
|
+
.map((file) => `=== ${file} ===\n\n${fs.readFileSync(path.join(dir, file), 'utf8').trim()}`);
|
|
113
|
+
return texts.length > 0 ? texts.join('\n\n') : null;
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
// A Linux or Windows addon carries its C++ runtime; a Windows one also carries parts of the
|
|
94
117
|
// mingw-w64 runtime and winpthreads, whose licenses ask for their notices in binary distributions.
|
|
95
118
|
// A macOS addon uses the system's C++ runtime.
|
|
@@ -106,7 +129,7 @@ function addonToolchainRows(platform) {
|
|
|
106
129
|
sourceUrl: 'https://github.com/llvm/llvm-project',
|
|
107
130
|
};
|
|
108
131
|
const llvmText = 'license text: https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT';
|
|
109
|
-
if (platform === 'linux') {
|
|
132
|
+
if (platform === 'linux' || platform === 'linuxmusl') {
|
|
110
133
|
return [{ ...llvmRuntimes, licenseNotes: `libc++ and libc++abi of the linux toolchain image, statically linked into the addon; ${llvmText}` }];
|
|
111
134
|
}
|
|
112
135
|
const mingwLicense = 'LicenseRef-MinGW-w64-runtime';
|
|
@@ -119,8 +142,57 @@ function addonToolchainRows(platform) {
|
|
|
119
142
|
licenseDeclared: mingwLicense,
|
|
120
143
|
sourceUrl: 'https://github.com/mingw-w64/mingw-w64',
|
|
121
144
|
licenseNotes: 'the mingw-w64 startup code and winpthreads, statically linked into the addon; a binary distribution includes their notices: https://github.com/mingw-w64/mingw-w64/blob/master/COPYING.MinGW-w64-runtime/COPYING.MinGW-w64-runtime.txt and https://github.com/mingw-w64/mingw-w64/blob/master/mingw-w64-libraries/winpthreads/COPYING (the windows image carries both in /opt/licenses/llvm-mingw)',
|
|
145
|
+
licenseText: toolchainNoticeTexts('win32'),
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// A Node-API addon compiles in crossbind's runtime: embind-jsi, which adapts Emscripten's embind,
|
|
151
|
+
// and node-api-jsi. The loader bundles the JavaScript half of the same runtime.
|
|
152
|
+
function nodeRuntimeRows({ isAddon }) {
|
|
153
|
+
const versionOf = (dir) => JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')).version;
|
|
154
|
+
const textOf = (file) => fs.readFileSync(file, 'utf8').trim();
|
|
155
|
+
const shared = {
|
|
156
|
+
nativeVersion: null, sha256: null, licenseSelected: null, isCopyleft: false,
|
|
157
|
+
};
|
|
158
|
+
const jsiRoot = resolveEmbindJsiRoot();
|
|
159
|
+
const rows = [
|
|
160
|
+
{
|
|
161
|
+
...shared,
|
|
162
|
+
name: 'crossbind',
|
|
163
|
+
npmName: 'crossbind',
|
|
164
|
+
version: versionOf(CROSSBIND_ROOT),
|
|
165
|
+
license: 'MIT',
|
|
166
|
+
licenseDeclared: 'MIT',
|
|
167
|
+
sourceUrl: 'https://github.com/crossbind/crossbind',
|
|
168
|
+
licenseNotes: 'the embind-jsi and embind-napi runtime and the bindings crossbind generates',
|
|
169
|
+
licenseText: textOf(path.join(CROSSBIND_ROOT, 'LICENSE')),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
...shared,
|
|
173
|
+
name: 'emscripten-embind',
|
|
174
|
+
npmName: '@crossbind/core-embind-jsi',
|
|
175
|
+
version: versionOf(jsiRoot),
|
|
176
|
+
license: 'MIT OR NCSA',
|
|
177
|
+
licenseDeclared: 'MIT OR NCSA',
|
|
178
|
+
sourceUrl: 'https://github.com/emscripten-core/emscripten',
|
|
179
|
+
licenseNotes: "Emscripten's embind, adapted by @crossbind/core-embind-jsi",
|
|
180
|
+
licenseText: textOf(path.join(jsiRoot, 'LICENSE')),
|
|
122
181
|
},
|
|
123
182
|
];
|
|
183
|
+
if (!isAddon) return rows;
|
|
184
|
+
const napiRoot = resolveEmbindNapiRoot();
|
|
185
|
+
return [...rows, {
|
|
186
|
+
...shared,
|
|
187
|
+
name: 'node-api-jsi',
|
|
188
|
+
npmName: '@crossbind/core-embind-napi',
|
|
189
|
+
version: versionOf(napiRoot),
|
|
190
|
+
license: 'MIT',
|
|
191
|
+
licenseDeclared: 'MIT',
|
|
192
|
+
sourceUrl: 'https://github.com/microsoft/node-api-jsi',
|
|
193
|
+
licenseNotes: 'JSI over Node-API, with the local patches @crossbind/core-embind-napi lists',
|
|
194
|
+
licenseText: textOf(path.join(napiRoot, 'third_party', 'node-api-jsi', 'LICENSE')),
|
|
195
|
+
}];
|
|
124
196
|
}
|
|
125
197
|
|
|
126
198
|
async function buildRow(node) {
|
|
@@ -143,12 +215,18 @@ async function buildRow(node) {
|
|
|
143
215
|
let licenseText;
|
|
144
216
|
if (upstream) {
|
|
145
217
|
license = upstream.selected || upstream.declared;
|
|
146
|
-
|
|
218
|
+
const sourceDir = findSourceDir(family.dir);
|
|
219
|
+
const fromSource = sourceDir ? readLicenseTexts(upstream.files, (file) => path.join(sourceDir, file)) : null;
|
|
220
|
+
const isComplete = Boolean(fromSource) && !fromSource.includes(MISSING_TEXT);
|
|
221
|
+
// An installed package has no extracted source, and some upstreams name no license file; the package
|
|
222
|
+
// ships the upstream text as its LICENSE either way.
|
|
223
|
+
const shipped = isComplete ? null : packageLicenseFile(node.paths.project);
|
|
224
|
+
if (isComplete) licenseText = fromSource;
|
|
225
|
+
else if (shipped) licenseText = `=== ${path.basename(shipped)} ===\n\n${fs.readFileSync(shipped, 'utf8').trim()}`;
|
|
226
|
+
else licenseText = readLicenseTexts(upstream.files, () => null);
|
|
147
227
|
} else {
|
|
148
228
|
license = manifest?.license || null;
|
|
149
|
-
const legacy =
|
|
150
|
-
.map((name) => `${node.paths.project}/${name}`)
|
|
151
|
-
.find((file) => fs.existsSync(file));
|
|
229
|
+
const legacy = packageLicenseFile(node.paths.project);
|
|
152
230
|
licenseText = legacy ? fs.readFileSync(legacy, 'utf8') : null;
|
|
153
231
|
}
|
|
154
232
|
|
|
@@ -168,23 +246,69 @@ async function buildRow(node) {
|
|
|
168
246
|
};
|
|
169
247
|
}
|
|
170
248
|
|
|
249
|
+
// A Conan package's facts come from its recipe, and its texts from the licenses/ its package ships.
|
|
250
|
+
function conanRow(node) {
|
|
251
|
+
const { conan } = node.general;
|
|
252
|
+
const licenseDir = `${node.paths.project}/licenses`;
|
|
253
|
+
const files = fs.existsSync(licenseDir)
|
|
254
|
+
? fs.readdirSync(licenseDir, { recursive: true, withFileTypes: true }).filter((entry) => entry.isFile())
|
|
255
|
+
: [];
|
|
256
|
+
const texts = files.map((entry) => path.join(entry.parentPath, entry.name)).sort()
|
|
257
|
+
.map((file) => `=== ${path.relative(licenseDir, file)} ===\n\n${fs.readFileSync(file, 'utf8').trim()}`);
|
|
258
|
+
return {
|
|
259
|
+
name: conan.name,
|
|
260
|
+
npmName: null,
|
|
261
|
+
version: null,
|
|
262
|
+
nativeVersion: conan.version,
|
|
263
|
+
license: conan.license,
|
|
264
|
+
licenseDeclared: null,
|
|
265
|
+
licenseSelected: null,
|
|
266
|
+
licenseNotes: `built from the ConanCenter recipe ${conan.ref}`,
|
|
267
|
+
sha256: conan.source?.sha256 ?? null,
|
|
268
|
+
sourceUrl: conan.source?.url ?? conan.homepage ?? null,
|
|
269
|
+
licenseText: texts.length > 0 ? texts.join('\n\n') : null,
|
|
270
|
+
isCopyleft: isCopyleft(conan.license),
|
|
271
|
+
purl: `pkg:conan/${conan.name}@${conan.version}`,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Conan packages join the graph from the stage a build makes; without it they would be left out unseen.
|
|
276
|
+
function assertConanStaged() {
|
|
277
|
+
const attached = new Set(state.config.allDependencies.filter((d) => d.general.conan).map((d) => d.general.conan.name));
|
|
278
|
+
const missing = Object.keys(state.config.conanDependencies ?? {}).filter((name) => !attached.has(name));
|
|
279
|
+
if (missing.length > 0) {
|
|
280
|
+
throw new Error(`crossbind: the Conan packages ${missing.join(', ')} are not installed for the current conanDependencies, so their rows would be missing. Build the project first.`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
171
284
|
// platform (optional, e.g. 'wasi') additionally includes what that platform's
|
|
172
285
|
// artifact statically links beyond the package graph: recipe-declared vendored
|
|
173
|
-
// copies and the toolchain runtime.
|
|
174
|
-
|
|
175
|
-
|
|
286
|
+
// copies and the toolchain runtime. runtimeEnv 'node' adds the crossbind runtime an
|
|
287
|
+
// addon of that platform compiles in, or without a platform the one its loader bundles.
|
|
288
|
+
export default async function collectLicenseRows(platform = null, { runtimeEnv = null } = {}) {
|
|
289
|
+
assertConanStaged();
|
|
290
|
+
// The root package is a component too when it is a port: leaf -wasi packages have no deps but ship
|
|
291
|
+
// their own upstream. A dependency that is no port, such as a library a project publishes, is one by
|
|
292
|
+
// its package name; a private package is the project's own code, like the app.
|
|
293
|
+
const keyOf = (node) => (node.general.conan ? `conan:${node.general.conan.name}` : node.general.alias?.package ?? node.package?.name);
|
|
176
294
|
const nodes = [state.config, ...state.config.allDependencies]
|
|
177
|
-
.filter((node) => node?.
|
|
295
|
+
.filter((node, index) => node?.paths?.project
|
|
296
|
+
&& (node.general?.alias?.package || node.general?.conan || (index > 0 && node.package?.name && !node.package.private)));
|
|
178
297
|
const rows = [];
|
|
179
298
|
const seen = new Set();
|
|
180
299
|
for (const node of nodes) {
|
|
181
|
-
const key = node
|
|
300
|
+
const key = keyOf(node);
|
|
182
301
|
if (seen.has(key)) continue;
|
|
183
302
|
seen.add(key);
|
|
184
|
-
rows.push(await buildRow(node));
|
|
185
|
-
|
|
303
|
+
rows.push(node.general.conan ? conanRow(node) : await buildRow(node));
|
|
304
|
+
// A Conan package's folder is written from what its recipe produced: nothing there is run.
|
|
305
|
+
if (platform && !node.general.conan) {
|
|
306
|
+
const familyProjects = nodes.filter((other) => keyOf(other) === key).map((other) => other.paths.project);
|
|
307
|
+
rows.push(...await bundledRowsOf(node, platform, familyProjects));
|
|
308
|
+
}
|
|
186
309
|
}
|
|
187
310
|
if (platform === 'wasi') rows.push(...wasiToolchainRows());
|
|
188
|
-
if (
|
|
311
|
+
if (['linux', 'linuxmusl', 'win32'].includes(platform)) rows.push(...addonToolchainRows(platform));
|
|
312
|
+
if (runtimeEnv === 'node') rows.push(...nodeRuntimeRows({ isAddon: platform !== null }));
|
|
189
313
|
return rows.sort((a, b) => a.name.localeCompare(b.name));
|
|
190
314
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import state, { setAllDependecyPaths } from '../state/index.js';
|
|
3
|
+
import refreshConanDependencies from '../state/refreshConanDependencies.js';
|
|
4
|
+
import installConanPackages from '../utils/conanInstall.js';
|
|
5
|
+
import { releaseTargetOf, HOST_BUILT_PLATFORMS } from '../utils/targets.js';
|
|
6
|
+
import createXCFramework from './createXCFramework.js';
|
|
7
|
+
|
|
8
|
+
const mtimeOf = (file) => fs.statSync(file, { throwIfNoEntry: false })?.mtimeMs ?? -Infinity;
|
|
9
|
+
// iOS and macOS build on a Mac alone: elsewhere the build leaves them out, and so does the staging.
|
|
10
|
+
const isBuiltHere = (target) => process.platform === 'darwin' || !HOST_BUILT_PLATFORMS.includes(target.platform);
|
|
11
|
+
|
|
12
|
+
// iOS headers and archives come out of an xcframework per library with a slice per SDK (state/index.js).
|
|
13
|
+
// One older than an archive it wraps was made before the package was staged again.
|
|
14
|
+
function makeConanXCFrameworks(iosTargets) {
|
|
15
|
+
const archiveOf = (dependency, target, name) => `${dependency.paths.output}/prebuilt/${target.path}/lib/lib${name}.a`;
|
|
16
|
+
const isFresh = (dependency, name) => mtimeOf(`${dependency.paths.project}/${name}.xcframework/Info.plist`)
|
|
17
|
+
>= Math.max(...iosTargets.map((target) => mtimeOf(archiveOf(dependency, target, name))));
|
|
18
|
+
state.config.allDependencies.filter((dependency) => dependency.general.conan).forEach((dependency) => {
|
|
19
|
+
// A library its recipe names on another platform only has no slice here.
|
|
20
|
+
const libName = dependency.export.libName.filter((name) => iosTargets.every((target) => fs.existsSync(archiveOf(dependency, target, name))));
|
|
21
|
+
if (libName.every((name) => isFresh(dependency, name))) return;
|
|
22
|
+
createXCFramework({
|
|
23
|
+
paths: { project: dependency.paths.project, output: dependency.paths.output },
|
|
24
|
+
export: { libName },
|
|
25
|
+
targetParams: {
|
|
26
|
+
platform: ['ios'], arch: iosTargets.map((target) => target.arch), runtime: ['mt'], buildType: ['release'],
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Conan packages are staged before any header is read: SWIG parses headers that include theirs, and an
|
|
33
|
+
// interface made without its includes stays that way until the header itself changes. Release trees
|
|
34
|
+
// only - a debug build links them like any prebuilt.
|
|
35
|
+
export default async function prepareConanDependencies(targets) {
|
|
36
|
+
if (Object.keys(state.config.conanDependencies ?? {}).length === 0) return;
|
|
37
|
+
// createBridgeFile reads every header for the first wasm target unless it is told otherwise, as it
|
|
38
|
+
// is by Metro, which builds the React Native bridges for the platform's own target, and by a Node
|
|
39
|
+
// build, which reads them for one of its own (nodeBridgeTarget).
|
|
40
|
+
const bridgeTarget = targets.some((target) => target.platform === 'wasm') && state.targets.find((target) => target.platform === 'wasm');
|
|
41
|
+
// One xcframework holds both SDKs, so an iOS build stages both.
|
|
42
|
+
const iosTargets = targets.some((target) => target.platform === 'ios') ? state.targets.filter((target) => target.platform === 'ios') : [];
|
|
43
|
+
const releaseTargets = [...new Map([...targets, bridgeTarget, ...iosTargets].filter(Boolean).map((target) => [
|
|
44
|
+
target.releasePath, releaseTargetOf(target),
|
|
45
|
+
])).values()].filter(isBuiltHere);
|
|
46
|
+
await installConanPackages(state.config, releaseTargets);
|
|
47
|
+
// Also when another process did the staging: this one may have loaded before the manifests existed.
|
|
48
|
+
refreshConanDependencies(state.config);
|
|
49
|
+
setAllDependecyPaths();
|
|
50
|
+
if (iosTargets.length > 0) makeConanXCFrameworks(releaseTargets.filter((target) => target.platform === 'ios'));
|
|
51
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import state from '../state/index.js';
|
|
4
|
+
import replaceFile from '../utils/replaceFile.js';
|
|
5
|
+
|
|
6
|
+
// paths.output defaults to paths.build, where a built command and its data already are; copying them onto
|
|
7
|
+
// themselves would delete them first.
|
|
8
|
+
const outputIsBuild = () => path.resolve(state.config.paths.output) === path.resolve(state.config.paths.build);
|
|
9
|
+
|
|
10
|
+
export function publishNativeCommand(target) {
|
|
11
|
+
if (outputIsBuild()) return;
|
|
12
|
+
fs.mkdirSync(state.config.paths.output, { recursive: true });
|
|
13
|
+
replaceFile(`${state.config.paths.build}/${target.commandName}`, `${state.config.paths.output}/${target.commandName}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function publishNodeAddon(target) {
|
|
17
|
+
if (outputIsBuild()) return;
|
|
18
|
+
fs.mkdirSync(state.config.paths.output, { recursive: true });
|
|
19
|
+
replaceFile(`${state.config.paths.build}/${target.addonName}`, `${state.config.paths.output}/${target.addonName}`);
|
|
20
|
+
fs.copyFileSync(`${state.config.paths.build}/${target.jsName}`, `${state.config.paths.output}/${target.jsName}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function publishWasiCommand(target) {
|
|
24
|
+
if (outputIsBuild()) return;
|
|
25
|
+
fs.copyFileSync(`${state.config.paths.build}/${target.wasmName}`, `${state.config.paths.output}/${target.wasmName}`);
|
|
26
|
+
// Copied (not renamed) so a later fingerprint cache-hit still has the
|
|
27
|
+
// build-dir tree to serve from.
|
|
28
|
+
if (fs.existsSync(`${state.config.paths.build}/data`)) {
|
|
29
|
+
fs.rmSync(`${state.config.paths.output}/data`, { recursive: true, force: true });
|
|
30
|
+
fs.cpSync(`${state.config.paths.build}/data`, `${state.config.paths.output}/data`, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
}
|