fix(console): the compile log tells the story once, without cmake's diary

ESP-IDF builds sprayed the compilation console with configure chatter
(-- cmake status, git-metadata probes that 'fatal' on the synthesized
project tree, IDF component-manager NOTEs, Python env checks) and then
reprinted the entire stdout a second time when parseCompileResult ran
over the final result after the live stream had already shown it.

- isNoiseBuildLine(): pattern list for build-system chatter, applied to
  the live stream deltas and to the final-result parsing.
- parseCompileResult(streamedLive): skips the stdout reprint on success;
  on failure re-emits only error/warning lines so they still get the
  red/yellow markers the plain live stream lacked.

Compiler errors, FAILED blocks, ninja [N/M] progress, esptool output and
binary-size summaries all still show. The full log stays available
server-side.
This commit is contained in:
David Montero Crespo 2026-08-09 07:36:36 +02:00
parent 980c564f66
commit 0a868cc254
2 changed files with 51 additions and 8 deletions

View File

@ -24,7 +24,7 @@ import { reportRunEvent } from '../../services/metricsService';
import { useProjectStore } from '../../store/useProjectStore';
import { LibraryManagerModal } from '../simulator/LibraryManagerModal';
import { InstallLibrariesModal } from '../simulator/InstallLibrariesModal';
import { parseCompileResult } from '../../utils/compilationLogger';
import { parseCompileResult, isNoiseBuildLine } from '../../utils/compilationLogger';
import type { CompilationLog, CompileTarget } from '../../utils/compilationLogger';
import { exportToWokwiZip } from '../../utils/wokwiZip';
import { importProjectFile, PROJECT_FILE_ACCEPT } from '../../utils/importProject';
@ -551,7 +551,9 @@ export const EditorToolbar = ({
if (stdout.length <= lastStreamedLen) return;
const delta = stdout.slice(lastStreamedLen);
lastStreamedLen = stdout.length;
const newLines = delta.split('\n').filter((s) => s.trim());
const newLines = delta
.split('\n')
.filter((s) => s.trim() && !isNoiseBuildLine(s));
if (!newLines.length) return;
const now = new Date();
setCompileLogs((prev: CompilationLog[]) => [
@ -581,8 +583,9 @@ export const EditorToolbar = ({
// After the build settles, append the structured analysis on top of
// the live stream — parseCompileResult highlights FAILED blocks and
// tags compiler errors with type='error', which the console uses for
// colour + the auto-switch-to-errors filter.
const resultLogs = parseCompileResult(result, boardLabel, boardTarget);
// colour + the auto-switch-to-errors filter. streamedLive tells it not
// to reprint the stdout the stream above already showed.
const resultLogs = parseCompileResult(result, boardLabel, boardTarget, lastStreamedLen > 0);
setCompileLogs((prev: CompilationLog[]) => [...prev, ...resultLogs]);
if (result.success) {
@ -1057,7 +1060,9 @@ export const EditorToolbar = ({
if (stdout.length <= lastStreamedLen) return;
const delta = stdout.slice(lastStreamedLen);
lastStreamedLen = stdout.length;
const newLines = delta.split('\n').filter((s) => s.trim());
const newLines = delta
.split('\n')
.filter((s) => s.trim() && !isNoiseBuildLine(s));
if (!newLines.length) return;
const now = new Date();
setCompileLogs((prev: CompilationLog[]) => [
@ -1074,7 +1079,7 @@ export const EditorToolbar = ({
{ boardOptions: board.boardOptions, spiffsFiles: board.spiffsFiles, libraries: board.libraries?.length ? board.libraries : null, language: board.languageMode === 'espidf' ? 'espidf' : undefined },
);
const resultLogs = parseCompileResult(result, label, boardTarget);
const resultLogs = parseCompileResult(result, label, boardTarget, lastStreamedLen > 0);
setCompileLogs((prev: CompilationLog[]) => [...prev, ...resultLogs]);
if (result.success) {

View File

@ -21,10 +21,40 @@ export interface CompilationLog {
target?: CompileTarget;
}
/** Build-system chatter with no signal for the user: CMake configure status,
* git-metadata probes inside the build container (the tree is a synthesized
* project, never a git repo — those "fatal:" lines are expected), IDF
* component-manager dependency solving, Python env checks. The full log is
* still available server-side; the console only drops these lines. */
const NOISE_LINE_PATTERNS: RegExp[] = [
/^-- /, // CMake configure status lines
/^fatal: not a git repository/,
/^Stopping at filesystem boundary/,
/^\.*NOTE: /, // IDF component manager (incl. leading progress dots)
/^warning: unknown kconfig symbol/,
/^Loading defaults file /,
/^Constraint file: /,
/^Requirement files:/,
/^- \/.*requirements[^ ]*\.txt$/,
/^Python being checked: /,
/^Python requirements are satisfied\.?$/,
/Project commit: HEAD-HASH-NOTFOUND/,
];
export function isNoiseBuildLine(line: string): boolean {
const stripped = line.trim();
return NOISE_LINE_PATTERNS.some((re) => re.test(stripped));
}
export function parseCompileResult(
result: CompileResult,
board: string,
target?: CompileTarget,
/** True when the caller already streamed the live cmake+ninja output into
* the console while the build ran. Skips re-printing result.stdout on
* success, and on failure re-emits only the error/warning lines (for the
* red/yellow highlighting the plain live stream lacked). */
streamedLive = false,
): CompilationLog[] {
const logs: CompilationLog[] = [];
const now = new Date();
@ -40,8 +70,10 @@ export function parseCompileResult(
}
}
// stdout — for ESP-IDF/ninja builds, compiler errors appear here
if (result.stdout) {
// stdout — for ESP-IDF/ninja builds, compiler errors appear here.
// When the live stream already showed it, a successful build has nothing
// left to say here (re-printing would duplicate the whole log).
if (result.stdout && !(streamedLive && result.success)) {
let inFailedBlock = false;
for (const line of result.stdout.split('\n')) {
if (!line.trim()) continue;
@ -71,6 +103,8 @@ export function parseCompileResult(
} else {
type = 'info';
}
if (type === 'info' && (streamedLive || isNoiseBuildLine(stripped))) continue;
if (type === 'warning' && isNoiseBuildLine(stripped)) continue;
logs.push({ timestamp: now, type, message: line });
}
}
@ -79,6 +113,7 @@ export function parseCompileResult(
if (result.stderr) {
for (const line of result.stderr.split('\n')) {
if (!line.trim()) continue;
if (isNoiseBuildLine(line)) continue;
let type: CompilationLog['type'] = 'error';
const lower = line.toLowerCase();
if (lower.includes('warning:') || lower.includes('warn ')) {
@ -96,6 +131,9 @@ export function parseCompileResult(
) {
type = 'info';
}
// Live-streamed builds already showed the chatter; only re-emit lines
// that gain something from re-classification (red/yellow markers).
if (type === 'info' && streamedLive) continue;
logs.push({ timestamp: now, type, message: line });
}
}