Skip to content

Commit 027dbd9

Browse files
committed
fix(builder): propagate compactc exit status
util-linux `script` exits 0 whatever the child did unless `-e` is given, so every failed compile on Linux was reported as a success: a green check, exit 0, and no artifact on disk. Surfaced when compiling under `--feature-zkir-v3`, where zkir fails late. The captured PTY output is also the only place the compiler's error text lives, since `script` merges stderr into it, so the failure path now falls back to stdout when stderr is empty.
1 parent 2f72cb0 commit 027dbd9

3 files changed

Lines changed: 52 additions & 6 deletions

File tree

packages/builder/src/Compiler.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,13 @@ export class CompactCompiler {
335335
UIService.printOutput(summary, chalk.cyan);
336336
}
337337

338-
const cleanStderr = cleanForDisplay(execError.stderr);
339-
if (cleanStderr) {
340-
UIService.printOutput(cleanStderr, chalk.red);
338+
// Under `script` the compiler's stderr is merged into the captured
339+
// PTY output, so stdout is the only place the error text lives.
340+
const details =
341+
cleanForDisplay(execError.stderr) ||
342+
cleanForDisplay(execError.stdout);
343+
if (details) {
344+
UIService.printOutput(details, chalk.red);
341345
}
342346
}
343347

packages/builder/src/services/CompilerService.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ function tokenizeFlags(flags: string): string[] {
4141
*
4242
* Handles both macOS and Linux `script` syntax:
4343
* - macOS: `script -q <file> <command> [args...]`
44-
* - Linux: `script -qc "<command> [args...]" <file>`
44+
* - Linux: `script -qec "<command> [args...]" <file>`
45+
*
46+
* util-linux `script` exits 0 whatever the child did unless `-e` is given.
47+
* BSD `script` on macOS already returns the child's status.
4548
*
4649
* Note: Circuit constraint output is only available when compiling WITHOUT
4750
* `--skip-zk`, as the k/rows values come from the ZK proving pass. When
@@ -61,11 +64,11 @@ async function spawnWithPty(
6164
// macOS: script -q <file> <command> [args...]
6265
scriptArgs = ['-q', tmpFile, 'compact', ...args];
6366
} else {
64-
// Linux: script -qc "<command>" <file>
67+
// Linux: script -qec "<command>" <file>
6568
const cmd = ['compact', ...args]
6669
.map((a) => (a.includes(' ') ? `'${a}'` : a))
6770
.join(' ');
68-
scriptArgs = ['-qc', cmd, tmpFile];
71+
scriptArgs = ['-qec', cmd, tmpFile];
6972
}
7073

7174
const proc = spawn('script', scriptArgs, {

packages/builder/test/Compiler.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,45 @@ describe('CompactCompiler', () => {
11531153
);
11541154
expect(testMockExec).toHaveBeenCalledTimes(4);
11551155
});
1156+
1157+
it('prints the captured output when the failure carries no stderr', async () => {
1158+
mockReaddir.mockResolvedValue([
1159+
{
1160+
name: 'MockElGamal.compact',
1161+
isFile: () => true,
1162+
isDirectory: () => false,
1163+
},
1164+
] as any);
1165+
mockExistsSync.mockReturnValue(true);
1166+
1167+
const ptyError = new Error('compact exited with code 1') as Error & {
1168+
stdout: string;
1169+
stderr: string;
1170+
};
1171+
ptyError.stdout =
1172+
'Exception in thread "main" Unsupported test_eq: JubjubScalar == JubjubScalar\n';
1173+
ptyError.stderr = '';
1174+
1175+
const testMockExec = vi
1176+
.fn()
1177+
.mockResolvedValueOnce({ stdout: 'compact 0.1.0', stderr: '' })
1178+
.mockResolvedValueOnce({ stdout: 'compact 0.1.0', stderr: '' })
1179+
.mockResolvedValueOnce({ stdout: 'Compactc 0.26.0', stderr: '' })
1180+
.mockRejectedValueOnce(ptyError);
1181+
1182+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
1183+
compiler = new CompactCompiler({}, testMockExec);
1184+
1185+
await expect(compiler.compile()).rejects.toThrow(CompilationError);
1186+
1187+
expect(logSpy).toHaveBeenCalledWith(
1188+
expect.stringContaining(
1189+
'Unsupported test_eq: JubjubScalar == JubjubScalar',
1190+
),
1191+
);
1192+
1193+
logSpy.mockRestore();
1194+
});
11561195
});
11571196

11581197
describe('Real-world scenarios', () => {

0 commit comments

Comments
 (0)