crossbind 2.0.0-beta.62 → 2.0.0-beta.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/AGENTS.md +2 -1
  2. package/README.md +2 -2
  3. package/package.json +7 -2
  4. package/src/actions/buildBinTools.js +0 -0
  5. package/src/actions/buildDependencies.js +15 -2
  6. package/src/actions/buildExternal.js +9 -1
  7. package/src/actions/buildJs.js +2 -9
  8. package/src/actions/buildLib.js +25 -8
  9. package/src/actions/buildNativeCommand.js +84 -0
  10. package/src/actions/buildNode.js +49 -10
  11. package/src/actions/buildTypes.js +8 -2
  12. package/src/actions/buildWasiCommand.js +10 -9
  13. package/src/actions/buildWasm.js +72 -31
  14. package/src/actions/cloudCommands.js +133 -0
  15. package/src/actions/createImportedBridges.js +71 -0
  16. package/src/actions/createInterface.js +247 -68
  17. package/src/actions/createLib.js +70 -42
  18. package/src/actions/getData.js +6 -2
  19. package/src/actions/getEnv.js +7 -0
  20. package/src/actions/getLinkInputs.js +12 -5
  21. package/src/actions/isSourceNewer.js +2 -0
  22. package/src/actions/licenses.js +143 -19
  23. package/src/actions/prepareConanDependencies.js +51 -0
  24. package/src/actions/publishBinary.js +32 -0
  25. package/src/actions/run.js +63 -66
  26. package/src/actions/runnerCommands.js +433 -0
  27. package/src/actions/writeRuntimeEntry.js +80 -0
  28. package/src/assets/cmake/command/CMakeLists.txt +21 -0
  29. package/src/assets/cmake/command/command.cpp +1 -0
  30. package/src/assets/cmake/dist.cmake +2 -1
  31. package/src/assets/configure/install.sh +22 -0
  32. package/src/assets/cpp-runtime/node.cpp +18 -1
  33. package/src/assets/js-runtime/adapters/path-url.js +6 -3
  34. package/src/assets/js-runtime/adapters/unboundMessage.js +2 -0
  35. package/src/assets/js-runtime/adapters/worker-comlink.js +138 -60
  36. package/src/assets/js-runtime/core.js +12 -0
  37. package/src/assets/node-import-hooks.mjs +61 -0
  38. package/src/assets/toolchain-digests.json +19 -19
  39. package/src/assets/wasi-runtime/http/README.md +17 -0
  40. package/src/assets/wasi-runtime/http/bindings/crossbind_http.c +2537 -0
  41. package/src/assets/wasi-runtime/http/bindings/crossbind_http.h +826 -0
  42. package/src/assets/wasi-runtime/http/bindings/crossbind_http_component_type.o +0 -0
  43. package/src/assets/wasi-runtime/http/handler.cpp +298 -0
  44. package/src/assets/wasi-runtime/http/include/crossbind/http.h +31 -0
  45. package/src/assets/wasi-runtime/http/wit/deps/LICENSE.md +8 -0
  46. package/src/assets/wasi-runtime/http/wit/deps/cli.wit +28 -0
  47. package/src/assets/wasi-runtime/http/wit/deps/clocks.wit +43 -0
  48. package/src/assets/wasi-runtime/http/wit/deps/http.wit +509 -0
  49. package/src/assets/wasi-runtime/http/wit/deps/random.wit +18 -0
  50. package/src/assets/wasi-runtime/http/wit/world.wit +6 -0
  51. package/src/assets/wasi-runtime/stubs.c +29 -0
  52. package/src/bin.js +218 -161
  53. package/src/cli.js +14 -0
  54. package/src/index.js +3 -0
  55. package/src/integration/bindHeaderImports.js +18 -0
  56. package/src/integration/getCrossbindScript.js +50 -13
  57. package/src/integration/getDependFilePath.js +9 -0
  58. package/src/integration/resolveNativeImport.js +14 -0
  59. package/src/node/dev.mjs +19 -0
  60. package/src/program.js +169 -0
  61. package/src/runner/blobs.js +135 -0
  62. package/src/runner/files.js +193 -0
  63. package/src/runner/server.js +320 -0
  64. package/src/state/attachConanDependencies.js +56 -0
  65. package/src/state/index.js +20 -11
  66. package/src/state/loadConfig.js +37 -35
  67. package/src/state/refreshBuiltDependencies.js +20 -0
  68. package/src/state/refreshConanDependencies.js +17 -0
  69. package/src/utils/androidToolchain.js +4 -0
  70. package/src/utils/appRustCrates.js +19 -1
  71. package/src/utils/appSources.js +97 -6
  72. package/src/utils/archiveFlags.js +12 -0
  73. package/src/utils/boundHeaders.js +79 -0
  74. package/src/utils/bridgeExtras.js +554 -0
  75. package/src/utils/bridgeRuntimeFixes.js +48 -0
  76. package/src/utils/bundledLicenses.js +26 -0
  77. package/src/utils/cargoLock.js +19 -0
  78. package/src/utils/cargoTarget.js +21 -3
  79. package/src/utils/cloudAccount.js +55 -0
  80. package/src/utils/conanDependencies.js +66 -0
  81. package/src/utils/conanImport.js +58 -0
  82. package/src/utils/conanInstall.js +154 -0
  83. package/src/utils/conanProfile.js +203 -0
  84. package/src/utils/conanStage.js +292 -0
  85. package/src/utils/copyToDistSource.js +13 -0
  86. package/src/utils/cppDts.js +60 -15
  87. package/src/utils/darwinToolchain.js +11 -0
  88. package/src/utils/dirLock.js +137 -28
  89. package/src/utils/downloadAndExtractFile.js +34 -7
  90. package/src/utils/embindArgumentGuards.js +41 -2
  91. package/src/utils/embindCallArguments.js +19 -0
  92. package/src/utils/embindJspiQueue.js +25 -0
  93. package/src/utils/embindObjectLifetime.js +35 -0
  94. package/src/utils/execContainer.js +49 -0
  95. package/src/utils/headerImports.js +16 -5
  96. package/src/utils/hostPlatform.js +21 -0
  97. package/src/utils/iosToolchain.js +6 -0
  98. package/src/utils/isSourceCmakePackage.js +10 -0
  99. package/src/utils/licenseReport.js +11 -2
  100. package/src/utils/linuxToolchain.js +24 -0
  101. package/src/utils/makeTreeWritable.js +12 -0
  102. package/src/utils/nativeImports.js +23 -0
  103. package/src/utils/nativeSourceStamp.js +36 -0
  104. package/src/utils/nodeDevBuild.js +92 -0
  105. package/src/utils/packageLicense.js +103 -0
  106. package/src/utils/pthreadMainScript.js +14 -0
  107. package/src/utils/pullDockerImage.js +3 -1
  108. package/src/utils/relocatePrebuilt.js +119 -0
  109. package/src/utils/remoteClient.js +127 -0
  110. package/src/utils/remoteOutputs.js +39 -0
  111. package/src/utils/remoteRunner.js +97 -0
  112. package/src/utils/remoteUpload.js +134 -0
  113. package/src/utils/replaceFile.js +8 -0
  114. package/src/utils/resetCMakeCacheForSource.js +26 -0
  115. package/src/utils/runCargo.js +48 -51
  116. package/src/utils/runConan.js +257 -0
  117. package/src/utils/runtimeEntries.js +56 -0
  118. package/src/utils/rustBridgeGen.js +22 -8
  119. package/src/utils/rustMt.js +9 -0
  120. package/src/utils/rustSysroot.js +7 -5
  121. package/src/utils/scopedEmbind.js +22 -0
  122. package/src/utils/selectRunner.js +81 -0
  123. package/src/utils/sourceFingerprint.js +4 -3
  124. package/src/utils/swigInterface.js +70 -4
  125. package/src/utils/systemConfig.js +13 -0
  126. package/src/utils/systemKeys.js +35 -3
  127. package/src/utils/targets.js +60 -4
  128. package/src/utils/toolchainNotices.js +5 -0
  129. package/src/utils/wasiHttp.js +50 -0
  130. package/src/utils/wasiToolchain.js +14 -1
  131. package/src/utils/windowsToolchain.js +18 -0
  132. package/vitest.config.js +11 -0
