crossbind 0.0.1 → 2.0.0-beta.51

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.
Files changed (95) hide show
  1. package/AGENTS.md +92 -0
  2. package/CHANGELOG.md +156 -0
  3. package/LICENSE +21 -0
  4. package/README.md +133 -0
  5. package/package.json +52 -4
  6. package/src/actions/buildBinTools.js +0 -0
  7. package/src/actions/buildCargo.js +88 -0
  8. package/src/actions/buildDependencies.js +157 -0
  9. package/src/actions/buildExternal.js +57 -0
  10. package/src/actions/buildJs.js +93 -0
  11. package/src/actions/buildLib.js +115 -0
  12. package/src/actions/buildTypes.js +33 -0
  13. package/src/actions/buildWasiCommand.js +101 -0
  14. package/src/actions/buildWasm.js +322 -0
  15. package/src/actions/createInterface.js +172 -0
  16. package/src/actions/createLib.js +249 -0
  17. package/src/actions/createXCFramework.js +82 -0
  18. package/src/actions/extensions.js +7 -0
  19. package/src/actions/getAllBridges.js +20 -0
  20. package/src/actions/getCmakeParameters.js +66 -0
  21. package/src/actions/getData.js +46 -0
  22. package/src/actions/getDependLibs.js +25 -0
  23. package/src/actions/isSourceNewer.js +35 -0
  24. package/src/actions/licenses.js +156 -0
  25. package/src/actions/run.js +307 -0
  26. package/src/actions/target.js +54 -0
  27. package/src/assets/cmake/CMakeLists.txt +61 -0
  28. package/src/assets/cmake/dist.cmake +57 -0
  29. package/src/assets/cmake/ios.toolchain.cmake +1176 -0
  30. package/src/assets/cpp-runtime/browser.cpp +12 -0
  31. package/src/assets/cpp-runtime/commonBridges.cpp +17 -0
  32. package/src/assets/cpp-runtime/crossbindEmptySource.cpp +9 -0
  33. package/src/assets/cpp-runtime/node.cpp +19 -0
  34. package/src/assets/js-runtime/adapters/exception-decode.js +79 -0
  35. package/src/assets/js-runtime/adapters/fs-browser.js +173 -0
  36. package/src/assets/js-runtime/adapters/fs-node.js +28 -0
  37. package/src/assets/js-runtime/adapters/path-fs.js +6 -0
  38. package/src/assets/js-runtime/adapters/path-url.js +8 -0
  39. package/src/assets/js-runtime/adapters/vector-coercion.js +209 -0
  40. package/src/assets/js-runtime/adapters/worker-comlink.js +309 -0
  41. package/src/assets/js-runtime/browser.js +16 -0
  42. package/src/assets/js-runtime/core.js +193 -0
  43. package/src/assets/js-runtime/edge.js +18 -0
  44. package/src/assets/js-runtime/node.js +20 -0
  45. package/src/assets/packaging/crossbind-package.podspec +17 -0
  46. package/src/assets/wasi-runtime/stubs.c +84 -0
  47. package/src/bin.js +416 -0
  48. package/src/index.js +19 -0
  49. package/src/integration/getCrossbindScript.js +180 -0
  50. package/src/integration/getDependFilePath.js +132 -0
  51. package/src/runtime/wasiRun.mjs +118 -0
  52. package/src/state/calculateDependencyParameters.js +97 -0
  53. package/src/state/index.js +128 -0
  54. package/src/state/loadConfig.js +215 -0
  55. package/src/utils/appRustCrates.js +106 -0
  56. package/src/utils/binTools.js +76 -0
  57. package/src/utils/bridgeAsyncGuard.js +37 -0
  58. package/src/utils/cargoTarget.js +15 -0
  59. package/src/utils/cppDts.js +201 -0
  60. package/src/utils/cppFieldBindings.js +23 -0
  61. package/src/utils/dependencyRebuild.js +99 -0
  62. package/src/utils/dirLock.js +71 -0
  63. package/src/utils/downloadAndExtractFile.js +102 -0
  64. package/src/utils/familyManifest.js +17 -0
  65. package/src/utils/findFiles.js +5 -0
  66. package/src/utils/fixPackageName.js +7 -0
  67. package/src/utils/flattenConfigForTable.js +75 -0
  68. package/src/utils/getAbsolutePath.js +14 -0
  69. package/src/utils/getCMakeListsFilePath.js +17 -0
  70. package/src/utils/getOsUserAndGroupId.js +11 -0
  71. package/src/utils/getParentPath.js +12 -0
  72. package/src/utils/hash.js +33 -0
  73. package/src/utils/inputStamp.js +28 -0
  74. package/src/utils/licenseReport.js +94 -0
  75. package/src/utils/linkLayout.js +34 -0
  76. package/src/utils/loadJs.js +31 -0
  77. package/src/utils/loadJson.js +16 -0
  78. package/src/utils/logger.js +110 -0
  79. package/src/utils/ociArtifact.js +124 -0
  80. package/src/utils/overrideDependency.js +99 -0
  81. package/src/utils/provenance.js +54 -0
  82. package/src/utils/pullDockerImage.js +105 -0
  83. package/src/utils/replaceBasePathForDocker.js +18 -0
  84. package/src/utils/resolveEmbindRust.js +66 -0
  85. package/src/utils/runCargo.js +228 -0
  86. package/src/utils/rustBridgeGen.js +1013 -0
  87. package/src/utils/rustMt.js +76 -0
  88. package/src/utils/rustSysroot.js +171 -0
  89. package/src/utils/rustSysrootPin.js +11 -0
  90. package/src/utils/systemKeys.js +29 -0
  91. package/src/utils/targets.js +109 -0
  92. package/src/utils/wasiToolchain.js +47 -0
  93. package/src/utils/writeIfChanged.js +8 -0
  94. package/src/utils/writeJson.js +9 -0
  95. package/vitest.config.js +46 -0
