crossbind 2.0.0-beta.68 → 2.0.0-beta.72

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 (88) hide show
  1. package/README.md +1 -1
  2. package/package.json +70 -59
  3. package/src/actions/addExtension.js +196 -0
  4. package/src/actions/androidGradle.js +26 -17
  5. package/src/actions/appInstaller.js +69 -153
  6. package/src/actions/appSwift.js +66 -0
  7. package/src/actions/buildApp.js +177 -74
  8. package/src/actions/buildDependencies.js +3 -3
  9. package/src/actions/buildNode.js +6 -1
  10. package/src/actions/buildWasm.js +0 -9
  11. package/src/actions/createImportedBridges.js +10 -8
  12. package/src/actions/createInterface.js +40 -29
  13. package/src/actions/createLib.js +3 -7
  14. package/src/actions/extensionApi.js +298 -0
  15. package/src/actions/extensionCommands.js +41 -0
  16. package/src/actions/extensionsInApp.js +19 -0
  17. package/src/actions/getAllBridges.js +20 -5
  18. package/src/actions/getDependLibs.js +5 -0
  19. package/src/actions/getLinkInputs.js +2 -1
  20. package/src/actions/gradleProject.js +207 -16
  21. package/src/actions/iosDevice.js +2 -2
  22. package/src/actions/iosSimulator.js +3 -3
  23. package/src/actions/licenses.js +113 -58
  24. package/src/actions/linuxPackages.js +48 -0
  25. package/src/actions/mavenVerification.js +37 -0
  26. package/src/actions/{prepareConanDependencies.js → preparePackageSources.js} +13 -12
  27. package/src/actions/publish.js +220 -0
  28. package/src/actions/windowsSigning.js +1 -1
  29. package/src/actions/writeRuntimeEntry.js +2 -2
  30. package/src/actions/xcodeProject.js +60 -29
  31. package/src/actions/xcodeTargets.js +122 -0
  32. package/src/actions/xcodebuild.js +65 -0
  33. package/src/app/index.d.ts +144 -132
  34. package/src/app/index.js +147 -185
  35. package/src/assets/configure/install.sh +0 -0
  36. package/src/assets/node-import-hooks.mjs +7 -6
  37. package/src/assets/toolchain-digests.json +20 -20
  38. package/src/bin.js +37 -37
  39. package/src/index.js +4 -3
  40. package/src/integration/getCrossbindScript.js +3 -2
  41. package/src/integration/getDependFilePath.js +10 -10
  42. package/src/program.js +32 -3
  43. package/src/state/{attachConanDependencies.js → attachPackageSources.js} +20 -16
  44. package/src/state/index.js +8 -6
  45. package/src/state/loadConfig.js +26 -9
  46. package/src/state/refreshPackageSources.js +19 -0
  47. package/src/utils/appExtensions.js +777 -0
  48. package/src/utils/appFiles.js +58 -0
  49. package/src/utils/appLaunches.js +249 -0
  50. package/src/utils/appOptions.js +142 -112
  51. package/src/utils/appRustCrates.js +2 -2
  52. package/src/utils/appTargets.js +168 -0
  53. package/src/utils/bridgeTools.js +71 -0
  54. package/src/utils/configChanges.js +30 -0
  55. package/src/utils/configWriter.js +143 -26
  56. package/src/utils/cppDts.js +11 -11
  57. package/src/utils/extensionSteps.js +172 -0
  58. package/src/utils/nativeImports.js +9 -2
  59. package/src/utils/nodeDevBuild.js +2 -1
  60. package/src/utils/openUrl.js +2 -1
  61. package/src/utils/packageSourceApi.js +87 -0
  62. package/src/utils/packageSourceImport.js +69 -0
  63. package/src/utils/packageSourceInstall.js +162 -0
  64. package/src/utils/{conanStage.js → packageSourceStage.js} +64 -90
  65. package/src/utils/packageSources.js +108 -0
  66. package/src/utils/plist.js +5 -0
  67. package/src/utils/printableText.js +5 -0
  68. package/src/utils/resolveAppHost.js +7 -0
  69. package/src/utils/runPackageSource.js +251 -0
  70. package/src/utils/rustBridgeGen.js +3 -3
  71. package/src/utils/systemConfig.js +16 -3
  72. package/src/utils/targetToolchain.js +77 -0
  73. package/vitest.config.js +6 -1
  74. package/src/actions/appSetup.js +0 -116
  75. package/src/actions/appStoreConnect.js +0 -188
  76. package/src/actions/appUpdate.js +0 -68
  77. package/src/actions/extensions.js +0 -7
  78. package/src/actions/googlePlay.js +0 -213
  79. package/src/actions/googleSignIn.js +0 -78
  80. package/src/actions/playSetup.js +0 -19
  81. package/src/actions/publishAndroid.js +0 -102
  82. package/src/actions/publishIos.js +0 -146
  83. package/src/state/refreshConanDependencies.js +0 -17
  84. package/src/utils/conanDependencies.js +0 -66
  85. package/src/utils/conanImport.js +0 -58
  86. package/src/utils/conanInstall.js +0 -154
  87. package/src/utils/conanProfile.js +0 -203
  88. package/src/utils/runConan.js +0 -257
