crossbind 2.0.0-beta.74 → 2.0.0-beta.76
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 -3
- package/src/actions/androidGradle.js +47 -4
- package/src/actions/buildApp.js +4 -3
- package/src/actions/buildNode.js +2 -10
- package/src/actions/buildWasm.js +20 -1
- package/src/actions/createInterface.js +266 -38
- package/src/actions/createLib.js +4 -1
- package/src/actions/getCmakeParameters.js +0 -1
- package/src/actions/licenses.js +65 -19
- package/src/actions/nativeNotices.js +43 -0
- package/src/actions/run.js +72 -6
- package/src/actions/wasmNotices.js +32 -0
- package/src/app/index.js +1 -1
- package/src/assets/js-runtime/adapters/worker-comlink.js +11 -0
- package/src/assets/toolchain-digests.json +20 -20
- package/src/integration/getDependFilePath.js +8 -1
- package/src/state/loadConfig.js +1 -1
- package/src/utils/androidToolchain.js +4 -0
- package/src/utils/bridgeRuntimeFixes.js +24 -11
- package/src/utils/embindPointerOwners.js +42 -0
- package/src/utils/sourceFingerprint.js +5 -1
- package/src/utils/swigInterface.js +22 -7
- package/src/utils/toolchainNotices.js +20 -0
- package/src/utils/typeClosure.js +170 -0
package/src/actions/licenses.js
CHANGED
|
@@ -19,7 +19,7 @@ import { ANDROIDX_WEBKIT } from './gradleProject.js';
|
|
|
19
19
|
import { extensionPackage, extensionPart } from '../utils/appExtensions.js';
|
|
20
20
|
import { declaredSources, declarationOf, dependenciesOf } from '../utils/packageSources.js';
|
|
21
21
|
import { shippedBundledLicense } from '../utils/bundledLicenses.js';
|
|
22
|
-
import toolchainNoticesDir from '../utils/toolchainNotices.js';
|
|
22
|
+
import toolchainNoticesDir, { MINGW_NOTICE, NATIVE_NOTICES, WASM_NOTICES } from '../utils/toolchainNotices.js';
|
|
23
23
|
|
|
24
24
|
const LEGACY_LICENSE_FILES = ['LICENSE', 'LICENSE.md', 'LICENSE.txt', 'COPYING'];
|
|
25
25
|
const WASI_LIBC_LICENSE = 'Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT';
|
|
@@ -53,8 +53,8 @@ function readLicenseTexts(files, locate) {
|
|
|
53
53
|
|
|
54
54
|
// Vendored copies compiled into the artifact (recipe `bundled` map, keyed by platform)
|
|
55
55
|
// become first-class notice/SBOM rows with texts from the family source tree, or from the dist
|
|
56
|
-
// of whichever package of the family shipped them.
|
|
57
|
-
async function bundledRowsOf(node, platform, familyProjects) {
|
|
56
|
+
// of whichever package of the family shipped them, and the source of the package they come in.
|
|
57
|
+
async function bundledRowsOf(node, platform, familyProjects, sourceUrl) {
|
|
58
58
|
const recipe = await loadJs(node.paths.project, 'crossbind.build');
|
|
59
59
|
const entries = recipe?.bundled?.[platform];
|
|
60
60
|
if (!entries || entries.length === 0) return [];
|
|
@@ -70,7 +70,7 @@ async function bundledRowsOf(node, platform, familyProjects) {
|
|
|
70
70
|
licenseSelected: null,
|
|
71
71
|
licenseNotes: `${entry.notes ? `${entry.notes}; ` : ''}vendored inside ${node.general.name} ${node.package?.nativeVersion || ''}`.trim(),
|
|
72
72
|
sha256: null,
|
|
73
|
-
sourceUrl
|
|
73
|
+
sourceUrl,
|
|
74
74
|
licenseText: readLicenseTexts(entry.files, (file) => (sourceDir
|
|
75
75
|
? path.join(sourceDir, file)
|
|
76
76
|
: shippedBundledLicense(familyProjects, entry.name, file))),
|
|
@@ -113,8 +113,34 @@ function wasiToolchainRows() {
|
|
|
113
113
|
];
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// What a build copied out of its toolchain image (actions/wasmNotices.js, actions/nativeNotices.js): a file's text, or a
|
|
117
|
+
// license file's under its name.
|
|
118
|
+
function toolchainNotice(platform, file) {
|
|
119
|
+
const build = state.config?.paths?.build;
|
|
120
|
+
const notice = build ? path.join(build, toolchainNoticesDir(platform), file) : null;
|
|
121
|
+
return notice && fs.existsSync(notice) ? fs.readFileSync(notice, 'utf8').trim() : null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function toolchainNoticeText(platform, file) {
|
|
125
|
+
const text = toolchainNotice(platform, file);
|
|
126
|
+
return text ? `=== ${file} ===\n\n${text}` : null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const clangVersionOf = (text) => text?.match(/clang version (\S+)/)?.[1] ?? null;
|
|
130
|
+
|
|
131
|
+
// libc++ spells 19.1.7 as 190107.
|
|
132
|
+
function libcxxVersionOf(text) {
|
|
133
|
+
const number = Number(text?.match(/_LIBCPP_VERSION\s+(\d+)/)?.[1]);
|
|
134
|
+
return number ? `${Math.floor(number / 10000)}.${Math.floor(number / 100) % 100}.${number % 100}` : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function mingwVersionOf(text) {
|
|
138
|
+
const part = (name) => text?.match(new RegExp(`__MINGW64_VERSION_${name}\\s+(\\d+)`))?.[1];
|
|
139
|
+
return part('MAJOR') ? [part('MAJOR'), part('MINOR') ?? 0, part('BUGFIX') ?? 0].join('.') : null;
|
|
140
|
+
}
|
|
141
|
+
|
|
116
142
|
// A wasm module statically links Emscripten's runtime and system libraries (its musl-based libc among them) and LLVM's
|
|
117
|
-
// C++ runtime from the same toolchain image.
|
|
143
|
+
// C++ runtime from the same toolchain image, whose texts and versions the build copied out of it.
|
|
118
144
|
function wasmToolchainRows() {
|
|
119
145
|
const shared = {
|
|
120
146
|
npmName: null, version: null, nativeVersion: null, sha256: null, licenseSelected: null, licenseText: null, isCopyleft: false,
|
|
@@ -123,35 +149,39 @@ function wasmToolchainRows() {
|
|
|
123
149
|
{
|
|
124
150
|
...shared,
|
|
125
151
|
name: 'emscripten',
|
|
152
|
+
nativeVersion: toolchainNotice('wasm', WASM_NOTICES.emscriptenVersion)?.replaceAll('"', '') || null,
|
|
126
153
|
license: 'MIT OR NCSA',
|
|
127
154
|
licenseDeclared: 'MIT OR NCSA',
|
|
128
155
|
sourceUrl: 'https://github.com/emscripten-core/emscripten',
|
|
129
156
|
licenseNotes: "Emscripten's runtime and system libraries, its musl-based libc among them, statically linked into the module; license text: https://github.com/emscripten-core/emscripten/blob/main/LICENSE",
|
|
157
|
+
licenseText: toolchainNoticeText('wasm', WASM_NOTICES.emscriptenText),
|
|
130
158
|
},
|
|
131
159
|
{
|
|
132
160
|
...shared,
|
|
133
161
|
name: 'llvm-runtimes',
|
|
134
162
|
license: 'Apache-2.0 WITH LLVM-exception',
|
|
135
163
|
licenseDeclared: 'Apache-2.0 WITH LLVM-exception',
|
|
164
|
+
nativeVersion: clangVersionOf(toolchainNotice('wasm', WASM_NOTICES.llvmVersion)),
|
|
136
165
|
sourceUrl: 'https://github.com/llvm/llvm-project',
|
|
137
166
|
licenseNotes: "libc++, libc++abi and compiler-rt of Emscripten's LLVM, statically linked into the module; license text: https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT",
|
|
167
|
+
licenseText: toolchainNoticeText('wasm', WASM_NOTICES.llvmText),
|
|
138
168
|
},
|
|
139
169
|
];
|
|
140
170
|
}
|
|
141
171
|
|
|
142
|
-
// The notices a build copied out of its toolchain image
|
|
143
|
-
function toolchainNoticeTexts(platform) {
|
|
172
|
+
// The notices of one runtime a build copied out of its toolchain image, each under its file's name.
|
|
173
|
+
function toolchainNoticeTexts(platform, pattern) {
|
|
144
174
|
const build = state.config?.paths?.build;
|
|
145
175
|
const dir = build ? path.join(build, toolchainNoticesDir(platform)) : null;
|
|
146
176
|
if (!dir || !fs.existsSync(dir)) return null;
|
|
147
|
-
const texts = fs.readdirSync(dir).sort()
|
|
177
|
+
const texts = fs.readdirSync(dir).filter((file) => pattern.test(file)).sort()
|
|
148
178
|
.map((file) => `=== ${file} ===\n\n${fs.readFileSync(path.join(dir, file), 'utf8').trim()}`);
|
|
149
179
|
return texts.length > 0 ? texts.join('\n\n') : null;
|
|
150
180
|
}
|
|
151
181
|
|
|
152
182
|
// A Linux or Windows addon carries its C++ runtime; a Windows one also carries parts of the
|
|
153
183
|
// mingw-w64 runtime and winpthreads, whose licenses ask for their notices in binary distributions.
|
|
154
|
-
// A macOS addon uses the system's C++ runtime.
|
|
184
|
+
// A macOS addon uses the system's C++ runtime. Their texts and versions are what the link copied out of its image.
|
|
155
185
|
function addonToolchainRows(platform) {
|
|
156
186
|
const shared = {
|
|
157
187
|
npmName: null, version: null, nativeVersion: null, sha256: null, licenseSelected: null, licenseText: null, isCopyleft: false,
|
|
@@ -163,22 +193,32 @@ function addonToolchainRows(platform) {
|
|
|
163
193
|
license: llvmLicense,
|
|
164
194
|
licenseDeclared: llvmLicense,
|
|
165
195
|
sourceUrl: 'https://github.com/llvm/llvm-project',
|
|
196
|
+
licenseText: toolchainNoticeText(platform, NATIVE_NOTICES.llvmText),
|
|
166
197
|
};
|
|
167
198
|
const llvmText = 'license text: https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT';
|
|
168
199
|
if (platform === 'linux' || platform === 'linuxmusl') {
|
|
169
|
-
return [{
|
|
200
|
+
return [{
|
|
201
|
+
...llvmRuntimes,
|
|
202
|
+
nativeVersion: libcxxVersionOf(toolchainNotice(platform, NATIVE_NOTICES.libcxxVersion)),
|
|
203
|
+
licenseNotes: `libc++ and libc++abi of the linux toolchain image, statically linked into the binary; ${llvmText}`,
|
|
204
|
+
}];
|
|
170
205
|
}
|
|
171
206
|
const mingwLicense = 'LicenseRef-MinGW-w64-runtime';
|
|
172
207
|
return [
|
|
173
|
-
{
|
|
208
|
+
{
|
|
209
|
+
...llvmRuntimes,
|
|
210
|
+
nativeVersion: clangVersionOf(toolchainNotice('win32', NATIVE_NOTICES.clangVersion)),
|
|
211
|
+
licenseNotes: `libc++, libc++abi, libunwind and compiler-rt of the windows toolchain image (llvm-mingw), statically linked into the binary; ${llvmText}`,
|
|
212
|
+
},
|
|
174
213
|
{
|
|
175
214
|
...shared,
|
|
176
215
|
name: 'mingw-w64-runtime',
|
|
216
|
+
nativeVersion: mingwVersionOf(toolchainNotice('win32', NATIVE_NOTICES.mingwVersion)),
|
|
177
217
|
license: mingwLicense,
|
|
178
218
|
licenseDeclared: mingwLicense,
|
|
179
219
|
sourceUrl: 'https://github.com/mingw-w64/mingw-w64',
|
|
180
220
|
licenseNotes: 'the mingw-w64 startup code and winpthreads, statically linked into the binary; 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)',
|
|
181
|
-
licenseText: toolchainNoticeTexts('win32'),
|
|
221
|
+
licenseText: toolchainNoticeTexts('win32', MINGW_NOTICE),
|
|
182
222
|
},
|
|
183
223
|
];
|
|
184
224
|
}
|
|
@@ -272,13 +312,15 @@ const HERMES_BUILDS = {
|
|
|
272
312
|
},
|
|
273
313
|
};
|
|
274
314
|
|
|
275
|
-
// What an Android app's Gradle build adds from Maven, and the C++ runtime the app carries beside its host
|
|
276
|
-
|
|
315
|
+
// What an Android app's Gradle build adds from Maven, and the C++ runtime the app carries beside its host: each a file
|
|
316
|
+
// of its own in the app, with its text from @crossbind/core-app-host, and the libc++ with the version of the NDK's clang.
|
|
317
|
+
function androidAppRows(hostRoot) {
|
|
277
318
|
const shared = {
|
|
278
|
-
npmName: null, sha256: null, licenseSelected: null,
|
|
319
|
+
npmName: null, sha256: null, licenseSelected: null, isCopyleft: false,
|
|
279
320
|
};
|
|
280
321
|
const apache = { ...shared, license: 'Apache-2.0', licenseDeclared: 'Apache-2.0' };
|
|
281
322
|
const llvmLicense = 'Apache-2.0 WITH LLVM-exception';
|
|
323
|
+
const textIn = (dir, file) => textOf(path.join(hostRoot, 'third_party', dir, file));
|
|
282
324
|
return [
|
|
283
325
|
{
|
|
284
326
|
...apache,
|
|
@@ -287,6 +329,7 @@ function androidAppRows() {
|
|
|
287
329
|
nativeVersion: FBJNI_VERSION,
|
|
288
330
|
sourceUrl: 'https://github.com/facebookincubator/fbjni',
|
|
289
331
|
licenseNotes: 'libfbjni.so, which Hermes loads and Gradle takes with it; license text: https://github.com/facebookincubator/fbjni/blob/main/LICENSE',
|
|
332
|
+
licenseText: textIn('fbjni', 'LICENSE'),
|
|
290
333
|
},
|
|
291
334
|
{
|
|
292
335
|
...apache,
|
|
@@ -295,16 +338,18 @@ function androidAppRows() {
|
|
|
295
338
|
nativeVersion: ANDROIDX_WEBKIT,
|
|
296
339
|
sourceUrl: 'https://developer.android.com/jetpack/androidx/releases/webkit',
|
|
297
340
|
licenseNotes: "androidx.webkit, the WebView's message ports and start scripts, from Google's Maven repository; license text: https://www.apache.org/licenses/LICENSE-2.0.txt",
|
|
341
|
+
licenseText: textIn('androidx-webkit', 'LICENSE'),
|
|
298
342
|
},
|
|
299
343
|
{
|
|
300
344
|
...shared,
|
|
301
345
|
name: 'llvm-runtimes',
|
|
302
346
|
version: null,
|
|
303
|
-
nativeVersion:
|
|
347
|
+
nativeVersion: clangVersionOf(toolchainNotice('android', NATIVE_NOTICES.clangVersion)),
|
|
304
348
|
license: llvmLicense,
|
|
305
349
|
licenseDeclared: llvmLicense,
|
|
306
350
|
sourceUrl: 'https://github.com/llvm/llvm-project',
|
|
307
351
|
licenseNotes: "libc++ of the android image's NDK, which the app carries as libc++_shared.so; license text: https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT",
|
|
352
|
+
licenseText: textIn('libcxx', 'LICENSE.TXT'),
|
|
308
353
|
},
|
|
309
354
|
];
|
|
310
355
|
}
|
|
@@ -424,7 +469,7 @@ function appRuntimeRows(platform) {
|
|
|
424
469
|
licenseNotes: "JSI's jsi.cpp from the same Hermes release, compiled into the host",
|
|
425
470
|
licenseText: hermesLicense,
|
|
426
471
|
},
|
|
427
|
-
...(platform === 'android' ? androidAppRows() : []),
|
|
472
|
+
...(platform === 'android' ? androidAppRows(hostRoot) : []),
|
|
428
473
|
...(platform === 'win32' ? windowsAppRows(hostRoot) : []),
|
|
429
474
|
// A mobile app makes its window with UIKit or an Android activity, without webview/webview.
|
|
430
475
|
...(['ios', 'android'].includes(platform) ? [] : [{
|
|
@@ -590,11 +635,12 @@ export default async function collectLicenseRows(platform = null, { runtimeEnv =
|
|
|
590
635
|
if (seen.has(key)) continue;
|
|
591
636
|
seen.add(key);
|
|
592
637
|
const node = nodes.find((other) => keyOf(other) === key && serves(other)) ?? listed;
|
|
593
|
-
|
|
638
|
+
const row = node.general.packageSource ? sourcePackageRow(node) : await buildRow(node);
|
|
639
|
+
rows.push(row);
|
|
594
640
|
// A package source's package folder is written from what its recipe produced: nothing there is run.
|
|
595
641
|
if (platform && !node.general.packageSource) {
|
|
596
642
|
const familyProjects = nodes.filter((other) => keyOf(other) === key).map((other) => other.paths.project);
|
|
597
|
-
rows.push(...await bundledRowsOf(node, platform, familyProjects));
|
|
643
|
+
rows.push(...await bundledRowsOf(node, platform, familyProjects, row.sourceUrl));
|
|
598
644
|
}
|
|
599
645
|
}
|
|
600
646
|
if (platform === 'wasi') rows.push(...wasiToolchainRows());
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import run from './run.js';
|
|
2
|
+
import toolchainNoticesDir, { NATIVE_NOTICES } from '../utils/toolchainNotices.js';
|
|
3
|
+
import { linuxTriple } from '../utils/linuxToolchain.js';
|
|
4
|
+
import { ANDROID_NDK } from '../utils/androidToolchain.js';
|
|
5
|
+
|
|
6
|
+
const MINGW_NOTICES = '/opt/licenses/llvm-mingw';
|
|
7
|
+
const MINGW_CLANG = '/opt/llvm-mingw/bin/clang';
|
|
8
|
+
// The x64 and arm64 targets share one mingw-w64 build.
|
|
9
|
+
const MINGW_VERSION_HEADER = '/opt/llvm-mingw/x86_64-w64-mingw32/include/_mingw_mac.h';
|
|
10
|
+
const LIBCXX_LICENSE = '/opt/licenses/linux-sysroot/libcxx-LICENSE.TXT';
|
|
11
|
+
const NDK_CLANG = `${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang`;
|
|
12
|
+
|
|
13
|
+
const ifFile = (file, command) => `if [ -f ${file} ]; then ${command}; fi`;
|
|
14
|
+
const clangVersion = (clang, dir) => `if [ -x ${clang} ]; then ${clang} --version > ${dir}/${NATIVE_NOTICES.clangVersion}; fi`;
|
|
15
|
+
|
|
16
|
+
// What each image keeps of the runtimes it links into a binary: their license texts and what tells their versions. An
|
|
17
|
+
// Android app's libc++ has its text from @crossbind/core-app-host, as the NDK keeps it only among the toolchain's.
|
|
18
|
+
const SCRIPTS = {
|
|
19
|
+
win32: (dir) => [
|
|
20
|
+
`if [ -d ${MINGW_NOTICES} ]; then cp ${MINGW_NOTICES}/COPYING* ${dir}/; fi`,
|
|
21
|
+
ifFile(`${MINGW_NOTICES}/LICENSE.TXT`, `cp ${MINGW_NOTICES}/LICENSE.TXT ${dir}/${NATIVE_NOTICES.llvmText}`),
|
|
22
|
+
clangVersion(MINGW_CLANG, dir),
|
|
23
|
+
ifFile(MINGW_VERSION_HEADER, `grep '#define __MINGW64_VERSION_' ${MINGW_VERSION_HEADER} > ${dir}/${NATIVE_NOTICES.mingwVersion}`),
|
|
24
|
+
],
|
|
25
|
+
linux: (dir, target) => {
|
|
26
|
+
const config = `/opt/crossbind/sysroots/${linuxTriple(target)}/usr/include/c++/v1/__config`;
|
|
27
|
+
return [
|
|
28
|
+
ifFile(LIBCXX_LICENSE, `cp ${LIBCXX_LICENSE} ${dir}/${NATIVE_NOTICES.llvmText}`),
|
|
29
|
+
ifFile(config, `grep '# *define _LIBCPP_VERSION ' ${config} > ${dir}/${NATIVE_NOTICES.libcxxVersion}`),
|
|
30
|
+
];
|
|
31
|
+
},
|
|
32
|
+
android: (dir) => [clangVersion(NDK_CLANG, dir)],
|
|
33
|
+
};
|
|
34
|
+
SCRIPTS.linuxmusl = SCRIPTS.linux;
|
|
35
|
+
|
|
36
|
+
// Copies them beside the build after a link in the target's image, where crossbind licenses reads them. A host
|
|
37
|
+
// toolchain keeps none of these files, and its build goes on without them.
|
|
38
|
+
export default function copyNativeNotices(target) {
|
|
39
|
+
const script = SCRIPTS[target.platform];
|
|
40
|
+
if (!script) return;
|
|
41
|
+
const dir = toolchainNoticesDir(target.platform);
|
|
42
|
+
run('sh', ['-c', [`mkdir -p ${dir}`, ...script(dir, target)].join('; ')], null, target);
|
|
43
|
+
}
|
package/src/actions/run.js
CHANGED
|
@@ -13,7 +13,7 @@ import state from '../state/index.js';
|
|
|
13
13
|
import { wasiCFlags, wasiCxxFlags, resolveWasiSdkPath, WASI_TARGET_TRIPLE } from '../utils/wasiToolchain.js';
|
|
14
14
|
import { HOST_BUILT_PLATFORMS } from '../utils/targets.js';
|
|
15
15
|
import { WASM_EXCEPTION_FLAGS } from '../utils/archiveFlags.js';
|
|
16
|
-
import { ANDROID_NDK, ANDROID_API_LEVEL } from '../utils/androidToolchain.js';
|
|
16
|
+
import { ANDROID_NDK, ANDROID_API_LEVEL, ANDROID_STL } from '../utils/androidToolchain.js';
|
|
17
17
|
import { IOS_DEVELOPER_DIR, IOS_DEPLOYMENT_TARGET, XCODE_TOOLCHAIN_BIN } from '../utils/iosToolchain.js';
|
|
18
18
|
import { LINUX_ARCHIVE_FLAGS, linuxBuildEnv, linuxToolchainFile } from '../utils/linuxToolchain.js';
|
|
19
19
|
import { windowsBuildEnv, windowsToolchainFile } from '../utils/windowsToolchain.js';
|
|
@@ -26,6 +26,31 @@ export const EXEC_MAX_BUFFER = 512 * 1024 * 1024;
|
|
|
26
26
|
// Containers share the memory of Docker's VM, and one compile at full -j can take most of a desktop VM
|
|
27
27
|
// (GDAL's bindings peak at 7.5 of 7.7 GiB), so the exclusive steps of parallel builds take turns.
|
|
28
28
|
const DOCKER_COMPILE_LOCK = path.join(os.homedir(), '.crossbind', 'docker-compile.lock');
|
|
29
|
+
// With CROSSBIND_SCCACHE set, C and C++ compiles go through sccache, which its SCCACHE_* variables configure, by CMake's
|
|
30
|
+
// launchers. A wasm build compiles without it: sccache 0.19 recognizes neither em++ nor emscripten's clang, which has
|
|
31
|
+
// no default target, and cannot cache the -Xclang flags emcc passes that clang.
|
|
32
|
+
const SCCACHE_LAUNCHERS = ['CMAKE_C_COMPILER_LAUNCHER', 'CMAKE_CXX_COMPILER_LAUNCHER'];
|
|
33
|
+
// sccache's key leaves out the target llvm-mingw's compilers take from their own names, so an x64 and an arm64 compile of
|
|
34
|
+
// one source would share it; the buster, which sccache hashes into every key, names the target.
|
|
35
|
+
const SCCACHE_BUSTER = 'SCCACHE_C_CUSTOM_CACHE_BUSTER';
|
|
36
|
+
const sccacheBuster = (target) => [process.env[SCCACHE_BUSTER], target?.platform, target?.arch].filter(Boolean).join('-');
|
|
37
|
+
const SCCACHE_SETTING =/^(SCCACHE_\w+|AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|AWS_SESSION_TOKEN)$/;
|
|
38
|
+
const SCCACHE_IN_IMAGE = 'command -v sccache >/dev/null || { echo "crossbind: CROSSBIND_SCCACHE needs toolchain images that carry sccache, '
|
|
39
|
+
+ 'image family 1.0.21 or later." >&2; exit 1; }; ';
|
|
40
|
+
// A docker run's sccache server ends with its container, so the command stops it first, which waits for the cache writes
|
|
41
|
+
// it holds; a DOCKER_EXEC container's server serves the commands after it.
|
|
42
|
+
const SCCACHE_IN_CONTAINER = {
|
|
43
|
+
DOCKER_RUN: `${SCCACHE_IN_IMAGE}"$0" "$@"; status=$?; sccache --stop-server >/dev/null 2>&1; exit $status`,
|
|
44
|
+
DOCKER_EXEC: `${SCCACHE_IN_IMAGE}exec "$0" "$@"`,
|
|
45
|
+
};
|
|
46
|
+
const isSccacheOn = () => /^(1|true)$/i.test(process.env.CROSSBIND_SCCACHE ?? '');
|
|
47
|
+
const sccacheSettings = () => Object.keys(process.env).filter((key) => SCCACHE_SETTING.test(key)).sort();
|
|
48
|
+
// Where a container finds a local sccache directory: a docker run mounts it here, a long-lived container sees the project.
|
|
49
|
+
const SCCACHE_CONTAINER_DIR = '/tmp/crossbind/sccache';
|
|
50
|
+
const isInside = (parent, child) => {
|
|
51
|
+
const relative = path.relative(parent, child);
|
|
52
|
+
return !relative.startsWith('..') && !path.isAbsolute(relative);
|
|
53
|
+
};
|
|
29
54
|
const envParams = (env) => Object.entries(env).flatMap(([variable, value]) => ['-e', `${variable}=${value}`]);
|
|
30
55
|
const CROSSCOMPILER_ARM64 = `aarch64-linux-android${ANDROID_API_LEVEL}`;
|
|
31
56
|
const CROSSCOMPILER_x86_64 = `x86_64-linux-android${ANDROID_API_LEVEL}`;
|
|
@@ -139,6 +164,15 @@ export default function run(program, params = [], platformPrefix = null, target
|
|
|
139
164
|
? { runner: 'LOCAL' }
|
|
140
165
|
: runnerFor(role, state.config.system);
|
|
141
166
|
const isDocker = runner === 'DOCKER_RUN' || runner === 'DOCKER_EXEC';
|
|
167
|
+
const isSccache = isSccacheOn() && target?.platform !== 'wasm';
|
|
168
|
+
if (isSccache && runner === 'REMOTE') {
|
|
169
|
+
throw new Error('crossbind: CROSSBIND_SCCACHE works with the DOCKER_RUN, DOCKER_EXEC and LOCAL runners; a remote runner would need your '
|
|
170
|
+
+ 'cache\'s keys. Unset it for a remote build.');
|
|
171
|
+
}
|
|
172
|
+
const sccacheDir = isSccache && isDocker ? process.env.SCCACHE_DIR : undefined;
|
|
173
|
+
if (sccacheDir && runner === 'DOCKER_EXEC' && !isInside(state.config.paths.base, sccacheDir)) {
|
|
174
|
+
throw new Error(`crossbind: a DOCKER_EXEC container reaches the project alone, so SCCACHE_DIR (${sccacheDir}) must sit inside ${state.config.paths.base}.`);
|
|
175
|
+
}
|
|
142
176
|
const wasiHostSdk = target?.platform === 'wasi' ? resolveWasiSdkPath(state.config.system) : null;
|
|
143
177
|
if (isDocker) {
|
|
144
178
|
// Google ships the linux NDK for x86_64 only, so the android index carries no arm64 leaf;
|
|
@@ -203,6 +237,7 @@ export default function run(program, params = [], platformPrefix = null, target
|
|
|
203
237
|
`-DANDROID_ABI=${target.arch === 'x86_64' ? 'x86_64' : 'arm64-v8a'}`,
|
|
204
238
|
`-DANDROID_PLATFORM=android-${ANDROID_API_LEVEL}`,
|
|
205
239
|
`-DANDROID_NDK=${ANDROID_NDK}`,
|
|
240
|
+
`-DANDROID_STL=${ANDROID_STL}`,
|
|
206
241
|
];
|
|
207
242
|
}
|
|
208
243
|
break;
|
|
@@ -212,10 +247,10 @@ export default function run(program, params = [], platformPrefix = null, target
|
|
|
212
247
|
if (dProgram === 'cmake') {
|
|
213
248
|
platformParams = [];
|
|
214
249
|
if (dParams[0] !== '--build' && dParams[0] !== '--install') {
|
|
250
|
+
// CMake's own iOS platform with Makefiles: the Xcode generator spent 40-55 s configuring even a
|
|
251
|
+
// small project. Not the app host's toolchain file, whose searches find the Mac's own packages.
|
|
215
252
|
dParams = [
|
|
216
253
|
...dParams,
|
|
217
|
-
'-G',
|
|
218
|
-
'Xcode',
|
|
219
254
|
'-DBUILD_SHARED_LIBS=OFF',
|
|
220
255
|
'-DFRAMEWORK=TRUE',
|
|
221
256
|
`-DCMAKE_OSX_DEPLOYMENT_TARGET=${IOS_DEPLOYMENT_TARGET}`,
|
|
@@ -227,6 +262,11 @@ export default function run(program, params = [], platformPrefix = null, target
|
|
|
227
262
|
'-DCMAKE_SYSTEM_PROCESSOR=arm64',
|
|
228
263
|
`-DCMAKE_C_FLAGS=${target.arch === 'iphoneos' ? '-fembed-bitcode' : '-fembed-bitcode-marker'}`,
|
|
229
264
|
`-DCMAKE_CXX_FLAGS=${target.arch === 'iphoneos' ? '-fembed-bitcode' : '-fembed-bitcode-marker'}`,
|
|
265
|
+
// The objects carry bitcode as before; a link, as of a configure check or a recipe's tests,
|
|
266
|
+
// takes none, since Xcode's linker reads -fembed-bitcode-marker's mode as a file.
|
|
267
|
+
'-DCMAKE_EXE_LINKER_FLAGS=-fembed-bitcode=off',
|
|
268
|
+
'-DCMAKE_SHARED_LINKER_FLAGS=-fembed-bitcode=off',
|
|
269
|
+
'-DCMAKE_MODULE_LINKER_FLAGS=-fembed-bitcode=off',
|
|
230
270
|
"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY='iPhone Developer'",
|
|
231
271
|
`-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=${state.config.system.XCODE_DEVELOPMENT_TEAM}`,
|
|
232
272
|
];
|
|
@@ -355,6 +395,18 @@ export default function run(program, params = [], platformPrefix = null, target
|
|
|
355
395
|
const containerOptions = { cwd: buildPath, stdio: dockerOptions.console ? 'inherit' : ['ignore', 'pipe', 'pipe'], maxBuffer: EXEC_MAX_BUFFER };
|
|
356
396
|
let fileExecParams;
|
|
357
397
|
if (runner === 'LOCAL') {
|
|
398
|
+
if (isSccache) {
|
|
399
|
+
// By its full path: a macOS build narrows the PATH below.
|
|
400
|
+
const sccache = findExecutable('sccache', process.env.PATH ?? '');
|
|
401
|
+
if (!sccache) throw new Error('crossbind: CROSSBIND_SCCACHE needs sccache on the PATH: install it with `brew install sccache`, or unset CROSSBIND_SCCACHE.');
|
|
402
|
+
SCCACHE_LAUNCHERS.forEach((key) => {
|
|
403
|
+
env[key] = sccache;
|
|
404
|
+
});
|
|
405
|
+
sccacheSettings().forEach((key) => {
|
|
406
|
+
env[key] = process.env[key];
|
|
407
|
+
});
|
|
408
|
+
env[SCCACHE_BUSTER] = sccacheBuster(target);
|
|
409
|
+
}
|
|
358
410
|
env.PATH = `/opt/homebrew/bin:${env.PATH}`;
|
|
359
411
|
// A package's *-config script on the PATH (nc-config, sfcgal-config, ...) would compile that
|
|
360
412
|
// package into the archives. cmake is the one host tool a darwin build takes from the PATH.
|
|
@@ -392,6 +444,11 @@ export default function run(program, params = [], platformPrefix = null, target
|
|
|
392
444
|
if (runner === 'DOCKER_RUN') {
|
|
393
445
|
imageOrContainer = getDockerImage(role);
|
|
394
446
|
runnerParams = ['run', '--rm', ...DOCKER_RUN_SECURITY_ARGS, '-v', `${state.config.paths.base}:/tmp/crossbind/live`];
|
|
447
|
+
if (sccacheDir) {
|
|
448
|
+
// Made here, as docker would make a missing one owned by root.
|
|
449
|
+
fs.mkdirSync(sccacheDir, { recursive: true });
|
|
450
|
+
runnerParams.push('-v', `${sccacheDir}:${SCCACHE_CONTAINER_DIR}`);
|
|
451
|
+
}
|
|
395
452
|
if (target?.platform === 'android') {
|
|
396
453
|
// Google ships the linux NDK for x86_64 only; use the amd64 leaf ref (classic store holds one platform per digest).
|
|
397
454
|
pullDockerImage(role, 'linux/amd64');
|
|
@@ -403,6 +460,16 @@ export default function run(program, params = [], platformPrefix = null, target
|
|
|
403
460
|
runnerParams = ['exec'];
|
|
404
461
|
}
|
|
405
462
|
|
|
463
|
+
// The settings go by name, docker taking their values from its own environment, so no key sits in the arguments.
|
|
464
|
+
const insideDir = sccacheDir && (runner === 'DOCKER_RUN' ? SCCACHE_CONTAINER_DIR : replaceBasePathForDocker(sccacheDir));
|
|
465
|
+
const sccache = isSccache ? [
|
|
466
|
+
...SCCACHE_LAUNCHERS.flatMap((key) => ['-e', `${key}=sccache`]),
|
|
467
|
+
...sccacheSettings().filter((key) => !(sccacheDir && key === 'SCCACHE_DIR') && key !== SCCACHE_BUSTER).flatMap((key) => ['-e', key]),
|
|
468
|
+
'-e',
|
|
469
|
+
`${SCCACHE_BUSTER}=${sccacheBuster(target)}`,
|
|
470
|
+
...(sccacheDir ? ['-e', `SCCACHE_DIR=${insideDir}`] : []),
|
|
471
|
+
] : [];
|
|
472
|
+
const command = isSccache ? ['sh', '-c', SCCACHE_IN_CONTAINER[runner], dProgram, ...dParams] : [dProgram, ...dParams];
|
|
406
473
|
const args = [
|
|
407
474
|
...runnerParams,
|
|
408
475
|
'--user',
|
|
@@ -410,10 +477,9 @@ export default function run(program, params = [], platformPrefix = null, target
|
|
|
410
477
|
'--workdir',
|
|
411
478
|
replaceBasePathForDocker(dockerOptions.workdir || buildPath),
|
|
412
479
|
...replaceBasePathForDocker(dockerEnv),
|
|
413
|
-
|
|
480
|
+
...sccache,
|
|
414
481
|
imageOrContainer,
|
|
415
|
-
replaceBasePathForDocker(
|
|
416
|
-
...replaceBasePathForDocker(dParams),
|
|
482
|
+
...replaceBasePathForDocker(command),
|
|
417
483
|
];
|
|
418
484
|
fileExecParams = ['docker', args, options];
|
|
419
485
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import run from './run.js';
|
|
4
|
+
import state from '../state/index.js';
|
|
5
|
+
import toolchainNoticesDir, { WASM_NOTICES } from '../utils/toolchainNotices.js';
|
|
6
|
+
import { getDockerImage, imageRoleFor } from '../utils/pullDockerImage.js';
|
|
7
|
+
|
|
8
|
+
const IMAGE_LICENSES = '/opt/licenses';
|
|
9
|
+
const EMSDK_UPSTREAM = '/emsdk/upstream';
|
|
10
|
+
|
|
11
|
+
const imageFile = () => path.join(state.config.paths.build, toolchainNoticesDir('wasm'), WASM_NOTICES.image);
|
|
12
|
+
|
|
13
|
+
export const hasWasmNotices = () => fs.existsSync(imageFile());
|
|
14
|
+
|
|
15
|
+
// A wasm module links Emscripten's libraries and LLVM's C++ runtime. The web image keeps their license texts, and its emsdk
|
|
16
|
+
// their versions: a link copies them beside the build once for each image, and crossbind licenses lists them from there.
|
|
17
|
+
export default function copyWasmNotices(target) {
|
|
18
|
+
const image = getDockerImage(imageRoleFor(target));
|
|
19
|
+
if (hasWasmNotices() && fs.readFileSync(imageFile(), 'utf8') === image) return;
|
|
20
|
+
const notices = toolchainNoticesDir('wasm');
|
|
21
|
+
const copy = (from, file) => `if [ -f ${from} ]; then cp ${from} ${notices}/${file}; fi`;
|
|
22
|
+
// run(null, …) on wasm wraps a configure or a make in emscripten's own: sh is named.
|
|
23
|
+
run('sh', ['-c', [
|
|
24
|
+
`mkdir -p ${notices}`,
|
|
25
|
+
copy(`${IMAGE_LICENSES}/emscripten-LICENSE`, WASM_NOTICES.emscriptenText),
|
|
26
|
+
copy(`${IMAGE_LICENSES}/llvm-LICENSE.TXT`, WASM_NOTICES.llvmText),
|
|
27
|
+
copy(`${EMSDK_UPSTREAM}/emscripten/emscripten-version.txt`, WASM_NOTICES.emscriptenVersion),
|
|
28
|
+
`if [ -x ${EMSDK_UPSTREAM}/bin/clang ]; then ${EMSDK_UPSTREAM}/bin/clang --version > ${notices}/${WASM_NOTICES.llvmVersion}; fi`,
|
|
29
|
+
].join('; ')], null, target);
|
|
30
|
+
fs.mkdirSync(path.dirname(imageFile()), { recursive: true });
|
|
31
|
+
fs.writeFileSync(imageFile(), image);
|
|
32
|
+
}
|
package/src/app/index.js
CHANGED
|
@@ -10,7 +10,7 @@ function hostOf(member, name) {
|
|
|
10
10
|
if (host) return host;
|
|
11
11
|
if (!globalThis.__crossbind_token && !globalThis.Crossbind?.app) throw new Error(`crossbind: ${name} works in an app alone, which crossbind app builds`);
|
|
12
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}`);
|
|
13
|
-
throw new Error(`crossbind: ${name} works once the page
|
|
13
|
+
throw new Error(`crossbind: ${name} works once the page's initNative() has resolved`);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const call = (name, options) => hostOf('app', name)(name, options);
|
|
@@ -45,8 +45,19 @@ const ownsObject = (obj) => {
|
|
|
45
45
|
// frees nothing.
|
|
46
46
|
const moduleObjects = new WeakSet();
|
|
47
47
|
|
|
48
|
+
// A pointer a member handed out holds a clone of the handle of the object it points into
|
|
49
|
+
// (utils/embindPointerOwners.js), which goes once no handle shares the pointer's count.
|
|
50
|
+
const releaseOwner = (obj) => {
|
|
51
|
+
const count = obj?.$$?.count;
|
|
52
|
+
if (!count?.owner || count.value > (obj.isDeleted() ? 0 : 1)) return;
|
|
53
|
+
const { owner } = count;
|
|
54
|
+
count.owner = undefined;
|
|
55
|
+
if (!owner.isDeleted()) owner.delete();
|
|
56
|
+
};
|
|
57
|
+
|
|
48
58
|
const freeIfOwned = (obj) => {
|
|
49
59
|
if (ownsObject(obj) && !moduleObjects.has(obj) && !obj.isDeleted()) obj.delete();
|
|
60
|
+
releaseOwner(obj);
|
|
50
61
|
};
|
|
51
62
|
|
|
52
63
|
// The worker side of a handle the main thread no longer reaches: its id goes, and the object with the last id.
|
|
@@ -1,55 +1,55 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0.
|
|
2
|
+
"version": "1.0.21",
|
|
3
3
|
"registry": "ghcr.io/crossbind",
|
|
4
4
|
"toolchains": {
|
|
5
5
|
"rust": "1.99.1"
|
|
6
6
|
},
|
|
7
7
|
"images": {
|
|
8
8
|
"rust-sysroot": {
|
|
9
|
-
"index": "sha256:
|
|
9
|
+
"index": "sha256:de0e2969f74bb458d134f796d04f60ab2b2342ddc4f7d336a2ac945973c628a0",
|
|
10
10
|
"platforms": {
|
|
11
|
-
"linux/
|
|
12
|
-
"linux/
|
|
11
|
+
"linux/amd64": "sha256:2b4b338f185a44dfffd28eece9a79eedf7cef27fdd613ac4428bded4b394e773",
|
|
12
|
+
"linux/arm64": "sha256:8f0f72be0386b0a823db7520e23b1eb424da9daf64a364dffa983738d0b07035"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"base": {
|
|
16
|
-
"index": "sha256:
|
|
16
|
+
"index": "sha256:8894f7f3deb8e3980cf138fea3552e3375d6250d08aba5b3828909bf55d796fc",
|
|
17
17
|
"platforms": {
|
|
18
|
-
"linux/
|
|
19
|
-
"linux/
|
|
18
|
+
"linux/arm64": "sha256:9e8c9c6495d4c0e743c2a9b471b3e76fe8117507b44e7eb469009cd241473262",
|
|
19
|
+
"linux/amd64": "sha256:4a8305251eb48835bc13c9640303f6e9dfdb14be8ccb28d57ed808175d4dc8e9"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"web": {
|
|
23
|
-
"index": "sha256:
|
|
23
|
+
"index": "sha256:5497576cba5a945249f9ff76f8cc40b43b0a26c1d0ce9dd7874ed842550b96c2",
|
|
24
24
|
"platforms": {
|
|
25
|
-
"linux/arm64": "sha256:
|
|
26
|
-
"linux/amd64": "sha256:
|
|
25
|
+
"linux/arm64": "sha256:a661e57cd9a748b8053a3b315474b5e42f39eb691ef2834f9b57dc2f0cb3db17",
|
|
26
|
+
"linux/amd64": "sha256:82d2163287aba2d77af0db0517ee4a96e69e34e2d54d45b4ef6f39151a466494"
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"android": {
|
|
30
|
-
"index": "sha256:
|
|
30
|
+
"index": "sha256:f54939cf8d648db75178d9a8cb1456453459b03d46fca7f2fa483b979677aa00",
|
|
31
31
|
"platforms": {
|
|
32
|
-
"linux/amd64": "sha256:
|
|
32
|
+
"linux/amd64": "sha256:1030809494407742bd71675d1c8d2967b08a42285ccb47ddf81fa52e8d41c2c6"
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"android-app": {
|
|
36
|
-
"index": "sha256:
|
|
36
|
+
"index": "sha256:9be66dedaaf5f36e5795d080eaa43a523b79246c014719783c15e522d6d6f6c4",
|
|
37
37
|
"platforms": {
|
|
38
|
-
"linux/amd64": "sha256:
|
|
38
|
+
"linux/amd64": "sha256:1f7963b928513a1892c39e706515a0929abd4b6f72946c2943d476be2741725e"
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
41
|
"linux": {
|
|
42
|
-
"index": "sha256:
|
|
42
|
+
"index": "sha256:4cdbe97aef06a84f82bf1f6e2f5badc354638c4f15364033ff1059247caa28ed",
|
|
43
43
|
"platforms": {
|
|
44
|
-
"linux/arm64": "sha256:
|
|
45
|
-
"linux/amd64": "sha256:
|
|
44
|
+
"linux/arm64": "sha256:b074c80b96455cfa8cfc1349bc1b77ce3286bff36d095831416c4e1111c69cf0",
|
|
45
|
+
"linux/amd64": "sha256:ca2194dd604dadfdb73f0bbe3b2e5a65ab6330bae6f9b8079b1c8ca9c9e2a11a"
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"windows": {
|
|
49
|
-
"index": "sha256:
|
|
49
|
+
"index": "sha256:89dea8be3a1415c87854c2bccfb2cee95ec1d2f775b1b4db6260882d01d02a46",
|
|
50
50
|
"platforms": {
|
|
51
|
-
"linux/amd64": "sha256:
|
|
52
|
-
"linux/arm64": "sha256:
|
|
51
|
+
"linux/amd64": "sha256:454b9c339628d033d43f64422a12be9445193f167183fbeca71b4ae8af128673",
|
|
52
|
+
"linux/arm64": "sha256:9d823e18683cedfcc94ff6fb3c637c74929b4ee5601f72b3b87a4cedc7cd2d9e"
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -4,6 +4,7 @@ import state from '../state/index.js';
|
|
|
4
4
|
import { parseCargoImport, writeCargoMarker } from '../utils/cargoImport.js';
|
|
5
5
|
import { cratesOf, sourceOfImport, resolveSourceHeader } from '../utils/packageSourceImport.js';
|
|
6
6
|
import refreshPackageSources from '../state/refreshPackageSources.js';
|
|
7
|
+
import isSourceCmakePackage from '../utils/isSourceCmakePackage.js';
|
|
7
8
|
|
|
8
9
|
const LAYOUT = { header: 'include', module: 'swig', source: '' };
|
|
9
10
|
|
|
@@ -118,7 +119,10 @@ export default function getDependFilePath(source, target) {
|
|
|
118
119
|
// Skip packages that don't produce artifacts for this target — multiple
|
|
119
120
|
// packages may share the same alias (one per platform), so this filters
|
|
120
121
|
// out the wrong-platform matches without needing explicit metadata.
|
|
121
|
-
if (!existsCached(prebuiltRoot))
|
|
122
|
+
if (!existsCached(prebuiltRoot)) {
|
|
123
|
+
tried.push(prebuiltRoot);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
122
126
|
|
|
123
127
|
const baseDir = subdir ? `${prebuiltRoot}/${subdir}` : prebuiltRoot;
|
|
124
128
|
for (const candidate of getFolderCandidates(pkg, alias)) {
|
|
@@ -131,6 +135,9 @@ export default function getDependFilePath(source, target) {
|
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
const matchNames = matches.map((m) => m.pkg.package.name).join(', ');
|
|
138
|
+
if (matches.every(({ pkg }) => pkg.export?.type === 'source' || isSourceCmakePackage(pkg))) {
|
|
139
|
+
throw new Error(`crossbind: ${matchNames} ships its sources, so "${source}" is for C++: include it in the app's native code and call that from JavaScript.`);
|
|
140
|
+
}
|
|
134
141
|
throw new Error(
|
|
135
142
|
`Could not resolve "${source}" in package(s) [${matchNames}] for target "${target.path}". Tried:\n`
|
|
136
143
|
+ tried.map((t) => ` - ${t}`).join('\n'),
|
package/src/state/loadConfig.js
CHANGED
|
@@ -179,7 +179,7 @@ export function getFilledConfig(config, options = { isDepend: false }) {
|
|
|
179
179
|
|
|
180
180
|
if (!newConfig?.general?.name) {
|
|
181
181
|
if (!newConfig.package) {
|
|
182
|
-
throw new Error(`crossbind: no package.json found in ${newConfig.paths.project}. Run crossbind from your project root, or scaffold a project with \`npm create crossbind\`.`);
|
|
182
|
+
throw new Error(`crossbind: no package.json found in ${newConfig.paths.project}. Run crossbind from your project root, or scaffold a project with \`npm create crossbind@beta\`.`);
|
|
183
183
|
}
|
|
184
184
|
newConfig.general.name = fixPackageName(newConfig.package.name) || 'crossbindsample';
|
|
185
185
|
}
|
|
@@ -2,3 +2,7 @@
|
|
|
2
2
|
// and Conan packages link into the same library, so they move together.
|
|
3
3
|
export const ANDROID_NDK = '/opt/android-sdk/ndk/current';
|
|
4
4
|
export const ANDROID_API_LEVEL = 33;
|
|
5
|
+
// The C++ runtime every Android library links. The NDK's default links libc++ into each library, which then exports it:
|
|
6
|
+
// an app of several would run as many runtimes, and Android compares type_info by address. One libc++_shared.so, which
|
|
7
|
+
// the app ships, serves them all.
|
|
8
|
+
export const ANDROID_STL = 'c++_shared';
|