crossbind 2.0.0-beta.62 → 2.0.0-beta.63

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 (107) hide show
  1. package/README.md +2 -2
  2. package/package.json +6 -1
  3. package/src/actions/buildDependencies.js +15 -2
  4. package/src/actions/buildExternal.js +9 -1
  5. package/src/actions/buildLib.js +25 -8
  6. package/src/actions/buildNativeCommand.js +84 -0
  7. package/src/actions/buildNode.js +48 -7
  8. package/src/actions/buildTypes.js +8 -2
  9. package/src/actions/buildWasiCommand.js +0 -4
  10. package/src/actions/buildWasm.js +49 -29
  11. package/src/actions/cloudCommands.js +133 -0
  12. package/src/actions/createImportedBridges.js +64 -0
  13. package/src/actions/createInterface.js +247 -68
  14. package/src/actions/createLib.js +57 -41
  15. package/src/actions/getData.js +4 -1
  16. package/src/actions/getLinkInputs.js +12 -5
  17. package/src/actions/isSourceNewer.js +2 -0
  18. package/src/actions/licenses.js +143 -19
  19. package/src/actions/prepareConanDependencies.js +51 -0
  20. package/src/actions/publishBinary.js +32 -0
  21. package/src/actions/run.js +63 -66
  22. package/src/actions/runnerCommands.js +421 -0
  23. package/src/actions/writeRuntimeEntry.js +69 -0
  24. package/src/assets/cmake/command/CMakeLists.txt +21 -0
  25. package/src/assets/cmake/command/command.cpp +1 -0
  26. package/src/assets/cmake/dist.cmake +2 -1
  27. package/src/assets/configure/install.sh +22 -0
  28. package/src/assets/js-runtime/adapters/path-url.js +6 -3
  29. package/src/assets/js-runtime/adapters/unboundMessage.js +2 -0
  30. package/src/assets/js-runtime/adapters/worker-comlink.js +87 -60
  31. package/src/assets/js-runtime/core.js +11 -0
  32. package/src/assets/node-import-hooks.mjs +61 -0
  33. package/src/assets/toolchain-digests.json +19 -19
  34. package/src/assets/wasi-runtime/stubs.c +29 -0
  35. package/src/bin.js +257 -50
  36. package/src/index.js +3 -0
  37. package/src/integration/bindHeaderImports.js +18 -0
  38. package/src/integration/getCrossbindScript.js +46 -10
  39. package/src/integration/getDependFilePath.js +9 -0
  40. package/src/integration/resolveNativeImport.js +14 -0
  41. package/src/node/dev.mjs +19 -0
  42. package/src/runner/blobs.js +135 -0
  43. package/src/runner/files.js +193 -0
  44. package/src/runner/server.js +320 -0
  45. package/src/state/attachConanDependencies.js +56 -0
  46. package/src/state/index.js +17 -9
  47. package/src/state/loadConfig.js +33 -24
  48. package/src/state/refreshBuiltDependencies.js +20 -0
  49. package/src/state/refreshConanDependencies.js +17 -0
  50. package/src/utils/androidToolchain.js +4 -0
  51. package/src/utils/appRustCrates.js +15 -1
  52. package/src/utils/appSources.js +97 -6
  53. package/src/utils/archiveFlags.js +12 -0
  54. package/src/utils/boundHeaders.js +79 -0
  55. package/src/utils/bridgeExtras.js +486 -0
  56. package/src/utils/bridgeRuntimeFixes.js +48 -0
  57. package/src/utils/bundledLicenses.js +26 -0
  58. package/src/utils/cargoTarget.js +21 -3
  59. package/src/utils/cloudAccount.js +55 -0
  60. package/src/utils/conanDependencies.js +66 -0
  61. package/src/utils/conanImport.js +58 -0
  62. package/src/utils/conanInstall.js +154 -0
  63. package/src/utils/conanProfile.js +203 -0
  64. package/src/utils/conanStage.js +292 -0
  65. package/src/utils/copyToDistSource.js +13 -0
  66. package/src/utils/cppDts.js +58 -15
  67. package/src/utils/darwinToolchain.js +11 -0
  68. package/src/utils/dirLock.js +137 -28
  69. package/src/utils/downloadAndExtractFile.js +34 -7
  70. package/src/utils/embindArgumentGuards.js +41 -2
  71. package/src/utils/embindCallArguments.js +19 -0
  72. package/src/utils/embindJspiQueue.js +25 -0
  73. package/src/utils/execContainer.js +49 -0
  74. package/src/utils/headerImports.js +16 -5
  75. package/src/utils/hostPlatform.js +21 -0
  76. package/src/utils/iosToolchain.js +6 -0
  77. package/src/utils/isSourceCmakePackage.js +10 -0
  78. package/src/utils/licenseReport.js +11 -2
  79. package/src/utils/linuxToolchain.js +24 -0
  80. package/src/utils/makeTreeWritable.js +12 -0
  81. package/src/utils/nativeImports.js +23 -0
  82. package/src/utils/nativeSourceStamp.js +36 -0
  83. package/src/utils/nodeDevBuild.js +92 -0
  84. package/src/utils/packageLicense.js +101 -0
  85. package/src/utils/pthreadMainScript.js +14 -0
  86. package/src/utils/pullDockerImage.js +3 -1
  87. package/src/utils/relocatePrebuilt.js +119 -0
  88. package/src/utils/remoteClient.js +127 -0
  89. package/src/utils/remoteOutputs.js +39 -0
  90. package/src/utils/remoteRunner.js +97 -0
  91. package/src/utils/remoteUpload.js +134 -0
  92. package/src/utils/replaceFile.js +8 -0
  93. package/src/utils/runCargo.js +48 -51
  94. package/src/utils/runConan.js +257 -0
  95. package/src/utils/runtimeEntries.js +56 -0
  96. package/src/utils/rustBridgeGen.js +9 -4
  97. package/src/utils/rustMt.js +4 -0
  98. package/src/utils/scopedEmbind.js +22 -0
  99. package/src/utils/selectRunner.js +81 -0
  100. package/src/utils/sourceFingerprint.js +4 -3
  101. package/src/utils/swigInterface.js +69 -4
  102. package/src/utils/systemKeys.js +35 -3
  103. package/src/utils/targets.js +49 -4
  104. package/src/utils/toolchainNotices.js +5 -0
  105. package/src/utils/wasiToolchain.js +14 -1
  106. package/src/utils/windowsToolchain.js +18 -0
  107. package/vitest.config.js +11 -0
