@git.zone/tsrust 1.13.2 → 1.15.0

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 (48) hide show
  1. package/.smartconfig.json +4 -0
  2. package/assets/notices/gcc-gpl-3.0.txt +674 -0
  3. package/assets/notices/gcc-runtime-library-exception-3.1.txt +73 -0
  4. package/assets/notices/glibc-lgpl-2.1.txt +502 -0
  5. package/assets/notices/musl-copyright.txt +193 -0
  6. package/dist_ts/00_commitinfo_data.js +1 -1
  7. package/dist_ts/index.d.ts +1 -0
  8. package/dist_ts/index.js +2 -1
  9. package/dist_ts/mod_cli/classes.tsrustcli.d.ts +10 -0
  10. package/dist_ts/mod_cli/classes.tsrustcli.js +91 -11
  11. package/dist_ts/mod_cli/helpers.targets.d.ts +2 -0
  12. package/dist_ts/mod_cli/helpers.targets.js +1 -1
  13. package/dist_ts/mod_matrix/classes.nativematrixbuilder.d.ts +6 -0
  14. package/dist_ts/mod_matrix/classes.nativematrixbuilder.js +44 -1
  15. package/dist_ts/mod_notices/classes.nativenoticesgenerator.d.ts +102 -0
  16. package/dist_ts/mod_notices/classes.nativenoticesgenerator.js +946 -0
  17. package/dist_ts/mod_notices/classes.noticetoolchain.d.ts +30 -0
  18. package/dist_ts/mod_notices/classes.noticetoolchain.js +69 -0
  19. package/dist_ts/mod_notices/helpers.licensetext.d.ts +12 -0
  20. package/dist_ts/mod_notices/helpers.licensetext.js +81 -0
  21. package/dist_ts/mod_notices/helpers.muslversion.d.ts +6 -0
  22. package/dist_ts/mod_notices/helpers.muslversion.js +47 -0
  23. package/dist_ts/mod_notices/helpers.noticesconfig.d.ts +71 -0
  24. package/dist_ts/mod_notices/helpers.noticesconfig.js +133 -0
  25. package/dist_ts/mod_notices/helpers.runtimetexts.d.ts +9 -0
  26. package/dist_ts/mod_notices/helpers.runtimetexts.js +45 -0
  27. package/dist_ts/mod_notices/helpers.spdx.d.ts +21 -0
  28. package/dist_ts/mod_notices/helpers.spdx.js +88 -0
  29. package/dist_ts/mod_notices/helpers.staticglibc.d.ts +26 -0
  30. package/dist_ts/mod_notices/helpers.staticglibc.js +45 -0
  31. package/dist_ts/mod_notices/index.d.ts +8 -0
  32. package/dist_ts/mod_notices/index.js +9 -0
  33. package/package.json +5 -5
  34. package/readme.md +123 -13
  35. package/ts/00_commitinfo_data.ts +1 -1
  36. package/ts/index.ts +1 -0
  37. package/ts/mod_cli/classes.tsrustcli.ts +104 -10
  38. package/ts/mod_cli/helpers.targets.ts +2 -0
  39. package/ts/mod_matrix/classes.nativematrixbuilder.ts +44 -0
  40. package/ts/mod_notices/classes.nativenoticesgenerator.ts +1201 -0
  41. package/ts/mod_notices/classes.noticetoolchain.ts +92 -0
  42. package/ts/mod_notices/helpers.licensetext.ts +111 -0
  43. package/ts/mod_notices/helpers.muslversion.ts +46 -0
  44. package/ts/mod_notices/helpers.noticesconfig.ts +235 -0
  45. package/ts/mod_notices/helpers.runtimetexts.ts +63 -0
  46. package/ts/mod_notices/helpers.spdx.ts +107 -0
  47. package/ts/mod_notices/helpers.staticglibc.ts +79 -0
  48. package/ts/mod_notices/index.ts +8 -0
