|
|
@@ -72,6 +72,41 @@ export function runProcess(command: string, args: string[], cwd: string): Promis
|
|
|
|
|
|
const FPC_LINE = /^(.+?)\((\d+),(\d+)\)\s+(Error|Warning|Fatal|Note|Hint):\s+(.*)$/;
|
|
|
|
|
|
+/** Parse Blaise diagnostics (`Parse error: msg at line L col C in F`). */
|
|
|
+export function parseBlaiseDiagnostics(projectRoot: string, main: string, output: string): BuildDiagnostic[] {
|
|
|
+ const diagnostics: BuildDiagnostic[] = [];
|
|
|
+ const re = /^(Parse|Semantic) error:\s*(.*?)\s+at line (\d+) col (\d+) in (\S+)\s*$/gm;
|
|
|
+ let m: RegExpExecArray | null;
|
|
|
+ while ((m = re.exec(output)) !== null) {
|
|
|
+ diagnostics.push({
|
|
|
+ file: resolveBlaiseFile(projectRoot, main, m[5]),
|
|
|
+ line: Math.max(0, Number(m[3]) - 1),
|
|
|
+ character: Math.max(0, Number(m[4]) - 1),
|
|
|
+ message: `${m[1]} error: ${m[2].trim()}`,
|
|
|
+ severity: 'error',
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return diagnostics;
|
|
|
+}
|
|
|
+
|
|
|
+/** Blaise reports a bare module name as often as a file name. */
|
|
|
+function resolveBlaiseFile(projectRoot: string, main: string, reported: string): string {
|
|
|
+ const direct = path.join(projectRoot, reported);
|
|
|
+ try {
|
|
|
+ if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return reported;
|
|
|
+ } catch {
|
|
|
+ // Fall through to the guesses below.
|
|
|
+ }
|
|
|
+ for (const candidate of [`${reported}.pas`, `${reported}.pp`]) {
|
|
|
+ try {
|
|
|
+ if (fs.existsSync(path.join(projectRoot, candidate))) return candidate;
|
|
|
+ } catch {
|
|
|
+ // Keep guessing.
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return main;
|
|
|
+}
|
|
|
+
|
|
|
/** Parse FPC-style diagnostics; context/caret lines are ignored. */
|
|
|
export function parseFpcDiagnostics(output: string): BuildDiagnostic[] {
|
|
|
const diagnostics: BuildDiagnostic[] = [];
|
|
|
@@ -94,10 +129,15 @@ export function parseFpcDiagnostics(output: string): BuildDiagnostic[] {
|
|
|
export class FreePascalCompilerService implements PascalCompiler {
|
|
|
async detect(compilerPath = 'fpc'): Promise<CompilerInfo | undefined> {
|
|
|
try {
|
|
|
- const result = await runProcess(compilerPath, ['-iV'], process.cwd());
|
|
|
- if (result.exitCode !== 0) return undefined;
|
|
|
- const version = (result.output.split('\n')[0] ?? '').trim();
|
|
|
- return { path: compilerPath, version };
|
|
|
+ const probe = await runProcess(compilerPath, ['-iV'], process.cwd());
|
|
|
+ const first = (probe.output.split('\n')[0] ?? '').trim();
|
|
|
+ if (probe.exitCode === 0 && /^\d+\.\d+/.test(first)) return { path: compilerPath, version: first };
|
|
|
+ const mark = probe.output.split('\n').find(l => l.includes('Blaise Compiler'));
|
|
|
+ if (mark) return { path: compilerPath, version: mark.trim() };
|
|
|
+ const help = await runProcess(compilerPath, ['--help'], process.cwd());
|
|
|
+ const banner = help.output.split('\n').find(l => l.includes('Blaise Compiler'));
|
|
|
+ if (banner) return { path: compilerPath, version: banner.trim() };
|
|
|
+ return undefined;
|
|
|
} catch {
|
|
|
return undefined;
|
|
|
}
|
|
|
@@ -106,7 +146,9 @@ export class FreePascalCompilerService implements PascalCompiler {
|
|
|
async build(project: PascalProject): Promise<BuildResult> {
|
|
|
const out = defaultOutput(project);
|
|
|
await fs.promises.mkdir(path.dirname(path.join(project.root, out)), { recursive: true });
|
|
|
- const args = ['-g', ...project.compiler.options, '-o' + out, project.main];
|
|
|
+ const args = project.compiler.type === 'blaise'
|
|
|
+ ? ['--source', project.main, '--output', out, ...project.compiler.options]
|
|
|
+ : ['-g', ...project.compiler.options, '-o' + out, project.main];
|
|
|
const command = [project.compiler.path, ...args].join(' ');
|
|
|
let result: ProcessResult;
|
|
|
try {
|
|
|
@@ -125,7 +167,9 @@ export class FreePascalCompilerService implements PascalCompiler {
|
|
|
exitCode: result.exitCode,
|
|
|
command,
|
|
|
output: result.output,
|
|
|
- diagnostics: parseFpcDiagnostics(result.output),
|
|
|
+ diagnostics: project.compiler.type === 'blaise'
|
|
|
+ ? parseBlaiseDiagnostics(project.root, project.main, result.output)
|
|
|
+ : parseFpcDiagnostics(result.output),
|
|
|
stats: success ? await collectBuildStats(project, out) : undefined,
|
|
|
};
|
|
|
}
|