@git.zone/tsrust 1.0.3 → 1.2.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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@git.zone/tsrust',
6
- version: '1.0.3',
6
+ version: '1.2.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
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxrQkFBa0I7SUFDeEIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLGtKQUFrSjtDQUNoSyxDQUFBIn0=
@@ -12,6 +12,11 @@ export declare class CargoRunner {
12
12
  build(options?: {
13
13
  debug?: boolean;
14
14
  clean?: boolean;
15
+ target?: string;
15
16
  }): Promise<ICargoRunResult>;
17
+ /**
18
+ * Ensures a rustup target is installed. If not present, installs it via `rustup target add`.
19
+ */
20
+ ensureTarget(triple: string): Promise<void>;
16
21
  clean(): Promise<ICargoRunResult>;
17
22
  }
@@ -21,9 +21,13 @@ export class CargoRunner {
21
21
  console.log('Cleaning previous build...');
22
22
  await this.clean();
23
23
  }
24
+ if (options.target) {
25
+ await this.ensureTarget(options.target);
26
+ }
24
27
  const profile = options.debug ? '' : ' --release';
25
- const command = `cd ${this.rustDir} && cargo build${profile}`;
26
- console.log(`Running: cargo build${profile}`);
28
+ const targetFlag = options.target ? ` --target ${options.target}` : '';
29
+ const command = `cd ${this.rustDir} && cargo build${profile}${targetFlag}`;
30
+ console.log(`Running: cargo build${profile}${targetFlag}`);
27
31
  const result = await this.shell.exec(command);
28
32
  return {
29
33
  success: result.exitCode === 0,
@@ -31,6 +35,21 @@ export class CargoRunner {
31
35
  stdout: result.stdout,
32
36
  };
33
37
  }
38
+ /**
39
+ * Ensures a rustup target is installed. If not present, installs it via `rustup target add`.
40
+ */
41
+ async ensureTarget(triple) {
42
+ const listResult = await this.shell.execSilent('rustup target list --installed');
43
+ const installedTargets = listResult.stdout.split('\n').map((l) => l.trim());
44
+ if (installedTargets.includes(triple)) {
45
+ return;
46
+ }
47
+ console.log(`Installing rustup target: ${triple}`);
48
+ const addResult = await this.shell.exec(`rustup target add ${triple}`);
49
+ if (addResult.exitCode !== 0) {
50
+ throw new Error(`Failed to install rustup target ${triple}`);
51
+ }
52
+ }
34
53
  async clean() {
35
54
  const command = `cd ${this.rustDir} && cargo clean`;
36
55
  const result = await this.shell.exec(command);
@@ -41,4 +60,4 @@ export class CargoRunner {
41
60
  };
42
61
  }
43
62
  }