@@ -0,0 +1,88 @@
1
+ const maximumAlternatives = 256;
2
+ const identifierPattern = /^(?:DocumentRef-[A-Za-z0-9.-]+:)?[A-Za-z0-9][A-Za-z0-9.-]*\+?$/;
3
+ const operatorTokens = new Set(['AND', 'OR', 'WITH', '(', ')']);
4
+ const tokenize = (expressionArg) => {
5
+ const tokens = [];
6
+ // Cargo still accepts the legacy `MIT/Apache-2.0` form, meaning OR.
7
+ for (const word of expressionArg.replace(/([()/])/g, ' $1 ').split(/\s+/)) {
8
+ if (!word)
9
+ continue;
10
+ tokens.push(word === '/' ? 'OR' : word);
11
+ }
12
+ return tokens;
13
+ };
14
+ const expand = (nodeArg) => {
15
+ if (nodeArg.kind === 'term')
16
+ return [[nodeArg.term]];
17
+ const left = expand(nodeArg.left);
18
+ const right = expand(nodeArg.right);
19
+ const alternatives = nodeArg.kind === 'or'
20
+ ? [...left, ...right]
21
+ : left.flatMap((leftTerms) => right.map((rightTerms) => [...leftTerms, ...rightTerms]));
22
+ if (alternatives.length > maximumAlternatives) {
23
+ throw new Error('SPDX expression has too many alternatives');
24
+ }
25
+ return alternatives;
26
+ };
27
+ /**
28
+ * Parses an SPDX license expression (`AND`, `OR`, `WITH`, parentheses, and
29
+ * Cargo's legacy `/` separator). Operators are case-sensitive; anything the
30
+ * grammar does not describe is rejected rather than guessed.
31
+ */
32
+ export function parseSpdxExpression(expressionArg) {
33
+ if (typeof expressionArg !== 'string' || expressionArg.trim() === '' || expressionArg.length > 1024) {
34
+ throw new Error('SPDX expression must be a non-empty string');
35
+ }
36
+ const tokens = tokenize(expressionArg);
37
+ let position = 0;
38
+ const fail = (reasonArg) => {
39
+ throw new Error(`Invalid SPDX expression ${JSON.stringify(expressionArg)}: ${reasonArg}`);
40
+ };
41
+ const peek = () => tokens[position];
42
+ const readIdentifier = (labelArg) => {
43
+ const token = tokens[position];
44
+ if (token === undefined || operatorTokens.has(token) || !identifierPattern.test(token)) {
45
+ fail(`expected ${labelArg} at ${JSON.stringify(token ?? 'end')}`);
46
+ }
47
+ position += 1;
48
+ return token;
49
+ };
50
+ const parsePrimary = () => {
51
+ if (peek() === '(') {
52
+ position += 1;
53
+ const inner = parseOr();
54
+ if (peek() !== ')')
55
+ fail('missing closing parenthesis');
56
+ position += 1;
57
+ return inner;
58
+ }
59
+ const license = readIdentifier('a license identifier');
60
+ if (peek() === 'WITH') {
61
+ position += 1;
62
+ const exception = readIdentifier('an exception identifier');
63
+ return { kind: 'term', term: { license, exception } };
64
+ }
65
+ return { kind: 'term', term: { license } };
66
+ };
67
+ const parseAnd = () => {
68
+ let node = parsePrimary();
69
+ while (peek() === 'AND') {
70
+ position += 1;
71
+ node = { kind: 'and', left: node, right: parsePrimary() };
72
+ }
73
+ return node;
74
+ };
75
+ const parseOr = () => {
76
+ let node = parseAnd();
77
+ while (peek() === 'OR') {
78
+ position += 1;
79
+ node = { kind: 'or', left: node, right: parseAnd() };
80
+ }
81
+ return node;
82
+ };
83
+ const root = parseOr();
84
+ if (position !== tokens.length)
85
+ fail(`unexpected ${JSON.stringify(tokens[position])}`);
86
+ return { alternatives: expand(root) };
87
+ }
88
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5zcGR4LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvbW9kX25vdGljZXMvaGVscGVycy5zcGR4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQXFCQSxNQUFNLG1CQUFtQixHQUFHLEdBQUcsQ0FBQztBQUNoQyxNQUFNLGlCQUFpQixHQUFHLGdFQUFnRSxDQUFDO0FBQzNGLE1BQU0sY0FBYyxHQUFHLElBQUksR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFFaEUsTUFBTSxRQUFRLEdBQUcsQ0FBQyxhQUFxQixFQUFZLEVBQUU7SUFDbkQsTUFBTSxNQUFNLEdBQWEsRUFBRSxDQUFDO0lBQzVCLG9FQUFvRTtJQUNwRSxLQUFLLE1BQU0sSUFBSSxJQUFJLGFBQWEsQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQzFFLElBQUksQ0FBQyxJQUFJO1lBQUUsU0FBUztRQUNwQixNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDMUMsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMsQ0FBQztBQUVGLE1BQU0sTUFBTSxHQUFHLENBQUMsT0FBa0IsRUFBaUIsRUFBRTtJQUNuRCxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssTUFBTTtRQUFFLE9BQU8sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ3JELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDbEMsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNwQyxN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
@@ -0,0 +1,26 @@
1
+ export interface IStaticGlibcCheckOptions {
2
+ targets: Array<{
3
+ triple: string;
4
+ friendly: string;
5
+ }>;
6
+ /** `static` from the configuration or `--static`. */
7
+ staticLinking: boolean;
8
+ /** `rustflags` from the tsrust configuration. */
9
+ rustflags: string[];
10
+ environment: NodeJS.ProcessEnv;
11
+ }
12
+ /**
13
+ * The error for a Linux GNU target that links glibc statically: static glibc
14
+ * carries LGPL-2.1 relinking obligations that native notices do not fulfil.
15
+ */
16
+ export declare function staticGlibcError(friendlyArg: string, tripleArg: string, reasonArg: string, remedyArg: string): Error;
17
+ /**
18
+ * Refuses a build with native notices that would link glibc statically, from
19
+ * any source tsrust can see before building: `static`, tsrust `rustflags`,
20
+ * and the rustflags environment variables Cargo reads. Static linking set up
21
+ * elsewhere (a Cargo configuration file) is caught by inspecting the built
22
+ * binary with `staticGlibcArtifactError`.
23
+ */
24
+ export declare function assertNoStaticGlibc(optionsArg: IStaticGlibcCheckOptions): void;
25
+ /** The error for a GNU binary that the build produced without an interpreter. */
26
+ export declare function staticGlibcArtifactError(friendlyArg: string, tripleArg: string): Error;
@@ -0,0 +1,45 @@
1
+ import { friendlyTargetName } from '../mod_cli/helpers.targets.js';
2
+ const isGnuTarget = (tripleArg) => tripleArg.endsWith('-linux-gnu');
3
+ /**
4
+ * The error for a Linux GNU target that links glibc statically: static glibc
5
+ * carries LGPL-2.1 relinking obligations that native notices do not fulfil.
6
+ */
7
+ export function staticGlibcError(friendlyArg, tripleArg, reasonArg, remedyArg) {
8
+ const musl = friendlyTargetName(tripleArg.replace(/-gnu$/, '-musl'));
9
+ return new Error(`${reasonArg}. Static glibc carries LGPL-2.1 relinking obligations that tsrust notices do not fulfil. ` +
10
+ `Choose (a) a static musl target such as ${musl} instead of ${friendlyArg}, or (b) dynamic glibc by ` +
11
+ `${remedyArg}.`);
12
+ }
13
+ /**
14
+ * Refuses a build with native notices that would link glibc statically, from
15
+ * any source tsrust can see before building: `static`, tsrust `rustflags`,
16
+ * and the rustflags environment variables Cargo reads. Static linking set up
17
+ * elsewhere (a Cargo configuration file) is caught by inspecting the built
18
+ * binary with `staticGlibcArtifactError`.
19
+ */
20
+ export function assertNoStaticGlibc(optionsArg) {
21
+ for (const target of optionsArg.targets.filter((entry) => isGnuTarget(entry.triple))) {
22
+ if (optionsArg.staticLinking) {
23
+ throw staticGlibcError(target.friendly, target.triple, `Target ${target.friendly} links glibc statically ("static" or --static)`, 'removing "static" from the @git.zone/tsrust configuration and not passing --static');
24
+ }
25
+ const sources = [
26
+ ['the @git.zone/tsrust rustflags', optionsArg.rustflags.join(' ')],
27
+ ['RUSTFLAGS', optionsArg.environment.RUSTFLAGS],
28
+ ['CARGO_ENCODED_RUSTFLAGS', optionsArg.environment.CARGO_ENCODED_RUSTFLAGS],
29
+ ['CARGO_BUILD_RUSTFLAGS', optionsArg.environment.CARGO_BUILD_RUSTFLAGS],
30
+ ];
31
+ const targetVariable = `CARGO_TARGET_${target.triple.toUpperCase().replace(/-/g, '_')}_RUSTFLAGS`;
32
+ sources.push([targetVariable, optionsArg.environment[targetVariable]]);
33
+ for (const [name, value] of sources) {
34
+ if (value?.includes('+crt-static')) {
35
+ throw staticGlibcError(target.friendly, target.triple, `Target ${target.friendly} links glibc statically (+crt-static in ${name})`, `removing +crt-static from ${name}`);
36
+ }
37
+ }
38
+ }
39
+ }
40
+ /** The error for a GNU binary that the build produced without an interpreter. */
41
+ export function staticGlibcArtifactError(friendlyArg, tripleArg) {
42
+ return staticGlibcError(friendlyArg, tripleArg, `${friendlyArg} was linked statically: the binary has no ELF interpreter although no tsrust setting or ` +
43
+ 'rustflags environment variable requested static linking', 'removing crt-static and static link arguments from the Cargo configuration (.cargo/config.toml)');
44
+ }
45
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5zdGF0aWNnbGliYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL21vZF9ub3RpY2VzL2hlbHBlcnMuc3RhdGljZ2xpYmMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFXbkUsTUFBTSxXQUFXLEdBQUcsQ0FBQyxTQUFpQixFQUFXLEVBQUUsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBRXJGOzs7R0FHRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FDOUIsV0FBbUIsRUFDbkIsU0FBaUIsRUFDakIsU0FBaUIsRUFDakIsU0FBaUI7SUFFakIsTUFBTSxJQUFJLEdBQUcsa0JBQWtCLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUNyRSxPQUFPLElBQUksS0FBSyxDQUNkLEdBQUcsU0FBUywyRkFBMkY7UUFDckcsMkNBQTJDLElBQUksZUFBZSxXQUFXLDRCQUE0QjtRQUNyRyxHQUFHLFNBQVMsR0FBRyxDQUNsQixDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxVQUFvQztJQUN0RSxLQUFLLE1BQU0sTUFBTSxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNyRixJQUFJLFVBQVUsQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUM3QixNQUFNLGdCQUFnQixDQUNwQixNQUFNLENBQUMsUUFBUSxFQUNmLE1BQU0sQ0FBQyxNQUFNLEVBQ2IsVUFBVSxNQUFNLENBQUMsUUFBUSxnREFBZ0QsRUFDekUsb0ZBQW9GLENBQ3JGLENBQUM7UUFDSixDQUFDO1FBQ0QsTUFBTSxPQUFPLEdBQXdDO1lBQ25ELENBQUMsZ0NBQWdDLEVBQUUsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDbEUsQ0FBQyxXQUFXLEVBQUUsVUFBVSxDQUFDLFdBQVcsQ0FBQyxTQUFTLENBQUM7WUFDL0MsQ0FBQyx5QkFBeUIsRUFBRSxVQUFVLENBQUMsV0FBVyxDQUFDLHVCQUF1QixDQUFDO1lBQzNFLENBQUMsdUJBQXVCLEVBQUUsVUFBVSxDQUFDLFdBQVcsQ0FBQyxxQkFBcUIsQ0FBQztTQUN4RSxDQUFDO1FBQ0YsTUFBTSxjQUFjLEdBQUcsZ0JBQWdCLE1BQU0sQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsWUFBWSxDQUFDO1FBQ2xHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxjQUFjLEVBQUUsVUFBVSxDQUFDLFdBQVcsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkUsS0FBSyxNQUFNLENBQUMsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ3BDLElBQUksS0FBSyxFQUFFLFFBQVEsQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDO2dCQUNuQyxNQUFNLGdCQUFnQixDQUNwQixNQUFNLENBQUMsUUFBUSxFQUNmLE1BQU0sQ0FBQyxNQUFNLEVBQ2IsVUFBVSxNQUFNLENBQUMsUUFBUSwyQ0FBMkMsSUFBSSxHQUFHLEVBQzNFLDZCQUE2QixJQUFJLEVBQUUsQ0FDcEMsQ0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztBQUNILENBQUM7QUFFRCxpRkFBaUY7QUFDakYsTUFBTSxVQUFVLHdCQUF3QixDQUFDLFdBQW1CLEVBQUUsU0FBaUI7SUFDN0UsT0FBTyxnQkFBZ0IsQ0FDckIsV0FBVyxFQUNYLFNBQVMsRUFDVCxHQUFHLFdBQVcsMEZBQTBGO1FBQ3RHLHlEQUF5RCxFQUMzRCxpR0FBaUcsQ0FDbEcsQ0FBQztBQUNKLENBQUMifQ==
@@ -0,0 +1,8 @@
1
+ export * from './helpers.spdx.js';
2
+ export * from './helpers.licensetext.js';
3
+ export * from './helpers.muslversion.js';
4
+ export * from './helpers.noticesconfig.js';
5
+ export * from './helpers.runtimetexts.js';
6
+ export * from './helpers.staticglibc.js';
7
+ export * from './classes.noticetoolchain.js';
8
+ export * from './classes.nativenoticesgenerator.js';
@@ -0,0 +1,9 @@
1
+ export * from './helpers.spdx.js';
2
+ export * from './helpers.licensetext.js';
3
+ export * from './helpers.muslversion.js';
4
+ export * from './helpers.noticesconfig.js';
5
+ export * from './helpers.runtimetexts.js';
6
+ export * from './helpers.staticglibc.js';
7
+ export * from './classes.noticetoolchain.js';
8
+ export * from './classes.nativenoticesgenerator.js';
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2Rfbm90aWNlcy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLG1CQUFtQixDQUFDO0FBQ2xDLGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLDRCQUE0QixDQUFDO0FBQzNDLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLDhCQUE4QixDQUFDO0FBQzdDLGNBQWMscUNBQXFDLENBQUMifQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@git.zone/tsrust",
3
- "version": "1.13.2",
3
+ "version": "1.15.0",
4
4
  "private": false,