@@ -1,203 +0,0 @@
1
- import { WASM_EXCEPTION_FLAGS, targetArchiveFlags } from './archiveFlags.js';
2
- import { ANDROID_NDK, ANDROID_API_LEVEL } from './androidToolchain.js';
3
- import { IOS_DEPLOYMENT_TARGET } from './iosToolchain.js';
4
- import {
5
- LINUX_ARCHIVE_FLAGS, linuxBuildEnv, linuxTool, linuxToolchainFile, linuxTriple,
6
- } from './linuxToolchain.js';
7
- import {
8
- DARWIN_CC, DARWIN_CXX, DARWIN_DEPLOYMENT_TARGET, DARWIN_HOST_PACKAGE_PREFIXES,
9
- } from './darwinToolchain.js';
10
- import {
11
- windowsBuildEnv, windowsTool, windowsToolchainFile, windowsTriple,
12
- } from './windowsToolchain.js';
13
-
14
- // The app's own sources compile as C++20 (assets/cmake/CMakeLists.txt), so C++ packages do too.
15
- const CPP_STANDARD = '20';
16
- // The flags change the ABI without changing a setting, and glibc and musl builds share every setting,
17
- // so these have to change the package id. A conf the profile leaves unset adds nothing to it.
18
- const PACKAGE_ID_CONFS = ['tools.build:cflags', 'tools.build:cxxflags', 'tools.build:exelinkflags', 'tools.build:sharedlinkflags', 'user.crossbind:libc'];
19
-
20
- // Conan renders profiles as Jinja templates: these read the toolchain of whatever runs Conan, the image
21
- // or the host (under RUNNER=LOCAL, and for iOS and macOS).
22
- const EMCC_VERSION = "{{ subprocess.check_output(['emcc', '-dumpversion'], text=True).strip() }}";
23
- const EMSCRIPTEN_TOOLCHAIN = "{{ subprocess.check_output(['em-config', 'EMSCRIPTEN_ROOT'], text=True).strip() }}/cmake/Modules/Platform/Emscripten.cmake";
24
- const clangMajorVersion = (clang) => `{{ subprocess.check_output(['${clang}', '-dumpversion'], text=True).split('.')[0] }}`;
25
- const NDK_CLANG_VERSION = clangMajorVersion(`${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang`);
26
- const XCODE_CLANG_VERSION = "{{ subprocess.check_output(['xcrun', 'clang', '-dumpversion'], text=True).split('.')[0] }}";
27
- const CMAKE_VERSION = "{{ subprocess.check_output(['cmake', '--version'], text=True).split()[2] }}";
28
-
29
- const ANDROID_ARCHS = { 'arm64-v8a': 'armv8', x86_64: 'x86_64' };
30
- // Node's names for the desktop arches, in Conan's spelling.
31
- const NODE_ARCHS = { x64: 'x86_64', arm64: 'armv8' };
32
- const LINUX_LIBCS = { linux: 'glibc', linuxmusl: 'musl' };
33
- const envLines = (env) => Object.entries(env).map(([variable, value]) => `${variable}=${value}`);
34
-
35
- // The image's clang wrappers through its CMake toolchain file for the triple, as a port archive for an
36
- // addon is built. The image runs no musl program, so a musl build is a cross build even on a machine of
37
- // the same arch, which Conan would take for a native one; configure is told the triple itself.
38
- const LINUX = {
39
- settings: (target) => [
40
- 'os=Linux',
41
- `arch=${NODE_ARCHS[target.arch]}`,
42
- 'compiler=clang',
43
- `compiler.version=${clangMajorVersion(linuxTool(target, 'clang'))}`,
44
- 'compiler.libcxx=libc++',
45
- `compiler.cppstd=${CPP_STANDARD}`,
46
- ],
47
- conf: (target) => [
48
- `tools.build:compiler_executables={'c': '${linuxTool(target, 'clang')}', 'cpp': '${linuxTool(target, 'clang++')}'}`,
49
- `tools.cmake.cmaketoolchain:user_toolchain=["${linuxToolchainFile(target)}"]`,
50
- `tools.gnu:host_triplet=${linuxTriple(target)}`,
51
- ...(target.platform === 'linuxmusl' ? ['tools.build.cross_building:cross_build=True'] : []),
52
- `user.crossbind:libc=${LINUX_LIBCS[target.platform]}`,
53
- ],
54
- buildenv: (target) => envLines(linuxBuildEnv(target)),
55
- flags: LINUX_ARCHIVE_FLAGS,
56
- };
57
-
58
- // What differs per platform; the rest of the host profile is shared.
59
- const PLATFORMS = {
60
- wasm: {
61
- settings: (target) => [
62
- 'os=Emscripten',
63
- `arch=${target.arch === 'wasm64' ? 'wasm64' : 'wasm'}`,
64
- 'compiler=emcc',
65
- `compiler.version=${EMCC_VERSION}`,
66
- 'compiler.libcxx=libc++',
67
- `compiler.cppstd=${CPP_STANDARD}`,
68
- ...(target.runtime === 'mt' ? ['compiler.threads=posix'] : []),
69
- ],
70
- conf: () => [
71
- "tools.build:compiler_executables={'c': 'emcc', 'cpp': 'em++'}",
72
- `tools.cmake.cmaketoolchain:user_toolchain=["${EMSCRIPTEN_TOOLCHAIN}"]`,
73
- ],
74
- buildenv: () => ['CC=emcc', 'CXX=em++', 'AR=emar', 'NM=emnm', 'RANLIB=emranlib', 'STRIP=emstrip'],
75
- flags: WASM_EXCEPTION_FLAGS,
76
- },
77
- // The image's NDK through its own CMake toolchain and default STL, as a port archive is built.
78
- android: {
79
- settings: (target) => [
80
- 'os=Android',
81
- `os.api_level=${ANDROID_API_LEVEL}`,
82
- `arch=${ANDROID_ARCHS[target.arch]}`,
83
- 'compiler=clang',
84
- `compiler.version=${NDK_CLANG_VERSION}`,
85
- 'compiler.libcxx=c++_static',
86
- `compiler.cppstd=${CPP_STANDARD}`,
87
- ],
88
- conf: () => [`tools.android:ndk_path=${ANDROID_NDK}`],
89
- buildenv: () => [],
90
- flags: [],
91
- },
92
- // Xcode's clang for the target's SDK, arm64 only like every crossbind iOS slice. No bitcode flag:
93
- // Xcode 27 ld takes the "marker" of -fembed-bitcode-marker for a file when CMake links its check.
94
- ios: {
95
- settings: (target) => [
96
- 'os=iOS',
97
- `os.version=${IOS_DEPLOYMENT_TARGET}`,
98
- `os.sdk=${target.arch}`,
99
- 'arch=armv8',
100
- 'compiler=apple-clang',
101
- `compiler.version=${XCODE_CLANG_VERSION}`,
102
- 'compiler.libcxx=libc++',
103
- `compiler.cppstd=${CPP_STANDARD}`,
104
- ],
105
- conf: () => [],
106
- buildenv: () => [],
107
- flags: [],
108
- },
109
- linux: LINUX,
110
- linuxmusl: LINUX,
111
- // The clang xcode-select picks, for the macOS the addons support, as a port archive for a macOS addon
112
- // is built; the machine's own packages stay out of reach.
113
- darwin: {
114
- settings: (target) => [
115
- 'os=Macos',
116
- `os.version=${DARWIN_DEPLOYMENT_TARGET}`,
117
- `arch=${NODE_ARCHS[target.arch]}`,
118
- 'compiler=apple-clang',
119
- `compiler.version=${clangMajorVersion(DARWIN_CC)}`,
120
- 'compiler.libcxx=libc++',
121
- `compiler.cppstd=${CPP_STANDARD}`,
122
- ],
123
- conf: () => [
124
- `tools.build:compiler_executables={'c': '${DARWIN_CC}', 'cpp': '${DARWIN_CXX}'}`,
125
- `tools.cmake.cmaketoolchain:extra_variables={'CMAKE_IGNORE_PREFIX_PATH': '${DARWIN_HOST_PACKAGE_PREFIXES.join(';')}'}`,
126
- ],
127
- buildenv: () => ['PKG_CONFIG_LIBDIR='],
128
- flags: [],
129
- },
130
- // The windows image's llvm-mingw through its CMake toolchain file for the triple, as a port archive for
131
- // an addon is built. No compiler.runtime: that would make it a clang standing in for MSVC.
132
- win32: {
133
- settings: (target) => [
134
- 'os=Windows',
135
- `arch=${NODE_ARCHS[target.arch]}`,
136
- 'compiler=clang',
137
- `compiler.version=${clangMajorVersion(windowsTool(target, 'clang'))}`,
138
- 'compiler.libcxx=libc++',
139
- `compiler.cppstd=${CPP_STANDARD}`,
140
- ],
141
- conf: (target) => [
142
- `tools.build:compiler_executables={'c': '${windowsTool(target, 'clang')}', 'cpp': '${windowsTool(target, 'clang++')}', 'rc': '${windowsTool(target, 'windres')}'}`,
143
- `tools.cmake.cmaketoolchain:user_toolchain=["${windowsToolchainFile(target)}"]`,
144
- `tools.gnu:host_triplet=${windowsTriple(target)}`,
145
- ],
146
- buildenv: (target) => envLines(windowsBuildEnv(target)),
147
- flags: [],
148
- },
149
- };
150
-
151
- export const BUILD_PROFILE = [
152
- '{% set compiler, version, compiler_exe = detect_api.detect_default_compiler() %}',
153
- '[settings]',
154
- 'os={{ detect_api.detect_os() }}',
155
- 'arch={{ detect_api.detect_arch() }}',
156
- 'build_type=Release',
157
- 'compiler={{ compiler }}',
158
- 'compiler.version={{ detect_api.default_compiler_version(compiler, version) }}',
159
- 'compiler.cppstd={{ detect_api.default_cppstd(compiler, version) }}',
160
- 'compiler.libcxx={{ detect_api.detect_libcxx(compiler, version, compiler_exe) }}',
161
- '',
162
- ].join('\n');
163
-
164
- const list = (values) => JSON.stringify(values);
165
-
166
- // Conan checks compiler.version against the versions its own release lists, and a compiler newer than the
167
- // Conan release lists none for it: Xcode's on the host, or an image's after a toolchain bump. Added to the
168
- // run's home, this lets any version of the profile's clang through.
169
- export function settingsUser(target) {
170
- const compiler = PLATFORMS[target.platform]?.settings(target).find((setting) => setting.startsWith('compiler='))?.slice('compiler='.length);
171
- return ['clang', 'apple-clang'].includes(compiler) ? `compiler:\n ${compiler}:\n version: ["ANY"]\n` : null;
172
- }
173
-
174
- export function hostProfile(target) {
175
- const platform = PLATFORMS[target.platform];
176
- if (!platform) {
177
- const names = Object.keys(PLATFORMS);
178
- throw new Error(`crossbind: conan: imports build for ${names.slice(0, -1).join(', ')} and ${names.at(-1)} so far; ${target.platform} is not supported yet, so its build cannot use conanDependencies.`);
179
- }
180
- const flags = list([...platform.flags, ...targetArchiveFlags(target)]);
181
- const buildenv = platform.buildenv(target);
182
- return [
183
- '[settings]',
184
- ...platform.settings(target),
185
- 'build_type=Release',
186
- '',
187
- '[options]',
188
- '*:shared=False',
189
- '',
190
- '[conf]',
191
- ...platform.conf(target),
192
- `tools.build:cflags=${flags}`,
193
- `tools.build:cxxflags=${flags}`,
194
- `tools.build:exelinkflags=${flags}`,
195
- `tools.build:sharedlinkflags=${flags}`,
196
- `tools.info.package_id:confs=${list(PACKAGE_ID_CONFS)}`,
197
- '',
198
- ...(buildenv.length > 0 ? ['[buildenv]', ...buildenv, ''] : []),
199
- '[platform_tool_requires]',
200
- `cmake/${CMAKE_VERSION}`,
201
- '',
202
- ].join('\n');
203
- }
@@ -1,257 +0,0 @@
1
- import { spawnSync } from 'node:child_process';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import getOsUserAndGroupId from './getOsUserAndGroupId.js';
6
- import makeTreeWritable from './makeTreeWritable.js';
7
- import pullDockerImage, { getDockerImage, getDockerContainerName, imageRoleFor } from './pullDockerImage.js';
8
- import { DOCKER_RUN_SECURITY_ARGS } from './dockerSecurity.js';
9
- import assertExecContainer from './execContainer.js';
10
- import { IOS_DEVELOPER_DIR, XCODE_TOOLCHAIN_BIN } from './iosToolchain.js';
11
- import { DARWIN_CC, DARWIN_TOOLS_BIN } from './darwinToolchain.js';
12
- import { HOST_BUILT_PLATFORMS } from './targets.js';
13
- import { remoteExecParams } from './remoteRunner.js';
14
- import { runnerFor } from './selectRunner.js';
15
-
16
- // Every conan invocation crossbind makes goes through here. The config is passed in instead of read
17
- // from state, because state attaches the staged Conan packages while it is still being built.
18
- //
19
- // A Conan home holds code conan runs (plugins, hooks) and settings every recipe obeys (global.conf,
20
- // remotes), and the recipes it builds can write to it. So every run gets a home of its own, made in a
21
- // fresh work directory and deleted with it; only the package store outlives a run. On the host the
22
- // environment is rebuilt from an allowlist, so CONAN_DEFAULT_PROFILE, a stray CC or a remote's
23
- // credentials never reach conan.
24
- const ALLOWED_ENV = [
25
- /^PATH$/,
26
- /^HOME$/,
27
- /^USER$/,
28
- /^LOGNAME$/,
29
- /^SHELL$/,
30
- /^TERM$/,
31
- /^TMPDIR$/,
32
- /^TEMP$/,
33
- /^TMP$/,
34
- /^LANG$/,
35
- /^LC_[A-Z_]+$/,
36
- /^(HTTP|HTTPS|ALL|NO)_PROXY$/i,
37
- /^SSL_CERT_(FILE|DIR)$/,
38
- /^REQUESTS_CA_BUNDLE$/,
39
- // A host Emscripten under RUNNER=LOCAL.
40
- /^EMSDK(_NODE|_PYTHON)?$/,
41
- /^EM_(CONFIG|CACHE)$/,
42
- /^SYSTEMROOT$/i,
43
- /^WINDIR$/i,
44
- /^USERPROFILE$/i,
45
- /^PROGRAMDATA$/i,
46
- ];
47
-
48
- // What conan can build on the host: wasm with a host Emscripten, iOS and macOS with Xcode.
49
- const HOST_PLATFORMS = ['wasm', 'ios', 'darwin'];
50
- const APPLE_PLATFORM_NAMES = { ios: 'iOS', darwin: 'macOS' };
51
- // Where a container sees conanRoot(): an exec container mounts all of it here, a run container only
52
- // the store and its own work directory, at the same places.
53
- const CONTAINER_ROOT = '/var/cache/crossbind/conan';
54
- // The package folders conan reports, which the host stages from: downloaded binaries sit in p/<ref>/p, ones
55
- // conan built in b/<ref>/p. Sources and build folders stay on a remote runner.
56
- const REMOTE_STORE_OUTPUTS = ['p/*/p', 'b/*/p'];
57
- // A recipe's build log is the whole compiler output of a library.
58
- const MAX_BUFFER = 256 * 1024 * 1024;
59
- // The JSON graph carries conandata, the source URL and SHA-256 of the license rows, from 2.19 on.
60
- const MIN_CONAN_VERSION = [2, 19];
61
-
62
- export function conanRunner(config, target) {
63
- const { runner } = runnerFor(imageRoleFor(target), config.system);
64
- // Xcode runs on the Mac alone, so iOS and macOS packages build there whatever runner the rest use.
65
- return HOST_BUILT_PLATFORMS.includes(target?.platform) ? 'LOCAL' : runner;
66
- }
67
-
68
- // Machine-wide like the cargo cache: built packages are reused by every project, and `pnpm run clear`
69
- // in one project must not throw them away. Containers write to the store and conan runs the recipes
70
- // it keeps there, so conan on the host never shares a store with them.
71
- export function conanRoot(runner = 'DOCKER_RUN') {
72
- return path.join(os.homedir(), '.crossbind', runner === 'LOCAL' ? 'conan-local' : 'conan');
73
- }
74
-
75
- // The directory one conan run reads its inputs from and writes its outputs to, with the home it runs
76
- // with. conanPath names a file of it as conan sees it.
77
- export function createConanWork(runner) {
78
- const root = conanRoot(runner);
79
- const store = path.join(root, 'store');
80
- fs.mkdirSync(store, { recursive: true });
81
- fs.mkdirSync(path.join(root, 'work'), { recursive: true });
82
- const dir = fs.mkdtempSync(path.join(root, 'work', 'run-'));
83
- const conanDir = runner === 'LOCAL' ? dir : `${CONTAINER_ROOT}/work/${path.basename(dir)}`;
84
- const conanPath = (file) => (runner === 'LOCAL' ? path.join(dir, file) : `${conanDir}/${file}`);
85
- // conan takes its home from the first .conanrc at or above its working directory, ahead of
86
- // CONAN_HOME; one in the working directory itself is always that first one.
87
- fs.writeFileSync(path.join(dir, '.conanrc'), 'conan_home=./home\n');
88
- fs.mkdirSync(path.join(dir, 'home'));
89
- fs.writeFileSync(path.join(dir, 'home', 'global.conf'), [
90
- `core.cache:storage_path=${runner === 'LOCAL' ? store : `${CONTAINER_ROOT}/store`}`,
91
- 'core:non_interactive=True',
92
- '',
93
- ].join('\n'));
94
- return {
95
- runner, root, store, dir, conanDir, conanPath,
96
- };
97
- }
98
-
99
- function removeTree(dir) {
100
- if (!fs.existsSync(dir)) return;
101
- makeTreeWritable(dir);
102
- fs.rmSync(dir, { recursive: true, force: true });
103
- }
104
-
105
- export const removeConanWork = (work) => removeTree(work.dir);
106
-
107
- // What runs that were killed left behind. Only while holding the install lock: no run is going then.
108
- export const clearConanWork = (runner) => removeTree(path.join(conanRoot(runner), 'work'));
109
-
110
- // conan reports paths as it sees them. In a container that is through the mounts, which all sit under
111
- // CONTAINER_ROOT, so mapping the prefix back is exact; a path anywhere else is none crossbind gave it.
112
- export function toHostPath(reported, work) {
113
- if (work.runner === 'LOCAL') return path.resolve(reported);
114
- const clean = path.posix.normalize(reported);
115
- // A backslash is a separator again once the path is joined on a Windows host.
116
- if (reported.includes('\\') || !clean.startsWith(`${CONTAINER_ROOT}/`)) {
117
- throw new Error(`crossbind: conan reported ${JSON.stringify(reported)}, outside the directories crossbind mounts for it.`);
118
- }
119
- return path.join(work.root, clean.slice(CONTAINER_ROOT.length + 1));
120
- }
121
-
122
- function allowedEnv() {
123
- return Object.fromEntries(Object.entries(process.env).filter(([key]) => ALLOWED_ENV.some((allowed) => allowed.test(key))));
124
- }
125
-
126
- // The Xcode every crossbind iOS archive builds with, whichever one xcode-select points at.
127
- const iosEnv = () => ({ ...allowedEnv(), DEVELOPER_DIR: IOS_DEVELOPER_DIR });
128
-
129
- // A GNU ar or nm ahead of Apple's on a Mac's PATH (Homebrew's binutils) writes archives Apple's linker
130
- // cannot read, and recipes, the build tools they build and Meson all take these tools by name. So the
131
- // run gets links to Xcode's first on its PATH; for macOS to the ones xcode-select picks, as the macOS
132
- // ports are built with.
133
- const APPLE_TOOLS = {
134
- ar: 'ar', as: 'as', nm: 'llvm-nm', ranlib: 'ranlib', strip: 'strip',
135
- };
136
- const appleToolOf = (target, name) => (target.platform === 'darwin'
137
- ? path.join(DARWIN_TOOLS_BIN, name)
138
- : path.join(XCODE_TOOLCHAIN_BIN, APPLE_TOOLS[name]));
139
-
140
- function withAppleTools(work, env, target) {
141
- if (process.platform !== 'darwin') return env;
142
- const dir = path.join(work.dir, 'apple-tools');
143
- if (!fs.existsSync(dir)) {
144
- fs.mkdirSync(dir);
145
- Object.keys(APPLE_TOOLS).forEach((name) => fs.symlinkSync(appleToolOf(target, name), path.join(dir, name)));
146
- }
147
- return { ...env, PATH: [dir, env.PATH].filter(Boolean).join(path.delimiter) };
148
- }
149
-
150
- const toolVersions = new Map();
151
- // The first line a tool prints for --version, asked once.
152
- function toolVersion(command, env = allowedEnv(), cwd = undefined) {
153
- const key = command.join(' ');
154
- if (!toolVersions.has(key)) {
155
- const [program, ...args] = command;
156
- const result = spawnSync(program, [...args, '--version'], { encoding: 'utf8', env, cwd });
157
- toolVersions.set(key, `${result.stdout ?? ''}`.split('\n')[0].trim());
158
- }
159
- return toolVersions.get(key);
160
- }
161
-
162
- // conan migrates the home it starts in, even for --version, so it is asked from a home of crossbind's
163
- // instead of the user's ~/.conan2.
164
- function conanVersion() {
165
- const dir = path.join(conanRoot('LOCAL'), 'probe');
166
- fs.mkdirSync(dir, { recursive: true });
167
- fs.writeFileSync(path.join(dir, '.conanrc'), 'conan_home=./home\n');
168
- return toolVersion(['conan'], allowedEnv(), dir);
169
- }
170
-
171
- function compilerIdentity(target) {
172
- if (target?.platform === 'ios') return toolVersion(['xcrun', 'clang'], iosEnv());
173
- if (target?.platform === 'darwin') return toolVersion([DARWIN_CC]);
174
- return toolVersion(['emcc']);
175
- }
176
-
177
- // What a host conan builds with; for a container the image digest says the same.
178
- export function localToolchainIdentity(target) {
179
- return `${compilerIdentity(target)}\n${conanVersion()}`;
180
- }
181
-
182
- function assertLocalConan(target) {
183
- const conan = conanVersion();
184
- const [major, minor] = (conan.match(/(\d+)\.(\d+)/) ?? []).slice(1).map(Number);
185
- const [needMajor, needMinor] = MIN_CONAN_VERSION;
186
- if (major > needMajor || (major === needMajor && minor >= needMinor)) return;
187
- const need = `Conan ${MIN_CONAN_VERSION.join('.')} or later; found ${conan || 'none'}.`;
188
- throw new Error(HOST_BUILT_PLATFORMS.includes(target.platform)
189
- ? `crossbind: Conan packages for ${APPLE_PLATFORM_NAMES[target.platform]} build on this Mac, with the conan on the PATH, which must be ${need} Install or upgrade it with \`brew install conan\` (\`brew upgrade conan\`) or \`pipx install conan\`.`
190
- : `crossbind: RUNNER=LOCAL runs conan from the PATH and needs ${need}`);
191
- }
192
-
193
- export default function runConan(args, { config, target, work }) {
194
- const options = { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8', maxBuffer: MAX_BUFFER };
195
- const role = imageRoleFor(target);
196
- if (work.runner === 'LOCAL') {
197
- if (!HOST_PLATFORMS.includes(target.platform)) {
198
- throw new Error(`crossbind: Conan packages for ${target.platform} build in the ${role} image, which RUNNER=LOCAL does not use.`);
199
- }
200
- if (HOST_BUILT_PLATFORMS.includes(target.platform) && process.platform !== 'darwin') {
201
- throw new Error(`crossbind: Conan packages for ${APPLE_PLATFORM_NAMES[target.platform]} build with Xcode, on a Mac.`);
202
- }
203
- assertLocalConan(target);
204
- const env = withAppleTools(work, { ...(target.platform === 'ios' ? iosEnv() : allowedEnv()), CONAN_HOME: path.join(work.dir, 'home') }, target);
205
- return spawnSync('conan', args, { ...options, cwd: work.dir, env });
206
- }
207
-
208
- // Google ships the linux NDK for x86_64 only.
209
- const platform = target.platform === 'android' ? 'linux/amd64' : undefined;
210
- if (work.runner === 'REMOTE') {
211
- return spawnSync(...remoteExecParams({
212
- remote: runnerFor(role, config.system).remote,
213
- role,
214
- image: getDockerImage(role, platform),
215
- mounts: [
216
- { host: work.store, container: `${CONTAINER_ROOT}/store`, inputRoots: [], outputRoots: REMOTE_STORE_OUTPUTS },
217
- { host: work.dir, container: work.conanDir, inputRoots: ['.'], outputRoots: ['.'] },
218
- ],
219
- cwd: work.conanDir,
220
- argv: ['conan', ...args],
221
- env: { CONAN_HOME: work.conanPath('home') },
222
- }, options));
223
- }
224
-
225
- const env = ['-e', `CONAN_HOME=${work.conanPath('home')}`];
226
- let runnerArgs;
227
- if (work.runner === 'DOCKER_EXEC') {
228
- const name = getDockerContainerName(config.paths.base, role);
229
- assertExecContainer({
230
- name,
231
- mounts: [[CONTAINER_ROOT, work.root]],
232
- workdir: work.conanDir,
233
- hint: `Recreate it with \`crossbind docker stop ${role}\`, \`crossbind docker delete ${role}\` and \`crossbind docker create ${role}\`, which mount ${work.root} at ${CONTAINER_ROOT}.`,
234
- mountNote: 'It predates the conan mount.',
235
- });
236
- runnerArgs = ['exec', ...env, '--user', getOsUserAndGroupId(), '--workdir', work.conanDir, name];
237
- } else {
238
- pullDockerImage(role, platform);
239
- runnerArgs = [
240
- 'run',
241
- '--rm',
242
- ...(platform ? ['--platform', platform] : []),
243
- ...DOCKER_RUN_SECURITY_ARGS,
244
- '-v',
245
- `${work.store}:${CONTAINER_ROOT}/store`,
246
- '-v',
247
- `${work.dir}:${work.conanDir}`,
248
- ...env,
249
- '--user',
250
- getOsUserAndGroupId(),
251
- '--workdir',
252
- work.conanDir,
253
- getDockerImage(role, platform),
254
- ];
255
- }
256
- return spawnSync('docker', [...runnerArgs, 'conan', ...args], options);
257
- }