crossbind 2.0.0-beta.58 → 2.0.0-beta.62

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 (43) hide show
  1. package/README.md +7 -5
  2. package/package.json +4 -4
  3. package/src/actions/buildDependencies.js +22 -7
  4. package/src/actions/buildJs.js +1 -1
  5. package/src/actions/buildLib.js +2 -0
  6. package/src/actions/buildNode.js +149 -0
  7. package/src/actions/buildWasm.js +43 -66
  8. package/src/actions/createInterface.js +101 -27
  9. package/src/actions/createLib.js +3 -2
  10. package/src/actions/getAllBridges.js +1 -1
  11. package/src/actions/getLinkInputs.js +63 -0
  12. package/src/actions/licenses.js +34 -0
  13. package/src/actions/run.js +98 -3
  14. package/src/assets/cmake/CMakeLists.txt +2 -2
  15. package/src/assets/cmake/dist.cmake +1 -1
  16. package/src/assets/js-runtime/adapters/worker-comlink.js +6 -2
  17. package/src/assets/toolchain-digests.json +26 -12
  18. package/src/bin.js +59 -13
  19. package/src/index.js +3 -1
  20. package/src/integration/getCrossbindScript.js +10 -12
  21. package/src/integration/getDependFilePath.js +9 -12
  22. package/src/state/index.js +16 -13
  23. package/src/state/loadConfig.js +3 -0
  24. package/src/utils/appRustCrates.js +2 -0
  25. package/src/utils/appSources.js +21 -0
  26. package/src/utils/cargoImport.js +44 -0
  27. package/src/utils/cargoTarget.js +2 -0
  28. package/src/utils/cppDts.js +77 -41
  29. package/src/utils/embindArgumentGuards.js +45 -0
  30. package/src/utils/embindRsFingerprint.js +26 -0
  31. package/src/utils/headerImports.js +45 -0
  32. package/src/utils/inputStamp.js +23 -0
  33. package/src/utils/linkLayout.js +8 -1
  34. package/src/utils/pullDockerImage.js +8 -5
  35. package/src/utils/resolveCompanionPackage.js +51 -0
  36. package/src/utils/resolveEmbindNapi.js +22 -0
  37. package/src/utils/resolveEmbindRust.js +17 -52
  38. package/src/utils/runCargo.js +4 -3
  39. package/src/utils/rustBridgeGen.js +895 -250
  40. package/src/utils/swigInterface.js +19 -4
  41. package/src/utils/systemKeys.js +8 -0
  42. package/src/utils/targets.js +56 -0
  43. package/src/utils/cppFieldBindings.js +0 -23
@@ -1,4 +1,5 @@
1
1
  import upath from 'upath';
2
+ import { ALL_NAMES } from './headerImports.js';
2
3
 
3
4
  // Libraries whose headers are not self-contained (spatialite expects sqlite3.h first) list the
4
5
  // prerequisites in export.headerPrelude of the package that ships the header: its output or project