@@ -0,0 +1,156 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import state from '../state/index.js';
4
+ import loadJs from '../utils/loadJs.js';
5
+ import { isCopyleft } from '../utils/licenseReport.js';
6
+ import familyManifestOf from '../utils/familyManifest.js';
7
+ import { wasiToolchainIdentity } from '../utils/provenance.js';
8
+ import { resolveWasiSdkPath } from '../utils/wasiToolchain.js';
9
+
10
+ const LEGACY_LICENSE_FILES = ['LICENSE', 'LICENSE.md', 'LICENSE.txt', 'COPYING'];
11
+ const WASI_LIBC_LICENSE = 'Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT';
12
+
13
+ // License texts come from any extracted upstream source tree of the family (variants share the tarball).
14
+ function findSourceDir(familyDir) {
15
+ const parent = path.dirname(familyDir);
16
+ for (const entry of fs.readdirSync(parent)) {
17
+ const src = path.join(parent, entry, '.crossbind', 'build', 'source');
18
+ if (fs.existsSync(src)) return src;
19
+ }
20
+ return null;
21
+ }
22
+
23
+ function readLicenseTexts(sourceDir, files) {
24
+ const texts = (files || []).map((file) => {
25
+ const filePath = sourceDir ? path.join(sourceDir, file) : null;
26
+ const body = filePath && fs.existsSync(filePath)
27
+ ? fs.readFileSync(filePath, 'utf8').trim()
28
+ : '(missing: build the package once to extract the upstream source)';
29
+ return `=== ${file} ===\n\n${body}`;
30
+ });
31
+ return texts.length > 0 ? texts.join('\n\n') : null;
32
+ }
33
+
34
+ // 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
+ async function bundledRowsOf(node, platform) {
37
+ const recipe = await loadJs(node.paths.project, 'crossbind.build');
38
+ const entries = recipe?.bundled?.[platform];
39
+ if (!entries || entries.length === 0) return [];
40
+ const family = familyManifestOf(node);
41
+ const sourceDir = family ? findSourceDir(family.dir) : null;
42
+ return entries.map((entry) => ({
43
+ name: entry.name,
44
+ npmName: null,
45
+ version: null,
46
+ nativeVersion: entry.version || null,
47
+ license: entry.license,
48
+ licenseDeclared: entry.license,
49
+ licenseSelected: null,
50
+ licenseNotes: `${entry.notes ? `${entry.notes}; ` : ''}vendored inside ${node.general.name} ${node.package?.nativeVersion || ''}`.trim(),
51
+ sha256: null,
52
+ sourceUrl: null,
53
+ licenseText: readLicenseTexts(sourceDir, entry.files),
54
+ isCopyleft: isCopyleft(entry.license),
55
+ }));
56
+ }
57
+
58
+ // The wasi toolchain statically links its C/C++ runtime into every artifact; the
59
+ // notice rows pin the identities from the actual sdk (or point at the docker digest).
60
+ function wasiToolchainRows() {
61
+ const sdkPath = resolveWasiSdkPath(state.config.system);
62
+ const identity = sdkPath && fs.existsSync(`${sdkPath}/bin/clang`) ? wasiToolchainIdentity(sdkPath) : null;
63
+ const sdkLabel = identity?.version
64
+ ? `wasi-sdk ${identity.version}`
65
+ : 'the digest-pinned wasi-sdk docker image (see crossbind.provenance)';
66
+ const libcRef = identity?.['wasi-libc'] || 'main';
67
+ const llvmRef = identity?.llvm || 'main';
68
+ const shared = {
69
+ npmName: null, version: null, sha256: null, licenseSelected: null, licenseText: null, isCopyleft: false,
70
+ };
71
+ return [
72
+ {
73
+ ...shared,
74
+ name: 'wasi-libc',
75
+ nativeVersion: identity?.['wasi-libc'] || null,
76
+ license: WASI_LIBC_LICENSE,
77
+ licenseDeclared: WASI_LIBC_LICENSE,
78
+ sourceUrl: `https://github.com/WebAssembly/wasi-libc/tree/${libcRef}`,
79
+ licenseNotes: `C runtime statically linked by ${sdkLabel}; license texts: https://github.com/WebAssembly/wasi-libc/blob/${libcRef}/LICENSE-APACHE-LLVM , .../LICENSE-APACHE , .../LICENSE-MIT (musl and cloudlibc portions are documented there)`,
80
+ },
81
+ {
82
+ ...shared,
83
+ name: 'llvm-runtimes',
84
+ nativeVersion: identity?.['llvm-version'] || null,
85
+ license: 'Apache-2.0 WITH LLVM-exception',
86
+ licenseDeclared: 'Apache-2.0 WITH LLVM-exception',
87
+ sourceUrl: `https://github.com/llvm/llvm-project/tree/${llvmRef}`,
88
+ licenseNotes: `libc++, libc++abi, compiler-rt and libunwind statically linked by ${sdkLabel}; license text: https://github.com/llvm/llvm-project/blob/${llvmRef}/LICENSE.TXT`,
89
+ },
90
+ ];
91
+ }
92
+
93
+ async function buildRow(node) {
94
+ const recipe = await loadJs(node.paths.project, 'crossbind.build');
95
+ const manifest = node.package || null;
96
+ const nativeVersion = manifest?.nativeVersion || null;
97
+ let sourceUrl = null;
98
+ if (recipe?.getURL && nativeVersion) {
99
+ try {
100
+ sourceUrl = recipe.getURL(nativeVersion);
101
+ } catch (e) {
102
+ sourceUrl = null;
103
+ }
104
+ }
105
+ if (!sourceUrl) sourceUrl = manifest?.homepage || null;
106
+
107
+ const family = familyManifestOf(node);
108
+ const upstream = family?.manifest?.crossbind?.upstream?.license || null;
109
+ let license;
110
+ let licenseText;
111
+ if (upstream) {
112
+ license = upstream.selected || upstream.declared;
113
+ licenseText = readLicenseTexts(findSourceDir(family.dir), upstream.files);
114
+ } else {
115
+ license = manifest?.license || null;
116
+ const legacy = LEGACY_LICENSE_FILES
117
+ .map((name) => `${node.paths.project}/${name}`)
118
+ .find((file) => fs.existsSync(file));
119
+ licenseText = legacy ? fs.readFileSync(legacy, 'utf8') : null;
120
+ }
121
+
122
+ return {
123
+ name: node.general.name,
124
+ npmName: manifest?.name || null,
125
+ version: manifest?.version || null,
126
+ nativeVersion,
127
+ license,
128
+ licenseDeclared: upstream?.declared || null,
129
+ licenseSelected: upstream?.selected || null,
130
+ licenseNotes: upstream?.notes || null,
131
+ sha256: recipe?.sha256 || null,
132
+ sourceUrl,
133
+ licenseText,
134
+ isCopyleft: isCopyleft(license),
135
+ };
136
+ }
137
+
138
+ // platform (optional, e.g. 'wasi') additionally includes what that platform's
139
+ // artifact statically links beyond the package graph: recipe-declared vendored
140
+ // copies and the toolchain runtime.
141
+ export default async function collectLicenseRows(platform = null) {
142
+ // The root package is a component too: leaf -wasi packages have no deps but ship their own upstream.
143
+ const nodes = [state.config, ...state.config.allDependencies]
144
+ .filter((node) => node?.general?.alias?.package && node.paths?.project);
145
+ const rows = [];
146
+ const seen = new Set();
147
+ for (const node of nodes) {
148
+ const key = node.general.alias.package;
149
+ if (seen.has(key)) continue;
150
+ seen.add(key);
151
+ rows.push(await buildRow(node));
152
+ if (platform) rows.push(...await bundledRowsOf(node, platform));
153
+ }
154
+ if (platform === 'wasi') rows.push(...wasiToolchainRows());
155
+ return rows.sort((a, b) => a.name.localeCompare(b.name));
156
+ }
@@ -0,0 +1,307 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import pullDockerImage, { getDockerImage, getDockerContainerName, imageRoleFor } from '../utils/pullDockerImage.js';
4
+ import getOsUserAndGroupId from '../utils/getOsUserAndGroupId.js';
5
+ import replaceBasePathForDockerUtil from '../utils/replaceBasePathForDocker.js';
6
+ import state from '../state/index.js';
7
+ import { wasiCFlags, wasiCxxFlags, resolveWasiSdkPath, WASI_TARGET_TRIPLE } from '../utils/wasiToolchain.js';
8
+
9
+ // Native builds can outrun Node's 1 MiB default pipe buffer; without a raised cap a successful build dies with ENOBUFS.
10
+ const EXEC_MAX_BUFFER = 512 * 1024 * 1024;
11
+ const CROSSCOMPILER_ARM64 = 'aarch64-linux-android33';
12
+ const CROSSCOMPILER_x86_64 = 'x86_64-linux-android33';
13
+ const ANDROID_NDK = '/opt/android-sdk/ndk/27.3.13750724';
14
+ const t = `${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin`;
15
+ const t2 = `${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64`;
16
+
17
+ const iOSDevPath = '/Applications/Xcode.app/Contents/Developer';
18
+ const iosBinPath = `${iOSDevPath}/Toolchains/XcodeDefault.xctoolchain/usr/bin`;
19
+ const iosSdkPath = `${iOSDevPath}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk`;
20
+ const iosSimSdkPath = `${iOSDevPath}/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk`;
21
+
22
+ const androidParamsArm64 = [
23
+ '-e', `AR=${t}/llvm-ar`,
24
+ '-e', `AS=${t}/llvm-as`,
25
+ '-e', `CC=${t}/${CROSSCOMPILER_ARM64}-clang`,
26
+ '-e', `CXX=${t}/${CROSSCOMPILER_ARM64}-clang++`,
27
+ '-e', `LD=${t}/ld`,
28
+ '-e', `RANLIB=${t}/llvm-ranlib`,
29
+ '-e', `STRIP=${t}/llvm-strip`,
30
+ '-e', `NM=${t}/nm`,
31
+ '-e', `CFLAGS=--sysroot=${t2}/sysroot`,
32
+ ];
33
+
34
+ const androidParamsX86_64 = [
35
+ '-e', `AR=${t}/llvm-ar`,
36
+ '-e', `AS=${t}/llvm-as`,
37
+ '-e', `CC=${t}/${CROSSCOMPILER_x86_64}-clang`,
38
+ '-e', `CXX=${t}/${CROSSCOMPILER_x86_64}-clang++`,
39
+ '-e', `LD=${t}/ld`,
40
+ '-e', `RANLIB=${t}/llvm-ranlib`,
41
+ '-e', `STRIP=${t}/llvm-strip`,
42
+ '-e', `NM=${t}/nm`,
43
+ '-e', `CFLAGS=--sysroot=${t2}/sysroot`,
44
+ ];
45
+
46
+ const IOS_HOST_FLAGS = `-arch arm64 -isysroot ${iosSdkPath} -fembed-bitcode`;
47
+ const IOS_SIM_HOST_FLAGS = `-arch arm64 -isysroot ${iosSimSdkPath} -fembed-bitcode`;
48
+ const IOS_IPHONE_PARAMS = [
49
+ '-e', `CFLAGS="${IOS_HOST_FLAGS}"`,
50
+ '-e', `CXXFLAGS="${IOS_HOST_FLAGS}"`,
51
+ '-e', `LDFLAGS="${IOS_HOST_FLAGS}"`,
52
+ ];
53
+ const IOS_SIM_PARAMS = [
54
+ '-e', `CFLAGS="${IOS_SIM_HOST_FLAGS}"`,
55
+ '-e', `CXXFLAGS="${IOS_SIM_HOST_FLAGS}"`,
56
+ '-e', `LDFLAGS="${IOS_SIM_HOST_FLAGS}"`,
57
+ ];
58
+ const iosParams = [
59
+ '-e', `AR=${iosBinPath}/ar`,
60
+ '-e', `AS=${iosBinPath}/as`,
61
+ '-e', `CC=${iosBinPath}/clang`,
62
+ '-e', `CXX=${iosBinPath}/clang++`,
63
+ '-e', `CPP=${iosBinPath}/cpp`,
64
+ '-e', `LD=${iosBinPath}/ld`,
65
+ '-e', `RANLIB=${iosBinPath}/ranlib`,
66
+ '-e', `STRIP=${iosBinPath}/strip`,
67
+ '-e', `NM=${iosBinPath}/llvm-nm`,
68
+ ];
69
+
70
+ /* const iosMetalParams = [
71
+ '-e', `AR=${iosBinPath}/metal-ar`,
72
+ '-e', `AS=${iosBinPath}/metal-as`,
73
+ '-e', `CC=${iosBinPath}/clang`,
74
+ '-e', `CXX=${iosBinPath}/clang++`,
75
+ '-e', `CPP=${iosBinPath}/cpp`,
76
+ '-e', `LD=${iosBinPath}/ld`,
77
+ '-e', `RANLIB=${iosBinPath}/metal-ranlib`,
78
+ '-e', `STRIP=${iosBinPath}/metal-strip`,
79
+ '-e', `NM=${iosBinPath}/metal-nm`,
80
+ '-e', `CFLAGS="${IOS_HOST_FLAGS}"`,
81
+ '-e', `CXXFLAGS="${IOS_HOST_FLAGS}"`,
82
+ '-e', `LDFLAGS="${IOS_HOST_FLAGS}"`,
83
+ ]; */
84
+
85
+ export default function run(program, params = [], platformPrefix = null, target = null, dockerOptions = {}) {
86
+ const buildPath = platformPrefix ? `${state.config.paths.build}/${platformPrefix}/${target.path}` : state.config.paths.build;
87
+ if (!fs.existsSync(buildPath)) {
88
+ fs.mkdirSync(buildPath, { recursive: true });
89
+ }
90
+
91
+ // wasi is host-run only with a locally configured wasi-sdk; otherwise docker carries it.
92
+ const wasiHostSdk = target?.platform === 'wasi' ? resolveWasiSdkPath(state.config.system) : null;
93
+ if ((target?.platform !== 'ios' && !(target?.platform === 'wasi' && wasiHostSdk)) || program !== null) {
94
+ // Google ships the linux NDK for x86_64 only, so the android index carries no arm64 leaf;
95
+ // pull its amd64 leaf by ref or an arm64 host finds no matching manifest.
96
+ pullDockerImage(imageRoleFor(target), target?.platform === 'android' ? 'linux/amd64' : undefined);
97
+ }
98
+
99
+ let dProgram = program;
100
+ let dParams = params;
101
+ let platformParams = [];
102
+ if (program === null) {
103
+ switch (target.platform) {
104
+ case 'wasm':
105
+ platformParams = ['-e', 'CXXFLAGS=-fwasm-exceptions', '-e', 'CFLAGS=-fwasm-exceptions'];
106
+ if (params[0].includes('configure')) dProgram = 'emconfigure';
107
+ else if (params[0] === 'make') dProgram = 'emmake';
108
+ else if (params[0] === 'cmake') dProgram = 'emcmake';
109
+ else if (params[0] === 'cc') dProgram = 'emcc';
110
+ break;
111
+ case 'wasi': {
112
+ const wasiSdk = wasiHostSdk || '/opt/wasi-sdk';
113
+ if (wasiHostSdk && !fs.existsSync(`${wasiHostSdk}/share/wasi-sysroot/lib/${WASI_TARGET_TRIPLE}`)) {
114
+ throw new Error(`crossbind: the wasi-sdk at ${wasiHostSdk} has no ${WASI_TARGET_TRIPLE} sysroot. platform:'wasi' targets WASI 0.3 (p3) - upgrade to wasi-sdk >= 34.`);
115
+ }
116
+ [dProgram, ...dParams] = params;
117
+ platformParams = [
118
+ '-e', `CC=${wasiSdk}/bin/clang`,
119
+ '-e', `CXX=${wasiSdk}/bin/clang++`,
120
+ '-e', `AR=${wasiSdk}/bin/ar`,
121
+ '-e', `RANLIB=${wasiSdk}/bin/ranlib`,
122
+ '-e', `NM=${wasiSdk}/bin/nm`,
123
+ '-e', `CFLAGS=${wasiCFlags().join(' ')}`,
124
+ '-e', `CXXFLAGS=${wasiCxxFlags().join(' ')}`,
125
+ ];
126
+ if (dProgram === 'cmake' && dParams[0] !== '--build' && dParams[0] !== '--install') {
127
+ dParams = [
128
+ ...dParams,
129
+ `-DCMAKE_TOOLCHAIN_FILE=${wasiSdk}/share/cmake/wasi-sdk-p3.cmake`,
130
+ ];
131
+ } else if (dProgram === 'wasi-clang++') {
132
+ dProgram = `${wasiSdk}/bin/clang++`;
133
+ platformParams = [];
134
+ } else if (dProgram === 'wasi-clang') {
135
+ dProgram = `${wasiSdk}/bin/clang`;
136
+ platformParams = [];
137
+ }
138
+ break;
139
+ }
140
+ case 'android':
141
+ [dProgram, ...dParams] = params;
142
+ platformParams = target.arch === 'x86_64' ? androidParamsX86_64 : androidParamsArm64;
143
+ if (dProgram === 'cmake') {
144
+ dParams = [
145
+ ...dParams,
146
+ `-DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake`,
147
+ `-DANDROID_ABI=${target.arch === 'x86_64' ? 'x86_64' : 'arm64-v8a'}`,
148
+ '-DANDROID_PLATFORM=android-33',
149
+ `-DANDROID_NDK=${ANDROID_NDK}`,
150
+ ];
151
+ }
152
+ break;
153
+ case 'ios':
154
+ [dProgram, ...dParams] = params;
155
+ platformParams = [...iosParams, ...(target.arch === 'iphoneos' ? IOS_IPHONE_PARAMS : IOS_SIM_PARAMS)];
156
+ if (dProgram === 'cmake') {
157
+ platformParams = [];
158
+ if (dParams[0] !== '--build' && dParams[0] !== '--install') {
159
+ dParams = [
160
+ ...dParams,
161
+ '-G', 'Xcode',
162
+ '-DBUILD_SHARED_LIBS=OFF',
163
+ '-DFRAMEWORK=TRUE',
164
+ '-DCMAKE_OSX_DEPLOYMENT_TARGET=13.0',
165
+ '-DCMAKE_SYSTEM_NAME=iOS',
166
+ `-DMACOSX_FRAMEWORK_IDENTIFIER=dev.crossbind.${state.config.general.name}`,
167
+ `-DCMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER=dev.crossbind.${state.config.general.name}`,
168
+ `-DCMAKE_OSX_SYSROOT='${target.arch === 'iphoneos' ? iosSdkPath : iosSimSdkPath}'`,
169
+ '-DCMAKE_OSX_ARCHITECTURES=arm64',
170
+ '-DCMAKE_SYSTEM_PROCESSOR=arm64',
171
+ `-DCMAKE_C_FLAGS=${target.arch === 'iphoneos' ? '-fembed-bitcode' : '-fembed-bitcode-marker'}`,
172
+ `-DCMAKE_CXX_FLAGS=${target.arch === 'iphoneos' ? '-fembed-bitcode' : '-fembed-bitcode-marker'}`,
173
+ '-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY=\'iPhone Developer\'',
174
+ `-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=${state.config.system.XCODE_DEVELOPMENT_TEAM}`,
175
+ ];
176
+ }
177
+ } else if (dProgram === 'ios-cmake') {
178
+ dProgram = 'cmake';
179
+ platformParams = [];
180
+ if (dParams[0] !== '--build' && dParams[0] !== '--install') {
181
+ dParams = [
182
+ ...dParams,
183
+ `-DCMAKE_TOOLCHAIN_FILE='${state.config.paths.cli}/assets/cmake/ios.toolchain.cmake'`,
184
+ `-DPLATFORM=${target.arch === 'iphoneos' ? 'OS64' : 'SIMULATORARM64'}`,
185
+ '-DARCHS=arm64',
186
+ '-DENABLE_BITCODE=TRUE',
187
+ '-DBUILD_SHARED_LIBS=OFF',
188
+ '-DFRAMEWORK=TRUE',
189
+ '-DCMAKE_OSX_DEPLOYMENT_TARGET=13.0',
190
+ '-DCMAKE_SYSTEM_NAME=iOS',
191
+ `-DMACOSX_FRAMEWORK_IDENTIFIER=dev.crossbind.${state.config.general.name}`,
192
+ `-DCMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER=dev.crossbind.${state.config.general.name}`,
193
+ `-DCMAKE_OSX_SYSROOT='${target.arch === 'iphoneos' ? iosSdkPath : iosSimSdkPath}'`,
194
+ '-DCMAKE_OSX_ARCHITECTURES=arm64',
195
+ '-DCMAKE_SYSTEM_PROCESSOR=arm64',
196
+ `-DCMAKE_C_FLAGS=${target.arch === 'iphoneos' ? '-fembed-bitcode' : '-fembed-bitcode-marker'}`,
197
+ `-DCMAKE_CXX_FLAGS=${target.arch === 'iphoneos' ? '-fembed-bitcode' : '-fembed-bitcode-marker'}`,
198
+ '-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY=\'iPhone Developer\'',
199
+ `-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=${state.config.system.XCODE_DEVELOPMENT_TEAM}`,
200
+ ];
201
+ }
202
+ }
203
+ break;
204
+ default:
205
+ }
206
+ }
207
+
208
+ const env = {};
209
+ let runner = 'DOCKER';
210
+ if (((target?.platform === 'ios' || (target?.platform === 'wasi' && wasiHostSdk)) && program === null) || state.config.system.RUNNER === 'LOCAL') {
211
+ runner = 'LOCAL';
212
+ }
213
+
214
+ if (runner === 'LOCAL') {
215
+ const allowedEnv = [
216
+ '^PWD$', '^SHELL$', '^LC_CTYPE$', '^PATH$', '^HOME$', '^TMPDIR$', '^USER$',
217
+ '^PODS_*', '^CONFIGURATION_BUILD_DIR$', '^UNLOCALIZED_RESOURCES_FOLDER_PATH$',
218
+ ];
219
+ Object.entries(process.env).forEach(([key, value]) => {
220
+ if (allowedEnv.some((e) => key.match(e))) {
221
+ env[key] = value;
222
+ }
223
+ });
224
+ }
225
+
226
+ const pParams = [...platformParams, ...(dockerOptions.params || [])];
227
+ for (let i = 0; i < pParams.length; i += 2) {
228
+ if (pParams[i] === '-e') {
229
+ const [key, ...rest] = pParams[i + 1].split('=');
230
+ const value = rest.join('=');
231
+ if (['CFLAGS', 'CXXFLAGS', 'CPPFLAGS', 'LDFLAGS'].includes(key)) {
232
+ let v = value;
233
+ if (v.startsWith('\'') || v.startsWith('"')) {
234
+ v = v.substring(1, v.length - 1);
235
+ }
236
+ if (env[key]) env[key] += ` ${v}`;
237
+ else env[key] = v;
238
+ } else {
239
+ env[key] = value;
240
+ }
241
+ }
242
+ }
243
+
244
+ let fileExecParams;
245
+ if (runner === 'LOCAL') {
246
+ env.PATH = `/opt/homebrew/bin:${env.PATH}`;
247
+
248
+ const options = {
249
+ cwd: dockerOptions.workdir || buildPath,
250
+ stdio: dockerOptions.console ? 'inherit' : ['ignore', 'pipe', 'pipe'],
251
+ env,
252
+ maxBuffer: EXEC_MAX_BUFFER,
253
+ };
254
+ fileExecParams = [dProgram, dParams, options];
255
+ } else if (runner === 'DOCKER') {
256
+ const dockerEnv = [];
257
+ Object.entries(env).forEach(([key, value]) => {
258
+ dockerEnv.push('-e', `${key}=${value}`);
259
+ });
260
+ const options = { cwd: buildPath, stdio: dockerOptions.console ? 'inherit' : ['ignore', 'pipe', 'pipe'], maxBuffer: EXEC_MAX_BUFFER };
261
+
262
+ let runnerParams;
263
+ let imageOrContainer;
264
+ const role = imageRoleFor(target);
265
+ if (state.config.system.RUNNER === 'DOCKER_RUN') {
266
+ imageOrContainer = getDockerImage(role);
267
+ runnerParams = ['run', '--rm', '-v', `${state.config.paths.base}:/tmp/crossbind/live`];
268
+ if (target?.platform === 'android') {
269
+ // Google ships the linux NDK for x86_64 only; use the amd64 leaf ref (classic store holds one platform per digest).
270
+ pullDockerImage(role, 'linux/amd64');
271
+ imageOrContainer = getDockerImage(role, 'linux/amd64');
272
+ runnerParams.push('--platform', 'linux/amd64');
273
+ }
274
+ } else if (state.config.system.RUNNER === 'DOCKER_EXEC') {
275
+ imageOrContainer = getDockerContainerName(state.config.paths.base, role);
276
+ runnerParams = ['exec'];
277
+ } else {
278
+ throw new Error(`The runner ${state.config.system.RUNNER} is invalid.`);
279
+ }
280
+
281
+ const args = [
282
+ ...runnerParams,
283
+ '--user', getOsUserAndGroupId(),
284
+ '--workdir', replaceBasePathForDocker(dockerOptions.workdir || buildPath),
285
+ ...replaceBasePathForDocker(dockerEnv),
286
+ // '-e', replaceBasePathForDocker(`CCACHE_DIR=${state.config.paths.build}/ccache`),
287
+ imageOrContainer,
288
+ replaceBasePathForDocker(dProgram),
289
+ ...replaceBasePathForDocker(dParams),
290
+ ];
291
+ fileExecParams = ['docker', args, options];
292
+ } else {
293
+ throw new Error(`The runner ${state.config.system.RUNNER} or command is invalid.`);
294
+ }
295
+
296
+ try {
297
+ execFileSync(...fileExecParams);
298
+ } catch (e) {
299
+ if (e?.stdout?.length) console.log(e.stdout.toString());
300
+ if (e?.stderr?.length) console.error(e.stderr.toString());
301
+ throw new Error(`crossbind: command failed${dProgram ? ` (${dProgram})` : ''} with exit code ${e?.status ?? 'unknown'}`, { cause: e });
302
+ }
303
+ }
304
+
305
+ function replaceBasePathForDocker(data) {
306
+ return replaceBasePathForDockerUtil(data, state.config.paths.base);
307
+ }
@@ -0,0 +1,54 @@
1
+ import state from '../state/index.js';
2
+ import { filterTargetSpecs } from '../utils/targets.js';
3
+
4
+ const platforms = [...new Set(state.targets.map(target => target.platform).filter(t => t))];
5
+ const archs = [...new Set(state.targets.map(target => target.arch).filter(t => t))];
6
+ const runtimes = [...new Set(state.targets.map(target => target.runtime).filter(t => t))];
7
+ const buildTypes = [...new Set(state.targets.map(target => target.buildType).filter(t => t))];
8
+ const runtimeEnvs = [...new Set(state.targets.map(target => target.runtimeEnv).filter(t => t))];
9
+
10
+ export function getTargetParams(givenTargetParams = {}, noParamCheck = false) {
11
+ const { platform, arch, runtime, buildType, runtimeEnv } = givenTargetParams;
12
+ if (!noParamCheck && (
13
+ (state.config.target.platform && platform && !platform.includes(state.config.target.platform))
14
+ || (state.config.target.arch && arch && !arch.includes(state.config.target.arch))
15
+ || (state.config.target.runtime && runtime && !runtime.includes(state.config.target.runtime))
16
+ || (state.config.target.buildType && buildType && !buildType.includes(state.config.target.buildType))
17
+ || (state.config.target.runtimeEnv && runtimeEnv && !runtimeEnv.includes(state.config.target.runtimeEnv))
18
+ )) {
19
+ throw new Error('Invalid target parameters');
20
+ }
21
+ return {
22
+ platform: state.config.target.platform ? [state.config.target.platform] : platform || platforms,
23
+ arch: state.config.target.arch ? [state.config.target.arch] : arch || archs,
24
+ runtime: state.config.target.runtime ? [state.config.target.runtime] : runtime || runtimes,
25
+ buildType: state.config.target.buildType ? [state.config.target.buildType] : buildType || buildTypes,
26
+ runtimeEnv: state.config.target.runtimeEnv ? [state.config.target.runtimeEnv] : runtimeEnv || runtimeEnvs,
27
+ };
28
+ }
29
+
30
+ export function getBuildTargets(targetParams) {
31
+ const { platform, arch, runtime, buildType, runtimeEnv } = targetParams;
32
+ return state.targets.filter(t => (
33
+ (!t.platform || platform.includes(t.platform))
34
+ && (!t.arch || arch.includes(t.arch))
35
+ && (!t.runtime || runtime.includes(t.runtime))
36
+ && (!t.buildType || buildType.includes(t.buildType))
37
+ && (!t.runtimeEnv || runtimeEnv.includes(t.runtimeEnv))
38
+ ));
39
+ }
40
+
41
+ export function getFilteredBuildTargets(targetParams, targetFilter) {
42
+ const buildTargets = getBuildTargets(targetParams);
43
+ return buildTargets.filter(t => (
44
+ (!targetFilter.platform || t.platform === targetFilter.platform)
45
+ && (!targetFilter.arch || t.arch === targetFilter.arch)
46
+ && (!targetFilter.runtime || t.runtime === targetFilter.runtime)
47
+ && (!targetFilter.buildType || t.buildType === targetFilter.buildType)
48
+ && (!targetFilter.runtimeEnv || t.runtimeEnv === targetFilter.runtimeEnv)
49
+ ));
50
+ }
51
+
52
+ export function getFilteredTargetSpec(targetSpecs, target) {
53
+ return filterTargetSpecs(targetSpecs, target);
54
+ }
@@ -0,0 +1,61 @@
1
+ cmake_minimum_required(VERSION 3.28)
2
+ set(CMAKE_CXX_STANDARD 17)
3
+ project("${PROJECT_NAME}")
4
+
5
+ option(BUILD_BRIDGE "Build Bridge" OFF)
6
+ option(BUILD_SOURCE "Build Source" OFF)
7
+
8
+ foreach(depend IN ZIP_LISTS DEPENDS_CMAKE_PATHS DEPENDS_CMAKE_NAMES)
9
+ # Source-cmake packages are linked (and their PUBLIC includes propagated) by this exact
10
+ # name below, so their CMakeLists must create the target as CROSSBIND_LIB_NAME.
11
+ set(CROSSBIND_LIB_NAME "${depend_1}")
12
+ add_subdirectory("${depend_0}" "${CMAKE_CURRENT_BINARY_DIR}/dependencies/${depend_1}")
13
+ endforeach()
14
+
15
+ if (BUILD_SOURCE)
16
+ file(GLOB_RECURSE BUILD_SRC_FILES ${NATIVE_GLOB})
17
+ endif(BUILD_SOURCE)
18
+
19
+ if (BUILD_BRIDGE)
20
+ file(GLOB BRIDGE_SRC_FILES "${BRIDGE_DIR}/*.i.cpp;${EXTERNAL_BRIDGE_GLOB}")
21
+ endif(BUILD_BRIDGE)
22
+
23
+ file(GLOB_RECURSE EXTERNAL_SRC_FILES ${EXTERNAL_NATIVE_GLOB})
24
+
25
+ set(SRC_FILES "${BUILD_SRC_FILES}" "${BRIDGE_SRC_FILES}" "${EXTERNAL_SRC_FILES}")
26
+ add_library("${PROJECT_NAME}" "${BUILD_TYPE}" ${SRC_FILES})
27
+
28
+ set(EXTRA_LINK_LIBRARIES "")
29
+ if(ANDROID)
30
+ set(EXTRA_LINK_LIBRARIES "log")
31
+ endif()
32
+
33
+ if (NOT ${COMPILE_OPTIONS} STREQUAL "")
34
+ target_compile_options("${PROJECT_NAME}" PUBLIC "${COMPILE_OPTIONS}")
35
+ endif()
36
+
37
+ if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "")
38
+ target_link_libraries("${PROJECT_NAME}" "${DEPENDS_CMAKE_NAMES}" "${EXTRA_LINK_LIBRARIES}")
39
+ endif()
40
+
41
+ file(GLOB_RECURSE HEADER_FILES ${HEADER_GLOB})
42
+ target_include_directories("${PROJECT_NAME}" PUBLIC "${HEADER_DIR}")
43
+
44
+ install(TARGETS "${PROJECT_NAME}" DESTINATION "lib")
45
+ if(NOT "${HEADER_FILES}" STREQUAL "")
46
+ target_sources("${PROJECT_NAME}"
47
+ PUBLIC FILE_SET HEADERS
48
+ BASE_DIRS "${HEADER_DIR}"
49
+ FILES "${HEADER_FILES}")
50
+ install(TARGETS "${PROJECT_NAME}" FILE_SET HEADERS DESTINATION "include")
51
+ endif()
52
+
53
+ if (BUILD_SOURCE)
54
+
55
+ endif(BUILD_SOURCE)
56
+ unset(BUILD_SOURCE CACHE)
57
+
58
+ if (BUILD_BRIDGE)
59
+ install(TARGETS "${PROJECT_NAME}")
60
+ endif(BUILD_BRIDGE)
61
+ unset(BUILD_BRIDGE CACHE)
@@ -0,0 +1,57 @@
1
+ set(MY_LIST "___PROJECT_HOST___")
2
+ LIST(FIND MY_LIST "${PROJECT_TARGET_HOST}" PACKAGE_HOST_INDEX)
3
+ if(PACKAGE_HOST_INDEX EQUAL -1)
4
+ set(PROJECT_TARGET_HOST "${PROJECT_TARGET_HOST_RELEASE}")
5
+ endif()
6
+ LIST(FIND MY_LIST "${PROJECT_TARGET_HOST}" PACKAGE_HOST_INDEX)
7
+
8
+ if(NOT PACKAGE_HOST_INDEX EQUAL -1)
9
+ cmake_minimum_required(VERSION 3.28)
10
+ set(CMAKE_CXX_STANDARD 11)
11
+ set(PROJECT_NAME "___PROJECT_NAME___")
12
+ set(PROJECT_LIBS "___PROJECT_LIBS___")
13
+ project("${PROJECT_NAME}")
14
+
15
+ if (APPLE AND CMAKE_SYSTEM_NAME STREQUAL "iOS")
16
+ set(PACKAGE_DIR "${PROJECT_SOURCE_DIR}/../..")
17
+ else()
18
+ set(PACKAGE_DIR "${PROJECT_SOURCE_DIR}/${PROJECT_TARGET_HOST}/lib")
19
+ endif()
20
+
21
+ set(PROJECT_LIBS_DIR)
22
+ foreach(L IN LISTS PROJECT_LIBS)
23
+ SET(FOUND_LIB "FOUND_LIB-NOTFOUND")
24
+ find_library(FOUND_LIB
25
+ NAMES "${L}"
26
+ PATHS "${PACKAGE_DIR}"
27
+ NO_CACHE
28
+ NO_DEFAULT_PATH
29
+ NO_CMAKE_FIND_ROOT_PATH
30
+ REQUIRED
31
+ )
32
+ LIST(APPEND PROJECT_LIBS_DIR ${FOUND_LIB})
33
+ endforeach()
34
+
35
+ add_library("${PROJECT_NAME}" INTERFACE)
36
+ # Cargo producers register through an init-array constructor nothing references. KEEP pins the
37
+ # generated keep symbol (--undefined) so only the registration object is pulled - never the
38
+ # whole archive, because each Rust staticlib bundles libstd and fully loading two of them
39
+ # duplicates thousands of std symbols. FORCE (manual-bindings crates, no keep symbol) falls
40
+ # back to whole-archive - safe only as the app's single loaded Rust archive. (Apple consumers
41
+ # get the equivalent flags from the generated podspec instead.)
42
+ set(PROJECT_RUST_LINK "___PROJECT_WHOLE_ARCHIVE___")
43
+ if(PROJECT_RUST_LINK STREQUAL "KEEP" AND NOT APPLE)
44
+ target_link_libraries("${PROJECT_NAME}" INTERFACE "${PROJECT_LIBS_DIR}")
45
+ foreach(L IN LISTS PROJECT_LIBS)
46
+ target_link_options("${PROJECT_NAME}" INTERFACE "-Wl,--undefined=crossbind_keep_${L}")
47
+ endforeach()
48
+ elseif(PROJECT_RUST_LINK STREQUAL "FORCE" AND NOT APPLE)
49
+ target_link_libraries("${PROJECT_NAME}" INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,${PROJECT_LIBS_DIR}>")
50
+ else()
51
+ target_link_libraries("${PROJECT_NAME}" INTERFACE "${PROJECT_LIBS_DIR}")
52
+ endif()
53
+
54
+ if(NOT APPLE)
55
+ target_include_directories("${PROJECT_NAME}" INTERFACE "${PROJECT_SOURCE_DIR}/${PROJECT_TARGET_HOST}/include")
56
+ endif()
57
+ endif()