Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | 12x 12x 12x 86x 86x 57x 57x 57x 39x 18x 18x 18x 18x 57x 57x 57x 57x 57x 57x 57x 41x 8x 8x 8x 8x 8x 57x 57x 57x 57x 57x 13x 57x 65x 65x 57x 57x 8x 8x 8x 8x 8x 8x 57x 57x 18x 41x 40x 8x | /**
* Local Compiler
*
* Handles local compilation of Arduino sketches using g++.
* Used as fallback when Docker is not available.
*/
import { spawn } from "child_process";
import { chmod, mkdir, access, rm } from "fs/promises";
import { dirname } from "path";
import { Logger } from "@shared/logger";
export class LocalCompiler {
private logger = new Logger("LocalCompiler");
private readonly compileTimeoutMs = 20000; // 20 seconds
/**
* Compiles a sketch file using g++
*
* @param sketchFile - Path to the .cpp file
* @param exeFile - Path for the output executable
* @throws Error if compilation fails or times out
*/
async compile(sketchFile: string, exeFile: string): Promise<void> {
// Ensure output directory exists before compilation
const outputDir = dirname(exeFile);
try {
await access(outputDir);
this.logger.debug(`Output directory exists: ${outputDir}`);
} catch (err) {
this.logger.info(`Output directory missing, creating: ${outputDir}`);
try {
await mkdir(outputDir, { recursive: true, mode: 0o755 });
this.logger.debug(`Created output directory with proper permissions: ${outputDir}`);
} catch (mkdirErr) {
const msg = mkdirErr instanceof Error ? mkdirErr.message : String(mkdirErr);
this.logger.error(`Failed to create output directory: ${msg}`);
throw mkdirErr;
}
}
// Ensure output directory is writable by removing any stale exe file
try {
await rm(exeFile, { force: true });
this.logger.debug(`Cleaned up stale executable: ${exeFile}`);
} catch (err) {
// Ignore - file might not exist yet
}
// Try compilation with retry logic for transient failures
let lastError: Error | null = null;
for (let attempt = 1; attempt <= 2; attempt++) {
try {
await this.runCompilation(sketchFile, exeFile, attempt);
return; // Success on this attempt
} catch (err) {
lastError = err instanceof Error ? err : new Error(String(err));
const isCompilerError = (lastError as any).isCompilerError === true;
Eif (isCompilerError || attempt >= 2) {
break;
}
if (attempt < 2) {
this.logger.warn(`Compilation attempt ${attempt} failed, retrying... (${lastError.message})`);
await new Promise(r => setTimeout(r, 500)); // Wait before retry
}
}
}
Eif (lastError) throw lastError;
}
/**
* Internal method to run the actual g++ compilation
*/
private runCompilation(sketchFile: string, exeFile: string, attempt: number): Promise<void> {
return new Promise((resolve, reject) => {
const compile = spawn("g++", [
sketchFile,
"-o",
exeFile,
"-pthread", // Required for threading support
]);
let errorOutput = "";
let completed = false;
compile.stderr.on("data", (data) => {
errorOutput += data.toString();
});
compile.on("close", (code) => {
completed = true;
if (code === 0) {
this.logger.info(`Compilation successful: ${exeFile}`);
resolve();
} else {
const cleanedError = this.cleanCompilerErrors(errorOutput);
const errorMsg = `Compiler error (Code ${code}, attempt ${attempt}): ${cleanedError}`;
this.logger.error(errorMsg);
const compileErr = new Error(cleanedError);
(compileErr as any).isCompilerError = true;
reject(compileErr);
}
});
compile.on("error", (err) => {
completed = true;
this.logger.error(`Compilation process error: ${err.message}`);
reject(err);
});
// Timeout protection
setTimeout(() => {
Iif (!completed) {
compile.kill("SIGKILL");
const timeoutError = new Error(
`g++ compilation timeout after ${this.compileTimeoutMs / 1000}s`,
);
this.logger.error(timeoutError.message);
reject(timeoutError);
}
}, this.compileTimeoutMs);
});
}
/**
* Makes the compiled executable file executable (chmod +x)
*/
async makeExecutable(exeFile: string): Promise<void> {
await chmod(exeFile, 0o755);
this.logger.debug(`Made executable: ${exeFile}`);
}
/**
* Cleans up compiler error messages for user display
* Removes temporary paths and makes errors more readable
*/
private cleanCompilerErrors(errors: string): string {
return errors
.replace(/\/sandbox\/sketch\.cpp/g, "sketch.ino") // Docker path
.replace(/\/[^\s:]+\/temp\/[a-f0-9-]+\/sketch\.cpp/gi, "sketch.ino") // Local temp path
.replace(/sketch\.cpp/g, "sketch.ino") // Generic .cpp references
.trim();
}
}
|