Skip to content

Commit b1bc210

Browse files
authored
fix: respect custom Cargo target directories
Co-authored-by: Andrew Barnes <bortstheboat@gmail.com>
1 parent 4c7581c commit b1bc210

6 files changed

Lines changed: 152 additions & 24 deletions

File tree

bin/builders/BaseBuilder.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,19 @@ export default abstract class BaseBuilder {
404404
}
405405
}
406406

407+
protected getCargoTargetDir(): string {
408+
return process.env.CARGO_TARGET_DIR || path.join('src-tauri', 'target');
409+
}
410+
411+
protected resolveBuildPath(npmDirectory: string, buildPath: string): string {
412+
return path.isAbsolute(buildPath)
413+
? buildPath
414+
: path.join(npmDirectory, buildPath);
415+
}
416+
407417
protected getBasePath(): string {
408418
const basePath = this.options.debug ? 'debug' : 'release';
409-
return `src-tauri/target/${basePath}/bundle/`;
419+
return path.join(this.getCargoTargetDir(), basePath, 'bundle');
410420
}
411421

412422
protected getBuildAppPath(
@@ -418,8 +428,7 @@ export default abstract class BaseBuilder {
418428
const bundleDir =
419429
fileType.toLowerCase() === 'app' ? 'macos' : fileType.toLowerCase();
420430
return path.join(
421-
npmDirectory,
422-
this.getBasePath(),
431+
this.resolveBuildPath(npmDirectory, this.getBasePath()),
423432
bundleDir,
424433
`${fileName}.${fileType}`,
425434
);
@@ -459,14 +468,17 @@ export default abstract class BaseBuilder {
459468
// Handle cross-platform builds
460469
if (this.options.multiArch || this.hasArchSpecificTarget()) {
461470
return path.join(
462-
npmDirectory,
463-
this.getArchSpecificPath(),
471+
this.resolveBuildPath(npmDirectory, this.getArchSpecificPath()),
464472
basePath,
465473
binaryName,
466474
);
467475
}
468476

469-
return path.join(npmDirectory, 'src-tauri/target', basePath, binaryName);
477+
return path.join(
478+
this.resolveBuildPath(npmDirectory, this.getCargoTargetDir()),
479+
basePath,
480+
binaryName,
481+
);
470482
}
471483

472484
/**
@@ -503,6 +515,6 @@ export default abstract class BaseBuilder {
503515
* Get architecture-specific path for binary
504516
*/
505517
protected getArchSpecificPath(): string {
506-
return 'src-tauri/target'; // Override in subclasses if needed
518+
return this.getCargoTargetDir(); // Override in subclasses if needed
507519
}
508520
}

bin/builders/LinuxBuilder.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,12 @@ post_remove() {
260260

261261
if (this.buildArch === 'arm64') {
262262
const target = this.getTauriTarget(this.buildArch, 'linux');
263-
return `src-tauri/target/${target}/${basePath}/bundle/`;
263+
if (!target) {
264+
throw new Error(
265+
`Unsupported architecture: ${this.buildArch} for Linux`,
266+
);
267+
}
268+
return path.join(this.getCargoTargetDir(), target, basePath, 'bundle');
264269
}
265270

266271
return super.getBasePath();
@@ -280,7 +285,12 @@ post_remove() {
280285
protected getArchSpecificPath(): string {
281286
if (this.buildArch === 'arm64') {
282287
const target = this.getTauriTarget(this.buildArch, 'linux');
283-
return `src-tauri/target/${target}`;
288+
if (!target) {
289+
throw new Error(
290+
`Unsupported architecture: ${this.buildArch} for Linux`,
291+
);
292+
}
293+
return path.join(this.getCargoTargetDir(), target);
284294
}
285295
return super.getArchSpecificPath();
286296
}

bin/builders/MacBuilder.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ export default class MacBuilder extends BaseBuilder {
7676
const actualArch = this.getActualArch();
7777
const target = this.getTauriTarget(actualArch, 'darwin');
7878

79-
return `src-tauri/target/${target}/${basePath}/bundle`;
79+
if (!target) {
80+
throw new Error(`Unsupported architecture: ${actualArch} for macOS`);
81+
}
82+
83+
return path.join(this.getCargoTargetDir(), target, basePath, 'bundle');
8084
}
8185

8286
protected hasArchSpecificTarget(): boolean {
@@ -86,6 +90,9 @@ export default class MacBuilder extends BaseBuilder {
8690
protected getArchSpecificPath(): string {
8791
const actualArch = this.getActualArch();
8892
const target = this.getTauriTarget(actualArch, 'darwin');
89-
return `src-tauri/target/${target}`;
93+
if (!target) {
94+
throw new Error(`Unsupported architecture: ${actualArch} for macOS`);
95+
}
96+
return path.join(this.getCargoTargetDir(), target);
9097
}
9198
}

bin/builders/WinBuilder.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from 'path';
22
import BaseBuilder from './BaseBuilder';
33
import { PakeAppOptions } from '@/types';
44
import tauriConfig from '@/helpers/tauriConfig';
5+
import { generateIdentifierSafeName } from '@/utils/name';
56

67
export default class WinBuilder extends BaseBuilder {
78
private buildFormat: string = 'msi';
@@ -39,7 +40,12 @@ export default class WinBuilder extends BaseBuilder {
3940
protected getBasePath(): string {
4041
const basePath = this.options.debug ? 'debug' : 'release';
4142
const target = this.getTauriTarget(this.buildArch, 'win32');
42-
return `src-tauri/target/${target}/${basePath}/bundle/`;
43+
if (!target) {
44+
throw new Error(
45+
`Unsupported architecture: ${this.buildArch} for Windows`,
46+
);
47+
}
48+
return path.join(this.getCargoTargetDir(), target, basePath, 'bundle');
4349
}
4450

4551
protected hasArchSpecificTarget(): boolean {
@@ -48,6 +54,19 @@ export default class WinBuilder extends BaseBuilder {
4854

4955
protected getArchSpecificPath(): string {
5056
const target = this.getTauriTarget(this.buildArch, 'win32');
51-
return `src-tauri/target/${target}`;
57+
if (!target) {
58+
throw new Error(
59+
`Unsupported architecture: ${this.buildArch} for Windows`,
60+
);
61+
}
62+
return path.join(this.getCargoTargetDir(), target);
63+
}
64+
65+
protected getRawBinaryPath(appName: string): string {
66+
return `${appName}.exe`;
67+
}
68+
69+
protected getBinaryName(appName: string): string {
70+
return `pake-${generateIdentifierSafeName(appName)}.exe`;
5271
}
5372
}

dist/cli.js

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,14 +1162,22 @@ class BaseBuilder {
11621162
return 0; // Disable proxy feature if version detection fails
11631163
}
11641164
}
1165+
getCargoTargetDir() {
1166+
return process.env.CARGO_TARGET_DIR || path.join('src-tauri', 'target');
1167+
}
1168+
resolveBuildPath(npmDirectory, buildPath) {
1169+
return path.isAbsolute(buildPath)
1170+
? buildPath
1171+
: path.join(npmDirectory, buildPath);
1172+
}
11651173
getBasePath() {
11661174
const basePath = this.options.debug ? 'debug' : 'release';
1167-
return `src-tauri/target/${basePath}/bundle/`;
1175+
return path.join(this.getCargoTargetDir(), basePath, 'bundle');
11681176
}
11691177
getBuildAppPath(npmDirectory, fileName, fileType) {
11701178
// For app bundles on macOS, the directory is 'macos', not 'app'
11711179
const bundleDir = fileType.toLowerCase() === 'app' ? 'macos' : fileType.toLowerCase();
1172-
return path.join(npmDirectory, this.getBasePath(), bundleDir, `${fileName}.${fileType}`);
1180+
return path.join(this.resolveBuildPath(npmDirectory, this.getBasePath()), bundleDir, `${fileName}.${fileType}`);
11731181
}
11741182
/**
11751183
* Copy raw binary file to output directory
@@ -1196,9 +1204,9 @@ class BaseBuilder {
11961204
const binaryName = this.getBinaryName(appName);
11971205
// Handle cross-platform builds
11981206
if (this.options.multiArch || this.hasArchSpecificTarget()) {
1199-
return path.join(npmDirectory, this.getArchSpecificPath(), basePath, binaryName);
1207+
return path.join(this.resolveBuildPath(npmDirectory, this.getArchSpecificPath()), basePath, binaryName);
12001208
}
1201-
return path.join(npmDirectory, 'src-tauri/target', basePath, binaryName);
1209+
return path.join(this.resolveBuildPath(npmDirectory, this.getCargoTargetDir()), basePath, binaryName);
12021210
}
12031211
/**
12041212
* Get the output path for the raw binary file
@@ -1229,7 +1237,7 @@ class BaseBuilder {
12291237
* Get architecture-specific path for binary
12301238
*/
12311239
getArchSpecificPath() {
1232-
return 'src-tauri/target'; // Override in subclasses if needed
1240+
return this.getCargoTargetDir(); // Override in subclasses if needed
12331241
}
12341242
}
12351243
BaseBuilder.ARCH_MAPPINGS = {
@@ -1315,15 +1323,21 @@ class MacBuilder extends BaseBuilder {
13151323
const basePath = this.options.debug ? 'debug' : 'release';
13161324
const actualArch = this.getActualArch();
13171325
const target = this.getTauriTarget(actualArch, 'darwin');
1318-
return `src-tauri/target/${target}/${basePath}/bundle`;
1326+
if (!target) {
1327+
throw new Error(`Unsupported architecture: ${actualArch} for macOS`);
1328+
}
1329+
return path.join(this.getCargoTargetDir(), target, basePath, 'bundle');
13191330
}
13201331
hasArchSpecificTarget() {
13211332
return true;
13221333
}
13231334
getArchSpecificPath() {
13241335
const actualArch = this.getActualArch();
13251336
const target = this.getTauriTarget(actualArch, 'darwin');
1326-
return `src-tauri/target/${target}`;
1337+
if (!target) {
1338+
throw new Error(`Unsupported architecture: ${actualArch} for macOS`);
1339+
}
1340+
return path.join(this.getCargoTargetDir(), target);
13271341
}
13281342
}
13291343

@@ -1354,14 +1368,26 @@ class WinBuilder extends BaseBuilder {
13541368
getBasePath() {
13551369
const basePath = this.options.debug ? 'debug' : 'release';
13561370
const target = this.getTauriTarget(this.buildArch, 'win32');
1357-
return `src-tauri/target/${target}/${basePath}/bundle/`;
1371+
if (!target) {
1372+
throw new Error(`Unsupported architecture: ${this.buildArch} for Windows`);
1373+
}
1374+
return path.join(this.getCargoTargetDir(), target, basePath, 'bundle');
13581375
}
13591376
hasArchSpecificTarget() {
13601377
return true;
13611378
}
13621379
getArchSpecificPath() {
13631380
const target = this.getTauriTarget(this.buildArch, 'win32');
1364-
return `src-tauri/target/${target}`;
1381+
if (!target) {
1382+
throw new Error(`Unsupported architecture: ${this.buildArch} for Windows`);
1383+
}
1384+
return path.join(this.getCargoTargetDir(), target);
1385+
}
1386+
getRawBinaryPath(appName) {
1387+
return `${appName}.exe`;
1388+
}
1389+
getBinaryName(appName) {
1390+
return `pake-${generateIdentifierSafeName(appName)}.exe`;
13651391
}
13661392
}
13671393

@@ -1556,7 +1582,10 @@ post_remove() {
15561582
const basePath = this.options.debug ? 'debug' : 'release';
15571583
if (this.buildArch === 'arm64') {
15581584
const target = this.getTauriTarget(this.buildArch, 'linux');
1559-
return `src-tauri/target/${target}/${basePath}/bundle/`;
1585+
if (!target) {
1586+
throw new Error(`Unsupported architecture: ${this.buildArch} for Linux`);
1587+
}
1588+
return path.join(this.getCargoTargetDir(), target, basePath, 'bundle');
15601589
}
15611590
return super.getBasePath();
15621591
}
@@ -1572,7 +1601,10 @@ post_remove() {
15721601
getArchSpecificPath() {
15731602
if (this.buildArch === 'arm64') {
15741603
const target = this.getTauriTarget(this.buildArch, 'linux');
1575-
return `src-tauri/target/${target}`;
1604+
if (!target) {
1605+
throw new Error(`Unsupported architecture: ${this.buildArch} for Linux`);
1606+
}
1607+
return path.join(this.getCargoTargetDir(), target);
15761608
}
15771609
return super.getArchSpecificPath();
15781610
}

tests/unit/base-builder.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ vi.mock('@/utils/dir', () => ({
1515
}));
1616

1717
import BaseBuilder from '@/builders/BaseBuilder';
18+
import WinBuilder from '@/builders/WinBuilder';
1819
import {
1920
_resetPackageManagerCache,
2021
configureCargoRegistry,
@@ -32,6 +33,7 @@ class TestBuilder extends BaseBuilder {
3233
}
3334

3435
const originalCnMirrorEnv = process.env[CN_MIRROR_ENV];
36+
const originalCargoTargetDir = process.env.CARGO_TARGET_DIR;
3537
const tempDirs: string[] = [];
3638

3739
const GENERATED_MIRROR_CONFIG = `[source.crates-io]
@@ -95,6 +97,12 @@ describe('BaseBuilder guards', () => {
9597
process.env[CN_MIRROR_ENV] = originalCnMirrorEnv;
9698
}
9799

100+
if (originalCargoTargetDir === undefined) {
101+
delete process.env.CARGO_TARGET_DIR;
102+
} else {
103+
process.env.CARGO_TARGET_DIR = originalCargoTargetDir;
104+
}
105+
98106
await Promise.all(tempDirs.splice(0).map((dir) => fsExtra.remove(dir)));
99107
});
100108

@@ -300,6 +308,46 @@ describe('BaseBuilder guards', () => {
300308
expect(command).toContain('--features cli-build');
301309
});
302310

311+
it('copies Windows build artifacts from CARGO_TARGET_DIR when it is set', () => {
312+
const cargoTargetDir = path.join(process.cwd(), '.short-cargo-target');
313+
process.env.CARGO_TARGET_DIR = cargoTargetDir;
314+
315+
const builder = new WinBuilder({
316+
debug: false,
317+
name: 'ChatGPT',
318+
targets: 'x64',
319+
} as any);
320+
321+
const appPath = (builder as any).getBuildAppPath(
322+
process.cwd(),
323+
'ChatGPT_1.0.0_x64_en-US',
324+
'msi',
325+
);
326+
const binaryPath = (builder as any).getRawBinarySourcePath(
327+
process.cwd(),
328+
'ChatGPT',
329+
);
330+
331+
expect(appPath).toBe(
332+
path.join(
333+
cargoTargetDir,
334+
'x86_64-pc-windows-msvc',
335+
'release',
336+
'bundle',
337+
'msi',
338+
'ChatGPT_1.0.0_x64_en-US.msi',
339+
),
340+
);
341+
expect(binaryPath).toBe(
342+
path.join(
343+
cargoTargetDir,
344+
'x86_64-pc-windows-msvc',
345+
'release',
346+
'pake-chatgpt.exe',
347+
),
348+
);
349+
});
350+
303351
it('tracks generated Pake config files in the Cargo build script', async () => {
304352
const buildScript = await fsExtra.readFile(
305353
path.join(process.cwd(), 'src-tauri', 'build.rs'),

0 commit comments

Comments
 (0)