Skip to content

Commit e616b9f

Browse files
schmonzclaude
andcommitted
fix(build): the fused output's .exe follows the TARGET, not the host
`clode build`'s default output name keyed its .exe suffix off process.platform (the build host), which got cross-builds exactly backwards: `--target windows-x64` from a POSIX host produced a `quaude` with no .exe (unrunnable on Windows until renamed), and a POSIX `--target` built FROM Windows produced a spurious `.exe`. Extract the naming into a pure, exported defaultBuildOut({target, self, hostPlatform}): a windows-* target is .exe regardless of host, a non-windows target never is, and with no --target it follows the host (native builds unchanged). Explicit --out is still respected verbatim. Unit-tested across the cross-build matrix (windows-from-POSIX, POSIX-from-Windows, native, --self). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0147MaTTj7XYS2PTUmJn3zUy
1 parent 6db5d52 commit e616b9f

2 files changed

Lines changed: 36 additions & 7 deletions

File tree

libexec/clode-fuse.cjs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,17 @@ function parseBuildArgs(args) {
709709
return { naude, self, out, target, listTargets };
710710
}
711711

712+
// Default output basename for a quaude/--self build (the naude branch names its
713+
// own). The `.exe` suffix follows the TARGET platform, not the host: a
714+
// `--target windows-*` cross-built from POSIX must still be a .exe, and a POSIX
715+
// `--target` built FROM Windows must NOT be. With no --target (a native build)
716+
// it follows the host. An explicit --out is respected verbatim by the caller.
717+
function defaultBuildOut({ target, self, hostPlatform }) {
718+
const name = self ? 'clode-native' : 'quaude';
719+
const isWin = target ? /^windows-/.test(target) : hostPlatform === 'win32';
720+
return name + (isWin ? '.exe' : '');
721+
}
722+
712723
// The default GitHub release download root. Overridable via CLODE_RELEASE_BASE
713724
// (forks / mirrors). No trailing slash.
714725
const CLODE_RELEASE_BASE_DEFAULT = 'https://github.com/schmonz/clode/releases/download';
@@ -1001,11 +1012,10 @@ async function clodeBuild(args, opts) {
10011012
}
10021013

10031014
// naude/self/out were already parsed + validated above (shared with the
1004-
// --naude branch, which returned before reaching here). On Windows a bare
1005-
// `clode build` should yield a runnable .exe. An explicit
1006-
// --out is respected verbatim (the user owns that name); only the DEFAULT
1007-
// gains .exe. win32-guarded → POSIX default (quaude / clode-native) unchanged.
1008-
out = path.resolve(out || (self ? 'clode-native' : 'quaude') + (process.platform === 'win32' ? '.exe' : ''));
1015+
// --naude branch, which returned before reaching here). The default name's
1016+
// .exe follows the TARGET, not the host (see defaultBuildOut); an explicit
1017+
// --out is respected verbatim (the user owns that name).
1018+
out = path.resolve(out || defaultBuildOut({ target: parsed.target, self, hostPlatform: process.platform }));
10091019

10101020
const ROOT = path.resolve(opts.libexec, '..');
10111021
const work = fs.mkdtempSync(path.join(os.tmpdir(), 'clode-build-'));
@@ -1393,7 +1403,7 @@ async function clodeBuild(args, opts) {
13931403
}
13941404

13951405
module.exports = {
1396-
clodeBuild, parseBuildArgs, makePhaseSpinner, startPongMock, cannedSSE, smokeTarget, timeoutScale, codesignAdHoc, thinToHostSlice, describeExit,
1406+
clodeBuild, parseBuildArgs, defaultBuildOut, makePhaseSpinner, startPongMock, cannedSSE, smokeTarget, timeoutScale, codesignAdHoc, thinToHostSlice, describeExit,
13971407
readDirectDeps, computeDepClosure, assertClosureMatchesLockfile,
13981408
scanBareSpecifiers, specifierPackageName, isBuiltinSpecifier, shimProvidedModules,
13991409
assertNoUnknownBareSpecifiers, KNOWN_UNREACHABLE, resolveClaudeNmDir,

test/clode-fuse-args.test.cjs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22
const { test } = require('node:test');
33
const assert = require('node:assert');
4-
const { parseBuildArgs } = require('../libexec/clode-fuse.cjs');
4+
const { parseBuildArgs, defaultBuildOut } = require('../libexec/clode-fuse.cjs');
55

66
test('parseBuildArgs: --list-targets', () => {
77
assert.deepStrictEqual(parseBuildArgs(['--list-targets']),
@@ -26,3 +26,22 @@ test('parseBuildArgs: plain build unchanged', () => {
2626
assert.deepStrictEqual(parseBuildArgs([]),
2727
{ naude: false, self: false, out: null, target: null, listTargets: false });
2828
});
29+
30+
// defaultBuildOut: the .exe suffix follows the TARGET platform, not the host —
31+
// the cross-build naming bug (keying off process.platform got both backwards).
32+
test('defaultBuildOut: .exe follows the target, not the host', () => {
33+
// windows target from a POSIX host -> still .exe
34+
assert.strictEqual(defaultBuildOut({ target: 'windows-x64', self: false, hostPlatform: 'linux' }), 'quaude.exe');
35+
assert.strictEqual(defaultBuildOut({ target: 'windows-arm64', self: false, hostPlatform: 'darwin' }), 'quaude.exe');
36+
// POSIX target from a Windows host -> NO .exe
37+
assert.strictEqual(defaultBuildOut({ target: 'netbsd-sparc', self: false, hostPlatform: 'win32' }), 'quaude');
38+
assert.strictEqual(defaultBuildOut({ target: 'linux-x64', self: false, hostPlatform: 'win32' }), 'quaude');
39+
});
40+
41+
test('defaultBuildOut: no --target follows the host; --self names clode-native', () => {
42+
assert.strictEqual(defaultBuildOut({ target: null, self: false, hostPlatform: 'win32' }), 'quaude.exe');
43+
assert.strictEqual(defaultBuildOut({ target: null, self: false, hostPlatform: 'linux' }), 'quaude');
44+
assert.strictEqual(defaultBuildOut({ target: null, self: true, hostPlatform: 'win32' }), 'clode-native.exe');
45+
assert.strictEqual(defaultBuildOut({ target: 'windows-x64', self: true, hostPlatform: 'linux' }), 'clode-native.exe');
46+
assert.strictEqual(defaultBuildOut({ target: null, self: true, hostPlatform: 'linux' }), 'clode-native');
47+
});

0 commit comments

Comments
 (0)