crossbind 2.0.0-beta.60 → 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 (118) hide show
  1. package/README.md +8 -6
  2. package/package.json +6 -1
  3. package/src/actions/buildDependencies.js +37 -9
  4. package/src/actions/buildExternal.js +9 -1
  5. package/src/actions/buildJs.js +1 -1
  6. package/src/actions/buildLib.js +27 -8
  7. package/src/actions/buildNativeCommand.js +84 -0
  8. package/src/actions/buildNode.js +190 -0
  9. package/src/actions/buildTypes.js +8 -2
  10. package/src/actions/buildWasiCommand.js +0 -4
  11. package/src/actions/buildWasm.js +92 -95
  12. package/src/actions/cloudCommands.js +133 -0
  13. package/src/actions/createImportedBridges.js +64 -0
  14. package/src/actions/createInterface.js +318 -65
  15. package/src/actions/createLib.js +60 -43
  16. package/src/actions/getAllBridges.js +1 -1
  17. package/src/actions/getData.js +4 -1
  18. package/src/actions/getLinkInputs.js +70 -0
  19. package/src/actions/isSourceNewer.js +2 -0
  20. package/src/actions/licenses.js +175 -17
  21. package/src/actions/prepareConanDependencies.js +51 -0
  22. package/src/actions/publishBinary.js +32 -0
  23. package/src/actions/run.js +124 -32
  24. package/src/actions/runnerCommands.js +421 -0
  25. package/src/actions/writeRuntimeEntry.js +69 -0
  26. package/src/assets/cmake/CMakeLists.txt +1 -1
  27. package/src/assets/cmake/command/CMakeLists.txt +21 -0
  28. package/src/assets/cmake/command/command.cpp +1 -0
  29. package/src/assets/cmake/dist.cmake +2 -1
  30. package/src/assets/configure/install.sh +22 -0
  31. package/src/assets/js-runtime/adapters/path-url.js +6 -3
  32. package/src/assets/js-runtime/adapters/unboundMessage.js +2 -0
  33. package/src/assets/js-runtime/adapters/worker-comlink.js +87 -56
  34. package/src/assets/js-runtime/core.js +11 -0
  35. package/src/assets/node-import-hooks.mjs +61 -0
  36. package/src/assets/toolchain-digests.json +27 -13
  37. package/src/assets/wasi-runtime/stubs.c +29 -0
  38. package/src/bin.js +290 -37
  39. package/src/index.js +6 -1
  40. package/src/integration/bindHeaderImports.js +18 -0
  41. package/src/integration/getCrossbindScript.js +53 -19
  42. package/src/integration/getDependFilePath.js +18 -12
  43. package/src/integration/resolveNativeImport.js +14 -0
  44. package/src/node/dev.mjs +19 -0
  45. package/src/runner/blobs.js +135 -0
  46. package/src/runner/files.js +193 -0
  47. package/src/runner/server.js +320 -0
  48. package/src/state/attachConanDependencies.js +56 -0
  49. package/src/state/index.js +31 -20
  50. package/src/state/loadConfig.js +36 -24
  51. package/src/state/refreshBuiltDependencies.js +20 -0
  52. package/src/state/refreshConanDependencies.js +17 -0
  53. package/src/utils/androidToolchain.js +4 -0
  54. package/src/utils/appRustCrates.js +17 -1
  55. package/src/utils/appSources.js +112 -0
  56. package/src/utils/archiveFlags.js +12 -0
  57. package/src/utils/boundHeaders.js +79 -0
  58. package/src/utils/bridgeExtras.js +486 -0
  59. package/src/utils/bridgeRuntimeFixes.js +48 -0
  60. package/src/utils/bundledLicenses.js +26 -0
  61. package/src/utils/cargoImport.js +44 -0
  62. package/src/utils/cargoTarget.js +23 -3
  63. package/src/utils/cloudAccount.js +55 -0
  64. package/src/utils/conanDependencies.js +66 -0
  65. package/src/utils/conanImport.js +58 -0
  66. package/src/utils/conanInstall.js +154 -0
  67. package/src/utils/conanProfile.js +203 -0
  68. package/src/utils/conanStage.js +292 -0
  69. package/src/utils/copyToDistSource.js +13 -0
  70. package/src/utils/cppDts.js +134 -55
  71. package/src/utils/darwinToolchain.js +11 -0
  72. package/src/utils/dirLock.js +137 -28
  73. package/src/utils/downloadAndExtractFile.js +34 -7
  74. package/src/utils/embindArgumentGuards.js +84 -0
  75. package/src/utils/embindCallArguments.js +19 -0
  76. package/src/utils/embindJspiQueue.js +25 -0
  77. package/src/utils/embindRsFingerprint.js +26 -0
  78. package/src/utils/execContainer.js +49 -0
  79. package/src/utils/headerImports.js +56 -0
  80. package/src/utils/hostPlatform.js +21 -0
  81. package/src/utils/inputStamp.js +23 -0
  82. package/src/utils/iosToolchain.js +6 -0
  83. package/src/utils/isSourceCmakePackage.js +10 -0
  84. package/src/utils/licenseReport.js +11 -2
  85. package/src/utils/linkLayout.js +8 -1
  86. package/src/utils/linuxToolchain.js +24 -0
  87. package/src/utils/makeTreeWritable.js +12 -0
  88. package/src/utils/nativeImports.js +23 -0
  89. package/src/utils/nativeSourceStamp.js +36 -0
  90. package/src/utils/nodeDevBuild.js +92 -0
  91. package/src/utils/packageLicense.js +101 -0
  92. package/src/utils/pthreadMainScript.js +14 -0
  93. package/src/utils/pullDockerImage.js +10 -5
  94. package/src/utils/relocatePrebuilt.js +119 -0
  95. package/src/utils/remoteClient.js +127 -0
  96. package/src/utils/remoteOutputs.js +39 -0
  97. package/src/utils/remoteRunner.js +97 -0
  98. package/src/utils/remoteUpload.js +134 -0
  99. package/src/utils/replaceFile.js +8 -0
  100. package/src/utils/resolveCompanionPackage.js +51 -0
  101. package/src/utils/resolveEmbindNapi.js +22 -0
  102. package/src/utils/resolveEmbindRust.js +17 -52
  103. package/src/utils/runCargo.js +51 -53
  104. package/src/utils/runConan.js +257 -0
  105. package/src/utils/runtimeEntries.js +56 -0
  106. package/src/utils/rustBridgeGen.js +903 -253
  107. package/src/utils/rustMt.js +4 -0
  108. package/src/utils/scopedEmbind.js +22 -0
  109. package/src/utils/selectRunner.js +81 -0
  110. package/src/utils/sourceFingerprint.js +4 -3
  111. package/src/utils/swigInterface.js +86 -6
  112. package/src/utils/systemKeys.js +43 -3
  113. package/src/utils/targets.js +104 -3
  114. package/src/utils/toolchainNotices.js +5 -0
  115. package/src/utils/wasiToolchain.js +14 -1
  116. package/src/utils/windowsToolchain.js +18 -0
  117. package/vitest.config.js +11 -0
  118. package/src/utils/cppFieldBindings.js +0 -23