5
5
  "description": "A tool for compiling Rust projects, detecting Cargo workspaces, building with cargo, and placing binaries in a conventional dist_rust directory.",
6
6
  "main": "dist_ts/index.js",
@@ -38,10 +38,10 @@
38
38
  "smol-toml": "^1.7.1"
39
39
  },
40
40
  "devDependencies": {
41
- "@git.zone/tsbuild": "^4.5.0",
42
- "@git.zone/tsrun": "^2.0.6",
43
- "@git.zone/tstest": "^6.1.1",
44
- "@types/node": "26.5.1"
41
+ "@git.zone/tsbuild": "^5.0.0",
42
+ "@git.zone/tsrun": "^3.0.0",
43
+ "@git.zone/tstest": "^6.2.0",
44
+ "@types/node": "26.6.1"
45
45
  },
46
46
  "files": [
47
47
  "ts/**/*",
package/readme.md CHANGED
@@ -134,7 +134,7 @@ dist_rust/
134
134
  └── rustproxy_linux_amd64.tsrust-build.json
135
135
  ```
136
136
 
137
- `tsrust` automatically installs missing rustup targets via `rustup target add` when needed.
137
+ `tsrust` automatically installs missing rustup targets via `rustup target add` when needed. With [native notices](#native-notices) configured, the pre-build notices check installs nothing: it refuses first and names the `rustup target add <triple>` or `rustup component add rust-src` command to run (or run `tsrust notices`, which installs them).
138
138
 
139
139
  ### Configuration via .smartconfig.json
140
140
 
@@ -286,32 +286,28 @@ Matrix workers are trusted execution principals, not sandboxes. A release theref
286
286
 
287
287
  ### Static Linking
288
288
 
289
- `tsrust` can produce fully statically linked Linux binaries (static-pie) that run on both glibc distros (Debian/Ubuntu) and musl distros (Alpine). Enable it via `.smartconfig.json`:
289
+ For static Linux binaries, build the musl targets. They are statically linked by default, run on glibc distros (Debian/Ubuntu) and musl distros (Alpine) alike, and contain musl libc, which is MIT-licensed:
290
290
 
291
291
  ```json
292
292
  {
293
293
  "@git.zone/tsrust": {
294
- "targets": ["linux_amd64", "linux_arm64"],
294
+ "targets": ["linux_amd64_musl", "linux_arm64_musl"],
295
295
  "static": true
296
296
  }
297
297
  }
298
298
  ```
299
299
 
300
- Or per invocation with the `--static` flag:
301
-
302
- ```bash
303
- tsrust --static
304
- ```
300
+ `static: true` (or `--static` per invocation) makes `tsrust` verify after the build that every Linux binary in `dist_rust/` is statically linked (no `PT_INTERP` ELF program header — the check is architecture-independent, so cross-compiled binaries are verified too) and fail the build otherwise.
305
301
 
306
302
  Behavior per target:
307
303
 
308
- - `*-linux-gnu`: `tsrust` injects `RUSTFLAGS="-C target-feature=+crt-static"` into its cargo invocation.
309
- - `*-linux-musl`: already statically linked by default; no flags are injected.
304
+ - `*-linux-musl`: statically linked by default; no flags are injected.
305
+ - `*-linux-gnu`: `tsrust` injects `RUSTFLAGS="-C target-feature=+crt-static"` and links glibc statically.
310
306
  - `*-apple-darwin`: full static linking is not applicable on macOS; the target builds with default linkage.
311
307
 
312
- After the build, `tsrust` verifies every Linux binary in `dist_rust/` is actually statically linked (no `PT_INTERP` ELF program header — the check is architecture-independent, so cross-compiled binaries are verified too) and fails the build otherwise.
308
+ **Prefer musl over static glibc.** glibc is licensed under the LGPL-2.1-or-later. A binary that links it statically must be distributed with the means to relink it that LGPL-2.1 section 6 requires: the application's object files or source, so users can relink it against a modified glibc, and the glibc source or a written offer for it. `tsrust` does not produce that material, and [native notices](#native-notices) refuse static glibc targets. Use the musl targets for static binaries, or leave GNU targets dynamically linked (without `static`): a dynamic binary loads the host's glibc at run time and redistributes none of it.
313
309
 
314
- Why `tsrust` injects the flag instead of the project setting `rustflags` in `rust/.cargo/config.toml`:
310
+ Why `tsrust` injects the crt-static flag for GNU targets instead of the project setting `rustflags` in `rust/.cargo/config.toml`:
315
311
 
316
312
  - `tsrust` always builds with an explicit `--target`, so the flag never applies to host artifacts. A repo-wide `rustflags` entry also applies to proc-macros and build scripts whenever cargo runs *without* `--target` (plain `cargo test`, `cargo check`, rust-analyzer) — and rustc cannot build proc-macros with `+crt-static` on linux-gnu, breaking those commands.
317
313
  - Keep `rust/.cargo/config.toml` free of `rustflags` when using `static` — the injected `RUSTFLAGS` environment variable *replaces* any config-file `rustflags` (cargo does not merge them). `linker` entries (e.g. for aarch64 cross-compilation) are unaffected and should stay.
@@ -323,7 +319,7 @@ Release binaries can embed absolute source paths in panic locations. Enable loca
323
319
  ```json
324
320
  {
325
321
  "@git.zone/tsrust": {
326
- "targets": ["linux_amd64", "linux_arm64"],
322
+ "targets": ["linux_amd64_musl", "linux_arm64_musl"],
327
323
  "static": true,
328
324
  "remapLocalPaths": true
329
325
  }
@@ -340,6 +336,117 @@ Release binaries can embed absolute source paths in panic locations. Enable loca
340
336
  }
341
337
  ```
342
338
 
339
+ ### Native Notices
340
+
341
+ A package that ships native binaries must carry the license notices of everything those binaries contain: the Rust crates from `Cargo.lock`, C libraries that crates compile in, the Rust standard library, and the C runtime. `tsrust notices` generates them into a committed directory next to your sources, and every build verifies it:
342
+
343
+ ```json
344
+ {
345
+ "@git.zone/tsrust": {
346
+ "targets": ["linux_amd64_musl", "linux_arm64_musl"],
347
+ "static": true,
348
+ "locked": true,
349
+ "notices": {}
350
+ }
351
+ }
352
+ ```
353
+
354
+ ```bash
355
+ tsrust notices # write native-notices/
356
+ tsrust notices --check # verify without writing
357
+ ```
358
+
359
+ Ship the directory with the package by listing it in `package.json`:
360
+
361
+ ```json
362
+ {
363
+ "files": ["dist_rust/**/*", "native-notices/**/*"]
364
+ }
365
+ ```
366
+
367
+ What the bundle holds (`manifest.json` records each package's license expression, the texts that cover it, each target's packages and runtime, and the SHA-256 digest of every file):
368
+
369
+ | Path | Contents |
370
+ | --- | --- |
371
+ | `crates/<name>-<version>/` | Every license, copyright and notice file each third-party package publishes, copied verbatim. |
372
+ | `crates/<name>-<version>/extra/` | Reviewed extra texts the project declares for a package in `notices.packages.<key>.extraTexts`. |
373
+ | `native/<crate>-<version>/<library>-<version>/` | License files of C/C++ libraries a crate compiles in, as declared in `notices.packages`. |
374
+ | `rust/` | The toolchain's standard-library copyright inventory and license texts, compiler-builtins' license, and (`rust/crates/`) the license files of standard-library dependencies the inventory omits. |
375
+ | `runtime/` | musl's copyright (static musl targets), the glibc LGPL-2.1 text (dynamic GNU targets, glibc itself is not redistributed), and GCC's GPL-3.0 with the Runtime Library Exception (Linux targets). macOS targets link Apple's system libraries, which are not redistributed. |
376
+ | `readme.md` | A summary of the targets, their linkage, and the obligations above. |
377
+
378
+ Packages are resolved per configured target with `cargo metadata --locked --filter-platform <triple>`: the normal and build dependencies of the workspace's binary packages, transitively, including procedural macros. Development dependencies and the workspace's own crates are excluded. The Rust runtime comes from the toolchain `rust-toolchain.toml` selects.
379
+
380
+ Generation fails instead of guessing when:
381
+
382
+ - a package declares no license, or an expression that is not valid SPDX (Cargo's legacy `MIT/Apache-2.0` form is accepted);
383
+ - no license file of a package covers its expression: every `AND` term of at least one alternative needs a recognised license text;
384
+ - a package has a `links` key and does not declare the native libraries it compiles;
385
+ - a Linux GNU target is linked statically (see [Static Linking](#static-linking));
386
+ - an override names no compiled package, or is no longer needed;
387
+ - an extra text is missing or its SHA-256 digest differs from its pin;
388
+ - the musl version in the toolchain has no reviewed copyright text in `tsrust`;
389
+ - `package.json` `files` does not ship the notices directory.
390
+
391
+ Overrides are keyed by the exact `name@version`, so a dependency upgrade retires them:
392
+
393
+ ```json
394
+ {
395
+ "@git.zone/tsrust": {
396
+ "notices": {
397
+ "directory": "native-notices",
398
+ "packages": {
399
+ "zstd-sys@2.0.16+zstd.1.5.7": {
400
+ "native": [
401
+ {
402
+ "name": "zstd",
403
+ "version": "1.5.7",
404
+ "license": "BSD-3-Clause OR GPL-2.0-only",
405
+ "files": ["zstd/LICENSE", "zstd/COPYING"]
406
+ }
407
+ ]
408
+ },
409
+ "crc32c@0.6.8": {
410
+ "files": [{ "from": "project", "path": "legal/crc32c-license-mit", "licenses": ["MIT"] }]
411
+ },
412
+ "ring@0.17.14": {
413
+ "extraTexts": [
414
+ {
415
+ "path": "legal/ring-0.17.14-once-cell-license-mit.txt",
416
+ "sha256": "6ee2ed6c77710de911761acd5fc1ad1da00f476beb1a7ef27e78c2d1858deafc",
417
+ "kind": "component-license",
418
+ "component": "once_cell"
419
+ },
420
+ {
421
+ "path": "legal/ring-0.17.14-source-attributions.txt",
422
+ "sha256": "675df5a11ef946e519a267eba5f54ce8125eee0d0d443e614302706db4819459",
423
+ "kind": "source-attribution"
424
+ }
425
+ ]
426
+ }
427
+ },
428
+ "runtime": {
429
+ "linux_amd64_musl": [
430
+ { "name": "musl-cross", "version": "20260823", "license": "MIT", "files": ["legal/musl-cross-copyright"] }
431
+ ]
432
+ }
433
+ }
434
+ }
435
+ }
436
+ ```
437
+
438
+ - `packages.<key>.native`: C/C++ libraries the crate compiles, with license files relative to the crate root. `[]` declares that a `links` crate bundles no third-party library.
439
+ - `packages.<key>.files`: reviewed texts for a package whose own files do not cover its license, either kept in the project (`"from": "project"`) or a crate file whose wording `tsrust` does not recognise (`"from": "crate"`), with the SPDX identifiers each text satisfies.
440
+ - `packages.<key>.extraTexts`: reviewed project texts to ship with a package in addition to the texts that cover its license, for example the license of a component the crate vendors or a supplement reproducing the copyright comments of its sources. Each entry names the project-relative `path`, the file's lowercase hex `sha256` digest, its `kind` (`"component-license"` or `"source-attribution"`) and, optionally, the vendored `component` it applies to. The texts are copied to `crates/<name>-<version>/extra/` (`rust/crates/…/extra/` for standard-library dependencies) and listed with their kind under the package's `extraTexts` in `manifest.json`. They never count towards covering the package's license, so a package whose own files do not cover it still needs `files`, and `files` that are no longer needed still fail. Generation and every check refuse a missing text or one whose digest differs from its pin; after reviewing a changed text, update the pin.
441
+ - `packages.<key>.license`: only for a package that declares no license or one that is not valid SPDX.
442
+ - `runtime.<target>`: runtime material a custom linker adds, for example the startup files of a musl cross toolchain.
443
+
444
+ The build (`tsrust`) checks the notices before compiling when `notices` is configured, and refuses targets the notices do not cover. `tsrust matrix check` and `tsrust matrix build` check them once on the coordinator, before any worker starts; workers then build with `--no-notices-check`, which skips only that comparison. After a dependency, toolchain, or target change, run `tsrust notices`, review the diff, and commit it.
445
+
446
+ Only `tsrust notices` uses the network and installs anything: it lets Cargo download missing packages, fetches the standard-library dependencies the toolchain inventory omits (verified against the checksums in the toolchain's `library/Cargo.lock`), and installs the `rust-src` component and missing musl targets. `tsrust notices --check`, the build check and the matrix check work offline and install nothing: they run `cargo metadata --offline` against the local Cargo cache, verify the committed `rust/crates/` notices against the toolchain's checksums and the digests in `manifest.json` and the project's extra texts against their pins, and refuse with the command to run when something is missing (`cargo fetch --locked` for an empty Cargo cache, `rustup component add rust-src`, `rustup target add <triple>`).
447
+
448
+ With `notices` configured, every build refuses static glibc, including builds with `--no-notices-check`: `static`/`--static` on a GNU target, and `+crt-static` in the tsrust `rustflags`, `RUSTFLAGS`, `CARGO_ENCODED_RUSTFLAGS`, `CARGO_BUILD_RUSTFLAGS` or `CARGO_TARGET_<TRIPLE>_RUSTFLAGS`. Static linking configured elsewhere, such as rustflags in `.cargo/config.toml`, is caught after compiling: a GNU binary without an ELF interpreter is removed from `dist_rust/` and the build fails.
449
+
343
450
  ### 🗑️ Clean Only
344
451
 
345
452
  Remove all build artifacts without rebuilding:
@@ -400,6 +507,7 @@ my-project/
400
507
  │ └── my-lib/
401
508
  │ ├── Cargo.toml
402
509
  │ └── src/
510
+ ├── native-notices/ # ⚖️ Third-party notices (tsrust notices, committed)
403
511
  ├── dist_rust/ # 📦 Output: compiled binaries go here
404
512
  │ ├── my-binary
405
513
  │ └── my-binary.tsrust-build.json
@@ -478,6 +586,8 @@ Important exported build and artifact APIs:
478
586
  | Matrix configuration and result types | Type `NativeMatrixBuilder`, worker transports, resolved plans, and check/build results. |
479
587
  | `captureGitSnapshot()` / `assertGitSnapshotUnchanged()` | Capture and compare Git commit and worktree state around a build. |
480
588
  | `ProvenanceStamper` | Read legacy tsrust 1.7/1.8 embedded trailers; new builds use `ProvenanceStore`. |
589
+ | `NativeNoticesGenerator` / `createProjectNoticesGenerator()` | Generate, write, or check the native notices bundle of a project. |
590
+ | `parseSpdxExpression()` / `classifyLicenseText()` | Expand SPDX expressions into alternatives and recognise license texts. |
481
591
 
482
592
  ## License and Legal Information
483
593
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@git.zone/tsrust',
6
- version: '1.13.2',
6
+ version: '1.15.0',
7
7
  description: 'A tool for compiling Rust projects, detecting Cargo workspaces, building with cargo, and placing binaries in a conventional dist_rust directory.'
8
8
  }
package/ts/index.ts CHANGED
@@ -9,5 +9,6 @@ export * from './mod_elf/index.js';
9
9
  export * from './mod_provenance/index.js';
10
10
  export * from './mod_toolchain/index.js';
11
11
  export * from './mod_matrix/index.js';
12
+ export * from './mod_notices/index.js';
12
13
 
13
14
  plugins.early.stop();
@@ -24,9 +24,15 @@ import {
24
24
  } from '../mod_provenance/index.js';
25
25
  import { ToolchainManager } from '../mod_toolchain/index.js';
26
26
  import { NativeMatrixBuilder } from '../mod_matrix/index.js';
27
+ import {
28
+ assertNoStaticGlibc,
29
+ createProjectNoticesGenerator,
30
+ staticGlibcArtifactError,
31
+ } from '../mod_notices/index.js';
27
32
  import { commitinfo } from '../00_commitinfo_data.js';
28
33
  import {
29
34
  configuredAssemblyTargets,
35
+ friendlyTargetName,
30
36
  normalizeTargets,
31
37
  resolveBuildTargets,
32
38
  type ITsrustConfig,
@@ -62,6 +68,7 @@ export class TsRustCli {
62
68
 
63
69
  private registerCommands(): void {
64
70
  this.registerStandardCommand();
71
+ this.registerNoticesCommand();
65
72
  this.registerCleanCommand();
66
73
  this.registerPruneCommand();
67
74
  this.registerAssembleCommand();
@@ -122,7 +129,7 @@ export class TsRustCli {
122
129
  * then falls back to a bundled toolchain at /tmp/tsrust_toolchain/.
123
130
  * Returns the envPrefix string to prepend to shell commands.
124
131
  */
125
- private async resolveToolchain(): Promise<string> {
132
+ private async resolveToolchain(installArg: boolean = true): Promise<string> {
126
133
  // 1. Try system cargo
127
134
  const systemRunner = new CargoRunner(this.cwd);
128
135
  if (await systemRunner.checkCargoInstalled()) {
@@ -132,6 +139,12 @@ export class TsRustCli {
132
139
  // 2. Fall back to bundled toolchain
133
140
  console.log('System cargo not found. Checking for bundled toolchain...');
134
141
  const toolchain = new ToolchainManager();
142
+ if (!installArg) {
143
+ if (!(await toolchain.isInstalled())) {
144
+ throw new Error('No Rust toolchain is installed; install rustup or run `tsrust notices` (checks install nothing)');
145
+ }
146
+ return toolchain.getEnvPrefix();
147
+ }
135
148
  await toolchain.ensureInstalled();
136
149
  return toolchain.getEnvPrefix();
137
150
  }
@@ -176,18 +189,53 @@ export class TsRustCli {
176
189
  const distDir = path.join(this.cwd, 'dist_rust');
177
190
  const profile = isDebug ? 'debug' : 'release';
178
191
  const managedTargetDir = resolveManagedTargetDir(this.cwd, this.config.targetDir);
192
+
193
+ // CLI targets override host-specific and legacy configured targets.
194
+ const cliTargets = (argvArg as any).target;
195
+ const cliTargetList: string[] | undefined = cliTargets
196
+ ? (Array.isArray(cliTargets) ? cliTargets : [cliTargets])
197
+ : undefined;
198
+ const hostTriple = new ToolchainManager().getHostTriple();
199
+ const resolvedTargets = resolveBuildTargets(this.config, hostTriple, cliTargetList);
200
+ const useStatic = !!(argvArg as any).static || !!this.config.static;
201
+
202
+ // With native notices, static glibc is refused by every build. Matrix
203
+ // workers pass --no-notices-check, which skips only the bundle comparison:
204
+ // their coordinator verified the notices of the exact commit.
205
+ if (this.config.notices !== undefined) {
206
+ assertNoStaticGlibc({
207
+ targets: resolvedTargets.length > 0
208
+ ? resolvedTargets
209
+ : [{ triple: hostTriple, friendly: friendlyTargetName(hostTriple) }],
210
+ staticLinking: useStatic,
211
+ rustflags: this.config.rustflags || [],
212
+ environment: process.env,
213
+ });
214
+ }
215
+ if (this.config.notices !== undefined && (argvArg as any).noticesCheck !== false) {
216
+ const noticeTriples = new Set(configuredAssemblyTargets(this.config).map((target) => target.triple));
217
+ const uncovered = resolvedTargets.filter((target) => !noticeTriples.has(target.triple));
218
+ if (resolvedTargets.length === 0 || uncovered.length > 0) {
219
+ throw new Error(
220
+ `Native notices cover the configured targets only; cannot build ${
221
+ uncovered.map((target) => target.friendly).join(', ') || 'the native host target'
222
+ }`,
223
+ );
224
+ }
225
+ const bundle = await (await createProjectNoticesGenerator({
226
+ projectDir: this.cwd,
227
+ rustDir,
228
+ config: this.config,
229
+ staticLinking: useStatic,
230
+ envPrefix,
231
+ install: false,
232
+ })).check();
233
+ console.log(`Verified native notices: ${path.relative(this.cwd, bundle.directory)}`);
234
+ }
235
+
179
236
  await withTargetCacheLock(managedTargetDir, async () => {
180
237
  await writeTargetCacheMarker(managedTargetDir);
181
238
 
182
- // CLI targets override host-specific and legacy configured targets.
183
- const cliTargets = (argvArg as any).target;
184
- const cliTargetList: string[] | undefined = cliTargets
185
- ? (Array.isArray(cliTargets) ? cliTargets : [cliTargets])
186
- : undefined;
187
- const hostTriple = new ToolchainManager().getHostTriple();
188
- const resolvedTargets = resolveBuildTargets(this.config, hostTriple, cliTargetList);
189
-
190
- const useStatic = !!(argvArg as any).static || !!this.config.static;
191
239
  if (useStatic) {
192
240
  console.log('Static linking enabled (crt-static for linux-gnu targets)');
193
241
  }
@@ -259,6 +307,7 @@ export class TsRustCli {
259
307
  console.log(
260
308
  `Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${destName}`,
261
309
  );
310
+ await this.assertNoticesLinkage(destBinary, triple, friendly);
262
311
 
263
312
  if (useStatic && isLinuxTriple(triple)) {
264
313
  if (!(await ElfInspector.isStaticElf(destBinary))) {
@@ -347,6 +396,8 @@ export class TsRustCli {
347
396
  console.log(
348
397
  `Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${binName}`,
349
398
  );
399
+ const builtTriple = nativeTriple || hostTriple;
400
+ await this.assertNoticesLinkage(destBinary, builtTriple, friendlyTargetName(builtTriple));
350
401
 
351
402
  if (useStatic && nativeTriple && isLinuxTriple(nativeTriple)) {
352
403
  if (!(await ElfInspector.isStaticElf(destBinary))) {
@@ -386,6 +437,49 @@ export class TsRustCli {
386
437
  });
387
438
  }
388
439
 
440
+ /**
441
+ * With native notices, a Linux GNU binary must load glibc dynamically: a
442
+ * binary without an ELF interpreter is removed and the build fails.
443
+ */
444
+ private async assertNoticesLinkage(binaryArg: string, tripleArg: string, friendlyArg: string): Promise<void> {
445
+ if (this.config.notices === undefined || !tripleArg.endsWith('-linux-gnu')) return;
446
+ if (await ElfInspector.isStaticElf(binaryArg)) {
447
+ await fs.promises.rm(binaryArg, { force: true });
448
+ throw staticGlibcArtifactError(friendlyArg, tripleArg);
449
+ }
450
+ }
451
+
452
+ /**
453
+ * `tsrust notices` writes the third-party notices of the configured native
454
+ * targets; `tsrust notices --check` verifies them without writing.
455
+ */
456
+ private registerNoticesCommand(): void {
457
+ this.cli.addCommand('notices').subscribe(async (argvArg) => {
458
+ if (this.config.notices === undefined) {
459
+ throw new Error('Native notices are not configured: add "notices" to the @git.zone/tsrust configuration');
460
+ }
461
+ const rustDir = await this.detectRustDir();
462
+ if (!rustDir) {
463
+ throw new Error('No rust/ or ts_rust/ directory found with a Cargo.toml');
464
+ }
465
+ const check = !!(argvArg as any).check;
466
+ const envPrefix = await this.resolveToolchain(!check);
467
+ const staticLinking = !!(argvArg as any).static || !!this.config.static;
468
+ const generator = await createProjectNoticesGenerator({
469
+ projectDir: this.cwd,
470
+ rustDir,
471
+ config: this.config,
472
+ staticLinking,
473
+ envPrefix,
474
+ install: !check,
475
+ });
476
+ const bundle = check ? await generator.check() : await generator.write();
477
+ console.log(
478
+ `${check ? 'Verified' : 'Wrote'} ${bundle.files.size} native notice files in ${path.relative(this.cwd, bundle.directory)}`,
479
+ );
480
+ });
481
+ }
482
+
389
483
  private shouldPruneAfterBuild(): boolean {
390
484
  const normalized = process.env.TSRUST_PRUNE_AFTER_BUILD?.toLowerCase();
391
485
  return (
@@ -1,4 +1,5 @@
1
1
  import type { ITsrustMatrixConfig } from '../mod_matrix/helpers.matrixconfig.js';
2
+ import type { ITsrustNoticesConfig } from '../mod_notices/helpers.noticesconfig.js';
2
3
 
3
4
  export const targetAliasMap: Record<string, string> = {
4
5
  linux_amd64: 'x86_64-unknown-linux-gnu',
@@ -32,6 +33,7 @@ export interface ITsrustConfig {
32
33
  targetDir?: string;
33
34
  pruneAfterBuild?: boolean;
34
35
  matrix?: ITsrustMatrixConfig;
36
+ notices?: ITsrustNoticesConfig;
35
37
  }
36
38
 
37
39
  export interface INormalizedTarget {