@@ -1,25 +1,40 @@
1
1
  import { execFileSync } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { withDirLockSync } from '../utils/dirLock.js';
3
6
  import pullDockerImage, { getDockerImage, getDockerContainerName, imageRoleFor } from '../utils/pullDockerImage.js';
4
7
  import getOsUserAndGroupId from '../utils/getOsUserAndGroupId.js';
5
8
  import replaceBasePathForDockerUtil from '../utils/replaceBasePathForDocker.js';
6
9
  import { DOCKER_RUN_SECURITY_ARGS } from '../utils/dockerSecurity.js';
10
+ import { remoteExecParams, baseMount, referencedPaths } from '../utils/remoteRunner.js';
11
+ import { runnerFor } from '../utils/selectRunner.js';
7
12
  import state from '../state/index.js';
8
13
  import { wasiCFlags, wasiCxxFlags, resolveWasiSdkPath, WASI_TARGET_TRIPLE } from '../utils/wasiToolchain.js';
9
14
  import { HOST_BUILT_PLATFORMS } from '../utils/targets.js';
15
+ import { WASM_EXCEPTION_FLAGS } from '../utils/archiveFlags.js';
16
+ import { ANDROID_NDK, ANDROID_API_LEVEL } from '../utils/androidToolchain.js';
17
+ import { IOS_DEVELOPER_DIR, IOS_DEPLOYMENT_TARGET, XCODE_TOOLCHAIN_BIN } from '../utils/iosToolchain.js';
18
+ import { LINUX_ARCHIVE_FLAGS, linuxBuildEnv, linuxToolchainFile } from '../utils/linuxToolchain.js';
19
+ import { windowsBuildEnv, windowsToolchainFile } from '../utils/windowsToolchain.js';
20
+ import {
21
+ DARWIN_CC, DARWIN_CXX, DARWIN_DEPLOYMENT_TARGET, DARWIN_HOST_PACKAGE_PREFIXES, DARWIN_TOOLS_BIN,
22
+ } from '../utils/darwinToolchain.js';
10
23
 
