Files
compiler-explorer/lib/compilers/carbon.ts
Matt Godbolt 53c7dd328b Configure Biome import organiser with grouped imports (#8431)
Enable Biome's `organizeImports` with groups matching the original
ESLint `import/order` configuration:

1. **Node builtins** (`node:fs`, `path`, etc.)
2. *(blank line)*
3. **Third-party packages** (`express`, `@sentry/node`, etc.)
4. *(blank line)*
5. **Local/relative imports** (`../foo.js`, `./bar.js`, aliases)

This resolves the inconsistency where Biome wasn't enforcing import
grouping, meaning new files would lose the blank-line separation that
the old ESLint config enforced.

### Impact
- **354 files** updated out of 738 checked (~48%)
- **+188 / -240 lines** (net -52) — almost entirely single blank line
additions/removals between import groups
- No import reordering; purely group separator consistency

Fixes #7373

🤖 Generated by LLM (Claude, via OpenClaw)
2026-02-01 20:50:46 -06:00

199 lines
7.8 KiB
TypeScript

// Copyright (c) 2022, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import fs from 'node:fs/promises';
import type {ParsedAsmResult} from '../../types/asmresult/asmresult.interfaces.js';
import {CompilationResult, ExecutionOptionsWithEnv} from '../../types/compilation/compilation.interfaces.js';
import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import type {ResultLine} from '../../types/resultline/resultline.interfaces.js';
import {unwrap} from '../assert.js';
import {BaseCompiler} from '../base-compiler.js';
import {CompilationEnvironment} from '../compilation-env.js';
import {BaseParser} from './argument-parsers.js';
export class CarbonCompiler extends BaseCompiler {
static get key() {
return 'carbon';
}
override orderArguments(
options: string[],
inputFilename: string,
libIncludes: string[],
libOptions: string[],
libPaths: string[],
libLinks: string[],
userOptions: string[],
staticLibLinks: string[],
): string[] {
return options.concat(
userOptions,
[this.filename(inputFilename)],
// We don't support libraries in Carbon, so drop all the `-L` args
);
}
override isCfgCompiler() {
return true;
}
override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string): string[] {
const args = [`--output=${outputFilename}`];
if (!filters.binary && !filters.binaryObject) args.push('--asm-output');
return args;
}
private getOutputPathFileFromOptions(options: string[]): string {
// `--output=<filename>` was added in optionsForFilter().
const outputArg = unwrap(options.filter(opt => opt.startsWith('--output='))[0]);
const outputFile = outputArg.split('=', 2)[1];
return outputFile;
}
private removePrebuiltRuntimesFromOptions(options: string[]): [string, string[]] {
const findArg = opt => opt.startsWith('--prebuilt-runtimes=');
const runtimesOpt = unwrap(options.filter(findArg)[0]);
const otherOptions = options.filter(opt => !findArg(opt));
return [runtimesOpt, otherOptions];
}
async moveFileToTemp(sourceFilePath: string): Promise<string> {
const tempFilePath = sourceFilePath + '.tmp';
await fs.rename(sourceFilePath, tempFilePath);
return tempFilePath;
}
override async runCompiler(
compiler: string,
options: string[],
inputFilename: string,
execOptions: ExecutionOptionsWithEnv,
filters?: ParseFiltersAndOutputOptions,
): Promise<CompilationResult> {
// `options` are all fed into the compile step except for `--prebuilt-runtimes` which is
// used for linking.
const [runtimesOpt, otherOptions] = this.removePrebuiltRuntimesFromOptions(options);
const compileOptions = ['compile'].concat(otherOptions);
const result = await super.runCompiler(compiler, compileOptions, inputFilename, execOptions, filters);
if (result.code !== 0) {
return result;
}
// The output of runCompiler is either an asm file or object file. It is an object file if
// we want to produce a binary, in which case we link it into a binary, overwriting the output
// path of the compile step.
if (filters?.binary) {
const compileOutputFilePath = this.getOutputPathFileFromOptions(options);
const objectFilePath = await this.moveFileToTemp(compileOutputFilePath);
const linkOptions = [runtimesOpt, 'link', `--output=${compileOutputFilePath}`, objectFilePath];
const linkResult = await this.exec(this.compiler.exe, linkOptions, execOptions);
return {...result, ...this.transformToCompilationResult(linkResult, inputFilename)};
}
return result;
}
}
export class CarbonExplorerCompiler extends BaseCompiler {
static get key() {
return 'carbon-explorer';
}
constructor(compilerInfo: PreliminaryCompilerInfo, env: CompilationEnvironment) {
super(compilerInfo, env);
this.compiler.demangler = '';
this.demanglerClass = null;
}
override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string): string[] {
return ['--color', `--trace_file=${outputFilename}`];
}
override async processAsm(
result,
filters: ParseFiltersAndOutputOptions,
options: string[],
): Promise<ParsedAsmResult> {
// Really should write a custom parser, but for now just don't filter anything.
return await super.processAsm(
result,
{
labels: false,
binary: false,
commentOnly: false,
demangle: false,
optOutput: false,
directives: false,
dontMaskFilenames: false,
execute: false,
intel: false,
libraryCode: false,
trim: false,
debugCalls: false,
},
options,
);
}
lastLine(lines?: ResultLine[]): string {
if (!lines || lines.length === 0) return '';
return lines[lines.length - 1].text;
}
override postCompilationPreCacheHook(result: CompilationResult): CompilationResult {
if (result.code === 0) {
// Hook to parse out the "result: 123" line at the end of the interpreted execution run.
const re = /^result: (\d+)$/;
const match = re.exec(this.lastLine(result.asm as ResultLine[]));
const code = match ? Number.parseInt(match[1], 10) : -1;
result.execResult = {
stdout: result.stdout,
stderr: [],
code: code,
didExecute: true,
timedOut: false,
buildResult: {
code: 0,
timedOut: false,
stdout: [],
stderr: [],
downloads: [],
executableFilename: '',
compilationOptions: [],
},
};
result.stdout = [];
result.languageId = 'no-highlight';
}
return result;
}
override getArgumentParserClass() {
// TODO: may need a custom one, based on/borrowing from ClangParser
return BaseParser;
}
}