@@ -39,7 +40,7 @@ export function interfaceIncludes(headerPath, prelude = []) {
39
40
 
40
41
  // The prelude and the completing includes reach only the compiled wrapper and swigMacros only SWIG's parse: SWIG still
41
42
  // wraps just the imported header. The ignored declarations follow the macros, so the retry without macros keeps them.
42
- export function buildInterfaceContent({ moduleName, headerPath, prelude = [], completing = [], swigMacros = [], ignored = [] }) {
43
+ export function buildInterfaceContent({ moduleName, headerPath, prelude = [], completing = [], swigMacros = [], ignored = [], constants = [] }) {
43
44
  const includes = [...new Set([...interfaceIncludes(headerPath, prelude), ...completing])]
44
45
  .map((header) => `#include "${header}"`)
45
46
  .join('\n');
@@ -55,7 +56,7 @@ export function buildInterfaceContent({ moduleName, headerPath, prelude = [], co
55
56
  ${includes}
56
57
  %}
57
58
 
58
- %feature("shared_ptr");
59
+ ${constantRequests(constants)}%feature("shared_ptr");
59
60
  %feature("polymorphic_shared_ptr");
60
61
 
61
62
  ${macros}${ignores}%include "${headerPath}"
@@ -64,6 +65,12 @@ ${macros}${ignores}%include "${headerPath}"
64
65
  `;
65
66
  }
66
67
 
68
+ // SWIG binds a constant only on request, so a header's thousands of macros cost nothing until the app imports one.
69
+ function constantRequests(constants) {
70
+ if (constants === ALL_NAMES) return '%feature("embind:constant");\n\n';
71
+ return constants.length ? `${constants.map((name) => `%feature("embind:constant") ${name};`).join('\n')}\n\n` : '';
72
+ }
73
+
67
74
  export function withoutSwigMacros(content) {
68
75
  return content.replace(/(%feature\("polymorphic_shared_ptr"\);\n\n)(?:#define [^\n]*\n)+\n/, '$1');
69
76
  }
@@ -111,7 +118,7 @@ const tokenize = (text) => text.match(TOKEN) ?? [];
111
118
  const identifiers = (text) => tokenize(text).filter((token) => IDENTIFIER.test(token));
112
119
  // Lines are spliced before comments go, as in the compiler, and a comment marker inside a literal is text.
113
120
  const LITERAL_OR_COMMENT = /"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'|\/\*[\s\S]*?\*\/|\/\/[^\n]*/g;
114
- const stripComments = (text) => text
121
+ export const stripComments = (text) => text
115
122
  .replace(/\\\r?\n/g, ' ')
116
123
  .replace(LITERAL_OR_COMMENT, (match) => (match.startsWith('/') ? ' ' : match));
117
124
 
@@ -154,7 +161,7 @@ function headerReferences(headerText) {
154
161
  // SWIG parses only the imported header, so export, linkage and attribute macros from its #includes read as syntax
155
162
  // errors. Those come from the compiler's macro table, with the flags and versions its #if lines test; type and value
156
163
  // macros stay undefined so every platform's compiler resolves them.
157
- export function selectSwigMacros({ headerText, macros, predefined }) {
164
+ export function selectSwigMacros({ headerText, macros, predefined, constants = [] }) {
158
165
  const memoize = (classify) => {
159
166
  const results = new Map();
160
167
  const check = (name) => {
@@ -190,6 +197,14 @@ export function selectSwigMacros({ headerText, macros, predefined }) {
190
197
  };
191
198
  declarations.forEach((name) => forward(name, isSyntax));
192
199
  conditions.forEach((name) => forward(name, isConstant));
200
+ // An imported constant reaches SWIG with the macros its value uses, so SWIG can type it: SWIG reads no #include, and
201
+ // drops a constant whose value uses a macro it has not seen.
202
+ const isObjectConstant = (name) => !macros.get(name).params && isConstant(name);
203
+ const imported = constants === ALL_NAMES ? [...defined] : constants;
204
+ imported.forEach((name) => {
205
+ if (!defined.has(name)) forward(name, isObjectConstant);
206
+ else identifiers(macros.get(name)?.body ?? '').forEach((token) => forward(token, isObjectConstant));
207
+ });
193
208
 
194
209
  const visible = new Set([
195
210
  ...tokenize(stripComments(headerText)),
@@ -24,6 +24,14 @@ const systemKeys = {
24
24
  description: 'Image reference used for android builds instead of the pinned one. A reference without the release digest disables the reproducibility guarantee. The CROSSBIND_IMAGE_ANDROID environment variable overrides it.',
25
25
  default: '',
26
26
  },
27
+ DOCKER_IMAGE_LINUX: {
28
+ description: 'Image reference used for linux (Node.js addon) builds instead of the pinned one. A reference without the release digest disables the reproducibility guarantee. The CROSSBIND_IMAGE_LINUX environment variable overrides it.',
29
+ default: '',
30
+ },
31
+ DOCKER_IMAGE_WINDOWS: {
32
+ description: 'Image reference used for win32 (Node.js addon) builds instead of the pinned one. A reference without the release digest disables the reproducibility guarantee. The CROSSBIND_IMAGE_WINDOWS environment variable overrides it.',
33
+ default: '',
34
+ },
27
35
  };
28
36
 
29
37
  export default systemKeys;
@@ -92,12 +92,68 @@ export const TARGETS = [
92
92
  {
93
93
  platform: 'ios', arch: 'iphonesimulator', runtime: 'mt', buildType: 'debug',
94
94
  },
95
+ {
96
+ platform: 'darwin', arch: 'arm64', runtime: 'mt', buildType: 'release', runtimeEnv: 'node',
97
+ },
98
+ {
99
+ platform: 'darwin', arch: 'arm64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
100
+ },
101
+ {
102
+ platform: 'darwin', arch: 'x64', runtime: 'mt', buildType: 'release', runtimeEnv: 'node',
103
+ },
104
+ {
105
+ platform: 'darwin', arch: 'x64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
106
+ },
107
+ {
108
+ platform: 'linux', arch: 'arm64', runtime: 'mt', buildType: 'release', runtimeEnv: 'node',
109
+ },
110
+ {
111
+ platform: 'linux', arch: 'arm64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
112
+ },
113
+ {
114
+ platform: 'linux', arch: 'x64', runtime: 'mt', buildType: 'release', runtimeEnv: 'node',
115
+ },
116
+ {
117
+ platform: 'linux', arch: 'x64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
118
+ },
119
+ {
120
+ platform: 'win32', arch: 'arm64', runtime: 'mt', buildType: 'release', runtimeEnv: 'node',
121
+ },
122
+ {
123
+ platform: 'win32', arch: 'arm64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
124
+ },
125
+ {
126
+ platform: 'win32', arch: 'x64', runtime: 'mt', buildType: 'release', runtimeEnv: 'node',
127
+ },
128
+ {
129
+ platform: 'win32', arch: 'x64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
130
+ },
95
131
  ];
96
132
 
133
+ // Built only when named with -p: a plain `crossbind build` keeps producing what it did before
134
+ // native Node.js addons existed.
135
+ export const OPT_IN_PLATFORMS = ['darwin', 'linux', 'win32'];
136
+
137
+ // Built on the host itself: Apple's linker and SDKs exist in no toolchain image.
138
+ export const HOST_BUILT_PLATFORMS = ['ios', 'darwin'];
139
+
97
140
  export function targetPathOf(target) {
98
141
  return `${target.platform}-${target.arch}-${target.runtime}-${target.buildType}`;
99
142
  }
100
143
 
144
+ // Every platform/arch addon of one build type sits next to a single loader, which fills in
145
+ // {platform} and {arch} from the running process. The loader is CommonJS whatever the package
146
+ // "type" says, since it needs __dirname and process.dlopen.
147
+ export function nodeAddonNamesOf(target, projectName) {
148
+ const suffix = target.buildType === 'debug' ? '.debug' : '';
149
+ const addonPattern = `${projectName}.{platform}-{arch}${suffix}.node`;
150
+ return {
151
+ addonPattern,
152
+ addonName: addonPattern.replace('{platform}', target.platform).replace('{arch}', target.arch),
153
+ jsName: `${projectName}.native${suffix}.cjs`,
154
+ };
155
+ }
156
+
101
157
  export function filterTargetSpecs(targetSpecs, target) {
102
158
  return targetSpecs?.filter(t => (
103
159
  (!t.platform || t.platform === target.platform)
@@ -1,23 +0,0 @@
1
- // SWIG's -embind backend emits functions and constructors but no member variables, so
2
- // public fields never reach JS from the generated bridge alone. This post-pass injects
3
- // embind .property lines for the value-semantics fields parseCppSurface captured - the
4
- // same post-processing precedent as bridgeAsyncGuard.
5
-
6
- export function buildFieldPropertyLines(cls) {
7
- return (cls.fields ?? []).map((f) => `.property("${f.name}", &${cls.name}::${f.name})`);
8
- }
9
-
10
- export function injectFieldBindings(bridgeContent, model) {
11
- let out = bridgeContent;
12
- for (const cls of model.classes ?? []) {
13
- const lines = buildFieldPropertyLines(cls)
14
- .filter((line) => !out.includes(line));
15
- if (!lines.length) continue;
16
- // Lazy template match still lands on the right opener: the first `>(\s*"Name")`
17
- // closes the template list even when it contains nested arguments like base<T>.
18
- const opener = new RegExp(`(emscripten::class_<[\\s\\S]*?>\\s*\\(\\s*"${cls.name}"\\s*\\))`);
19
- if (!opener.test(out)) continue;
20
- out = out.replace(opener, `$1\n ${lines.join('\n ')}`);
21
- }
22
- return out;
23
- }