11
24
  // Native builds can outrun Node's 1 MiB default pipe buffer; without a raised cap a successful build dies with ENOBUFS.
12
25
  const EXEC_MAX_BUFFER = 512 * 1024 * 1024;
13
- const CROSSCOMPILER_ARM64 = 'aarch64-linux-android33';
14
- const CROSSCOMPILER_x86_64 = 'x86_64-linux-android33';
15
- const ANDROID_NDK = '/opt/android-sdk/ndk/current';
26
+ // Containers share the memory of Docker's VM, and one compile at full -j can take most of a desktop VM
27
+ // (GDAL's bindings peak at 7.5 of 7.7 GiB), so the exclusive steps of parallel builds take turns.
28
+ const DOCKER_COMPILE_LOCK = path.join(os.homedir(), '.crossbind', 'docker-compile.lock');
29
+ const envParams = (env) => Object.entries(env).flatMap(([variable, value]) => ['-e', `${variable}=${value}`]);
30
+ const CROSSCOMPILER_ARM64 = `aarch64-linux-android${ANDROID_API_LEVEL}`;
31
+ const CROSSCOMPILER_x86_64 = `x86_64-linux-android${ANDROID_API_LEVEL}`;
16
32
  const t = `${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin`;
17
33
  const t2 = `${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64`;
