crossbind 2.0.0-beta.76 → 2.0.0-beta.77

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crossbind",
3
- "version": "2.0.0-beta.76",
3
+ "version": "2.0.0-beta.77",
4
4
  "license": "MIT",
5
5
  "homepage": "https://crossbind.dev",
6
6
  "repository": {
@@ -55,7 +55,7 @@
55
55
  "upath": "^3.0.8"
56
56
  },
57
57
  "optionalDependencies": {
58
- "@crossbind/bridge-tools": "2.0.0-beta.76"
58
+ "@crossbind/bridge-tools": "2.0.0-beta.77"
59
59
  },
60
60
  "keywords": [
61
61
  "webassembly",
@@ -4,10 +4,12 @@ import { fileURLToPath } from 'node:url';
4
4
  import run from './run.js';
5
5
  import getLinkInputs from './getLinkInputs.js';
6
6
  import getData from './getData.js';
7
+ import copyNativeNotices from './nativeNotices.js';
7
8
  import state from '../state/index.js';
8
9
  import logger from '../utils/logger.js';
9
10
  import { getContentHash, getFilesFingerprint } from '../utils/hash.js';
10
11
  import { buildLinkLibArgs } from '../utils/linkLayout.js';
12
+ import resetCMakeCacheForSource from '../utils/resetCMakeCacheForSource.js';
11
13
 
12
14
  const cpuCount = Math.max(1, os.cpus().length - 1);
13
15
  const PROJECT_SOURCE = fileURLToPath(new URL('../assets/cmake/command', import.meta.url));
@@ -67,8 +69,10 @@ export default async function buildNativeCommand(target, options = {}) {
67
69
 
68
70
  const platformPrefix = `Native-${buildType}`;
69
71
  logger.startStep(target, 'native command');
72
+ const projectDir = stageProject(build);
73
+ resetCMakeCacheForSource(`${build}/${platformPrefix}/${target.path}`, projectDir, target);
70
74
  run(null, [
71
- 'cmake', stageProject(build),
75
+ 'cmake', projectDir,
72
76
  `-DCMAKE_BUILD_TYPE=${buildType}`,
73
77
  `-DCROSSBIND_COMMAND_FILE=${target.commandName}`,
74
78
  `-DCROSSBIND_LINK_ARGS=${linkArgs.join(';')}`,
@@ -76,6 +80,7 @@ export default async function buildNativeCommand(target, options = {}) {
76
80
  `-DCROSSBIND_STATIC=${isStatic ? 'ON' : 'OFF'}`,
77
81
  ], platformPrefix, target);
78
82
  run(null, ['cmake', '--build', '.', '-j', String(cpuCount)], platformPrefix, target, { exclusive: true });
83
+ copyNativeNotices(target);
79
84
  fs.copyFileSync(`${build}/${platformPrefix}/${target.path}/${target.commandName}`, `${build}/${target.commandName}`);
80
85
  logger.doneStep(target, 'native command');
81
86
 
@@ -179,9 +179,9 @@ function toolchainNoticeTexts(platform, pattern) {
179
179
  return texts.length > 0 ? texts.join('\n\n') : null;
180
180
  }
181
181
 
182
- // A Linux or Windows addon carries its C++ runtime; a Windows one also carries parts of the
182
+ // A Linux or Windows addon, app or executable carries its C++ runtime; a Windows one also carries parts of the
183
183
  // mingw-w64 runtime and winpthreads, whose licenses ask for their notices in binary distributions.
184
- // A macOS addon uses the system's C++ runtime. Their texts and versions are what the link copied out of its image.
184
+ // A macOS binary uses the system's C++ runtime. Their texts and versions are what the link copied out of its image.
185
185
  function addonToolchainRows(platform) {
186
186
  const shared = {
187
187
  npmName: null, version: null, nativeVersion: null, sha256: null, licenseSelected: null, licenseText: null, isCopyleft: false,
@@ -219,10 +219,43 @@ function addonToolchainRows(platform) {
219
219
  sourceUrl: 'https://github.com/mingw-w64/mingw-w64',
220
220
  licenseNotes: 'the mingw-w64 startup code and winpthreads, statically linked into the binary; a binary distribution includes their notices: https://github.com/mingw-w64/mingw-w64/blob/master/COPYING.MinGW-w64-runtime/COPYING.MinGW-w64-runtime.txt and https://github.com/mingw-w64/mingw-w64/blob/master/mingw-w64-libraries/winpthreads/COPYING (the windows image carries both in /opt/licenses/llvm-mingw)',
221
221
  licenseText: toolchainNoticeTexts('win32', MINGW_NOTICE),
222
+ isNoticeRequired: true,
222
223
  },
223
224
  ];
224
225
  }
225
226
 
227
+ // A static musl executable carries musl's libc, whose MIT license asks every copy for its notice. Its text and version
228
+ // are what the link copied out of its image; an addon and a library leave the C library to what runs them.
229
+ function muslRow() {
230
+ return {
231
+ npmName: null,
232
+ version: null,
233
+ sha256: null,
234
+ licenseSelected: null,
235
+ isCopyleft: false,
236
+ name: 'musl',
237
+ license: 'MIT',
238
+ licenseDeclared: 'MIT',
239
+ sourceUrl: 'https://git.musl-libc.org/cgit/musl',
240
+ nativeVersion: toolchainNotice('linuxmusl', NATIVE_NOTICES.muslVersion),
241
+ licenseText: toolchainNoticeText('linuxmusl', NATIVE_NOTICES.muslText),
242
+ licenseNotes: 'the musl libc of the linux toolchain image, statically linked into the executable',
243
+ isNoticeRequired: true,
244
+ };
245
+ }
246
+
247
+ const BINARY_KINDS = { node: 'addon', app: 'app', native: 'executable' };
248
+
249
+ // The runtimes a binary carries without the notice their licenses ask its distribution for, which its link copies out of
250
+ // the image. A list of no binary (no runtimeEnv), such as a static library's, leaves those notices to the binary's own.
251
+ export function missingNotices(rows, { platform, runtimeEnv }) {
252
+ const kind = BINARY_KINDS[runtimeEnv];
253
+ if (!kind) return [];
254
+ return rows
255
+ .filter((row) => row.isNoticeRequired && !row.licenseText)
256
+ .map((row) => `x ${row.name}: its license asks a distribution of the ${platform} ${kind} for its notices, and the list has none; build the ${platform} ${kind}, whose link copies them out of its toolchain image`);
257
+ }
258
+
226
259
  const versionOf = (dir) => JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')).version;
227
260
  const textOf = (file) => fs.readFileSync(file, 'utf8').trim();
228
261
 
@@ -613,7 +646,8 @@ export async function vendoringLibraries() {
613
646
  // copies and the toolchain runtime, and for wasm crossbind's runtime, which the
614
647
  // module's glue bundles. runtimeEnv 'node' adds the crossbind runtime an
615
648
  // addon of that platform compiles in, or without a platform the one its loader bundles;
616
- // runtimeEnv 'app' adds what an app's host carries.
649
+ // runtimeEnv 'app' adds what an app's host carries; runtimeEnv 'native' adds the C library
650
+ // a static musl executable links.
617
651
  export default async function collectLicenseRows(platform = null, { runtimeEnv = null } = {}) {
618
652
  assertSourcesStaged();
619
653
  // The root package is a component too when it is a port: leaf -wasi packages have no deps but ship
@@ -646,6 +680,7 @@ export default async function collectLicenseRows(platform = null, { runtimeEnv =
646
680
  if (platform === 'wasi') rows.push(...wasiToolchainRows());
647
681
  if (platform === 'wasm') rows.push(...wasmToolchainRows(), crossbindRow("crossbind's JavaScript runtime, which the module's glue bundles"));
648
682
  if (['linux', 'linuxmusl', 'win32'].includes(platform)) rows.push(...addonToolchainRows(platform));
683
+ if (platform === 'linuxmusl' && runtimeEnv === 'native') rows.push(muslRow());
649
684
  if (runtimeEnv === 'node') rows.push(...nodeRuntimeRows({ isAddon: platform !== null }));
650
685
  if (runtimeEnv === 'app') {
651
686
  rows.push(...appRuntimeRows(platform));
@@ -8,6 +8,7 @@ const MINGW_CLANG = '/opt/llvm-mingw/bin/clang';
8
8
  // The x64 and arm64 targets share one mingw-w64 build.
9
9
  const MINGW_VERSION_HEADER = '/opt/llvm-mingw/x86_64-w64-mingw32/include/_mingw_mac.h';
10
10
  const LIBCXX_LICENSE = '/opt/licenses/linux-sysroot/libcxx-LICENSE.TXT';
11
+ const MUSL_LICENSE = '/opt/licenses/linux-sysroot/musl/musl-COPYRIGHT';
11
12
  const NDK_CLANG = `${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang`;
12
13
 
13
14
  const ifFile = (file, command) => `if [ -f ${file} ]; then ${command}; fi`;
@@ -31,7 +32,16 @@ const SCRIPTS = {
31
32
  },
32
33
  android: (dir) => [clangVersion(NDK_CLANG, dir)],
33
34
  };
34
- SCRIPTS.linuxmusl = SCRIPTS.linux;
35
+ // A static musl executable also carries musl's libc, whose notice every musl link keeps. musl records its version only
36
+ // in libc, as the one string of version.lo.
37
+ SCRIPTS.linuxmusl = (dir, target) => {
38
+ const libc = `/opt/crossbind/sysroots/${linuxTriple(target)}/usr/lib/libc.a`;
39
+ return [
40
+ ...SCRIPTS.linux(dir, target),
41
+ ifFile(MUSL_LICENSE, `cp ${MUSL_LICENSE} ${dir}/${NATIVE_NOTICES.muslText}`),
42
+ ifFile(libc, `ar p ${libc} version.lo | tr -cs '[:print:]' '\\n' | grep -x -E '[0-9]+\\.[0-9]+\\.[0-9]+' > ${dir}/${NATIVE_NOTICES.muslVersion}`),
43
+ ];
44
+ };
35
45
 
36
46
  // Copies them beside the build after a link in the target's image, where crossbind licenses reads them. A host
37
47
  // toolchain keeps none of these files, and its build goes on without them.
package/src/bin.js CHANGED
@@ -48,7 +48,7 @@ import { cargoHome } from './utils/runCargo.js';
48
48
  import { containerRootOf, sourceRoot } from './utils/runPackageSource.js';
49
49
  import { importSchemesOf, sourceNamesOf } from './utils/packageSources.js';
50
50
  import { cleanDepsCache } from './utils/dependencyRebuild.js';
51
- import collectLicenseRows, { vendoringLibraries } from './actions/licenses.js';
51
+ import collectLicenseRows, { missingNotices, vendoringLibraries } from './actions/licenses.js';
52
52
  import { formatNoticesMarkdown, formatCycloneDxSbom, validateSpdx } from './utils/licenseReport.js';
53
53
  import { SBOM_FILE, nodePackageMeta, shippedRows, writeNodePackageLicense } from './utils/packageLicense.js';
54
54
  import findFiles from './utils/findFiles.js';
@@ -225,9 +225,9 @@ program.command('licenses')
225
225
  .description('list bundled native dependencies with SPDX license, versions and source URL')
226
226
  .option('--notices [file]', 'write a THIRD-PARTY-NOTICES markdown file (default: THIRD-PARTY-NOTICES.md)')
227
227
  .option('--sbom [file]', 'write a CycloneDX SBOM json file (default: sbom.cdx.json)')
228
- .option('--check', 'exit non-zero when a license field is missing or not valid SPDX')
228
+ .option('--check', 'exit non-zero when a license field is missing or not valid SPDX, and with -e when the binary lacks a notice the license of a runtime it carries asks for')
229
229
  .option('--platform <platform>', "also list what this platform's artifact statically links beyond the package graph (vendored copies, toolchain runtime)")
230
- .addOption(new Option('-e, --runtime-env <runtimeEnv>', "also list the crossbind runtime an addon of --platform compiles in (without --platform the one its loader bundles), or what an app's host carries").choices(['node', 'app']))
230
+ .addOption(new Option('-e, --runtime-env <runtimeEnv>', "also list the crossbind runtime an addon of --platform compiles in (without --platform the one its loader bundles), what an app's host carries, or the C library a static musl executable links").choices(['node', 'app', 'native']))
231
231
  .option('--json [file]', 'write the rows, license texts included, as JSON (default: licenses.json)')
232
232
  .option('--package', "write this Node-API package's LICENSE, sbom.cdx.json and package.json license field (with -e node)")
233
233
  .action(async (options) => {
@@ -280,7 +280,9 @@ program.command('licenses')
280
280
  .map((row) => ({ row, result: validateSpdx(row.license) }))
281
281
  .filter(({ result }) => !result.isValid);
282
282
  invalid.forEach(({ row, result }) => console.error(`x ${row.name} (${row.npmName}): ${result.error}`));
283
- if (invalid.length > 0) process.exit(1);
283
+ const unnoticed = missingNotices(rows, { platform: options.platform, runtimeEnv: options.runtimeEnv });
284
+ unnoticed.forEach((problem) => console.error(problem));
285
+ if (invalid.length > 0 || unnoticed.length > 0) process.exit(1);
284
286
  console.log('✓ all license fields are valid SPDX');
285
287
  }
286
288
  });
@@ -20,6 +20,8 @@ export const NATIVE_NOTICES = {
20
20
  clangVersion: 'clang-version.txt',
21
21
  libcxxVersion: 'libcxx-version.txt',
22
22
  mingwVersion: 'mingw-w64-version.txt',
23
+ muslText: 'musl-COPYRIGHT',
24
+ muslVersion: 'musl-version.txt',
23
25
  };
24
26
 
25
27
  export const MINGW_NOTICE = /^COPYING/;