@@ -16,6 +16,29 @@ export function findIgnoredDeclarations(headerFile, configs, headerPath) {
16
16
  return findHeaderOption('ignoredDeclarations', headerFile, configs, headerPath);
17
17
  }
18
18
 
19
+ // SWIG preprocessor lines a package puts right before one of its public headers, in export.swigPreamble: GDAL hides
20
+ // its C SRS API behind `#ifndef SWIG` for its own bindings, and picks VSIStatBufL's typedef by whether VSI_STAT64_T is
21
+ // defined, which a macro defined as itself answers while each platform's compiler still resolves the name.
22
+ export function findSwigPreamble(headerFile, configs, headerPath) {
23
+ return findHeaderOption('swigPreamble', headerFile, configs, headerPath);
24
+ }
25
+
26
+ // Includes SWIG reads inlined into one of a package's headers, in export.swigInlineIncludes. SWIG reads no #include, and
27
+ // libgeotiff fills its key and code enums from .inc files included inside the enum bodies, where the file markers
28
+ // -includeall wraps around an included file do not parse.
29
+ export function findSwigInlineIncludes(headerFile, configs, headerPath) {
30
+ return findHeaderOption('swigInlineIncludes', headerFile, configs, headerPath);
31
+ }
32
+
33
+ const INCLUDE_LINE = /^[ \t]*#[ \t]*include[ \t]*"([^"]+)"[^\n]*$/gm;
34
+
35
+ export function inlineIncludes(headerText, names, readInclude) {
36
+ const included = new Set([...headerText.matchAll(INCLUDE_LINE)].map(([, name]) => name));
37
+ const missing = names.filter((name) => !included.has(name));
38
+ if (missing.length) throw new Error(`crossbind: swigInlineIncludes lists ${missing.join(', ')}, which the header does not include.`);
39
+ return headerText.replace(INCLUDE_LINE, (line, name) => (names.includes(name) ? readInclude(name) : line));
40
+ }
41
+
19
42
  function findHeaderOption(option, headerFile, configs, headerPath) {
20
43
  const file = upath.normalize(headerFile);
21
44
  let owner = null;
@@ -40,26 +63,35 @@ export function interfaceIncludes(headerPath, prelude = []) {
40
63
 
41
64
  // The prelude and the completing includes reach only the compiled wrapper and swigMacros only SWIG's parse: SWIG still
42
65
  // wraps just the imported header. The ignored declarations follow the macros, so the retry without macros keeps them.
43
- export function buildInterfaceContent({ moduleName, headerPath, prelude = [], completing = [], swigMacros = [], ignored = [], constants = [] }) {
66
+ export function buildInterfaceContent({
67
+ moduleName, headerPath, prelude = [], completing = [], swigMacros = [], ignored = [], constants = [], preamble = [], functions = ALL_NAMES,
68
+ extras = [],
69
+ }) {
44
70
  const includes = [...new Set([...interfaceIncludes(headerPath, prelude), ...completing])]
45
71
  .map((header) => `#include "${header}"`)
46
72
  .join('\n');
47
73
  const macros = swigMacros.length ? `${swigMacros.join('\n')}\n\n` : '';
48
74
  const ignores = ignored.length ? `${ignored.map((name) => `%ignore ${name};`).join('\n')}\n\n` : '';
75
+ // The marker keeps a preamble that starts with #define apart from the macro block the retry drops.
76
+ const preambleBlock = preamble.length ? `// package preamble\n${preamble.join('\n')}\n\n` : '';
77
+ const extraBlock = extras.length ? `${extras.join('\n')}\n\n` : '';
78
+ // Named constants are asked for ahead of the macros, which may define one. Every constant is asked for after them:
79
+ // SWIG binds an interface's own #define lines, and the prelude only carries other headers' values for the parse.
80
+ const everyConstant = constants === ALL_NAMES;
49
81
  const module = moduleName.replace(/\W/g, '_');
50
82
  return `#ifndef _${module}_I
51
83
  #define _${module}_I
52
84
 
53
85
  %module ${module}
54
86
 
55
- %{
87
+ ${extraBlock}%{
56
88
  ${includes}
57
89
  %}
58
90
 
59
- ${constantRequests(constants)}%feature("shared_ptr");
91
+ ${everyConstant ? '' : constantRequests(constants)}%feature("shared_ptr");
60
92
  %feature("polymorphic_shared_ptr");
61
93
 
62
- ${macros}${ignores}%include "${headerPath}"
94
+ ${macros}${functionRequests(functions)}${ignores}${preambleBlock}${everyConstant ? constantRequests(constants) : ''}%include "${headerPath}"
63
95
 
64
96
  #endif
65
97
  `;
@@ -71,6 +103,19 @@ function constantRequests(constants) {
71
103
  return constants.length ? `${constants.map((name) => `%feature("embind:constant") ${name};`).join('\n')}\n\n` : '';
72
104
  }
73
105
 
106
+ // SWIG ignores every global function, then wraps the listed names again: class members, enums and constants stay. The
107
+ // package's own ignores come after, since SWIG applies the later of two renames of a name.
108
+ function functionRequests(functions) {
109
+ if (functions === ALL_NAMES) return '';
110
+ const names = [...new Set(functions)].sort();
111
+ return `%rename($ignore, %$isfunction, %$isglobal) "";\n${names.map((name) => `%rename("%s") ${name};\n`).join('')}\n`;
112
+ }
113
+
114
+ // Editor declarations need the whole header even when the compiled bridge selects functions by import.
115
+ export function interfaceWithAllFunctions(content) {
116
+ return content.replace(/%rename\(\$ignore, %\$isfunction, %\$isglobal\) "";\n(?:%rename\("%s"\) [A-Za-z_]\w*;\n)*\n/g, '');
117
+ }
118
+
74
119
  export function withoutSwigMacros(content) {
75
120
  return content.replace(/(%feature\("polymorphic_shared_ptr"\);\n\n)(?:#define [^\n]*\n)+\n/, '$1');
76
121
  }
@@ -333,6 +378,26 @@ function walkScopes(text, visit) {
333
378
  });
334
379
  }
335
380
 
381
+ // Whether the token at `index` sits inside the parentheses of the declaration it belongs to: a prototype's parameter.
382
+ function isInsideParentheses(tokens, index) {
383
+ let depth = 0;
384
+ for (let at = index - 1; at >= 0 && ![';', '{', '}'].includes(tokens[at]); at -= 1) {
385
+ if (tokens[at] === '(') depth += 1;
386
+ else if (tokens[at] === ')') depth -= 1;
387
+ }
388
+ return depth > 0;
389
+ }
390
+
391
+ // Whether a variable `name` is declared outside every class, namespace and block, as `extern int name;` is.
392
+ export function declaresAtFileScope(text, name) {
393
+ let found = false;
394
+ walkScopes(text, (token, index, tokens, scopes) => {
395
+ if (token === name && scopes.every((scope) => scope.kind === 'extern') && [';', '=', '[', ','].includes(tokens[index + 1])
396
+ && !isInsideParentheses(tokens, index)) found = true;
397
+ });
398
+ return found;
399
+ }
400
+
336
401
  // Classes, structs and enums by qualified name, including the names C typedefs give them, each mapped to the first header
337
402
  // defining it. Preprocessor branches are all read.
338
403
  export function indexTypeDefinitions(files) {
@@ -4,12 +4,36 @@ const systemKeys = {
4
4
  default: '',
5
5
  },
6
6
  RUNNER: {
7
- description: 'The execution environment for running the application.',
8
- options: ['DOCKER_RUN', 'DOCKER_EXEC', 'LOCAL'],
7
+ description: 'Where toolchain steps run: DOCKER_RUN, DOCKER_EXEC, LOCAL, or REMOTE on the runners REMOTE_URL and REMOTE_URL_<IMAGE> name; a machine that names none builds on crossbind cloud once signed in with crossbind login. The CROSSBIND_RUNNER environment variable overrides it.',
8
+ options: ['DOCKER_RUN', 'DOCKER_EXEC', 'LOCAL', 'REMOTE'],
9
9
  default: 'DOCKER_RUN',
10
10
  },
11
+ REMOTE_URL: {
12
+ description: 'Under RUNNER=REMOTE, the runner of every image without an address of its own. Its token comes from the CROSSBIND_TOKEN environment variable. The CROSSBIND_REMOTE_URL environment variable overrides it.',
13
+ default: '',
14
+ },
15
+ REMOTE_URL_WEB: {
16
+ description: 'Under RUNNER=REMOTE, the runner of the web image: wasm builds, wasi builds and the bridge steps of every platform but android. Token: CROSSBIND_TOKEN_WEB. The CROSSBIND_REMOTE_URL_WEB environment variable overrides it.',
17
+ default: '',
18
+ },
19
+ REMOTE_URL_ANDROID: {
20
+ description: 'Under RUNNER=REMOTE, the runner of the android image. Token: CROSSBIND_TOKEN_ANDROID. The CROSSBIND_REMOTE_URL_ANDROID environment variable overrides it.',
21
+ default: '',
22
+ },
23
+ REMOTE_URL_LINUX: {
24
+ description: 'Under RUNNER=REMOTE, the runner of the linux image (linux and linuxmusl builds). Token: CROSSBIND_TOKEN_LINUX. The CROSSBIND_REMOTE_URL_LINUX environment variable overrides it.',
25
+ default: '',
26
+ },
27
+ REMOTE_URL_WINDOWS: {
28
+ description: 'Under RUNNER=REMOTE, the runner of the windows image (win32 builds). Token: CROSSBIND_TOKEN_WINDOWS. The CROSSBIND_REMOTE_URL_WINDOWS environment variable overrides it.',
29
+ default: '',
30
+ },
31
+ CLOUD_URL: {
32
+ description: 'The crossbind cloud that crossbind login, logout and usage talk to. The CROSSBIND_CLOUD_URL environment variable overrides it.',
33
+ default: 'https://api.crossbind.dev',
34
+ },
11
35
  WASI_SDK_PATH: {
12
- description: 'Path to an extracted wasi-sdk (>= 34, wasm32-wasip3 sysroot) used by platform:\'wasi\' builds. The CROSSBIND_WASI_SDK_PATH environment variable overrides it.',
36
+ description: 'Path to an extracted wasi-sdk (>= 34, wasm32-wasip3 sysroot) that platform:\'wasi\' builds use under RUNNER=LOCAL; the other runners use the sdk in the image. The CROSSBIND_WASI_SDK_PATH environment variable overrides it.',
13
37
  default: '',
14
38
  },
15
39
  DOCKER_REGISTRY_MIRROR: {
@@ -35,3 +59,11 @@ const systemKeys = {
35
59
  };
36
60
 
37
61
  export default systemKeys;
62
+
63
+ // Read as LOCAL, a runner crossbind does not know would run recipes and build scripts on the host.
64
+ export function assertRunner(runner, name = 'RUNNER') {
65
+ if (!systemKeys.RUNNER.options.includes(runner)) {
66
+ throw new Error(`crossbind: the runner ${runner} is invalid; ${name} is one of ${systemKeys.RUNNER.options.join(', ')}.`);
67
+ }
68
+ return runner;
69
+ }
@@ -1,7 +1,7 @@
1
1
  // Single source for the build-target matrix and its naming; state/ copies (and then
2
2
  // decorates) these entries, and consumer runtimes read them without touching CLI state.
3
3
 
4
- export const TARGETS = [
4
+ const BASE_TARGETS = [
5
5
  {
6
6
  platform: 'wasm', arch: 'wasm32', runtime: 'st', buildType: 'release', runtimeEnv: 'browser',
7
7
  },
@@ -63,10 +63,10 @@ export const TARGETS = [
63
63
  platform: 'wasm', arch: 'wasm64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
64
64
  },
65
65
  {
66
- platform: 'wasi', arch: 'wasm32', runtime: 'st', buildType: 'release',
66
+ platform: 'wasi', arch: 'wasm32', runtime: 'st', buildType: 'release', runtimeEnv: 'wasi',
67
67
  },
68
68
  {
69
- platform: 'wasi', arch: 'wasm32', runtime: 'st', buildType: 'debug',
69
+ platform: 'wasi', arch: 'wasm32', runtime: 'st', buildType: 'debug', runtimeEnv: 'wasi',
70
70
  },
71
71
  {
72
72
  platform: 'android', arch: 'arm64-v8a', runtime: 'mt', buildType: 'release',
@@ -116,6 +116,18 @@ export const TARGETS = [
116
116
  {
117
117
  platform: 'linux', arch: 'x64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
118
118
  },
119
+ {
120
+ platform: 'linuxmusl', arch: 'arm64', runtime: 'mt', buildType: 'release', runtimeEnv: 'node',
121
+ },
122
+ {
123
+ platform: 'linuxmusl', arch: 'arm64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
124
+ },
125
+ {
126
+ platform: 'linuxmusl', arch: 'x64', runtime: 'mt', buildType: 'release', runtimeEnv: 'node',
127
+ },
128
+ {
129
+ platform: 'linuxmusl', arch: 'x64', runtime: 'mt', buildType: 'debug', runtimeEnv: 'node',
130
+ },
119
131
  {
120
132
  platform: 'win32', arch: 'arm64', runtime: 'mt', buildType: 'release', runtimeEnv: 'node',
121
133
  },
@@ -130,17 +142,43 @@ export const TARGETS = [
130
142
  },
131
143
  ];
132
144
 
145
+ // A native executable links the same archives as the Node-API addon of its platform and arch.
146
+ export const TARGETS = [
147
+ ...BASE_TARGETS,
148
+ ...BASE_TARGETS
149
+ .filter((target) => target.runtimeEnv === 'node' && target.platform !== 'wasm')
150
+ .map((target) => ({ ...target, runtimeEnv: 'native' })),
151
+ ];
152
+
133
153
  // Built only when named with -p: a plain `crossbind build` keeps producing what it did before
134
154
  // native Node.js addons existed.
135
- export const OPT_IN_PLATFORMS = ['darwin', 'linux', 'win32'];
155
+ export const OPT_IN_PLATFORMS = ['darwin', 'linux', 'linuxmusl', 'win32'];
136
156
 
137
157
  // Built on the host itself: Apple's linker and SDKs exist in no toolchain image.
138
158
  export const HOST_BUILT_PLATFORMS = ['ios', 'darwin'];
139
159
 
160
+ // The runtime environment names the binary a build makes; android and ios have none, since
161
+ // React Native links them.
162
+ export const runtimeEnvsOf = (platform) => [
163
+ ...new Set(TARGETS.filter((target) => target.platform === platform && target.runtimeEnv).map((target) => target.runtimeEnv)),
164
+ ];
165
+
166
+ export const RUNTIME_ENVS = [...new Set(TARGETS.map((target) => target.runtimeEnv).filter(Boolean))];
167
+
168
+ // A build makes only the binaries it is asked for: -e first, then the config's target.runtimeEnv.
169
+ // Bundler plugins ask through getTargetParams instead.
170
+ export function selectRuntimeEnvs(cliRuntimeEnvs, configRuntimeEnv) {
171
+ if (cliRuntimeEnvs?.length) return cliRuntimeEnvs;
172
+ return configRuntimeEnv ? [configRuntimeEnv] : [];
173
+ }
174
+
140
175
  export function targetPathOf(target) {
141
176
  return `${target.platform}-${target.arch}-${target.runtime}-${target.buildType}`;
142
177
  }
143
178
 
179
+ // The release build of a target, whose prebuilts a debug build of it falls back to.
180
+ export const releaseTargetOf = (target) => ({ ...target, buildType: 'release', path: target.releasePath });
181
+
144
182
  // Every platform/arch addon of one build type sits next to a single loader, which fills in
145
183
  // {platform} and {arch} from the running process. The loader is CommonJS whatever the package
146
184
  // "type" says, since it needs __dirname and process.dlopen.
@@ -154,6 +192,13 @@ export function nodeAddonNamesOf(target, projectName) {
154
192
  };
155
193
  }
156
194
 
195
+ // One executable per platform, arch and build type, named like the addons it replaces.
196
+ export function nativeCommandNamesOf(target, projectName) {
197
+ const suffix = target.buildType === 'debug' ? '.debug' : '';
198
+ const extension = target.platform === 'win32' ? '.exe' : '';
199
+ return { commandName: `${projectName}.${target.platform}-${target.arch}${suffix}${extension}` };
200
+ }
201
+
157
202
  export function filterTargetSpecs(targetSpecs, target) {
158
203
  return targetSpecs?.filter(t => (
159
204
  (!t.platform || t.platform === target.platform)
@@ -0,0 +1,5 @@
1
+ // Where a build keeps the notices of the runtime a toolchain image linked into its binaries,
2
+ // relative to the build directory; crossbind licenses reads them from there.
3
+ export default function toolchainNoticesDir(platform) {
4
+ return `toolchain-licenses/${platform}`;
5
+ }
@@ -1,5 +1,7 @@
1
1
  // Toolchain surface for platform:'wasi' (wasm32-wasip3, wasi-sdk >= 34; run under `wasmtime`).
2
2
 
3
+ import { isLocalRunner } from './selectRunner.js';
4
+
3
5
  export const WASI_TARGET_TRIPLE = 'wasm32-wasip3';
4
6
  // The sdk's clang still defaults to wasip1, so every compile/link carries --target explicitly.
5
7
  export const WASI_TARGET_FLAGS = [`--target=${WASI_TARGET_TRIPLE}`];
@@ -31,6 +33,7 @@ export const WASI_LINK_LIBS = [
31
33
  '-lunwind',
32
34
  '-lsetjmp',
33
35
  ...WASI_EMULATION_LIBS,
36
+ '-Wl,--wrap=lseek',
34
37
  ];
35
38
 
36
39
  export function wasiCFlags() {
@@ -41,7 +44,17 @@ export function wasiCxxFlags() {
41
44
  return [...WASI_TARGET_FLAGS, ...WASI_COMPILE_DEFINES, '-fwasm-exceptions', ...WASI_EH_CFLAGS];
42
45
  }
43
46
 
44
- // Env override first, then persistent ~/.crossbind.json; null when unset.
47
+ // The host sdk of a RUNNER=LOCAL build: env override first, then persistent ~/.crossbind.json; null when
48
+ // unset, and under the other runners, whose builds use the sdk in the image.
45
49
  export function resolveWasiSdkPath(system, env = process.env) {
50
+ if (!isLocalRunner(system, env)) return null;
46
51
  return env.CROSSBIND_WASI_SDK_PATH || system?.WASI_SDK_PATH || null;
47
52
  }
53
+
54
+ // Recipes read the wasi-sdk location from the environment (lazily, in getBuildParams), so it names
55
+ // the sdk the build uses: the host's under RUNNER=LOCAL, none (the image's) under the other runners.
56
+ export function exportWasiSdkPath(system, env = process.env) {
57
+ const sdk = resolveWasiSdkPath(system, env);
58
+ if (sdk) env.CROSSBIND_WASI_SDK_PATH = sdk;
59
+ else delete env.CROSSBIND_WASI_SDK_PATH;
60
+ }
@@ -0,0 +1,18 @@
1
+ // The windows image's llvm-mingw and its CMake toolchain files, one per target triple: port archives and
2
+ // Conan packages link into the same addon, so they move together.
3
+ const LLVM_MINGW = '/opt/llvm-mingw';
4
+ const WINDOWS_TOOLCHAIN = '/opt/crossbind/windows';
5
+ const WINDOWS_TRIPLES = { x64: 'x86_64-w64-mingw32', arm64: 'aarch64-w64-mingw32' };
6
+
7
+ export const windowsTriple = (target) => WINDOWS_TRIPLES[target.arch];
8
+ export const windowsToolchainFile = (target) => `${WINDOWS_TOOLCHAIN}/${windowsTriple(target)}.cmake`;
9
+ export const windowsTool = (target, name) => `${LLVM_MINGW}/bin/${windowsTriple(target)}-${name}`;
10
+
11
+ // The compilers, archive and resource tools of the target's triple, by the variables build systems read
12
+ // them from. The image's own .pc files describe its libraries, not the target's, so pkg-config reads none.
13
+ export const windowsBuildEnv = (target) => ({
14
+ ...Object.fromEntries(Object.entries({
15
+ CC: 'clang', CXX: 'clang++', AR: 'ar', RANLIB: 'ranlib', NM: 'nm', STRIP: 'strip', RC: 'windres', WINDRES: 'windres',
16
+ }).map(([variable, tool]) => [variable, windowsTool(target, tool)])),
17
+ PKG_CONFIG_LIBDIR: '',
18
+ });
package/vitest.config.js CHANGED
@@ -12,6 +12,17 @@ export default defineConfig({
12
12
  test: {
13
13
  include: ['test/**/*.test.js'],
14
14
  environment: 'node',
15
+ // A developer's own runner settings must not decide where the tested steps run, and neither must their
16
+ // crossbind login: no one signs in to a cloud at this address.
17
+ env: {
18
+ CROSSBIND_RUNNER: '',
19
+ CROSSBIND_REMOTE_URL: '',
20
+ CROSSBIND_REMOTE_URL_WEB: '',
21
+ CROSSBIND_REMOTE_URL_ANDROID: '',
22
+ CROSSBIND_REMOTE_URL_LINUX: '',
23
+ CROSSBIND_REMOTE_URL_WINDOWS: '',
24
+ CROSSBIND_CLOUD_URL: 'http://127.0.0.1:9',
25
+ },
15
26
  coverage: {
16
27
  provider: 'v8',
17
28
  reporter: ['text', 'html'],