44
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5jYXJnb3J1bm5lci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL21vZF9jYXJnby9jbGFzc2VzLmNhcmdvcnVubmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBUXpDLE1BQU0sT0FBTyxXQUFXO0lBQ2QsS0FBSyxDQUFnQztJQUNyQyxPQUFPLENBQVM7SUFFeEIsWUFBWSxPQUFlO1FBQ3pCLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQztZQUM3QyxRQUFRLEVBQUUsTUFBTTtTQUNqQixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU0sS0FBSyxDQUFDLG1CQUFtQjtRQUM5QixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDOUQsT0FBTyxNQUFNLENBQUMsUUFBUSxLQUFLLENBQUMsQ0FBQztJQUMvQixDQUFDO0lBRU0sS0FBSyxDQUFDLGVBQWU7UUFDMUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQzlELE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUM5QixDQUFDO0lBRU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxVQUFnRCxFQUFFO1FBQ25FLElBQUksT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2xCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUMsQ0FBQztZQUMxQyxNQUFNLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNyQixDQUFDO1FBRUQsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUM7UUFDbEQsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxrQkFBa0IsT0FBTyxFQUFFLENBQUM7UUFFOUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1QkFBdUIsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUM5QyxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRTlDLE9BQU87WUFDTCxPQUFPLEVBQUUsTUFBTSxDQUFDLFFBQVEsS0FBSyxDQUFDO1lBQzlCLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUTtZQUN6QixNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU07U0FDdEIsQ0FBQztJQUNKLENBQUM7SUFFTSxLQUFLLENBQUMsS0FBSztRQUNoQixNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLGlCQUFpQixDQUFDO1FBQ3BELE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFOUMsT0FBTztZQUNMLE9BQU8sRUFBRSxNQUFNLENBQUMsUUFBUSxLQUFLLENBQUM7WUFDOUIsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRO1lBQ3pCLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTTtTQUN0QixDQUFDO0lBQ0osQ0FBQztDQUNGIn0=
63
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5jYXJnb3J1bm5lci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL21vZF9jYXJnby9jbGFzc2VzLmNhcmdvcnVubmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBUXpDLE1BQU0sT0FBTyxXQUFXO0lBQ2QsS0FBSyxDQUFnQztJQUNyQyxPQUFPLENBQVM7SUFFeEIsWUFBWSxPQUFlO1FBQ3pCLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQztZQUM3QyxRQUFRLEVBQUUsTUFBTTtTQUNqQixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU0sS0FBSyxDQUFDLG1CQUFtQjtRQUM5QixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDOUQsT0FBTyxNQUFNLENBQUMsUUFBUSxLQUFLLENBQUMsQ0FBQztJQUMvQixDQUFDO0lBRU0sS0FBSyxDQUFDLGVBQWU7UUFDMUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQzlELE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUM5QixDQUFDO0lBRU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxVQUFpRSxFQUFFO1FBQ3BGLElBQUksT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2xCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUMsQ0FBQztZQUMxQyxNQUFNLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNyQixDQUFDO1FBRUQsSUFBSSxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDbkIsTUFBTSxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMxQyxDQUFDO1FBRUQsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUM7UUFDbEQsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsYUFBYSxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUN2RSxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLGtCQUFrQixPQUFPLEdBQUcsVUFBVSxFQUFFLENBQUM7UUFFM0UsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1QkFBdUIsT0FBTyxHQUFHLFVBQVUsRUFBRSxDQUFDLENBQUM7UUFDM0QsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUU5QyxPQUFPO1lBQ0wsT0FBTyxFQUFFLE1BQU0sQ0FBQyxRQUFRLEtBQUssQ0FBQztZQUM5QixRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVE7WUFDekIsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNO1NBQ3RCLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsWUFBWSxDQUFDLE1BQWM7UUFDdEMsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDO1FBQ2pGLE1BQU0sZ0JBQWdCLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUM1RSxJQUFJLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQ3RDLE9BQU87UUFDVCxDQUFDO1FBQ0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyw2QkFBNkIsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUNuRCxNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLHFCQUFxQixNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZFLElBQUksU0FBUyxDQUFDLFFBQVEsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUM3QixNQUFNLElBQUksS0FBSyxDQUFDLG1DQUFtQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQy9ELENBQUM7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLEtBQUs7UUFDaEIsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxpQkFBaUIsQ0FBQztRQUNwRCxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRTlDLE9BQU87WUFDTCxPQUFPLEVBQUUsTUFBTSxDQUFDLFFBQVEsS0FBSyxDQUFDO1lBQzlCLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUTtZQUN6QixNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU07U0FDdEIsQ0FBQztJQUNKLENBQUM7Q0FDRiJ9
@@ -1,6 +1,7 @@
1
1
  export declare class TsRustCli {
2
2
  private cli;
3
3
  private cwd;
4
+ private config;
4
5
  constructor(cwd?: string);
5
6
  private registerCommands;
6
7
  private registerStandardCommand;
@@ -3,12 +3,47 @@ import * as plugins from '../plugins.js';
3
3
  import { CargoConfig } from '../mod_cargo/index.js';
4
4
  import { CargoRunner } from '../mod_cargo/index.js';
5
5
  import { FsHelpers } from '../mod_fs/index.js';
6
+ /** Maps friendly target names to Rust target triples */
7
+ const targetAliasMap = {
8
+ linux_amd64: 'x86_64-unknown-linux-gnu',
9
+ linux_arm64: 'aarch64-unknown-linux-gnu',
10
+ linux_amd64_musl: 'x86_64-unknown-linux-musl',
11
+ linux_arm64_musl: 'aarch64-unknown-linux-musl',
12
+ macos_amd64: 'x86_64-apple-darwin',
13
+ macos_arm64: 'aarch64-apple-darwin',
14
+ };
15
+ /** Reverse map: Rust triple → friendly name */
16
+ const tripleToFriendlyMap = {};
17
+ for (const [friendly, triple] of Object.entries(targetAliasMap)) {
18
+ tripleToFriendlyMap[triple] = friendly;
19
+ }
20
+ /**
21
+ * Resolves a user-provided target name to a Rust target triple.
22
+ * Accepts both friendly names (linux_arm64) and raw triples (aarch64-unknown-linux-gnu).
23
+ */
24
+ function resolveTargetAlias(name) {
25
+ return targetAliasMap[name] || name;
26
+ }
27
+ /**
28
+ * Derives a friendly name from a Rust target triple for use in output filenames.
29
+ * Falls back to the raw triple with dashes replaced by underscores.
30
+ */
31
+ function friendlyName(triple) {
32
+ if (tripleToFriendlyMap[triple]) {
33
+ return tripleToFriendlyMap[triple];
34
+ }
35
+ // Derive from triple: e.g. x86_64-unknown-linux-gnu → x86_64_unknown_linux_gnu
36
+ return triple.replace(/-/g, '_');
37
+ }
6
38
  export class TsRustCli {
7
39
  cli;
8
40
  cwd;
41
+ config;
9
42
  constructor(cwd = process.cwd()) {
10
43
  this.cwd = cwd;
11
44
  this.cli = new plugins.smartcli.Smartcli();
45
+ const npmextraInstance = new plugins.npmextra.Npmextra(this.cwd);
46
+ this.config = npmextraInstance.dataFor('@git.zone/tsrust', { targets: [] });
12
47
  this.registerCommands();
13
48
  }
14
49
  registerCommands() {
@@ -45,34 +80,77 @@ export class TsRustCli {
45
80
  process.exit(1);
46
81
  }
47
82
  console.log(`Binary targets: ${workspaceInfo.binTargets.join(', ')}`);
48
- // Build
49
83
  const isDebug = !!argvArg.debug;
50
84
  const shouldClean = !!argvArg.clean;
51
- const cargoRunner = new CargoRunner(rustDir);
52
- const buildResult = await cargoRunner.build({ debug: isDebug, clean: shouldClean });
53
- if (!buildResult.success) {
54
- console.error(`Build failed with exit code ${buildResult.exitCode}`);
55
- process.exit(1);
56
- }
57
- // Copy binaries to dist_rust/
58
- const profile = isDebug ? 'debug' : 'release';
59
- const targetDir = path.join(rustDir, 'target', profile);
60
85
  const distDir = path.join(this.cwd, 'dist_rust');
61
- await FsHelpers.ensureEmptyDir(distDir);
62
- for (const binName of workspaceInfo.binTargets) {
63
- const srcBinary = path.join(targetDir, binName);
64
- const destBinary = path.join(distDir, binName);
65
- if (!(await FsHelpers.fileExists(srcBinary))) {
66
- console.warn(`Warning: Expected binary not found: ${srcBinary}`);
67
- continue;
86
+ const profile = isDebug ? 'debug' : 'release';
87
+ // Parse --target flag (can appear multiple times), fall back to npmextra.json config
88
+ const cliTargets = argvArg.target;
89
+ const targets = cliTargets
90
+ ? (Array.isArray(cliTargets) ? cliTargets : [cliTargets])
91
+ : this.config.targets || [];
92
+ if (targets.length > 0) {
93
+ // Cross-compilation mode
94
+ const resolvedTargets = targets.map((t) => ({
95
+ triple: resolveTargetAlias(t),
96
+ friendly: friendlyName(resolveTargetAlias(t)),
97
+ }));
98
+ console.log(`Cross-compiling for: ${resolvedTargets.map((t) => `${t.friendly} (${t.triple})`).join(', ')}`);
99
+ await FsHelpers.ensureEmptyDir(distDir);
100
+ for (const { triple, friendly } of resolvedTargets) {
101
+ console.log(`\n--- Building for ${friendly} (${triple}) ---`);
102
+ const cargoRunner = new CargoRunner(rustDir);
103
+ const buildResult = await cargoRunner.build({ debug: isDebug, clean: shouldClean, target: triple });
104
+ if (!buildResult.success) {
105
+ console.error(`Build failed for target ${triple} with exit code ${buildResult.exitCode}`);
106
+ process.exit(1);
107
+ }
108
+ // Cross-compiled binaries go to target/<triple>/<profile>/
109
+ const targetDir = path.join(rustDir, 'target', triple, profile);
110
+ for (const binName of workspaceInfo.binTargets) {
111
+ const srcBinary = path.join(targetDir, binName);
112
+ const destName = `${binName}_${friendly}`;
113
+ const destBinary = path.join(distDir, destName);
114
+ if (!(await FsHelpers.fileExists(srcBinary))) {
115
+ console.warn(`Warning: Expected binary not found: ${srcBinary}`);
116
+ continue;
117
+ }
118
+ await FsHelpers.copyFile(srcBinary, destBinary);
119
+ await FsHelpers.makeExecutable(destBinary);
120
+ const size = await FsHelpers.getFileSize(destBinary);
121
+ console.log(`Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${destName}`);
122
+ }
123
+ // Only clean on first iteration
124
+ if (shouldClean) {
125
+ argvArg.clean = false;
126
+ }
127
+ }
128
+ }
129
+ else {
130
+ // Native build (unchanged behavior)
131
+ const cargoRunner = new CargoRunner(rustDir);
132
+ const buildResult = await cargoRunner.build({ debug: isDebug, clean: shouldClean });
133
+ if (!buildResult.success) {
134
+ console.error(`Build failed with exit code ${buildResult.exitCode}`);
135
+ process.exit(1);
136
+ }
137
+ const targetDir = path.join(rustDir, 'target', profile);
138
+ await FsHelpers.ensureEmptyDir(distDir);
139
+ for (const binName of workspaceInfo.binTargets) {
140
+ const srcBinary = path.join(targetDir, binName);
141
+ const destBinary = path.join(distDir, binName);
142
+ if (!(await FsHelpers.fileExists(srcBinary))) {
143
+ console.warn(`Warning: Expected binary not found: ${srcBinary}`);
144
+ continue;
145
+ }
146
+ await FsHelpers.copyFile(srcBinary, destBinary);
147
+ await FsHelpers.makeExecutable(destBinary);
148
+ const size = await FsHelpers.getFileSize(destBinary);
149
+ console.log(`Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${binName}`);
68
150
  }
69
- await FsHelpers.copyFile(srcBinary, destBinary);
70
- await FsHelpers.makeExecutable(destBinary);
71
- const size = await FsHelpers.getFileSize(destBinary);
72
- console.log(`Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${binName}`);
73
151
  }
74
152
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
75
- console.log(`Done in ${elapsed}s`);
153
+ console.log(`\nDone in ${elapsed}s`);
76
154
  });
77
155
  }
78
156
  registerCleanCommand() {
@@ -115,4 +193,4 @@ export const runCli = async () => {
115
193
  const cli = new TsRustCli();
116
194
  cli.run();
117
195
  };
118
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50c3J1c3RjbGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfY2xpL2NsYXNzZXMudHNydXN0Y2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDcEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRS9DLE1BQU0sT0FBTyxTQUFTO0lBQ1osR0FBRyxDQUE0QjtJQUMvQixHQUFHLENBQVM7SUFFcEIsWUFBWSxNQUFjLE9BQU8sQ0FBQyxHQUFHLEVBQUU7UUFDckMsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDZixJQUFJLENBQUMsR0FBRyxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUMzQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztJQUMxQixDQUFDO0lBRU8sZ0JBQWdCO1FBQ3RCLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1FBQy9CLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO0lBQzlCLENBQUM7SUFFTyx1QkFBdUI7UUFDN0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxlQUFlLEVBQUUsQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFO1lBQ3JELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUU3QiwyQkFBMkI7WUFDM0IsTUFBTSxNQUFNLEdBQUcsSUFBSSxXQUFXLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsb0NBQW9DO1lBQzlFLElBQUksQ0FBQyxDQUFDLE1B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
196
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50c3J1c3RjbGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfY2xpL2NsYXNzZXMudHNydXN0Y2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDcEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRS9DLHdEQUF3RDtBQUN4RCxNQUFNLGNBQWMsR0FBMkI7SUFDN0MsV0FBVyxFQUFFLDBCQUEwQjtJQUN2QyxXQUFXLEVBQUUsMkJBQTJCO0lBQ3hDLGdCQUFnQixFQUFFLDJCQUEyQjtJQUM3QyxnQkFBZ0IsRUFBRSw0QkFBNEI7SUFDOUMsV0FBVyxFQUFFLHFCQUFxQjtJQUNsQyxXQUFXLEVBQUUsc0JBQXNCO0NBQ3BDLENBQUM7QUFFRiwrQ0FBK0M7QUFDL0MsTUFBTSxtQkFBbUIsR0FBMkIsRUFBRSxDQUFDO0FBQ3ZELEtBQUssTUFBTSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUM7SUFDaEUsbUJBQW1CLENBQUMsTUFBTSxDQUFDLEdBQUcsUUFBUSxDQUFDO0FBQ3pDLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFTLGtCQUFrQixDQUFDLElBQVk7SUFDdEMsT0FBTyxjQUFjLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDO0FBQ3RDLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFTLFlBQVksQ0FBQyxNQUFjO0lBQ2xDLElBQUksbUJBQW1CLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUNoQyxPQUFPLG1CQUFt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
@@ -1,6 +1,7 @@
1
1
  import * as early from '@push.rocks/early';
2
+ import * as npmextra from '@push.rocks/npmextra';
2
3
  import * as smartcli from '@push.rocks/smartcli';
3
4
  import * as smartfile from '@push.rocks/smartfile';
4
5
  import * as smartpath from '@push.rocks/smartpath';
5
6
  import * as smartshell from '@push.rocks/smartshell';
6
- export { early, smartcli, smartfile, smartpath, smartshell, };
7
+ export { early, npmextra, smartcli, smartfile, smartpath, smartshell, };
@@ -1,7 +1,8 @@
1
1
  import * as early from '@push.rocks/early';
2
+ import * as npmextra from '@push.rocks/npmextra';
2
3
  import * as smartcli from '@push.rocks/smartcli';
3
4
  import * as smartfile from '@push.rocks/smartfile';
4
5
  import * as smartpath from '@push.rocks/smartpath';
5
6
  import * as smartshell from '@push.rocks/smartshell';
6
- export { early, smartcli, smartfile, smartpath, smartshell, };
7
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEtBQUssTUFBTSxtQkFBbUIsQ0FBQztBQUMzQyxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBRXJELE9BQU8sRUFDTCxLQUFLLEVBQ0wsUUFBUSxFQUNSLFNBQVMsRUFDVCxTQUFTLEVBQ1QsVUFBVSxHQUNYLENBQUMifQ==
7
+ export { early, npmextra, smartcli, smartfile, smartpath, smartshell, };
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEtBQUssTUFBTSxtQkFBbUIsQ0FBQztBQUMzQyxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxRQUFRLE1BQU0sc0JBQXNCLENBQUM7QUFDakQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssU0FBUyxNQUFNLHVCQUF1QixDQUFDO0FBQ25ELE9BQU8sS0FBSyxVQUFVLE1BQU0sd0JBQXdCLENBQUM7QUFFckQsT0FBTyxFQUNMLEtBQUssRUFDTCxRQUFRLEVBQ1IsUUFBUSxFQUNSLFNBQVMsRUFDVCxTQUFTLEVBQ1QsVUFBVSxHQUNYLENBQUMifQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@git.zone/tsrust",
3
- "version": "1.0.3",
3
+ "version": "1.2.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",
@@ -34,6 +34,7 @@
34
34
  "homepage": "https://code.foss.global/git.zone/tsrust#README",
35
35
  "dependencies": {
36
36
  "@push.rocks/early": "^4.0.4",
37
+ "@push.rocks/npmextra": "^5.3.3",
37
38
  "@push.rocks/smartcli": "^4.0.19",
38
39
  "@push.rocks/smartfile": "^13.1.2",
39
40
  "@push.rocks/smartpath": "^6.0.0",
package/readme.md CHANGED
@@ -83,6 +83,56 @@ Run `cargo clean` before building to force a full rebuild:
83
83
  tsrust --clean
84
84
  ```
85
85
 
86
+ ### Cross-Compilation
87
+
88
+ Cross-compile for different OS/architecture combinations using the `--target` flag:
89
+
90
+ ```bash
91
+ # Cross-compile for a single target
92
+ tsrust --target linux_arm64
93
+
94
+ # Cross-compile for multiple targets
95
+ tsrust --target linux_arm64 --target linux_amd64
96
+
97
+ # Full Rust triples are also accepted
98
+ tsrust --target aarch64-unknown-linux-gnu
99
+ ```
100
+
101
+ Supported friendly target names:
102
+
103
+ | Friendly name | Rust target triple |
104
+ |---|---|
105
+ | `linux_amd64` | `x86_64-unknown-linux-gnu` |
106
+ | `linux_arm64` | `aarch64-unknown-linux-gnu` |
107
+ | `linux_amd64_musl` | `x86_64-unknown-linux-musl` |
108
+ | `linux_arm64_musl` | `aarch64-unknown-linux-musl` |
109
+ | `macos_amd64` | `x86_64-apple-darwin` |
110
+ | `macos_arm64` | `aarch64-apple-darwin` |
111
+
112
+ When using `--target`, output binaries are named `<binname>_<os>_<arch>`:
113
+
114
+ ```
115
+ dist_rust/
116
+ ├── rustproxy_linux_arm64
117
+ └── rustproxy_linux_amd64
118
+ ```
119
+
120
+ `tsrust` automatically installs missing rustup targets via `rustup target add` when needed.
121
+
122
+ ### Configuration via npmextra.json
123
+
124
+ You can set default cross-compilation targets in your project's `npmextra.json` file so you don't need to pass `--target` flags every time:
125
+
126
+ ```json
127
+ {
128
+ "@git.zone/tsrust": {
129
+ "targets": ["linux_arm64", "linux_amd64"]
130
+ }
131
+ }
132
+ ```
133
+
134
+ When targets are configured in `npmextra.json`, simply running `tsrust` will cross-compile for all listed targets. CLI `--target` flags take full precedence — if any `--target` is provided, the `npmextra.json` targets are ignored entirely.
135
+
86
136
  ### 🗑️ Clean Only
87
137
 
88
138
  Remove all build artifacts without rebuilding:
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@git.zone/tsrust',
6
- version: '1.0.3',
6
+ version: '1.2.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
  }
@@ -27,16 +27,21 @@ export class CargoRunner {
27
27
  return result.stdout.trim();
28
28
  }
29
29
 
30
- public async build(options: { debug?: boolean; clean?: boolean } = {}): Promise<ICargoRunResult> {
30
+ public async build(options: { debug?: boolean; clean?: boolean; target?: string } = {}): Promise<ICargoRunResult> {
31
31
  if (options.clean) {
32
32
  console.log('Cleaning previous build...');
33
33
  await this.clean();
34
34
  }
35
35
 
36
+ if (options.target) {
37
+ await this.ensureTarget(options.target);
38
+ }
39
+
36
40
  const profile = options.debug ? '' : ' --release';
37
- const command = `cd ${this.rustDir} && cargo build${profile}`;
41
+ const targetFlag = options.target ? ` --target ${options.target}` : '';
42
+ const command = `cd ${this.rustDir} && cargo build${profile}${targetFlag}`;
38
43
 
39
- console.log(`Running: cargo build${profile}`);
44
+ console.log(`Running: cargo build${profile}${targetFlag}`);
40
45
  const result = await this.shell.exec(command);
41
46
 
42
47
  return {
@@ -46,6 +51,22 @@ export class CargoRunner {
46
51
  };
47
52
  }
48
53
 
54
+ /**
55
+ * Ensures a rustup target is installed. If not present, installs it via `rustup target add`.
56
+ */
57
+ public async ensureTarget(triple: string): Promise<void> {
58
+ const listResult = await this.shell.execSilent('rustup target list --installed');
59
+ const installedTargets = listResult.stdout.split('\n').map((l) => l.trim());
60
+ if (installedTargets.includes(triple)) {
61
+ return;
62
+ }
63
+ console.log(`Installing rustup target: ${triple}`);
64
+ const addResult = await this.shell.exec(`rustup target add ${triple}`);
65
+ if (addResult.exitCode !== 0) {
66
+ throw new Error(`Failed to install rustup target ${triple}`);
67
+ }
68
+ }
69
+
49
70
  public async clean(): Promise<ICargoRunResult> {
50
71
  const command = `cd ${this.rustDir} && cargo clean`;
51
72
  const result = await this.shell.exec(command);
@@ -4,13 +4,56 @@ import { CargoConfig } from '../mod_cargo/index.js';
4
4
  import { CargoRunner } from '../mod_cargo/index.js';
5
5
  import { FsHelpers } from '../mod_fs/index.js';
6
6
 
7
+ /** Maps friendly target names to Rust target triples */
8
+ const targetAliasMap: Record<string, string> = {
9
+ linux_amd64: 'x86_64-unknown-linux-gnu',
10
+ linux_arm64: 'aarch64-unknown-linux-gnu',
11
+ linux_amd64_musl: 'x86_64-unknown-linux-musl',
12
+ linux_arm64_musl: 'aarch64-unknown-linux-musl',
13
+ macos_amd64: 'x86_64-apple-darwin',
14
+ macos_arm64: 'aarch64-apple-darwin',
15
+ };
16
+
17
+ /** Reverse map: Rust triple → friendly name */
18
+ const tripleToFriendlyMap: Record<string, string> = {};
19
+ for (const [friendly, triple] of Object.entries(targetAliasMap)) {
20
+ tripleToFriendlyMap[triple] = friendly;
21
+ }
22
+
23
+ /**
24
+ * Resolves a user-provided target name to a Rust target triple.
25
+ * Accepts both friendly names (linux_arm64) and raw triples (aarch64-unknown-linux-gnu).
26
+ */
27
+ function resolveTargetAlias(name: string): string {
28
+ return targetAliasMap[name] || name;
29
+ }
30
+
31
+ /**
32
+ * Derives a friendly name from a Rust target triple for use in output filenames.
33
+ * Falls back to the raw triple with dashes replaced by underscores.
34
+ */
35
+ function friendlyName(triple: string): string {
36
+ if (tripleToFriendlyMap[triple]) {
37
+ return tripleToFriendlyMap[triple];
38
+ }
39
+ // Derive from triple: e.g. x86_64-unknown-linux-gnu → x86_64_unknown_linux_gnu
40
+ return triple.replace(/-/g, '_');
41
+ }
42
+
43
+ interface ITsrustConfig {
44
+ targets?: string[];
45
+ }
46
+
7
47
  export class TsRustCli {
8
48
  private cli: plugins.smartcli.Smartcli;
9
49
  private cwd: string;
50
+ private config: ITsrustConfig;
10
51
 
11
52
  constructor(cwd: string = process.cwd()) {
12
53
  this.cwd = cwd;
13
54
  this.cli = new plugins.smartcli.Smartcli();
55
+ const npmextraInstance = new plugins.npmextra.Npmextra(this.cwd);
56
+ this.config = npmextraInstance.dataFor<ITsrustConfig>('@git.zone/tsrust', { targets: [] });
14
57
  this.registerCommands();
15
58
  }
16
59
 
@@ -58,42 +101,96 @@ export class TsRustCli {
58
101
 
59
102
  console.log(`Binary targets: ${workspaceInfo.binTargets.join(', ')}`);
60
103
 
61
- // Build
62
104
  const isDebug = !!(argvArg as any).debug;
63
105
  const shouldClean = !!(argvArg as any).clean;
64
- const cargoRunner = new CargoRunner(rustDir);
65
- const buildResult = await cargoRunner.build({ debug: isDebug, clean: shouldClean });
106
+ const distDir = path.join(this.cwd, 'dist_rust');
107
+ const profile = isDebug ? 'debug' : 'release';
66
108
 
67
- if (!buildResult.success) {
68
- console.error(`Build failed with exit code ${buildResult.exitCode}`);
69
- process.exit(1);
70
- }
109
+ // Parse --target flag (can appear multiple times), fall back to npmextra.json config
110
+ const cliTargets = (argvArg as any).target;
111
+ const targets: string[] = cliTargets
112
+ ? (Array.isArray(cliTargets) ? cliTargets : [cliTargets])
113
+ : this.config.targets || [];
114
+
115
+ if (targets.length > 0) {
116
+ // Cross-compilation mode
117
+ const resolvedTargets = targets.map((t: string) => ({
118
+ triple: resolveTargetAlias(t),
119
+ friendly: friendlyName(resolveTargetAlias(t)),
120
+ }));
121
+
122
+ console.log(`Cross-compiling for: ${resolvedTargets.map((t) => `${t.friendly} (${t.triple})`).join(', ')}`);
123
+
124
+ await FsHelpers.ensureEmptyDir(distDir);
125
+
126
+ for (const { triple, friendly } of resolvedTargets) {
127
+ console.log(`\n--- Building for ${friendly} (${triple}) ---`);
128
+ const cargoRunner = new CargoRunner(rustDir);
129
+ const buildResult = await cargoRunner.build({ debug: isDebug, clean: shouldClean, target: triple });
130
+
131
+ if (!buildResult.success) {
132
+ console.error(`Build failed for target ${triple} with exit code ${buildResult.exitCode}`);
133
+ process.exit(1);
134
+ }
135
+
136
+ // Cross-compiled binaries go to target/<triple>/<profile>/
137
+ const targetDir = path.join(rustDir, 'target', triple, profile);
138
+
139
+ for (const binName of workspaceInfo.binTargets) {
140
+ const srcBinary = path.join(targetDir, binName);
141
+ const destName = `${binName}_${friendly}`;
142
+ const destBinary = path.join(distDir, destName);
143
+
144
+ if (!(await FsHelpers.fileExists(srcBinary))) {
145
+ console.warn(`Warning: Expected binary not found: ${srcBinary}`);
146
+ continue;
147
+ }
148
+
149
+ await FsHelpers.copyFile(srcBinary, destBinary);
150
+ await FsHelpers.makeExecutable(destBinary);
151
+
152
+ const size = await FsHelpers.getFileSize(destBinary);
153
+ console.log(`Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${destName}`);
154
+ }
155
+
156
+ // Only clean on first iteration
157
+ if (shouldClean) {
158
+ (argvArg as any).clean = false;
159
+ }
160
+ }
161
+ } else {
162
+ // Native build (unchanged behavior)
163
+ const cargoRunner = new CargoRunner(rustDir);
164
+ const buildResult = await cargoRunner.build({ debug: isDebug, clean: shouldClean });
165
+
166
+ if (!buildResult.success) {
167
+ console.error(`Build failed with exit code ${buildResult.exitCode}`);
168
+ process.exit(1);
169
+ }
71
170
 
72
- // Copy binaries to dist_rust/
73
- const profile = isDebug ? 'debug' : 'release';
74
- const targetDir = path.join(rustDir, 'target', profile);
75
- const distDir = path.join(this.cwd, 'dist_rust');
171
+ const targetDir = path.join(rustDir, 'target', profile);
76
172
 
77
- await FsHelpers.ensureEmptyDir(distDir);
173
+ await FsHelpers.ensureEmptyDir(distDir);
78
174
 
79
- for (const binName of workspaceInfo.binTargets) {
80
- const srcBinary = path.join(targetDir, binName);
81
- const destBinary = path.join(distDir, binName);
175
+ for (const binName of workspaceInfo.binTargets) {
176
+ const srcBinary = path.join(targetDir, binName);
177
+ const destBinary = path.join(distDir, binName);
82
178
 
83
- if (!(await FsHelpers.fileExists(srcBinary))) {
84
- console.warn(`Warning: Expected binary not found: ${srcBinary}`);
85
- continue;
86
- }
179
+ if (!(await FsHelpers.fileExists(srcBinary))) {
180
+ console.warn(`Warning: Expected binary not found: ${srcBinary}`);
181
+ continue;
182
+ }
87
183
 
88
- await FsHelpers.copyFile(srcBinary, destBinary);
89
- await FsHelpers.makeExecutable(destBinary);
184
+ await FsHelpers.copyFile(srcBinary, destBinary);
185
+ await FsHelpers.makeExecutable(destBinary);
90
186
 
91
- const size = await FsHelpers.getFileSize(destBinary);
92
- console.log(`Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${binName}`);
187
+ const size = await FsHelpers.getFileSize(destBinary);
188
+ console.log(`Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${binName}`);
189
+ }
93
190
  }
94
191
 
95
192
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
96
- console.log(`Done in ${elapsed}s`);
193
+ console.log(`\nDone in ${elapsed}s`);
97
194
  });
98
195
  }
99
196
 
package/ts/plugins.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as early from '@push.rocks/early';
2
+ import * as npmextra from '@push.rocks/npmextra';
2
3
  import * as smartcli from '@push.rocks/smartcli';
3
4
  import * as smartfile from '@push.rocks/smartfile';
4
5
  import * as smartpath from '@push.rocks/smartpath';
@@ -6,6 +7,7 @@ import * as smartshell from '@push.rocks/smartshell';
6
7
 
7
8
  export {
8
9
  early,
10
+ npmextra,
9
11
  smartcli,
10
12
  smartfile,
11
13
  smartpath,