@@ -0,0 +1,32 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import state from '../state/index.js';
4
+ import replaceFile from '../utils/replaceFile.js';
5
+
6
+ // paths.output defaults to paths.build, where a built command and its data already are; copying them onto
7
+ // themselves would delete them first.
8
+ const outputIsBuild = () => path.resolve(state.config.paths.output) === path.resolve(state.config.paths.build);
9
+
10
+ export function publishNativeCommand(target) {
11
+ if (outputIsBuild()) return;
12
+ fs.mkdirSync(state.config.paths.output, { recursive: true });
13
+ replaceFile(`${state.config.paths.build}/${target.commandName}`, `${state.config.paths.output}/${target.commandName}`);
14
+ }
15
+
16
+ export function publishNodeAddon(target) {
17
+ if (outputIsBuild()) return;
18
+ fs.mkdirSync(state.config.paths.output, { recursive: true });
19
+ replaceFile(`${state.config.paths.build}/${target.addonName}`, `${state.config.paths.output}/${target.addonName}`);
20
+ fs.copyFileSync(`${state.config.paths.build}/${target.jsName}`, `${state.config.paths.output}/${target.jsName}`);
21
+ }
22
+
23
+ export function publishWasiCommand(target) {
24
+ if (outputIsBuild()) return;
25
+ fs.copyFileSync(`${state.config.paths.build}/${target.wasmName}`, `${state.config.paths.output}/${target.wasmName}`);
26
+ // Copied (not renamed) so a later fingerprint cache-hit still has the
27
+ // build-dir tree to serve from.
28
+ if (fs.existsSync(`${state.config.paths.build}/data`)) {
29
+ fs.rmSync(`${state.config.paths.output}/data`, { recursive: true, force: true });
30
+ fs.cpSync(`${state.config.paths.build}/data`, `${state.config.paths.output}/data`, { recursive: true });
31
+ }
32
+ }
@@ -1,24 +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';
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';
9
23
 