18
34
 
19
- const iOSDevPath = '/Applications/Xcode.app/Contents/Developer';
20
- const iosBinPath = `${iOSDevPath}/Toolchains/XcodeDefault.xctoolchain/usr/bin`;
21
- const iosSdkPath = `${iOSDevPath}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk`;
22
- const iosSimSdkPath = `${iOSDevPath}/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk`;
35
+ const iosBinPath = XCODE_TOOLCHAIN_BIN;
36
+ const iosSdkPath = `${IOS_DEVELOPER_DIR}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk`;
37
+ const iosSimSdkPath = `${IOS_DEVELOPER_DIR}/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk`;
23
38
 
24
39
  const androidParamsArm64 = [
25
40
  '-e',
@@ -63,20 +78,10 @@ const androidParamsX86_64 = [
63
78
  `CFLAGS=--sysroot=${t2}/sysroot`,
64
79
  ];
65
80
 
66
- // Xcode 27 refuses deployment targets below 15.0; React Native's own floor is 15.1.
67
- const IOS_DEPLOYMENT_TARGET = '15.1';
68
- // Node 22, the oldest supported line, needs macOS 11.
69
- const DARWIN_DEPLOYMENT_TARGET = '11.0';
70
81
  const DARWIN_HOST_ARCH = process.arch === 'x64' ? 'x86_64' : 'arm64';
71
- const DARWIN_HOST_PACKAGE_PREFIXES = ['/opt/homebrew', '/usr/local', '/opt/local'];
72
82
  // Apple's own tools only, which find the SDK through xcrun. A GNU ar earlier on the PATH (Homebrew's
73
83
  // binutils) writes archives that Apple's linker rejects.
74
- const DARWIN_BUILD_PATH = '/usr/bin:/bin:/usr/sbin:/sbin';
75
- // The linux image's clang wrappers and CMake toolchain files, one per target triple.
76
- const LINUX_TOOLCHAIN = '/opt/crossbind/linux';
77
- // The windows image's llvm-mingw and its CMake toolchain files, one per target triple.
78
- const LLVM_MINGW = '/opt/llvm-mingw';
79
- const WINDOWS_TOOLCHAIN = '/opt/crossbind/windows';
84
+ const DARWIN_BUILD_PATH = [DARWIN_TOOLS_BIN, '/bin', '/usr/sbin', '/sbin'].join(':');
80
85
  const IOS_HOST_FLAGS = `-arch arm64 -isysroot ${iosSdkPath} -fembed-bitcode`;
81
86
  const IOS_SIM_HOST_FLAGS = `-arch arm64 -isysroot ${iosSimSdkPath} -fembed-bitcode`;
82
87
  const IOS_IPHONE_PARAMS = ['-e', `CFLAGS="${IOS_HOST_FLAGS}"`, '-e', `CXXFLAGS="${IOS_HOST_FLAGS}"`, '-e', `LDFLAGS="${IOS_HOST_FLAGS}"`];
@@ -129,12 +134,16 @@ export default function run(program, params = [], platformPrefix = null, target
129
134
  fs.mkdirSync(buildPath, { recursive: true });
130
135
  }
131
136
 
132
- // wasi is host-run only with a locally configured wasi-sdk; otherwise docker carries it.
137
+ const role = imageRoleFor(target);
138
+ const { runner, remote } = HOST_BUILT_PLATFORMS.includes(target?.platform) && program === null
139
+ ? { runner: 'LOCAL' }
140
+ : runnerFor(role, state.config.system);
141
+ const isDocker = runner === 'DOCKER_RUN' || runner === 'DOCKER_EXEC';
133
142
  const wasiHostSdk = target?.platform === 'wasi' ? resolveWasiSdkPath(state.config.system) : null;
134
- if ((!HOST_BUILT_PLATFORMS.includes(target?.platform) && !(target?.platform === 'wasi' && wasiHostSdk)) || program !== null) {
143
+ if (isDocker) {
135
144
  // Google ships the linux NDK for x86_64 only, so the android index carries no arm64 leaf;
136
145
  // pull its amd64 leaf by ref or an arm64 host finds no matching manifest.
137
- pullDockerImage(imageRoleFor(target), target?.platform === 'android' ? 'linux/amd64' : undefined);
146
+ pullDockerImage(role, target?.platform === 'android' ? 'linux/amd64' : undefined);
138
147
  }
139
148
 
140
149
  let dProgram = program;
@@ -143,7 +152,7 @@ export default function run(program, params = [], platformPrefix = null, target
143
152
  if (program === null) {
144
153
  switch (target.platform) {
145
154
  case 'wasm':
146
- platformParams = ['-e', 'CXXFLAGS=-fwasm-exceptions', '-e', 'CFLAGS=-fwasm-exceptions'];
155
+ platformParams = ['-e', `CXXFLAGS=${WASM_EXCEPTION_FLAGS.join(' ')}`, '-e', `CFLAGS=${WASM_EXCEPTION_FLAGS.join(' ')}`];
147
156
  if (params[0].toLowerCase().includes('configure')) dProgram = 'emconfigure';
148
157
  else if (params[0] === 'make') dProgram = 'emmake';
149
158
  else if (params[0] === 'cmake') dProgram = 'emcmake';
@@ -192,7 +201,7 @@ export default function run(program, params = [], platformPrefix = null, target
192
201
  ...dParams,
193
202
  `-DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake`,
194
203
  `-DANDROID_ABI=${target.arch === 'x86_64' ? 'x86_64' : 'arm64-v8a'}`,
195
- '-DANDROID_PLATFORM=android-33',
204
+ `-DANDROID_PLATFORM=android-${ANDROID_API_LEVEL}`,
196
205
  `-DANDROID_NDK=${ANDROID_NDK}`,
197
206
  ];
198
207
  }
@@ -250,46 +259,31 @@ export default function run(program, params = [], platformPrefix = null, target
250
259
  }
251
260
  }
252
261
  break;
253
- case 'linux': {
262
+ case 'linux':
263
+ case 'linuxmusl': {
254
264
  [dProgram, ...dParams] = params;
255
- const triple = target.arch === 'x64' ? 'x86_64-linux-gnu' : 'aarch64-linux-gnu';
256
- const tool = (name) => `${LINUX_TOOLCHAIN}/bin/${triple}-${name}`;
257
- // The image's own .pc files describe its libraries, not the sysroot's. Every archive
258
- // ends up inside a loadable .node module, so -fPIC holds even where a project turns
259
- // position-independent code off for static builds, as GDAL does.
265
+ const archiveFlags = LINUX_ARCHIVE_FLAGS.join(' ');
260
266
  platformParams = [
261
- '-e', `CC=${tool('clang')}`, '-e', `CXX=${tool('clang++')}`,
262
- '-e', `AR=${tool('ar')}`, '-e', `RANLIB=${tool('ranlib')}`,
263
- '-e', `NM=${tool('nm')}`, '-e', `STRIP=${tool('strip')}`,
264
- '-e', 'PKG_CONFIG_LIBDIR=',
265
- '-e', 'CFLAGS=-fPIC', '-e', 'CXXFLAGS=-fPIC',
267
+ ...envParams(linuxBuildEnv(target)),
268
+ '-e', `CFLAGS=${archiveFlags}`, '-e', `CXXFLAGS=${archiveFlags}`,
266
269
  ];
267
270
  if (dProgram === 'cmake' && dParams[0] !== '--build' && dParams[0] !== '--install') {
268
- dParams = [...dParams, `-DCMAKE_TOOLCHAIN_FILE=${LINUX_TOOLCHAIN}/${triple}.cmake`];
271
+ dParams = [...dParams, `-DCMAKE_TOOLCHAIN_FILE=${linuxToolchainFile(target)}`];
269
272
  }
270
273
  break;
271
274
  }
272
275
  case 'win32': {
273
276
  [dProgram, ...dParams] = params;
274
- const triple = target.arch === 'x64' ? 'x86_64-w64-mingw32' : 'aarch64-w64-mingw32';
275
- const tool = (name) => `${LLVM_MINGW}/bin/${triple}-${name}`;
276
- platformParams = [
277
- '-e', `CC=${tool('clang')}`, '-e', `CXX=${tool('clang++')}`,
278
- '-e', `AR=${tool('ar')}`, '-e', `RANLIB=${tool('ranlib')}`,
279
- '-e', `NM=${tool('nm')}`, '-e', `STRIP=${tool('strip')}`,
280
- '-e', `RC=${tool('windres')}`, '-e', `WINDRES=${tool('windres')}`,
281
- '-e', 'PKG_CONFIG_LIBDIR=',
282
- ];
277
+ platformParams = envParams(windowsBuildEnv(target));
283
278
  if (dProgram === 'cmake' && dParams[0] !== '--build' && dParams[0] !== '--install') {
284
- dParams = [...dParams, `-DCMAKE_TOOLCHAIN_FILE=${WINDOWS_TOOLCHAIN}/${triple}.cmake`];
279
+ dParams = [...dParams, `-DCMAKE_TOOLCHAIN_FILE=${windowsToolchainFile(target)}`];
285
280
  }
286
281
  break;
287
282
  }
288
283
  case 'darwin': {
289
284
  [dProgram, ...dParams] = params;
290
285
  const appleArch = target.arch === 'x64' ? 'x86_64' : 'arm64';
291
- // Homebrew and MacPorts packages exist on the build machine only: an archive compiled
292
- // against one fails to link, or to load, anywhere else. The SDK's libraries stay visible.
286
+ // None of the machine's own packages (DARWIN_HOST_PACKAGE_PREFIXES); the SDK's libraries stay visible.
293
287
  platformParams = ['-e', 'PKG_CONFIG_LIBDIR='];
294
288
  if (dProgram === 'cmake') {
295
289
  if (dParams[0] !== '--build' && dParams[0] !== '--install') {
@@ -309,7 +303,7 @@ export default function run(program, params = [], platformPrefix = null, target
309
303
  // A --host triple sends configure after prefixed compilers (aarch64-apple-darwin-cc) that
310
304
  // Xcode does not ship, and SQLite's autosetup has no fallback to the plain names.
311
305
  platformParams.push(
312
- '-e', 'CC=/usr/bin/clang', '-e', 'CXX=/usr/bin/clang++',
306
+ '-e', `CC=${DARWIN_CC}`, '-e', `CXX=${DARWIN_CXX}`,
313
307
  '-e', `CFLAGS=${flags}`, '-e', `CXXFLAGS=${flags}`, '-e', `LDFLAGS=${flags}`,
314
308
  );
315
309
  }
@@ -320,14 +314,6 @@ export default function run(program, params = [], platformPrefix = null, target
320
314
  }
321
315
 
322
316
  const env = {};
323
- let runner = 'DOCKER';
324
- if (
325
- ((HOST_BUILT_PLATFORMS.includes(target?.platform) || (target?.platform === 'wasi' && wasiHostSdk)) && program === null) ||
326
- state.config.system.RUNNER === 'LOCAL'
327
- ) {
328
- runner = 'LOCAL';
329
- }
330
-
331
317
  if (runner === 'LOCAL') {
332
318
  const allowedEnv = [
333
319
  '^PWD$',
@@ -366,6 +352,7 @@ export default function run(program, params = [], platformPrefix = null, target
366
352
  }
367
353
  }
368
354
 
355
+ const containerOptions = { cwd: buildPath, stdio: dockerOptions.console ? 'inherit' : ['ignore', 'pipe', 'pipe'], maxBuffer: EXEC_MAX_BUFFER };
369
356
  let fileExecParams;
370
357
  if (runner === 'LOCAL') {
371
358
  env.PATH = `/opt/homebrew/bin:${env.PATH}`;
@@ -383,17 +370,26 @@ export default function run(program, params = [], platformPrefix = null, target
383
370
  maxBuffer: EXEC_MAX_BUFFER,
384
371
  };
385
372
  fileExecParams = [dProgram, dParams, options];
386
- } else if (runner === 'DOCKER') {
373
+ } else if (runner === 'REMOTE') {
374
+ fileExecParams = remoteExecParams({
375
+ remote,
376
+ mounts: [baseMount(state.config, { extraInputs: referencedPaths(state.config.paths.base, [dProgram, ...dParams, ...Object.values(env)]) })],
377
+ role,
378
+ image: getDockerImage(role, target?.platform === 'android' ? 'linux/amd64' : undefined),
379
+ cwd: replaceBasePathForDocker(dockerOptions.workdir || buildPath),
380
+ argv: [replaceBasePathForDocker(dProgram), ...replaceBasePathForDocker(dParams)],
381
+ env: replaceBasePathForDocker(env),
382
+ }, containerOptions);
383
+ } else {
387
384
  const dockerEnv = [];
388
385
  Object.entries(env).forEach(([key, value]) => {
389
386
  dockerEnv.push('-e', `${key}=${value}`);
390
387
  });
391
- const options = { cwd: buildPath, stdio: dockerOptions.console ? 'inherit' : ['ignore', 'pipe', 'pipe'], maxBuffer: EXEC_MAX_BUFFER };
388
+ const options = containerOptions;
392
389
 
393
390
  let runnerParams;
394
391
  let imageOrContainer;
395
- const role = imageRoleFor(target);
396
- if (state.config.system.RUNNER === 'DOCKER_RUN') {
392
+ if (runner === 'DOCKER_RUN') {
397
393
  imageOrContainer = getDockerImage(role);
398
394
  runnerParams = ['run', '--rm', ...DOCKER_RUN_SECURITY_ARGS, '-v', `${state.config.paths.base}:/tmp/crossbind/live`];
399
395
  if (target?.platform === 'android') {
@@ -402,11 +398,9 @@ export default function run(program, params = [], platformPrefix = null, target
402
398
  imageOrContainer = getDockerImage(role, 'linux/amd64');
403
399
  runnerParams.push('--platform', 'linux/amd64');
404
400
  }
405
- } else if (state.config.system.RUNNER === 'DOCKER_EXEC') {
401
+ } else {
406
402
  imageOrContainer = getDockerContainerName(state.config.paths.base, role);
407
403
  runnerParams = ['exec'];
408
- } else {
409
- throw new Error(`The runner ${state.config.system.RUNNER} is invalid.`);
410
404
  }
411
405
 
412
406
  const args = [
@@ -422,12 +416,15 @@ export default function run(program, params = [], platformPrefix = null, target
422
416
  ...replaceBasePathForDocker(dParams),
423
417
  ];
424
418
  fileExecParams = ['docker', args, options];
425
- } else {
426
- throw new Error(`The runner ${state.config.system.RUNNER} or command is invalid.`);
427
419
  }
428
420
 
429
421
  try {
430
- execFileSync(...fileExecParams);
422
+ // A remote step takes no share of the local Docker VM, and its runner runs one step at a time.
423
+ if (isDocker && dockerOptions.exclusive) {
424
+ withDirLockSync(DOCKER_COMPILE_LOCK, () => execFileSync(...fileExecParams), { heldBy: 'another build is compiling in Docker' });
425
+ } else {
426
+ execFileSync(...fileExecParams);
427
+ }
431
428
  } catch (e) {
432
429
  if (e?.stdout?.length) console.log(e.stdout.toString());
433
430
  if (e?.stderr?.length) console.error(e.stderr.toString());