10
24
  // Native builds can outrun Node's 1 MiB default pipe buffer; without a raised cap a successful build dies with ENOBUFS.
11
25
  const EXEC_MAX_BUFFER = 512 * 1024 * 1024;
12
- const CROSSCOMPILER_ARM64 = 'aarch64-linux-android33';
13
- const CROSSCOMPILER_x86_64 = 'x86_64-linux-android33';
14
- 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}`;
15
32
  const t = `${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin`;
16
33
  const t2 = `${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64`;
17
34
 
18
- const iOSDevPath = '/Applications/Xcode.app/Contents/Developer';
19
- const iosBinPath = `${iOSDevPath}/Toolchains/XcodeDefault.xctoolchain/usr/bin`;
20
- const iosSdkPath = `${iOSDevPath}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk`;
21
- 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`;
22
38
 
23
39
  const androidParamsArm64 = [
24
40
  '-e',
@@ -62,8 +78,10 @@ const androidParamsX86_64 = [
62
78
  `CFLAGS=--sysroot=${t2}/sysroot`,
63
79
  ];
64
80
 
65
- // Xcode 27 refuses deployment targets below 15.0; React Native's own floor is 15.1.
66
- const IOS_DEPLOYMENT_TARGET = '15.1';
81
+ const DARWIN_HOST_ARCH = process.arch === 'x64' ? 'x86_64' : 'arm64';
82
+ // Apple's own tools only, which find the SDK through xcrun. A GNU ar earlier on the PATH (Homebrew's
83
+ // binutils) writes archives that Apple's linker rejects.
84
+ const DARWIN_BUILD_PATH = [DARWIN_TOOLS_BIN, '/bin', '/usr/sbin', '/sbin'].join(':');
67
85
  const IOS_HOST_FLAGS = `-arch arm64 -isysroot ${iosSdkPath} -fembed-bitcode`;
68
86
  const IOS_SIM_HOST_FLAGS = `-arch arm64 -isysroot ${iosSimSdkPath} -fembed-bitcode`;
69
87
  const IOS_IPHONE_PARAMS = ['-e', `CFLAGS="${IOS_HOST_FLAGS}"`, '-e', `CXXFLAGS="${IOS_HOST_FLAGS}"`, '-e', `LDFLAGS="${IOS_HOST_FLAGS}"`];
@@ -116,12 +134,16 @@ export default function run(program, params = [], platformPrefix = null, target
116
134
  fs.mkdirSync(buildPath, { recursive: true });
117
135
  }
118
136
 
119
- // 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';
120
142
  const wasiHostSdk = target?.platform === 'wasi' ? resolveWasiSdkPath(state.config.system) : null;
121
- if ((target?.platform !== 'ios' && !(target?.platform === 'wasi' && wasiHostSdk)) || program !== null) {
143
+ if (isDocker) {
122
144
  // Google ships the linux NDK for x86_64 only, so the android index carries no arm64 leaf;
123
145
  // pull its amd64 leaf by ref or an arm64 host finds no matching manifest.
124
- pullDockerImage(imageRoleFor(target), target?.platform === 'android' ? 'linux/amd64' : undefined);
146
+ pullDockerImage(role, target?.platform === 'android' ? 'linux/amd64' : undefined);
125
147
  }
126
148
 
127
149
  let dProgram = program;
@@ -130,7 +152,7 @@ export default function run(program, params = [], platformPrefix = null, target
130
152
  if (program === null) {
131
153
  switch (target.platform) {
132
154
  case 'wasm':
133
- platformParams = ['-e', 'CXXFLAGS=-fwasm-exceptions', '-e', 'CFLAGS=-fwasm-exceptions'];
155
+ platformParams = ['-e', `CXXFLAGS=${WASM_EXCEPTION_FLAGS.join(' ')}`, '-e', `CFLAGS=${WASM_EXCEPTION_FLAGS.join(' ')}`];
134
156
  if (params[0].toLowerCase().includes('configure')) dProgram = 'emconfigure';
135
157
  else if (params[0] === 'make') dProgram = 'emmake';
136
158
  else if (params[0] === 'cmake') dProgram = 'emcmake';
@@ -179,7 +201,7 @@ export default function run(program, params = [], platformPrefix = null, target
179
201
  ...dParams,
180
202
  `-DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake`,
181
203
  `-DANDROID_ABI=${target.arch === 'x86_64' ? 'x86_64' : 'arm64-v8a'}`,
182
- '-DANDROID_PLATFORM=android-33',
204
+ `-DANDROID_PLATFORM=android-${ANDROID_API_LEVEL}`,
183
205
  `-DANDROID_NDK=${ANDROID_NDK}`,
184
206
  ];
185
207
  }
@@ -237,19 +259,61 @@ export default function run(program, params = [], platformPrefix = null, target
237
259
  }
238
260
  }
239
261
  break;
262
+ case 'linux':
263
+ case 'linuxmusl': {
264
+ [dProgram, ...dParams] = params;
265
+ const archiveFlags = LINUX_ARCHIVE_FLAGS.join(' ');
266
+ platformParams = [
267
+ ...envParams(linuxBuildEnv(target)),
268
+ '-e', `CFLAGS=${archiveFlags}`, '-e', `CXXFLAGS=${archiveFlags}`,
269
+ ];
270
+ if (dProgram === 'cmake' && dParams[0] !== '--build' && dParams[0] !== '--install') {
271
+ dParams = [...dParams, `-DCMAKE_TOOLCHAIN_FILE=${linuxToolchainFile(target)}`];
272
+ }
273
+ break;
274
+ }
275
+ case 'win32': {
276
+ [dProgram, ...dParams] = params;
277
+ platformParams = envParams(windowsBuildEnv(target));
278
+ if (dProgram === 'cmake' && dParams[0] !== '--build' && dParams[0] !== '--install') {
279
+ dParams = [...dParams, `-DCMAKE_TOOLCHAIN_FILE=${windowsToolchainFile(target)}`];
280
+ }
281
+ break;
282
+ }
283
+ case 'darwin': {
284
+ [dProgram, ...dParams] = params;
285
+ const appleArch = target.arch === 'x64' ? 'x86_64' : 'arm64';
286
+ // None of the machine's own packages (DARWIN_HOST_PACKAGE_PREFIXES); the SDK's libraries stay visible.
287
+ platformParams = ['-e', 'PKG_CONFIG_LIBDIR='];
288
+ if (dProgram === 'cmake') {
289
+ if (dParams[0] !== '--build' && dParams[0] !== '--install') {
290
+ dParams = [
291
+ ...dParams,
292
+ `-DCMAKE_OSX_ARCHITECTURES=${appleArch}`,
293
+ `-DCMAKE_OSX_DEPLOYMENT_TARGET=${DARWIN_DEPLOYMENT_TARGET}`,
294
+ // Every archive ends up inside a loadable .node module.
295
+ '-DCMAKE_POSITION_INDEPENDENT_CODE=ON',
296
+ `-DCMAKE_IGNORE_PREFIX_PATH=${DARWIN_HOST_PACKAGE_PREFIXES.join(';')}`,
297
+ // The other architecture's probes cannot run here, so CMake has to know it cross-compiles.
298
+ ...(appleArch === DARWIN_HOST_ARCH ? [] : ['-DCMAKE_SYSTEM_NAME=Darwin', `-DCMAKE_SYSTEM_PROCESSOR=${appleArch}`]),
299
+ ];
300
+ }
301
+ } else {
302
+ const flags = `-arch ${appleArch} -mmacosx-version-min=${DARWIN_DEPLOYMENT_TARGET}`;
303
+ // A --host triple sends configure after prefixed compilers (aarch64-apple-darwin-cc) that
304
+ // Xcode does not ship, and SQLite's autosetup has no fallback to the plain names.
305
+ platformParams.push(
306
+ '-e', `CC=${DARWIN_CC}`, '-e', `CXX=${DARWIN_CXX}`,
307
+ '-e', `CFLAGS=${flags}`, '-e', `CXXFLAGS=${flags}`, '-e', `LDFLAGS=${flags}`,
308
+ );
309
+ }
310
+ break;
311
+ }
240
312
  default:
241
313
  }
242
314
  }
243
315
 
244
316
  const env = {};
245
- let runner = 'DOCKER';
246
- if (
247
- ((target?.platform === 'ios' || (target?.platform === 'wasi' && wasiHostSdk)) && program === null) ||
248
- state.config.system.RUNNER === 'LOCAL'
249
- ) {
250
- runner = 'LOCAL';
251
- }
252
-
253
317
  if (runner === 'LOCAL') {
254
318
  const allowedEnv = [
255
319
  '^PWD$',
@@ -288,9 +352,16 @@ export default function run(program, params = [], platformPrefix = null, target
288
352
  }
289
353
  }
290
354
 
355
+ const containerOptions = { cwd: buildPath, stdio: dockerOptions.console ? 'inherit' : ['ignore', 'pipe', 'pipe'], maxBuffer: EXEC_MAX_BUFFER };
291
356
  let fileExecParams;
292
357
  if (runner === 'LOCAL') {
293
358
  env.PATH = `/opt/homebrew/bin:${env.PATH}`;
359
+ // A package's *-config script on the PATH (nc-config, sfcgal-config, ...) would compile that
360
+ // package into the archives. cmake is the one host tool a darwin build takes from the PATH.
361
+ if (target?.platform === 'darwin') {
362
+ if (dProgram === 'cmake') dProgram = findExecutable('cmake', env.PATH) ?? dProgram;
363
+ env.PATH = DARWIN_BUILD_PATH;
364
+ }
294
365
 
295
366
  const options = {
296
367
  cwd: dockerOptions.workdir || buildPath,
@@ -299,17 +370,26 @@ export default function run(program, params = [], platformPrefix = null, target
299
370
  maxBuffer: EXEC_MAX_BUFFER,
300
371
  };
301
372
  fileExecParams = [dProgram, dParams, options];
302
- } 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 {
303
384
  const dockerEnv = [];
304
385
  Object.entries(env).forEach(([key, value]) => {
305
386
  dockerEnv.push('-e', `${key}=${value}`);
306
387
  });
307
- const options = { cwd: buildPath, stdio: dockerOptions.console ? 'inherit' : ['ignore', 'pipe', 'pipe'], maxBuffer: EXEC_MAX_BUFFER };
388
+ const options = containerOptions;
308
389
 
309
390
  let runnerParams;
310
391
  let imageOrContainer;
311
- const role = imageRoleFor(target);
312
- if (state.config.system.RUNNER === 'DOCKER_RUN') {
392
+ if (runner === 'DOCKER_RUN') {
313
393
  imageOrContainer = getDockerImage(role);
314
394
  runnerParams = ['run', '--rm', ...DOCKER_RUN_SECURITY_ARGS, '-v', `${state.config.paths.base}:/tmp/crossbind/live`];
315
395
  if (target?.platform === 'android') {
@@ -318,11 +398,9 @@ export default function run(program, params = [], platformPrefix = null, target
318
398
  imageOrContainer = getDockerImage(role, 'linux/amd64');
319
399
  runnerParams.push('--platform', 'linux/amd64');
320
400
  }
321
- } else if (state.config.system.RUNNER === 'DOCKER_EXEC') {
401
+ } else {
322
402
  imageOrContainer = getDockerContainerName(state.config.paths.base, role);
323
403
  runnerParams = ['exec'];
324
- } else {
325
- throw new Error(`The runner ${state.config.system.RUNNER} is invalid.`);
326
404
  }
327
405
 
328
406
  const args = [
@@ -338,12 +416,15 @@ export default function run(program, params = [], platformPrefix = null, target
338
416
  ...replaceBasePathForDocker(dParams),
339
417
  ];
340
418
  fileExecParams = ['docker', args, options];
341
- } else {
342
- throw new Error(`The runner ${state.config.system.RUNNER} or command is invalid.`);
343
419
  }
344
420
 
345
421
  try {
346
- 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
+ }
347
428
  } catch (e) {
348
429
  if (e?.stdout?.length) console.log(e.stdout.toString());
349
430
  if (e?.stderr?.length) console.error(e.stderr.toString());
@@ -351,6 +432,17 @@ export default function run(program, params = [], platformPrefix = null, target
351
432
  }
352
433
  }
353
434
 
435
+ function findExecutable(name, searchPath) {
436
+ return searchPath.split(':').map((dir) => `${dir}/${name}`).find((file) => {
437
+ try {
438
+ fs.accessSync(file, fs.constants.X_OK);
439
+ return true;
440
+ } catch {
441
+ return false;
442
+ }
443
+ });
444
+ }
445
+
354
446
  function replaceBasePathForDocker(data) {
355
447
  return replaceBasePathForDockerUtil(data, state.config.paths.base);
356
448
  }