Compiler overrides (#5001)

This commit is contained in:
Patrick Quist
2023-05-16 03:53:57 +02:00
committed by GitHub
parent d6deb08cee
commit 079d495757
50 changed files with 2279 additions and 302 deletions

View File

@@ -61,6 +61,10 @@ const compilerParsers = {
ts: Parsers.TypeScriptNativeParser,
turboc: Parsers.TurboCParser,
toit: Parsers.ToitParser,
circle: Parsers.CircleParser,
ghc: Parsers.GHCParser,
tendra: Parsers.TendraParser,
golang: Parsers.GolangParser,
};
class CompilerArgsApp {
@@ -88,28 +92,64 @@ class CompilerArgsApp {
}
}
async doTheParsing() {
async getPossibleStdvers() {
const parser = this.getParser();
return await parser.getPossibleStdvers(this.compiler);
}
async getPossibleTargets() {
const parser = this.getParser();
return await parser.getPossibleTargets(this.compiler);
}
async getPossibleEditions() {
const parser = this.getParser();
return await parser.getPossibleEditions(this.compiler);
}
getParser() {
if (compilerParsers[this.parserName]) {
const parser = compilerParsers[this.parserName];
await parser.parse(this.compiler);
return compilerParsers[this.parserName];
} else {
console.error('Unknown parser type');
process.exit(1);
}
}
print() {
async doTheParsing() {
const parser = this.getParser();
await parser.parse(this.compiler);
const options = this.compiler.possibleArguments.possibleArguments;
if (parser.hasSupportStartsWith(options, '--target=')) {
console.log('supportsTargetIs');
} else if (parser.hasSupportStartsWith(options, '--target ')) {
console.log('supportsTarget');
} else if (parser.hasSupportStartsWith(options, '--march=')) {
console.log('supportsMarch');
} else {
console.log('none of the things?');
}
}
async print() {
const args = _.keys(this.compiler.possibleArguments.possibleArguments);
for (const arg of args) {
console.log(padRight(arg, this.pad) + this.compiler.possibleArguments.possibleArguments[arg].description);
}
console.log('Stdvers:');
console.log(await this.getPossibleStdvers());
console.log('Targets:');
console.log(await this.getPossibleTargets());
console.log('Editions:');
console.log(await this.getPossibleEditions());
}
}
if (!opts.parser || !opts.exe) {
console.error(
'Usage: ' +
'node -r esm -r ts-node/register compiler-args-app.ts ' +
'ts-node-esm compiler-args-app.ts ' +
'--parser=<compilertype> --exe=<path> [--padding=<number>]\n' +
'for example: --parser=clang --exe=/opt/compiler-explorer/clang-15.0.0/bin/clang++ --padding=50',
);
@@ -117,8 +157,6 @@ if (!opts.parser || !opts.exe) {
} else {
const app = new CompilerArgsApp();
app.doTheParsing()
.then(() => {
app.print();
})
.then(() => app.print())
.catch(e => console.error(e));
}

View File

@@ -60,7 +60,7 @@ import type {BuildEnvDownloadInfo} from './buildenvsetup/buildenv.interfaces.js'
import * as cfg from './cfg/cfg.js';
import {CompilationEnvironment} from './compilation-env.js';
import {CompilerArguments} from './compiler-arguments.js';
import {ClangParser, GCCParser} from './compilers/argument-parsers.js';
import {ClangParser, GCCParser, ICCParser} from './compilers/argument-parsers.js';
import {BaseDemangler, getDemanglerTypeByKey} from './demangler/index.js';
import {LLVMIRDemangler} from './demangler/llvm.js';
import * as exec from './exec.js';
@@ -78,10 +78,27 @@ import {AsmParser} from './parsers/asm-parser.js';
import type {IAsmParser} from './parsers/asm-parser.interfaces.js';
import {LlvmPassDumpParser} from './parsers/llvm-pass-dump-parser.js';
import type {PropertyGetter} from './properties.interfaces.js';
import {getToolchainPath, removeToolchainArg} from './toolchain-utils.js';
import {
clang_style_sysroot_flag,
getSpecificTargetBasedOnToolchainPath,
getSysrootByToolchainPath,
getToolchainFlagFromOptions,
getToolchainPath,
hasSysrootArg,
hasToolchainArg,
removeToolchainArg,
replaceSysrootArg,
replaceToolchainArg,
} from './toolchain-utils.js';
import type {ITool} from './tooling/base-tool.interface.js';
import * as utils from './utils.js';
import {unwrap} from './assert.js';
import {
CompilerOverrideOption,
CompilerOverrideOptions,
CompilerOverrideType,
ConfiguredOverrides,
} from '../types/compilation/compiler-overrides.interfaces.js';
const compilationTimeHistogram = new PromClient.Histogram({
name: 'ce_base_compiler_compilation_duration_seconds',
@@ -95,6 +112,18 @@ const executionTimeHistogram = new PromClient.Histogram({
buckets: [0.1, 0.5, 1, 5, 10, 20, 30],
});
export const c_default_target_description =
'Change the target architecture of the compiler. ' +
'Be aware that the architecture might not be fully supported by the compiler' +
' eventhough the option is available. ' +
'The compiler might also require additional arguments to be fully functional.';
export const c_default_toolchain_description =
'Change the default GCC toolchain for this compiler. ' +
'This may or may not affect header usage (e.g. libstdc++ version) and linking to GCCs pre-built binaries.';
export const c_value_placeholder = '<value>';
export class BaseCompiler implements ICompiler {
protected compiler: CompilerInfo; // TODO: Some missing types still present in Compiler type
public lang: Language;
@@ -288,6 +317,7 @@ export class BaseCompiler implements ICompiler {
env.CC = this.compiler.exe;
}
// TODO(#5051): support changing of toolchainPath per compile
if (this.toolchainPath) {
if (process.platform === 'win32') {
const ldPath = `${this.toolchainPath}/bin/ld.exe`;
@@ -348,6 +378,11 @@ export class BaseCompiler implements ICompiler {
options.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths([]);
}
if (options.createAndUseTempDir) {
const tmpDir = await this.newTempDir();
options.customCwd = tmpDir;
}
const key = this.getCompilerCacheKey(compiler, args, options);
let result = await this.env.compilerCacheGet(key as any);
if (!result) {
@@ -364,6 +399,8 @@ export class BaseCompiler implements ICompiler {
}
}
if (options.createAndUseTempDir) fs.remove(options.customCwd);
return result;
}
@@ -813,13 +850,13 @@ export class BaseCompiler implements ICompiler {
) as string[];
}
protected getSharedLibraryPathsAsArguments(libraries, libDownloadPath?) {
protected getSharedLibraryPathsAsArguments(libraries, libDownloadPath?: string, toolchainPath?: string) {
const pathFlag = this.compiler.rpathFlag || '-Wl,-rpath,';
const libPathFlag = this.compiler.libpathFlag || '-L';
let toolchainLibraryPaths: string[] = [];
if (this.toolchainPath) {
toolchainLibraryPaths = [path.join(this.toolchainPath, '/lib64'), path.join(this.toolchainPath, '/lib32')];
if (toolchainPath) {
toolchainLibraryPaths = [path.join(toolchainPath, '/lib64'), path.join(toolchainPath, '/lib32')];
}
if (!libDownloadPath) {
@@ -900,6 +937,88 @@ export class BaseCompiler implements ICompiler {
);
}
getDefaultOrOverridenToolchainPath(overrides: ConfiguredOverrides): string {
for (const override of overrides) {
if (override.value) {
const possible = this.compiler.possibleOverrides?.find(ov => ov.name === override.name);
if (possible && possible.name === CompilerOverrideType.toolchain) {
return override.value;
}
}
}
return this.toolchainPath;
}
getOverridenToolchainPath(overrides: ConfiguredOverrides): string | false {
for (const override of overrides) {
if (override.value) {
const possible = this.compiler.possibleOverrides?.find(ov => ov.name === override.name);
if (possible && possible.name === CompilerOverrideType.toolchain) {
return override.value;
}
}
}
return false;
}
changeOptionsBasedOnOverrides(options: string[], overrides: ConfiguredOverrides): string[] {
const overriddenToolchainPath = this.getOverridenToolchainPath(overrides);
const sysrootPath: string | false =
overriddenToolchainPath ?? getSysrootByToolchainPath(overriddenToolchainPath);
for (const override of overrides) {
if (override.value) {
const possible = this.compiler.possibleOverrides?.find(ov => ov.name === override.name);
if (!possible) continue;
switch (possible.name) {
case CompilerOverrideType.toolchain: {
if (hasToolchainArg(options)) {
options = replaceToolchainArg(options, override.value);
} else {
for (const flag of possible.flags) {
options.push(flag.replace(c_value_placeholder, override.value));
}
}
if (sysrootPath) {
if (hasSysrootArg(options)) {
options = replaceSysrootArg(options, sysrootPath);
} else {
options.push(clang_style_sysroot_flag + sysrootPath);
}
}
break;
}
case CompilerOverrideType.arch: {
let betterTarget = override.value;
if (overriddenToolchainPath) {
betterTarget = getSpecificTargetBasedOnToolchainPath(
override.value,
overriddenToolchainPath,
);
}
for (const flag of possible.flags) {
options.push(flag.replace(c_value_placeholder, betterTarget));
}
break;
}
default: {
for (const flag of possible.flags) {
options.push(flag.replace(c_value_placeholder, override.value));
}
break;
}
}
}
}
return options;
}
prepareArguments(
userOptions: string[],
filters: ParseFiltersAndOutputOptions,
@@ -907,6 +1026,7 @@ export class BaseCompiler implements ICompiler {
inputFilename: string,
outputFilename: string,
libraries,
overrides: ConfiguredOverrides,
) {
let options = this.optionsForFilter(filters, outputFilename, userOptions);
backendOptions = backendOptions || {};
@@ -921,6 +1041,8 @@ export class BaseCompiler implements ICompiler {
options = options.concat(unwrap(this.compiler.optArg));
}
const toolchainPath = this.getDefaultOrOverridenToolchainPath(backendOptions.overrides || []);
const libIncludes = this.getIncludeArguments(libraries);
const libOptions = this.getLibraryOptions(libraries);
let libLinks: string[] = [];
@@ -929,12 +1051,14 @@ export class BaseCompiler implements ICompiler {
if (filters.binary) {
libLinks = this.getSharedLibraryLinks(libraries) || [];
libPaths = this.getSharedLibraryPathsAsArguments(libraries);
libPaths = this.getSharedLibraryPathsAsArguments(libraries, undefined, toolchainPath);
staticLibLinks = this.getStaticLibraryLinks(libraries) || [];
}
userOptions = this.filterUserOptions(userOptions) || [];
options = this.fixIncompatibleOptions(options, userOptions);
options = this.changeOptionsBasedOnOverrides(options, overrides);
return this.orderArguments(
options,
inputFilename,
@@ -1536,6 +1660,8 @@ export class BaseCompiler implements ICompiler {
buildFilters.binary = true;
buildFilters.execute = true;
const overrides = this.sanitizeCompilerOverrides(key.backendOptions.overrides || []);
const compilerArguments = _.compact(
this.prepareArguments(
key.options,
@@ -1544,12 +1670,15 @@ export class BaseCompiler implements ICompiler {
inputFilename,
outputFilename,
key.libraries,
overrides,
),
);
const execOptions = this.getDefaultExecOptions();
execOptions.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths(key.libraries);
this.applyOverridesToExecOptions(execOptions, overrides);
const result = await this.buildExecutable(key.compiler.exe, compilerArguments, inputFilename, execOptions);
return {
@@ -1794,18 +1923,56 @@ export class BaseCompiler implements ICompiler {
}
}
sanitizeCompilerOverrides(overrides: ConfiguredOverrides): ConfiguredOverrides {
const allowedRegex = /^[A-Z_]{1,}[A-Z0-9_]*$/;
for (const override of overrides) {
if (override.name === CompilerOverrideType.env && override.values) {
// lowercase names are allowed, but let's assume everyone means to use uppercase
override.values.forEach(env => (env.name = env.name.trim().toUpperCase()));
override.values = override.values.filter(
env => env.name !== 'LD_PRELOAD' && env.name.match(allowedRegex),
);
}
}
return overrides;
}
applyOverridesToExecOptions(execOptions: ExecutionOptions, overrides: ConfiguredOverrides): void {
if (!execOptions.env) execOptions.env = {};
for (const override of overrides) {
if (override.name === CompilerOverrideType.env && override.values) {
for (const env of override.values) {
execOptions.env[env.name] = env.value;
}
}
}
}
async doCompilation(inputFilename, dirPath, key, options, filters, backendOptions, libraries, tools) {
const inputFilenameSafe = this.filename(inputFilename);
const outputFilename = this.getOutputFilename(dirPath, this.outputFilebase, key);
const overrides = this.sanitizeCompilerOverrides(backendOptions.overrides || []);
options = _.compact(
this.prepareArguments(options, filters, backendOptions, inputFilename, outputFilename, libraries),
this.prepareArguments(
options,
filters,
backendOptions,
inputFilename,
outputFilename,
libraries,
overrides,
),
);
const execOptions = this.getDefaultExecOptions();
execOptions.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths([]);
this.applyOverridesToExecOptions(execOptions, overrides);
const makeAst = backendOptions.produceAst && this.compiler.supportsAstView;
const makePp = backendOptions.producePp && this.compiler.supportsPpView;
const makeGnatDebug = backendOptions.produceGnatDebug && this.compiler.supportsGnatDebugViews;
@@ -2008,7 +2175,7 @@ export class BaseCompiler implements ICompiler {
return stepResult;
}
createCmakeExecParams(execParams, dirPath, libsAndOptions) {
createCmakeExecParams(execParams: ExecutionOptions, dirPath: string, libsAndOptions, toolchainPath: string) {
const cmakeExecParams = Object.assign({}, execParams);
const libIncludes = this.getIncludeArguments(libsAndOptions.libraries);
@@ -2026,7 +2193,7 @@ export class BaseCompiler implements ICompiler {
cmakeExecParams.ldPath = [dirPath];
// todo: if we don't use nsjail, the path should not be /app but dirPath
const libPaths = this.getSharedLibraryPathsAsArguments(libsAndOptions.libraries, '/app');
const libPaths = this.getSharedLibraryPathsAsArguments(libsAndOptions.libraries, '/app', toolchainPath);
cmakeExecParams.env.LDFLAGS = libPaths.join(' ');
return cmakeExecParams;
@@ -2045,9 +2212,10 @@ export class BaseCompiler implements ICompiler {
return [];
}
getCMakeExtToolchainParam(): string {
if (this.toolchainPath) {
return `-DCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=${this.toolchainPath}`;
getCMakeExtToolchainParam(overrides: ConfiguredOverrides): string {
const toolchainPath = this.getDefaultOrOverridenToolchainPath(overrides);
if (toolchainPath) {
return `-DCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=${toolchainPath}`;
}
return '';
@@ -2087,6 +2255,8 @@ export class BaseCompiler implements ICompiler {
const libsAndOptions = this.createLibsAndOptions(key);
const toolchainPath = this.getDefaultOrOverridenToolchainPath(key.backendOptions.overrides || []);
const doExecute = key.filters.execute;
const executeParameters: ExecutableExecutionOptions = {
ldPath: this.getSharedLibraryPathsAsLdLibraryPaths(key.libraries),
@@ -2122,7 +2292,7 @@ export class BaseCompiler implements ICompiler {
await fs.mkdir(execParams.customCwd);
const makeExecParams = this.createCmakeExecParams(execParams, dirPath, libsAndOptions);
const makeExecParams = this.createCmakeExecParams(execParams, dirPath, libsAndOptions, toolchainPath);
fullResult = {
buildsteps: [],
@@ -2131,7 +2301,7 @@ export class BaseCompiler implements ICompiler {
fullResult.downloads = await this.setupBuildEnvironment(cacheKey, dirPath, true);
const toolchainparam = this.getCMakeExtToolchainParam();
const toolchainparam = this.getCMakeExtToolchainParam(key.backendOptions.overrides || []);
const cmakeArgs = utils.splitArguments(key.backendOptions.cmakeArgs);
const partArgs: string[] = [toolchainparam, ...this.getExtraCMakeArgs(key), ...cmakeArgs, '..'];
@@ -2722,7 +2892,9 @@ but nothing was dumped. Possible causes are:
protected getArgumentParser(): any {
const exe = this.compiler.exe.toLowerCase();
if (exe.includes('clang') || exe.includes('icpx') || exe.includes('icx')) {
if (exe.includes('icc')) {
return ICCParser;
} else if (exe.includes('clang') || exe.includes('icpx') || exe.includes('icx')) {
// check this first as "clang++" matches "g++"
return ClangParser;
} else if (exe.includes('g++') || exe.includes('gcc')) {
@@ -2756,6 +2928,87 @@ but nothing was dumped. Possible causes are:
this.supportedLibraries = this.getSupportedLibraries(this.compiler.libsArr, clientOptions.libs[this.lang.id]);
}
async getTargetsAsOverrideValues(): Promise<CompilerOverrideOption[]> {
if (!this.buildenvsetup || !this.buildenvsetup.getCompilerArch()) {
const parser = this.getArgumentParser();
const targets = await parser.getPossibleTargets(this);
return targets.map(target => {
return {
name: target,
value: target,
};
});
} else {
return [];
}
}
async getPossibleStdversAsOverrideValues(): Promise<CompilerOverrideOption[]> {
const parser = this.getArgumentParser();
return await parser.getPossibleStdvers(this);
}
async populatePossibleOverrides() {
const targets = await this.getTargetsAsOverrideValues();
if (targets.length > 0) {
this.compiler.possibleOverrides?.push({
name: CompilerOverrideType.arch,
display_title: 'Target architecture',
description: c_default_target_description,
flags: this.getTargetFlags(),
values: targets,
});
}
const compilerOptions = utils.splitArguments(this.compiler.options);
if (hasToolchainArg(compilerOptions)) {
const possibleToolchains: CompilerOverrideOptions = await this.getPossibleToolchains();
if (possibleToolchains.length > 0) {
const flag = getToolchainFlagFromOptions(compilerOptions);
this.compiler.possibleOverrides?.push({
name: CompilerOverrideType.toolchain,
display_title: 'Toolchain',
description: c_default_toolchain_description,
flags: [flag + '<value>'],
values: possibleToolchains,
});
}
}
const stdVersions = await this.getPossibleStdversAsOverrideValues();
if (stdVersions.length > 0) {
this.compiler.possibleOverrides?.push({
name: CompilerOverrideType.stdver,
display_title: 'Std version',
description: this.getStdVerOverrideDescription(),
flags: this.getStdverFlags(),
values: stdVersions,
});
}
}
getStdVerOverrideDescription(): string {
return 'Change the C/C++ standard version of the compiler.';
}
getStdverFlags(): string[] {
return ['-std=<value>'];
}
getTargetFlags(): string[] {
if (this.compiler.supportsMarch) return [`-march=${c_value_placeholder}`];
if (this.compiler.supportsTargetIs) return [`--target=${c_value_placeholder}`];
if (this.compiler.supportsTarget) return ['--target', c_value_placeholder];
return [];
}
async getPossibleToolchains(): Promise<CompilerOverrideOptions> {
return this.env.getPossibleToolchains();
}
async initialise(mtime: Date, clientOptions, isPrediscovered = false) {
this.mtime = mtime;
@@ -2812,6 +3065,9 @@ but nothing was dumped. Possible causes are:
return this;
} else {
const initResult = await this.getArgumentParser().parse(this);
await this.populatePossibleOverrides();
logger.info(`${compiler} ${version} is ready`);
return initResult;
}

View File

@@ -38,6 +38,7 @@ import {logger} from './logger.js';
import {CompilerProps} from './properties.js';
import type {PropertyGetter} from './properties.interfaces.js';
import {unwrap} from './assert.js';
import {CompilerOverrideOptions} from '../types/compilation/compiler-overrides.interfaces.js';
export class CompilationEnvironment {
ceProps: PropertyGetter;
@@ -52,6 +53,7 @@ export class CompilationEnvironment {
multiarch: string | null;
baseEnv: Record<string, string | undefined>;
formatHandler: FormattingHandler;
possibleToolchains?: CompilerOverrideOptions;
constructor(compilerProps, compilationQueue, doCache) {
this.ceProps = compilerProps.ceProps;
@@ -109,6 +111,14 @@ export class CompilationEnvironment {
return env;
}
setPossibleToolchains(toolchains: CompilerOverrideOptions) {
this.possibleToolchains = toolchains;
}
getPossibleToolchains(): CompilerOverrideOptions {
return this.possibleToolchains || [];
}
async cacheGet(object: CacheableValue) {
const result = await this.cache.get(BaseCache.hash(object));
if (this.cache.gets % this.reportCacheEvery === 0) {

View File

@@ -42,6 +42,7 @@ import {ClientOptionsHandler, OptionHandlerArguments} from './options-handler.js
import {CompilerProps} from './properties.js';
import type {PropertyGetter} from './properties.interfaces.js';
import {basic_comparator, remove} from './common-utils.js';
import {getPossibleGccToolchainsFromCompilerInfo} from './toolchain-utils.js';
const sleep = promisify(setTimeout);
@@ -332,6 +333,7 @@ export class CompilerFinder {
name: props<string>('licenseName'),
preamble: props<string>('licensePreamble'),
},
possibleOverrides: [],
};
if (props('demanglerClassFile') !== undefined) {
@@ -463,6 +465,10 @@ export class CompilerFinder {
async find() {
const compilerList = await this.getCompilers();
const toolchains = await getPossibleGccToolchainsFromCompilerInfo(compilerList);
this.compileHandler.setPossibleToolchains(toolchains);
const compilers = await this.compileHandler.setCompilers(compilerList, this.optionsHandler.get());
if (!compilers) {
logger.error('#### No compilers found: no compilation will be done!');

View File

@@ -29,6 +29,7 @@ import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import {BaseCompiler} from '../base-compiler.js';
import * as utils from '../utils.js';
import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js';
export class AdaCompiler extends BaseCompiler {
static get key() {
@@ -76,6 +77,7 @@ export class AdaCompiler extends BaseCompiler {
inputFilename: string,
outputFilename: string,
libraries,
overrides: ConfiguredOverrides,
) {
backendOptions = backendOptions || {};

File diff suppressed because it is too large Load Diff

View File

@@ -143,6 +143,8 @@ export class AssemblyCompiler extends BaseCompiler {
buildFilters.binary = true;
buildFilters.execute = false;
const overrides = this.sanitizeCompilerOverrides(key.backendOptions.overrides);
const compilerArguments = _.compact(
this.prepareArguments(
key.options,
@@ -151,6 +153,7 @@ export class AssemblyCompiler extends BaseCompiler {
inputFilename,
outputFilename,
key.libraries,
overrides,
),
);

View File

@@ -25,13 +25,17 @@
import path from 'path';
import {BaseCompiler} from '../base-compiler.js';
import {ConanBuildProperties} from '../buildenvsetup/ceconan.js';
import {CircleParser} from './argument-parsers.js';
export class CircleCompiler extends BaseCompiler {
static get key() {
return 'circle';
}
protected override getArgumentParser() {
return CircleParser;
}
override optionsForFilter(filters, outputFilename) {
let options = [`-o=${this.filename(outputFilename)}`];
if (this.compiler.intelAsm && filters.intel && !filters.binary) {

View File

@@ -36,6 +36,7 @@ import {AmdgpuAsmParser} from '../parsers/asm-parser-amdgpu.js';
import {SassAsmParser} from '../parsers/asm-parser-sass.js';
import * as utils from '../utils.js';
import {ArtifactType} from '../../types/tool.interfaces.js';
import {ClangParser} from './argument-parsers.js';
const offloadRegexp = /^#\s+__CLANG_OFFLOAD_BUNDLE__(__START__|__END__)\s+(.*)$/gm;
@@ -70,6 +71,10 @@ export class ClangCompiler extends BaseCompiler {
}
}
protected override getArgumentParser(): any {
return ClangParser;
}
async addTimeTraceToResult(result: CompilationResult, dirPath: string, outputFilename: string) {
let timeTraceJson = '';
const outputExt = path.extname(outputFilename);

View File

@@ -31,6 +31,7 @@ import {DartAsmParser} from '../parsers/asm-parser-dart.js';
import * as utils from '../utils.js';
import {BaseParser} from './argument-parsers.js';
import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js';
export class DartCompiler extends BaseCompiler {
constructor(info: PreliminaryCompilerInfo, env) {
@@ -49,6 +50,7 @@ export class DartCompiler extends BaseCompiler {
inputFilename: string,
outputFilename: string,
libraries,
overrides: ConfiguredOverrides,
) {
let options = this.optionsForFilter(filters, outputFilename, userOptions);

View File

@@ -27,12 +27,17 @@ import path from 'path';
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import {FortranCompiler} from './fortran.js';
import {FlangParser} from './argument-parsers.js';
export class FlangCompiler extends FortranCompiler {
static override get key() {
return 'flang';
}
protected override getArgumentParser(): any {
return FlangParser;
}
override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string) {
let options = ['-o', this.filename(outputFilename)];
if (this.compiler.intelAsm && filters.intel && !filters.binary) {

View File

@@ -27,12 +27,21 @@ import path from 'path';
import type {CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces.js';
import {BaseCompiler} from '../base-compiler.js';
import * as utils from '../utils.js';
import {GccFortranParser} from './argument-parsers.js';
export class FortranCompiler extends BaseCompiler {
static get key() {
return 'fortran';
}
protected override getArgumentParser(): any {
return GccFortranParser;
}
override getStdVerOverrideDescription(): string {
return 'Change the Fortran standard version of the compiler.';
}
override async runCompiler(
compiler: string,
options: string[],

View File

@@ -31,7 +31,7 @@ import {unwrap} from '../assert.js';
import {BaseCompiler} from '../base-compiler.js';
import * as utils from '../utils.js';
import {ClangParser} from './argument-parsers.js';
import {GolangParser} from './argument-parsers.js';
// Each arch has a list of jump instructions in
// Go source src/cmd/asm/internal/arch.
@@ -268,7 +268,7 @@ export class GolangCompiler extends BaseCompiler {
return options;
}
override getArgumentParser() {
return ClangParser;
override getArgumentParser(): any {
return GolangParser;
}
}

View File

@@ -28,7 +28,7 @@ import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import {BaseCompiler} from '../base-compiler.js';
import {ClangParser} from './argument-parsers.js';
import {GHCParser} from './argument-parsers.js';
export class HaskellCompiler extends BaseCompiler {
static get key() {
@@ -76,7 +76,7 @@ export class HaskellCompiler extends BaseCompiler {
return [libPathFlag + '.', ...this.getSharedLibraryPaths(libraries).map(path => libPathFlag + path)];
}
override getArgumentParser() {
return ClangParser;
override getArgumentParser(): any {
return GHCParser;
}
}

View File

@@ -22,6 +22,7 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js';
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import {AssemblyCompiler} from './assembly.js';
@@ -38,6 +39,7 @@ export class NasmCompiler extends AssemblyCompiler {
inputFilename: string,
outputFilename: string,
libraries,
overrides: ConfiguredOverrides,
) {
let options = super.prepareArguments(
userOptions,
@@ -46,6 +48,7 @@ export class NasmCompiler extends AssemblyCompiler {
inputFilename,
outputFilename,
libraries,
overrides,
);
let fmode;

View File

@@ -35,6 +35,8 @@ import type {BuildEnvDownloadInfo} from '../buildenvsetup/buildenv.interfaces.js
import {parseRustOutput} from '../utils.js';
import {RustParser} from './argument-parsers.js';
import {CompilerOverrideType} from '../../types/compilation/compiler-overrides.interfaces.js';
import {SemVer} from 'semver';
export class RustCompiler extends BaseCompiler {
linker: string;
@@ -50,7 +52,7 @@ export class RustCompiler extends BaseCompiler {
this.compiler.supportsLLVMOptPipelineView = true;
this.compiler.supportsRustMirView = true;
const isNightly = info.name === 'nightly' || info.semver === 'nightly';
const isNightly = this.isNightly();
// Macro expansion (-Zunpretty=expanded) and HIR (-Zunpretty=hir-tree)
// are only available for Nightly
this.compiler.supportsRustMacroExpView = isNightly;
@@ -63,6 +65,46 @@ export class RustCompiler extends BaseCompiler {
this.linker = this.compilerProps<string>('linker');
}
private isNightly() {
return (
this.compiler.name === 'nightly' ||
this.compiler.semver === 'nightly' ||
this.compiler.semver === 'beta' ||
this.compiler.semver.includes('master') ||
this.compiler.semver.includes('trunk')
);
}
override async populatePossibleOverrides() {
const possibleEditions = await RustParser.getPossibleEditions(this);
if (possibleEditions.length > 0) {
let defaultEdition: undefined | string = undefined;
if (!this.compiler.semver || this.isNightly()) {
defaultEdition = '2021';
} else {
const compilerVersion = new SemVer(this.compiler.semver);
if (compilerVersion.compare('1.56.0') >= 0) {
defaultEdition = '2021';
}
}
this.compiler.possibleOverrides?.push({
name: CompilerOverrideType.edition,
display_title: 'Edition',
description:
'The default edition for Rust compilers is usually 2015. ' +
'Some editions might not be available for older compilers.',
flags: ['--edition', '<value>'],
values: possibleEditions.map(ed => {
return {name: ed, value: ed};
}),
default: defaultEdition,
});
}
await super.populatePossibleOverrides();
}
override getSharedLibraryPathsAsArguments(libraries, libDownloadPath) {
return [];
}

View File

@@ -34,6 +34,7 @@ import {logger} from '../logger.js';
import {SPIRVAsmParser} from '../parsers/asm-parser-spirv.js';
import * as utils from '../utils.js';
import {unwrap} from '../assert.js';
import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js';
export class SPIRVCompiler extends BaseCompiler {
protected translatorPath: string;
@@ -59,6 +60,7 @@ export class SPIRVCompiler extends BaseCompiler {
inputFilename: string,
outputFilename: string,
libraries,
overrides: ConfiguredOverrides,
) {
let options = this.optionsForFilter(filters, outputFilename);
backendOptions = backendOptions || {};

View File

@@ -24,7 +24,7 @@
import {BaseCompiler} from '../base-compiler.js';
import {ClangParser} from './argument-parsers.js';
import {SwiftParser} from './argument-parsers.js';
export class SwiftCompiler extends BaseCompiler {
static get key() {
@@ -36,7 +36,7 @@ export class SwiftCompiler extends BaseCompiler {
}
override getArgumentParser() {
return ClangParser;
return SwiftParser;
}
override isCfgCompiler(/*compilerVersion*/) {

View File

@@ -23,6 +23,7 @@
// POSSIBILITY OF SUCH DAMAGE.
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import {TendraParser} from './argument-parsers.js';
import {GCCCompiler} from './gcc.js';
@@ -36,4 +37,8 @@ export class TenDRACompiler extends GCCCompiler {
if (!filters.binary) options = options.concat('-S');
return options;
}
protected override getArgumentParser(): any {
return TendraParser;
}
}

View File

@@ -36,6 +36,7 @@ import {AsmParser} from '../parsers/asm-parser.js';
import {PELabelReconstructor} from '../pe32-support.js';
import * as utils from '../utils.js';
import {unwrap} from '../assert.js';
import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js';
export class Win32Compiler extends BaseCompiler {
static get key() {
@@ -50,6 +51,10 @@ export class Win32Compiler extends BaseCompiler {
this.binaryAsmParser = new AsmParser(this.compilerProps);
}
override getStdverFlags(): string[] {
return ['/std:<value>'];
}
override newTempDir() {
return new Promise<string>((resolve, reject) => {
temp.mkdir({prefix: 'compiler-explorer-compiler', dir: process.env.TMP}, (err, dirPath) => {
@@ -98,6 +103,7 @@ export class Win32Compiler extends BaseCompiler {
inputFilename: string,
outputFilename: string,
libraries,
overrides: ConfiguredOverrides,
) {
let options = this.optionsForFilter(filters, outputFilename, userOptions);
backendOptions = backendOptions || {};

View File

@@ -28,10 +28,11 @@ import type {ExecutionOptions} from '../../types/compilation/compilation.interfa
import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import {ArtifactType} from '../../types/tool.interfaces.js';
import {BaseCompiler} from '../base-compiler.js';
import {BaseCompiler, c_default_target_description} from '../base-compiler.js';
import {logger} from '../logger.js';
import {AsmParserZ88dk} from '../parsers/asm-parser-z88dk.js';
import * as utils from '../utils.js';
import {Z88dkParser} from './argument-parsers.js';
export class z88dkCompiler extends BaseCompiler {
static get key() {
@@ -44,6 +45,14 @@ export class z88dkCompiler extends BaseCompiler {
this.asm = new AsmParserZ88dk(this.compilerProps);
}
protected override getArgumentParser() {
return Z88dkParser;
}
override getTargetFlags(): string[] {
return ['+<value>'];
}
public override getOutputFilename(dirPath: string, outputFilebase: string, key?: any): string {
let filename;
if (key && key.backendOptions && key.backendOptions.customOutputFilename) {
@@ -71,8 +80,17 @@ export class z88dkCompiler extends BaseCompiler {
userOptions: string[],
staticLibLinks: string[],
) {
return userOptions.concat(
options,
let targetOpt = options.filter(opt => opt.startsWith('+'));
const withoutTarget = options.filter(opt => !opt.startsWith('+'));
const withoutTargetUser = userOptions.filter(opt => !opt.startsWith('+'));
if (targetOpt.length === 0) {
targetOpt = userOptions.filter(opt => opt.startsWith('+'));
}
return targetOpt.concat(
withoutTargetUser,
withoutTarget,
[this.filename(inputFilename)],
libIncludes,
libOptions,

View File

@@ -26,6 +26,7 @@ import path from 'path';
import Semver from 'semver';
import _ from 'underscore';
import fs from 'fs-extra';
import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';

View File

@@ -28,6 +28,7 @@ import type {CompilerOutputOptions, ParseFiltersAndOutputOptions} from '../../ty
import {asSafeVer} from '../utils.js';
import {ClangCompiler} from './clang.js';
import {ZigCxxParser} from './argument-parsers.js';
export class ZigCXX extends ClangCompiler {
private readonly needsForcedBinary: boolean;
@@ -43,6 +44,10 @@ export class ZigCXX extends ClangCompiler {
Semver.lt(asSafeVer(this.compiler.semver), '0.9.0', true);
}
protected override getArgumentParser(): any {
return ZigCxxParser;
}
override preProcess(source: string, filters: CompilerOutputOptions): string {
if (this.needsForcedBinary) {
// note: zig versions > 0.6 don't emit asm, only binary works - https://github.com/ziglang/zig/issues/8153

View File

@@ -49,6 +49,7 @@ import {
ExecutionRequestParams,
} from './compile.interfaces.js';
import {remove} from '../common-utils.js';
import {CompilerOverrideOptions} from '../../types/compilation/compiler-overrides.interfaces.js';
temp.track();
@@ -266,6 +267,10 @@ export class CompileHandler {
}
}
setPossibleToolchains(toolchains: CompilerOverrideOptions) {
this.compilerEnv.setPossibleToolchains(toolchains);
}
compilerAliasMatch(compiler, compilerId): boolean {
return compiler.compiler.alias && compiler.compiler.alias.includes(compilerId);
}

View File

@@ -23,15 +23,21 @@
// POSSIBILITY OF SUCH DAMAGE.
import path from 'path';
import fs from 'fs-extra';
import {splitArguments} from './utils.js';
import {PreliminaryCompilerInfo} from '../types/compiler.interfaces.js';
import {CompilerOverrideOptions} from '../types/compilation/compiler-overrides.interfaces.js';
export function getToolchainPath(compilerExe: string | null, compilerOptions?: string): string | false {
const options = compilerOptions ? splitArguments(compilerOptions) : [];
const existingChain = options.find(elem => elem.includes('--gcc-toolchain='));
export const clang_style_toolchain_flag = '--gcc-toolchain=';
export const icc_style_toolchain_flag = '--gxx-name=';
export const clang_style_sysroot_flag = '--sysroot=';
export function getToolchainPathWithOptionsArr(compilerExe: string | null, options: string[]): string | false {
const existingChain = options.find(elem => elem.includes(clang_style_toolchain_flag));
if (existingChain) return existingChain.substring(16);
const gxxname = options.find(elem => elem.includes('--gxx-name='));
const gxxname = options.find(elem => elem.includes(icc_style_toolchain_flag));
if (gxxname) {
return path.resolve(path.dirname(gxxname.substring(11)), '..');
} else if (typeof compilerExe === 'string' && compilerExe.includes('/g++')) {
@@ -41,6 +47,111 @@ export function getToolchainPath(compilerExe: string | null, compilerOptions?: s
}
}
export function removeToolchainArg(compilerOptions: string[]): string[] {
return compilerOptions.filter(elem => !elem.includes('--gcc-toolchain=') && !elem.includes('--gxx-name='));
export function getToolchainPath(compilerExe: string | null, compilerOptions?: string): string | false {
const options = compilerOptions ? splitArguments(compilerOptions) : [];
return getToolchainPathWithOptionsArr(compilerExe, options);
}
export function removeToolchainArg(compilerOptions: string[]): string[] {
return compilerOptions.filter(
elem => !elem.includes(clang_style_toolchain_flag) && !elem.includes(icc_style_toolchain_flag),
);
}
export function removeSysrootArg(compilerOptions: string[]): string[] {
return compilerOptions.filter(elem => !elem.includes(clang_style_sysroot_flag));
}
export function replaceToolchainArg(compilerOptions: string[], newPath: string): string[] {
return compilerOptions.map(elem => {
if (elem.includes(clang_style_toolchain_flag)) {
return clang_style_toolchain_flag + path.normalize(newPath);
} else if (elem.includes(icc_style_toolchain_flag)) {
return icc_style_toolchain_flag + path.normalize(newPath);
}
return elem;
});
}
export function replaceSysrootArg(compilerOptions: string[], newPath: string): string[] {
return compilerOptions.map(elem => {
if (elem.includes(clang_style_sysroot_flag)) {
return clang_style_sysroot_flag + path.normalize(newPath);
}
return elem;
});
}
export function getToolchainFlagFromOptions(options: string[]): string | false {
for (const elem of options) {
if (elem.includes(clang_style_toolchain_flag)) return clang_style_toolchain_flag;
if (elem.includes(icc_style_toolchain_flag)) return icc_style_toolchain_flag;
}
return false;
}
export function hasToolchainArg(options: string[]): boolean {
return !!getToolchainFlagFromOptions(options);
}
export function getSysrootFlagFromOptions(options: string[]): string | false {
for (const elem of options) {
if (elem.includes(clang_style_sysroot_flag)) return clang_style_sysroot_flag;
}
return false;
}
export function hasSysrootArg(options: string[]): boolean {
return !!getSysrootFlagFromOptions(options);
}
export async function getPossibleGccToolchainsFromCompilerInfo(
compilers: PreliminaryCompilerInfo[],
): Promise<CompilerOverrideOptions> {
const overrideOptions: CompilerOverrideOptions = [];
for (const compiler of compilers) {
if (
compiler.compilerCategories?.includes('gcc') &&
!compiler.compilerCategories?.includes('mingw') &&
!compiler.hidden &&
compiler.exe &&
path.isAbsolute(compiler.exe)
) {
try {
await fs.stat(compiler.exe);
} catch {
continue;
}
const toolchainPath = path.resolve(path.dirname(compiler.exe), '..');
if (!overrideOptions.find(opt => opt.value === toolchainPath)) {
overrideOptions.push({
name: compiler.name,
value: toolchainPath,
});
}
}
}
return overrideOptions;
}
export function getSpecificTargetBasedOnToolchainPath(target: string, toolchainPath: string) {
const lastPathBit = path.basename(toolchainPath);
if (lastPathBit.startsWith(target)) {
return lastPathBit;
}
return target;
}
export function getSysrootByToolchainPath(toolchainPath: string): string | undefined {
const lastPathBit = path.basename(toolchainPath);
const possibleSysrootPath = path.join(toolchainPath, lastPathBit, 'sysroot');
if (fs.existsSync(possibleSysrootPath)) {
return possibleSysrootPath;
}
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2023, 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 type {ConfiguredOverrides} from './compilation/compiler-overrides.interfaces.js';
import type {CompilerState} from './panes/compiler.interfaces.js';
import type {ExecutorState} from './panes/executor.interfaces.js';
export interface ICompilerShared {
updateState(state: CompilerState | ExecutorState);
getOverrides(): ConfiguredOverrides | undefined;
}

68
static/compiler-shared.ts Normal file
View File

@@ -0,0 +1,68 @@
// Copyright (c) 2023, 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 $ from 'jquery';
import type {ICompilerShared} from './compiler-shared.interfaces.js';
import {CompilerOverridesWidget} from './widgets/compiler-overrides.js';
import type {CompilerState} from './panes/compiler.interfaces.js';
import type {ConfiguredOverrides} from './compilation/compiler-overrides.interfaces.js';
import type {ExecutorState} from './panes/executor.interfaces.js';
export class CompilerShared implements ICompilerShared {
private domRoot: JQuery<HTMLElement>;
private overridesButton: JQuery<HTMLElement>;
private overridesWidget: CompilerOverridesWidget;
constructor(domRoot: JQuery, onChange: () => void) {
this.domRoot = domRoot;
this.initButtons(onChange);
this.initCallbacks();
}
public getOverrides(): ConfiguredOverrides | undefined {
return this.overridesWidget.get();
}
public updateState(state: CompilerState | ExecutorState) {
this.overridesWidget.setCompiler(state.compiler, state.lang);
if (state.overrides) {
this.overridesWidget.set(state.overrides);
} else {
this.overridesWidget.setDefaults();
}
}
private initButtons(onChange: () => void) {
this.overridesButton = this.domRoot.find('.btn.show-overrides');
this.overridesWidget = new CompilerOverridesWidget(this.domRoot, this.overridesButton, onChange);
}
private initCallbacks() {
this.overridesButton.on('click', () => {
this.overridesWidget.show();
});
}
}

View File

@@ -26,6 +26,8 @@ import {CompilerOutputOptions} from '../types/features/filters.interfaces.js';
import {CfgState} from './panes/cfg-view.interfaces.js';
import {LLVMOptPipelineViewState} from './panes/llvm-opt-pipeline.interfaces.js';
import {GccDumpViewState} from './panes/gccdump-view.interfaces.js';
import {PossibleArguments} from './compiler-arguments.interfaces.js';
import {ConfiguredOverrides} from './compilation/compiler-overrides.interfaces.js';
export const COMPILER_COMPONENT_NAME = 'compiler';
export const EXECUTOR_COMPONENT_NAME = 'executor';
export const EDITOR_COMPONENT_NAME = 'codeEditor';
@@ -90,6 +92,7 @@ export type PopulatedExecutorState = StateWithLanguage &
options: unknown;
compilationPanelShown: boolean;
compilerOutShown: boolean;
overrides?: ConfiguredOverrides;
};
export type ExecutorForTreeState = StateWithLanguage &
StateWithTree & {

View File

@@ -104,6 +104,7 @@ import {
EmptyLLVMOptPipelineViewState,
PopulatedLLVMOptPipelineViewState,
} from './components.interfaces.js';
import {ConfiguredOverrides} from './compilation/compiler-overrides.interfaces.js';
/** Get an empty compiler component. */
export function getCompiler(editorId: number, lang: string): ComponentConfig<EmptyCompilerState> {
@@ -181,6 +182,7 @@ export function getExecutorWith(
libraries: unknown,
compilerArgs,
treeId: number,
overrides?: ConfiguredOverrides,
): ComponentConfig<PopulatedExecutorState> {
return {
type: 'component',
@@ -194,6 +196,7 @@ export function getExecutorWith(
lang,
compilationPanelShown: true,
compilerOutShown: true,
overrides: overrides,
},
};
}

View File

@@ -0,0 +1,97 @@
// Copyright (c) 2023, 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 type {LLVMOptPipelineBackendOptions} from '../../types/compilation/llvm-opt-pipeline-output.interfaces.js';
import type {PPOptions} from './pp-view.interfaces.js';
import type {GccDumpViewSelectedPass} from './gccdump-view.interfaces.js';
import type {FiledataPair} from '../../types/compilation/compilation.interfaces.js';
import type {ConfiguredOverrides} from '../compilation/compiler-overrides.interfaces.js';
export type ActiveTools = {
id: number;
args: string[];
stdin: string;
};
export type CompilationRequestOptions = {
userArguments: string;
compilerOptions: {
executorRequest?: boolean;
skipAsm?: boolean;
producePp?: PPOptions | null;
produceAst?: boolean;
produceGccDump?: {
opened: boolean;
pass?: GccDumpViewSelectedPass;
treeDump?: boolean;
rtlDump?: boolean;
ipaDump?: boolean;
dumpFlags: any;
};
produceOptInfo?: boolean;
produceCfg?: boolean;
produceGnatDebugTree?: boolean;
produceGnatDebug?: boolean;
produceIr?: boolean;
produceLLVMOptPipeline?: LLVMOptPipelineBackendOptions | null;
produceDevice?: boolean;
produceRustMir?: boolean;
produceRustMacroExp?: boolean;
produceRustHir?: boolean;
produceHaskellCore?: boolean;
produceHaskellStg?: boolean;
produceHaskellCmm?: boolean;
cmakeArgs?: string;
customOutputFilename?: string;
overrides?: ConfiguredOverrides;
};
executeParameters: {
args: string;
stdin: string;
};
filters: Record<string, boolean>;
tools: ActiveTools[];
libraries: CompileChildLibraries[];
};
export type CompilationRequest = {
source: string;
compiler: string;
options: CompilationRequestOptions;
lang: string | null;
files: FiledataPair[];
bypassCache?: boolean;
};
export type LangInfo = {
compiler: string;
options: string;
execArgs: string;
execStdin: string;
};
export type CompileChildLibraries = {
id: string;
version: string;
};

View File

@@ -22,7 +22,9 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import type {ConfiguredOverrides} from '../compilation/compiler-overrides.interfaces.js';
import {WidgetState} from '../widgets/libs-widget.interfaces.js';
import {MonacoPaneState} from './pane.interfaces.js';
export type CompilerState = WidgetState & {
tree?: number;
@@ -33,4 +35,10 @@ export type CompilerState = WidgetState & {
deviceViewOpen?: boolean;
wantOptInfo?: boolean;
lang?: string;
overrides?: ConfiguredOverrides;
};
export type CompilerCurrentState = CompilerState &
MonacoPaneState & {
filters: Record<string, boolean>;
};

View File

@@ -48,7 +48,7 @@ import {CompilerInfo} from '../../types/compiler.interfaces.js';
import {MonacoPaneState} from './pane.interfaces.js';
import {Hub} from '../hub.js';
import {Container} from 'golden-layout';
import {CompilerState} from './compiler.interfaces.js';
import {CompilerCurrentState, CompilerState} from './compiler.interfaces.js';
import {ComponentConfig, ToolViewState} from '../components.interfaces.js';
import {LanguageLibs} from '../options.interfaces.js';
import {GccDumpFiltersState, GccDumpViewSelectedPass} from './gccdump-view.interfaces.js';
@@ -69,6 +69,9 @@ import {CompilerOutputOptions} from '../../types/features/filters.interfaces.js'
import {AssemblyDocumentationInstructionSet} from '../../types/features/assembly-documentation.interfaces.js';
import {SourceAndFiles} from '../download-service.js';
import fileSaver = require('file-saver');
import {ICompilerShared} from '../compiler-shared.interfaces.js';
import {CompilerShared} from '../compiler-shared.js';
import type {ActiveTools, CompilationRequest, CompilationRequestOptions} from './compiler-request.interfaces.js';
const toolIcons = require.context('../../views/resources/logos', false, /\.(png|svg)$/);
@@ -100,17 +103,6 @@ function patchOldFilters(filters) {
const languages = options.languages;
type CompilerCurrentState = CompilerState &
MonacoPaneState & {
filters: Record<string, boolean>;
};
type ActiveTools = {
id: number;
args: string[];
stdin: string;
};
type NewToolSettings = {
toolId: number;
args: string[];
@@ -129,52 +121,6 @@ type LinkedCode = {
type Decorations = Record<string, monaco.editor.IModelDeltaDecoration[]>;
type CompileRequestOptions = {
userArguments: string;
compilerOptions: {
producePp: PPOptions | null;
produceAst: boolean;
produceGccDump: {
opened: boolean;
pass?: GccDumpViewSelectedPass;
treeDump?: boolean;
rtlDump?: boolean;
ipaDump?: boolean;
dumpFlags: any;
};
produceOptInfo: boolean;
produceCfg: boolean;
produceGnatDebugTree: boolean;
produceGnatDebug: boolean;
produceIr: boolean;
produceLLVMOptPipeline: LLVMOptPipelineBackendOptions | null;
produceDevice: boolean;
produceRustMir: boolean;
produceRustMacroExp: boolean;
produceRustHir: boolean;
produceHaskellCore: boolean;
produceHaskellStg: boolean;
produceHaskellCmm: boolean;
cmakeArgs?: string;
customOutputFilename?: string;
};
filters: Record<string, boolean>;
tools: ActiveTools[];
libraries: {
id: string;
version: string;
}[];
};
type CompileRequest = {
source: string;
compiler: string;
options: CompileRequestOptions;
lang: string | null;
files: FiledataPair[];
bypassCache: boolean;
};
type Assembly = {
labels?: any[];
source?: {
@@ -222,8 +168,8 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
private lastTimeTaken: number;
private pendingRequestSentAt: number;
private pendingCMakeRequestSentAt: number;
private nextRequest: CompileRequest | null;
private nextCMakeRequest: CompileRequest | null;
private nextRequest: CompilationRequest | null;
private nextCMakeRequest: CompilationRequest | null;
private flagsViewOpen: boolean;
private optViewOpen: boolean;
private cfgViewOpen: boolean;
@@ -327,6 +273,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
private mouseMoveThrottledFunction?: ((e: monaco.editor.IEditorMouseEvent) => void) & _.Cancelable;
private cursorSelectionThrottledFunction?: ((e: monaco.editor.ICursorSelectionChangedEvent) => void) & _.Cancelable;
private mouseUpThrottledFunction?: ((e: monaco.editor.IEditorMouseEvent) => void) & _.Cancelable;
private compilerShared: ICompilerShared;
// eslint-disable-next-line max-statements
constructor(hub: Hub, container: Container, state: MonacoPaneState & CompilerState) {
@@ -379,6 +326,8 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
this.onCompilerChange.bind(this),
);
this.initLibraries(state);
this.compilerShared = new CompilerShared(this.domRoot, this.onCompilerOverridesChange.bind(this));
this.compilerShared.updateState(state);
// MonacoPane's registerCallbacks is not called late enough either
this.initCallbacks();
// Handle initial settings
@@ -700,6 +649,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
libs,
currentState.options,
treeId ?? 0,
currentState.overrides,
);
};
@@ -753,7 +703,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
}
this.container.layoutManager
.createDragSource(this.ppButton, createPpView())
.createDragSource(this.ppButton, createPpView as any)
// @ts-ignore
._dragListener.on('dragStart', togglePannerAdder);
@@ -1225,7 +1175,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
this.needsCompile = false;
this.compileInfoLabel.text(' - Compiling...');
const options: CompileRequestOptions = {
const options: CompilationRequestOptions = {
userArguments: this.options,
compilerOptions: {
producePp: this.ppViewOpen ? this.ppOptions : null,
@@ -1251,6 +1201,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
produceHaskellCore: this.haskellCoreViewOpen,
produceHaskellStg: this.haskellStgViewOpen,
produceHaskellCmm: this.haskellCmmViewOpen,
overrides: this.getCurrentState().overrides,
},
filters: this.getEffectiveFilters(),
tools: this.getActiveTools(newTools),
@@ -1259,6 +1210,10 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
id: item.libId,
version: item.versionId,
})) ?? [],
executeParameters: {
args: '',
stdin: '',
},
};
if (this.sourceTreeId) {
@@ -1268,7 +1223,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
}
}
compileFromTree(options: CompileRequestOptions, bypassCache: boolean): void {
compileFromTree(options: CompilationRequestOptions, bypassCache: boolean): void {
const tree = this.hub.getTreeById(this.sourceTreeId ?? 0);
if (!tree) {
this.sourceTreeId = null;
@@ -1276,7 +1231,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
return;
}
const request: CompileRequest = {
const request: CompilationRequest = {
source: tree.multifileService.getMainSource(),
compiler: this.compiler ? this.compiler.id : '',
options: options,
@@ -1326,9 +1281,9 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
});
}
compileFromEditorSource(options: CompileRequestOptions, bypassCache: boolean) {
compileFromEditorSource(options: CompilationRequestOptions, bypassCache: boolean) {
this.compilerService.expandToFiles(this.source).then((sourceAndFiles: SourceAndFiles) => {
const request: CompileRequest = {
const request: CompilationRequest = {
source: sourceAndFiles.source || '',
compiler: this.compiler ? this.compiler.id : '',
options: options,
@@ -1345,7 +1300,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
});
}
sendCMakeCompile(request: CompileRequest) {
sendCMakeCompile(request: CompilationRequest) {
if (this.pendingCMakeRequestSentAt) {
// If we have a request pending, then just store this request to do once the
// previous request completes.
@@ -1380,7 +1335,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
});
}
sendCompile(request: CompileRequest) {
sendCompile(request: CompilationRequest) {
const onCompilerResponse = this.onCompileResponse.bind(this);
if (this.pendingRequestSentAt) {
@@ -2008,6 +1963,14 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
}
}
onCompilerOverridesChange(): void {
this.updateState();
if (this.settings.compileOnChange) {
this.compile();
}
}
onToolSettingsChange(id: number): void {
if (this.id === id) {
this.compile();
@@ -3090,6 +3053,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
selection: this.selection,
flagsViewOpen: this.flagsViewOpen,
deviceViewOpen: this.deviceViewOpen,
overrides: this.compilerShared.getOverrides(),
};
this.paneRenaming.addState(state);
this.fontScale.addState(state);
@@ -3650,6 +3614,12 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
}
}
override updateState() {
const state = this.getCurrentState();
this.container.setState(state);
this.compilerShared.updateState(state);
}
override getPaneTag() {
const editorId = this.sourceEditorId;
const treeId = this.sourceTreeId;

View File

@@ -22,7 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import {WidgetState} from '../widgets/libs-widget.interfaces.js';
import type {ConfiguredOverrides} from '../compilation/compiler-overrides.interfaces.js';
import type {WidgetState} from '../widgets/libs-widget.interfaces.js';
export type ExecutorState = WidgetState & {
tree?: number;
@@ -38,4 +39,5 @@ export type ExecutorState = WidgetState & {
wrap?: boolean;
lang?: string;
compiler: string;
overrides?: ConfiguredOverrides;
};

View File

@@ -45,14 +45,15 @@ import {ExecutorState} from './executor.interfaces.js';
import {CompilerInfo} from '../../types/compiler.interfaces.js';
import {Language} from '../../types/languages.interfaces.js';
import {LanguageLibs} from '../options.interfaces.js';
import {LLVMOptPipelineBackendOptions} from '../../types/compilation/llvm-opt-pipeline-output.interfaces.js';
import {PPOptions} from './pp-view.interfaces.js';
import {FiledataPair, CompilationResult} from '../../types/compilation/compilation.interfaces.js';
import {CompilationResult, FiledataPair} from '../../types/compilation/compilation.interfaces.js';
import {ResultLine} from '../../types/resultline/resultline.interfaces.js';
import {CompilationStatus as CompilerServiceCompilationStatus} from '../compiler-service.interfaces.js';
import {CompilerPicker} from '../widgets/compiler-picker.js';
import {GccDumpViewSelectedPass} from './gccdump-view.interfaces.js';
import {SourceAndFiles} from '../download-service.js';
import {ICompilerShared} from '../compiler-shared.interfaces.js';
import {CompilerShared} from '../compiler-shared.js';
import type {ConfiguredOverrides} from '../compilation/compiler-overrides.interfaces.js';
import {CompilationRequest, CompilationRequestOptions, LangInfo} from './compiler-request.interfaces.js';
const languages = options.languages;
@@ -69,73 +70,6 @@ function makeAnsiToHtml(color?: string): AnsiToHtml {
});
}
type ActiveTools = {
id: number;
args: string[];
stdin: string;
};
type CompilationRequestOptions = {
userArguments: string;
compilerOptions: {
executorRequest?: boolean;
skipAsm?: boolean;
producePp?: PPOptions | null;
produceAst?: boolean;
produceGccDump?: {
opened: boolean;
pass?: GccDumpViewSelectedPass;
treeDump?: boolean;
rtlDump?: boolean;
ipaDump?: boolean;
dumpFlags: any;
};
produceOptInfo?: boolean;
produceCfg?: boolean;
produceGnatDebugTree?: boolean;
produceGnatDebug?: boolean;
produceIr?: boolean;
produceLLVMOptPipeline?: LLVMOptPipelineBackendOptions | null;
produceDevice?: boolean;
produceRustMir?: boolean;
produceRustMacroExp?: boolean;
produceRustHir?: boolean;
produceHaskellCore?: boolean;
produceHaskellStg?: boolean;
produceHaskellCmm?: boolean;
cmakeArgs?: string;
customOutputFilename?: string;
};
executeParameters: {
args: string;
stdin: string;
};
filters: Record<string, boolean>;
tools: ActiveTools[];
libraries: CompileChildLibraries[];
};
type CompilationRequest = {
source: string;
compiler: string;
options: CompilationRequestOptions;
lang: string | null;
files: FiledataPair[];
bypassCache?: boolean;
};
type LangInfo = {
compiler: string;
options: string;
execArgs: string;
execStdin: string;
};
type CompileChildLibraries = {
id: string;
version: string;
};
export class Executor extends Pane<ExecutorState> {
private contentRoot: JQuery<HTMLElement>;
private readonly sourceEditorId: number | null;
@@ -190,6 +124,7 @@ export class Executor extends Pane<ExecutorState> {
private libsWidget?: LibsWidget;
private readonly infoByLang: Record<string, LangInfo | undefined>;
private compiler: CompilerInfo | null;
private compilerShared: ICompilerShared;
constructor(hub: Hub, container: Container, state: PaneState & ExecutorState) {
super(hub, container, state);
@@ -231,11 +166,13 @@ export class Executor extends Pane<ExecutorState> {
);
this.initLibraries(state);
this.compilerShared = new CompilerShared(this.domRoot, this.onCompilerOverridesChange.bind(this));
this.compilerShared.updateState(state);
this.initCallbacks();
// Handle initial settings
this.onSettingsChange(this.settings);
this.updateCompilerInfo();
this.saveState();
this.updateState();
if (this.sourceTreeId) {
this.compile();
@@ -340,6 +277,7 @@ export class Executor extends Pane<ExecutorState> {
compilerOptions: {
executorRequest: true,
skipAsm: true,
overrides: this.compilerShared.getOverrides(),
},
filters: {execute: true},
tools: [],
@@ -900,7 +838,7 @@ export class Executor extends Pane<ExecutorState> {
}
onLibsChanged(): void {
this.saveState();
this.updateState();
this.compile();
}
@@ -920,13 +858,13 @@ export class Executor extends Pane<ExecutorState> {
}
onFontScale(): void {
this.saveState();
this.updateState();
}
initListeners(): void {
// this.filters.on('change', _.bind(this.onFilterChange, this));
this.fontScale.on('change', this.onFontScale.bind(this));
this.paneRenaming.on('renamePane', this.saveState.bind(this));
this.paneRenaming.on('renamePane', this.updateState.bind(this));
this.toggleWrapButton.on('change', this.onToggleWrapChange.bind(this));
this.container.on('destroy', this.close, this);
@@ -967,7 +905,7 @@ export class Executor extends Pane<ExecutorState> {
} else {
this.hidePanel(button, panel);
}
this.saveState();
this.updateState();
}
initCallbacks(): void {
@@ -1051,7 +989,7 @@ export class Executor extends Pane<ExecutorState> {
onOptionsChange(options: string): void {
this.options = options;
this.saveState();
this.updateState();
if (this.shouldEmitExecutionOnFieldChange()) {
this.compile();
}
@@ -1059,7 +997,14 @@ export class Executor extends Pane<ExecutorState> {
onExecArgsChange(args: string): void {
this.executionArguments = args;
this.saveState();
this.updateState();
if (this.shouldEmitExecutionOnFieldChange()) {
this.compile();
}
}
onCompilerOverridesChange(): void {
this.updateState();
if (this.shouldEmitExecutionOnFieldChange()) {
this.compile();
}
@@ -1067,7 +1012,7 @@ export class Executor extends Pane<ExecutorState> {
onExecStdinChange(newStdin: string): void {
this.executionStdin = newStdin;
this.saveState();
this.updateState();
if (this.shouldEmitExecutionOnFieldChange()) {
this.compile();
}
@@ -1103,16 +1048,16 @@ export class Executor extends Pane<ExecutorState> {
onCompilerChange(value: string): void {
this.compiler = this.hub.compilerService.findCompiler(this.currentLangId, value);
this.updateLibraries();
this.saveState();
this.updateState();
this.compile();
this.updateCompilerUI();
}
onToggleWrapChange(): void {
const state = this.currentState();
const state = this.getCurrentState();
this.contentRoot.toggleClass('wrap', state.wrap);
this.wrapButton.prop('title', '[' + (state.wrap ? 'ON' : 'OFF') + '] ' + this.wrapTitle);
this.saveState();
this.updateState();
}
sendExecutor(): void {
@@ -1137,7 +1082,7 @@ export class Executor extends Pane<ExecutorState> {
}
}
currentState(): ExecutorState & PaneState {
override getCurrentState(): ExecutorState & PaneState {
const state: ExecutorState & PaneState = {
id: this.id,
compilerName: '',
@@ -1154,6 +1099,7 @@ export class Executor extends Pane<ExecutorState> {
argsPanelShown: !this.panelArgs.hasClass('d-none'),
stdinPanelShown: !this.panelStdin.hasClass('d-none'),
wrap: this.toggleWrapButton.get().wrap,
overrides: this.compilerShared.getOverrides(),
};
this.paneRenaming.addState(state);
@@ -1161,8 +1107,10 @@ export class Executor extends Pane<ExecutorState> {
return state;
}
saveState(): void {
this.container.setState(this.currentState());
override updateState(): void {
const state = this.getCurrentState();
this.container.setState(state);
this.compilerShared.updateState(state);
}
getCompilerName(): string {
@@ -1327,7 +1275,7 @@ export class Executor extends Pane<ExecutorState> {
this.initLangAndCompiler({compilerName: '', id: 0, lang: newLangId, compiler: info?.compiler ?? ''});
this.updateCompilersSelector(info);
this.updateCompilerUI();
this.saveState();
this.updateState();
}
}

View File

@@ -665,6 +665,61 @@ div.populararguments div.dropdown-menu {
width: 300px;
}
#overrides-selection .modal-body {
overflow-y: scroll;
}
#overrides-selection .override-search-button {
margin-left: 10px;
}
#overrides-selection .overrides-how-to-use {
font-size: smaller;
}
#overrides-selection .overrides-selected-col {
padding: 0 15px 0 0;
min-width: 250px;
max-width: 250px;
}
#overrides-selection .overrides-results-col {
padding: 0 0 0 0;
min-width: 450px;
max-width: 650px;
}
#overrides-selection .override-results-items .card {
margin-bottom: 3px;
}
#overrides-selection.mobile .overrides-results-col {
min-width: 250px;
max-width: 450px;
}
#overrides-selection .overrides-results-col span.override {
float: right;
}
#overrides-selection .overrides-results-col span.override-fav {
float: right;
}
#overrides-selection .overrides-favorites-col {
padding: 0 0 0 15px;
min-width: 325px;
max-width: 350px;
}
#overrides-selection .overrides-favorites-col button {
width: 300px;
}
#overrides-selection.mobile .overrides-favorites-col {
display: none;
}
.ces-content-root {
min-height: 100px;
max-height: calc(

View File

@@ -28,7 +28,8 @@ body {
background-color: #333 !important;
}
input {
input,
textarea {
color: #eee !important;
background-color: #474747;
border: 0 !important;

View File

@@ -0,0 +1,380 @@
// Copyright (c) 2023, 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 $ from 'jquery';
import {
CompilerOverrideType,
ConfiguredOverride,
ConfiguredOverrides,
EnvVarOverrides,
} from '../../types/compilation/compiler-overrides.interfaces.js';
import {options} from '../options.js';
import {CompilerInfo} from '../compiler.interfaces.js';
import * as local from '../local.js';
const FAV_OVERRIDES_STORE_KEY = 'favoverrides';
export type CompilerOverridesChangeCallback = () => void;
type FavOverride = {
name: CompilerOverrideType;
value: string;
meta: string;
};
type FavOverrides = FavOverride[];
export class CompilerOverridesWidget {
private domRoot: JQuery;
private popupDomRoot: JQuery<HTMLElement>;
private envVarsInput: JQuery<HTMLElement>;
private dropdownButton: JQuery;
private onChangeCallback: CompilerOverridesChangeCallback;
private configured: ConfiguredOverrides = [];
private compiler: CompilerInfo | undefined;
constructor(domRoot: JQuery, dropdownButton: JQuery, onChangeCallback: CompilerOverridesChangeCallback) {
this.domRoot = domRoot;
this.popupDomRoot = $('#overrides-selection');
this.dropdownButton = dropdownButton;
this.envVarsInput = this.popupDomRoot.find('.envvars');
this.onChangeCallback = onChangeCallback;
}
private loadStateFromUI(): ConfiguredOverrides {
const overrides: ConfiguredOverrides = [];
const envOverrides = this.getEnvOverrides();
if (envOverrides.length > 0) {
overrides.push({
name: CompilerOverrideType.env,
values: envOverrides,
});
}
const selects = this.popupDomRoot.find('select');
for (const select of selects) {
const jqSelect = $(select);
const name = jqSelect.attr('name');
const val = jqSelect.val();
if (val) {
overrides.push({
name: name as CompilerOverrideType,
value: val.toString(),
});
}
}
return overrides;
}
private envvarsToString(envVars: EnvVarOverrides): string {
return envVars.map(env => `${env.name}=${env.value}`).join('\n');
}
private stringToEnvvars(envVars: string): EnvVarOverrides {
return envVars
.split('\n')
.map(env => {
const arr = env.split('=');
if (arr[0]) {
return {
name: arr[0],
value: arr[1],
};
} else {
return false;
}
})
.filter(Boolean) as EnvVarOverrides;
}
private getEnvOverrides(): EnvVarOverrides {
return this.stringToEnvvars(this.envVarsInput.val() as string);
}
private selectOverrideFromFave(event) {
const elem = $(event.target).parent();
const name = elem.data('ov-name');
const value = elem.data('ov-value');
const possibleOverride = this.compiler?.possibleOverrides?.find(ov => ov.name === name);
if (possibleOverride) {
const override = possibleOverride.values.find(v => v.value === value);
if (override) {
const currentOverrides = this.loadStateFromUI();
const configOv = currentOverrides.find(ov => ov.name === name);
if (configOv) {
configOv.value = value;
} else {
currentOverrides.push({
name: name,
value: value,
});
}
this.loadStateIntoUI(currentOverrides);
}
}
}
private newFavoriteOverrideDiv(fave: FavOverride) {
const div = $('#overrides-favorite-tpl').children().clone();
const prefix = fave.name + ': ';
div.find('.overrides-name').html(prefix + fave.value);
div.data('ov-name', fave.name);
div.data('ov-value', fave.value);
div.on('click', this.selectOverrideFromFave.bind(this));
return div;
}
private loadFavoritesIntoUI() {
const favoritesDiv = this.popupDomRoot.find('.overrides-favorites');
favoritesDiv.html('');
const faves = this.getFavorites();
for (const fave of faves) {
const div: any = this.newFavoriteOverrideDiv(fave);
favoritesDiv.append(div);
}
}
private addToFavorites(override: ConfiguredOverride) {
if (!override.value) return;
const faves = this.getFavorites();
const fave: FavOverride = {
name: override.name,
value: override.value,
meta: this.compiler?.baseName || this.compiler?.groupName || this.compiler?.name || this.compiler?.id || '',
};
faves.push(fave);
this.setFavorites(faves);
}
private removeFromFavorites(override: ConfiguredOverride) {
if (!override.value) return;
const faves = this.getFavorites();
const faveIdx = faves.findIndex(f => f.name === override.name && f.value === override.value);
if (faveIdx !== -1) {
faves.splice(faveIdx, 1);
this.setFavorites(faves);
}
}
private isAFavorite(override: ConfiguredOverride) {
if (!override.value) return false;
const faves = this.getFavorites();
const fave = faves.find(f => f.name === override.name && f.value === override.value);
return !!fave;
}
private loadStateIntoUI(configured: ConfiguredOverrides) {
this.envVarsInput.val('');
for (const config of configured) {
if (config.name === CompilerOverrideType.env) {
this.envVarsInput.val(this.envvarsToString(config.values || []));
}
}
const container = this.popupDomRoot.find('.possible-overrides');
container.html('');
if (this.compiler && this.compiler.possibleOverrides) {
for (const possibleOverride of this.compiler.possibleOverrides) {
const card = $('#possible-override').children().clone();
card.find('.override-name').html(possibleOverride.display_title);
card.find('.override-description').html(possibleOverride.description);
const select = card.find<HTMLSelectElement>('.override select');
select.attr('name', possibleOverride.name);
const faveButton = card.find('.override-fav-button');
const faveStar = faveButton.find('.override-fav-btn-icon');
faveButton.hide();
const config = configured.find(c => c.name === possibleOverride.name);
let option = $('<option />');
select.append(option);
for (const value of possibleOverride.values) {
option = $('<option />');
option.html(value.name);
option.val(value.value);
if (config && config.value && config.value === value.value) {
option.attr('selected', 'selected');
if (this.isAFavorite(config)) {
faveStar.removeClass('far').addClass('fas');
}
faveButton.show();
}
select.append(option);
}
select.off('change').on('change', () => {
const option = select.find('option:selected');
if (option.length > 0) {
const value = option.val()?.toString();
const name = possibleOverride.name;
const ov: ConfiguredOverride = {
name: name,
value: value,
};
if (this.isAFavorite(ov)) {
faveStar.removeClass('far').addClass('fas');
} else {
faveStar.removeClass('fas').addClass('far');
}
if (ov.value !== '') {
faveButton.show();
} else {
faveButton.hide();
}
}
});
faveButton.on('click', () => {
const option = select.find('option:selected');
if (option.length > 0) {
const value = option.val()?.toString();
const name = possibleOverride.name;
const ov: ConfiguredOverride = {name, value};
if (this.isAFavorite(ov)) {
this.removeFromFavorites(ov);
faveStar.removeClass('fas').addClass('far');
} else {
this.addToFavorites(ov);
faveStar.removeClass('far').addClass('fas');
}
}
this.loadFavoritesIntoUI();
});
container.append(card);
}
}
this.loadFavoritesIntoUI();
}
set(configured: ConfiguredOverrides) {
this.configured = configured;
this.updateButton();
}
setDefaults() {
this.configured = [];
if (this.compiler && this.compiler.possibleOverrides) {
for (const ov of this.compiler.possibleOverrides) {
if (ov.name !== CompilerOverrideType.env && ov.default) {
this.configured.push({
name: ov.name,
value: ov.default,
});
}
}
}
this.updateButton();
}
setCompiler(compilerId: string, languageId?: string) {
this.compiler = options.compilers.find(c => c.id === compilerId);
}
get(): ConfiguredOverrides | undefined {
if (this.compiler) {
return this.configured;
} else {
return undefined;
}
}
private getFavorites(): FavOverrides {
return JSON.parse(local.get(FAV_OVERRIDES_STORE_KEY, '[]'));
}
private setFavorites(faves: FavOverrides) {
local.set(FAV_OVERRIDES_STORE_KEY, JSON.stringify(faves));
}
private updateButton() {
const selected = this.get();
if (selected && selected.length > 0) {
this.dropdownButton
.addClass('btn-success')
.removeClass('btn-light')
.prop(
'title',
'Current overrides:\n' +
selected
.map(ov => {
let line = '- ' + ov.name;
if (ov.value) {
line += ' = ' + ov.value;
}
return line;
})
.join('\n'),
);
} else {
this.dropdownButton.removeClass('btn-success').addClass('btn-light').prop('title', 'Overrides');
}
}
show() {
this.loadStateIntoUI(this.configured);
const lastOverrides = JSON.stringify(this.configured);
const popup = this.popupDomRoot.modal();
// popup is shared, so clear the events first
popup.off('hidden.bs.modal').on('hidden.bs.modal', () => {
this.configured = this.loadStateFromUI();
const newOverrides = JSON.stringify(this.configured);
if (lastOverrides !== newOverrides) {
this.updateButton();
this.onChangeCallback();
}
});
}
}

View File

@@ -27,6 +27,7 @@ import {BuildEnvSetupBase} from '../lib/buildenvsetup/index.js';
import {CompilationEnvironment} from '../lib/compilation-env.js';
import {Win32Compiler} from '../lib/compilers/win32.js';
import * as exec from '../lib/exec.js';
import {CompilerOverrideType, ConfiguredOverrides} from '../types/compilation/compiler-overrides.interfaces.js';
import {CompilerInfo} from '../types/compiler.interfaces.js';
import {
@@ -222,6 +223,7 @@ describe('Compiler execution', function () {
inputFilename,
outputFilename,
libraries,
[],
);
args.should.deep.equal([
'-g',
@@ -250,6 +252,7 @@ describe('Compiler execution', function () {
inputFilename,
outputFilename,
libraries,
[],
);
win32args.should.deep.equal([
'/nologo',
@@ -282,6 +285,39 @@ describe('Compiler execution', function () {
buildenv.compilerSupportsX86.should.equal(true);
});
it('compiler overrides should be sanitized', () => {
const original_overrides: ConfiguredOverrides = [
{
name: CompilerOverrideType.env,
values: [
{
name: 'somevar',
value: '123',
},
{
name: 'ABC$#%@6@5',
value: '456',
},
{
name: 'LD_PRELOAD',
value: '/path/to/my/malloc.so /bin/ls',
},
],
},
];
const sanitized = compiler.sanitizeCompilerOverrides(original_overrides);
const execOptions = compiler.getDefaultExecOptions();
compiler.applyOverridesToExecOptions(execOptions, sanitized);
Object.keys(execOptions.env).should.include('SOMEVAR');
execOptions.env['SOMEVAR'].should.equal('123');
Object.keys(execOptions.env).should.not.include('LD_PRELOAD');
Object.keys(execOptions.env).should.not.include('ABC$#%@6@5');
});
// it('should compile', async () => {
// const execStub = stub(compiler, 'exec');
// stubOutCallToExec(

View File

@@ -23,7 +23,14 @@
// POSSIBILITY OF SUCH DAMAGE.
import {CompilerArguments} from '../../lib/compiler-arguments.js';
import {BaseParser, ClangParser, GCCParser, PascalParser} from '../../lib/compilers/argument-parsers.js';
import {
BaseParser,
ClangParser,
GCCParser,
ICCParser,
PascalParser,
VCParser,
} from '../../lib/compilers/argument-parsers.js';
import {FakeCompiler} from '../../lib/compilers/fake-for-test.js';
import {makeCompilationEnvironment, should} from '../utils.js';
@@ -124,7 +131,7 @@ describe('clang parser', () => {
});
it('should handle options', () => {
return ClangParser.parse(
makeCompiler('-fno-crash-diagnostics\n-fsave-optimization-record\n-fcolor-diagnostics'),
makeCompiler(' -fno-crash-diagnostics\n -fsave-optimization-record\n -fcolor-diagnostics'),
).should.eventually.satisfy(result => {
return Promise.all([
result.compiler.supportsOptOutput.should.equals(true),
@@ -151,7 +158,7 @@ describe('popular compiler arguments', () => {
before(() => {
compiler = makeCompiler(
'-fsave-optimization-record\n-x\n-g\n-fcolor-diagnostics\n-O<number> optimization level\n-std=<c++11,c++14,c++17z>',
' -fsave-optimization-record\n -x\n -g\n -fcolor-diagnostics\n -O<number> Optimization level\n -std=<c++11,c++14,c++17z>',
);
});
@@ -160,7 +167,7 @@ describe('popular compiler arguments', () => {
return compiler.should.satisfy(compiler => {
return Promise.all([
compiler.possibleArguments.getPopularArguments().should.deep.equal({
'-O<number>': {description: 'optimization level', timesused: 0},
'-O<number>': {description: 'Optimization level', timesused: 0},
'-fcolor-diagnostics': {description: '', timesused: 0},
'-fsave-optimization-record': {description: '', timesused: 0},
'-g': {description: '', timesused: 0},
@@ -192,7 +199,7 @@ describe('popular compiler arguments', () => {
return compiler.should.satisfy(compiler => {
return Promise.all([
compiler.possibleArguments.getPopularArguments(['-std=c++14', '-g', '--hello']).should.deep.equal({
'-O<number>': {description: 'optimization level', timesused: 0},
'-O<number>': {description: 'Optimization level', timesused: 0},
'-fcolor-diagnostics': {description: '', timesused: 0},
'-fsave-optimization-record': {description: '', timesused: 0},
'-x': {description: '', timesused: 0},
@@ -202,3 +209,66 @@ describe('popular compiler arguments', () => {
});
});
});
describe('VC argument parser', () => {
it('Should extract stdversions', () => {
const lines = [
' /helloWorld',
' /std:<c++14|c++17|c++20|c++latest> C++ standard version',
' c++14 - ISO/IEC 14882:2014 (default)',
' c++17 - ISO/IEC 14882:2017',
' c++20 - ISO/IEC 14882:2020',
' c++latest - latest draft standard (feature set subject to change)',
' /something:<else> Something Else',
' /etc Etcetera',
];
const stdvers = VCParser.extractPossibleStdvers(lines);
stdvers.should.deep.equal([
{
name: 'c++14: ISO/IEC 14882:2014 (default)',
value: 'c++14',
},
{
name: 'c++17: ISO/IEC 14882:2017',
value: 'c++17',
},
{
name: 'c++20: ISO/IEC 14882:2020',
value: 'c++20',
},
{
name: 'c++latest: latest draft standard (feature set subject to change)',
value: 'c++latest',
},
]);
});
});
describe('ICC argument parser', () => {
it('Should extract stdversions', () => {
const lines = [
'-test',
'-std=<std>',
' enable language support for <std>, as described below',
' c99 conforms to ISO/IEC 9899:1999 standard for C programs',
' c++11 enables C++11 support for C++ programs',
' gnu++98 conforms to 1998 ISO C++ standard plus GNU extensions',
'-etc',
];
const stdvers = ICCParser.extractPossibleStdvers(lines);
stdvers.should.deep.equal([
{
name: 'c99: conforms to ISO/IEC 9899:1999 standard for C programs',
value: 'c99',
},
{
name: 'c++11: enables C++11 support for C++ programs',
value: 'c++11',
},
{
name: 'gnu++98: conforms to 1998 ISO C++ standard plus GNU extensions',
value: 'gnu++98',
},
]);
});
});

View File

@@ -22,6 +22,13 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import {
getToolchainFlagFromOptions,
getToolchainPathWithOptionsArr,
hasToolchainArg,
removeToolchainArg,
replaceToolchainArg,
} from '../lib/toolchain-utils.js';
import {CompilerDropinTool} from '../lib/tooling/compiler-dropin-tool.js';
import {path} from './utils.js';
@@ -172,4 +179,62 @@ describe('CompilerDropInTool', () => {
'-pthread',
]);
});
it('More toolchain magic', () => {
const options = [
'-gdwarf-4',
'-g',
'-o',
'output.s',
'-mllvm',
'--x86-asm-syntax=intel',
'-S',
'--gcc-toolchain=/opt/compiler-explorer/gcc-12.2.0',
'-fcolor-diagnostics',
'-fno-crash-diagnostics',
'/app/example.cpp',
];
hasToolchainArg(options).should.be.true;
getToolchainFlagFromOptions(options).should.equal('--gcc-toolchain=');
const newOptions = removeToolchainArg(options);
hasToolchainArg(newOptions).should.be.false;
});
it('Should be able to swap toolchain', () => {
const exe = '/opt/compiler-explorer/clang-16.0.0/bin/clang++';
const options = [
'-gdwarf-4',
'-g',
'-o',
'output.s',
'-mllvm',
'--x86-asm-syntax=intel',
'-S',
'--gcc-toolchain=/opt/compiler-explorer/gcc-12.2.0',
'-fcolor-diagnostics',
'-fno-crash-diagnostics',
'/app/example.cpp',
];
const toolchain = getToolchainPathWithOptionsArr(exe, options);
toolchain.should.equals('/opt/compiler-explorer/gcc-12.2.0');
const replacedOptions = replaceToolchainArg(options, '/opt/compiler-explorer/gcc-11.1.0');
replacedOptions.should.deep.equal([
'-gdwarf-4',
'-g',
'-o',
'output.s',
'-mllvm',
'--x86-asm-syntax=intel',
'-S',
'--gcc-toolchain=/opt/compiler-explorer/gcc-11.1.0',
'-fcolor-diagnostics',
'-fno-crash-diagnostics',
'/app/example.cpp',
]);
});
});

View File

@@ -123,6 +123,7 @@ export type ExecutionOptions = {
ldPath?: string[];
appHome?: string;
customCwd?: string;
createAndUseTempDir?: boolean;
// Stdin
input?: any;
killChild?: () => void;

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2023, 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.
export enum CompilerOverrideType {
stdlib = 'stdlib',
gcclib = 'gcclib',
toolchain = 'toolchain',
arch = 'arch',
env = 'env',
edition = 'edition',
stdver = 'stdver',
}
export type CompilerOverrideTypes = Set<CompilerOverrideType>;
export type CompilerOverrideOption = {
name: string;
value: string;
};
export type CompilerOverrideOptions = Array<CompilerOverrideOption>;
export type CompilerOverrideNameAndOptions = {
name: CompilerOverrideType;
display_title: string;
description: string;
flags: string[];
values: CompilerOverrideOptions;
default?: string;
};
export type AllCompilerOverrideOptions = Array<CompilerOverrideNameAndOptions>;
export type EnvVarOverride = {
name: string;
value: string;
};
export type EnvVarOverrides = Array<EnvVarOverride>;
export type ConfiguredOverride = {
name: CompilerOverrideType;
value?: string;
values?: EnvVarOverrides;
};
export type ConfiguredOverrides = Array<ConfiguredOverride>;

View File

@@ -22,6 +22,7 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import {AllCompilerOverrideOptions} from './compilation/compiler-overrides.interfaces.js';
import {ICompilerArguments} from './compiler-arguments.interfaces.js';
import {Language, LanguageKey} from './languages.interfaces.js';
import {Library} from './libraries/libraries.interfaces.js';
@@ -79,6 +80,9 @@ export type CompilerInfo = {
supportsCfg?: boolean;
supportsGnatDebugViews?: boolean;
supportsLibraryCodeFilter?: boolean;
supportsMarch?: boolean;
supportsTarget?: boolean;
supportsTargetIs?: boolean;
executionWrapper: string;
executionWrapperArgs: string[];
postProcess: string[];
@@ -114,6 +118,7 @@ export type CompilerInfo = {
target: string;
path: string;
};
possibleOverrides?: AllCompilerOverrideOptions;
disabledFilters: string[];
optArg?: string;
externalparser: any;

View File

@@ -20,6 +20,8 @@ include history
include library-selection
include overrides-selection
include timing
include jsbeebemu

View File

@@ -0,0 +1,34 @@
#overrides-selection.modal.fade.gl_keep(tabindex="-1" role="dialog")
.modal-dialog.modal-lg
.modal-content
.modal-header
h5.modal-title Compiler overrides
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body
.card
.card-body
.container
.row
.col-lg.overrides-how-to-use
| To override a compiler's defaults, you can change the following options.
| Note that this might lead to errors and unsupported situations.
| These also only apply to compilation, not for executing your code.
.row
.col-lg &nbsp;
.row
.overrides-results-col.col-md
.override-results.items
.card
.card-header
span
b.override-name Compiler environment variables
.card-body
span.description One environment variable per line, KEY=VALUE, that will be set during compilation.
span.custom-override
textarea.envvars(cols="30")
.possible-overrides.items
.overrides-favorites-col.col-md
h6 Favorites
.overrides-favorites

View File

@@ -43,6 +43,10 @@ mixin newPaneButton(classId, text, title, icon)
button.btn.btn-sm.btn-light.show-libs(title="Include libs" aria-label="Toggle libraries dropdown")
span.fas.fa-book
span.dp-text.hideable Libraries
.btn-group.btn-group-sm(role="group" aria-label="Compiler overrides")
button.btn.btn-sm.btn-light.show-overrides(title="Overrides" aria-label="Configure overrides for standards and architectures etc")
span.fas.fa-wrench
span.dp-text.hideable Overrides
.btn-group.btn-group-sm(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle.add-pane(type="button" title="Add a new pane" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Add new element for this compiler" data-cy="new-compiler-dropdown-btn")
span.fas.fa-plus

View File

@@ -9,6 +9,9 @@
button.btn.btn-sm.btn-light.show-libs(title="Include libs" aria-label="Toggle libraries dropdown")
span.fas.fa-book
span.dp-text.hideable Libraries
button.btn.btn-sm.btn-light.show-overrides(title="Overrides" aria-label="Configure overrides for standards and architectures etc")
span.fas.fa-wrench
span.dp-text.hideable Overrides
button.btn.btn-sm.btn-light.toggle-compilation.active(title="Compiler options" aria-label="Toggle compiler options input")
span.fas.fa-cogs
span.hideable Compilation

View File

@@ -48,6 +48,10 @@ mixin monacopane(id)
include widgets/tree-editor-tpl
include widgets/possible-override-tpl
include widgets/overrides-favorite-tpl
+monacopane("opt")
+monacopane("flags")

View File

@@ -0,0 +1,3 @@
#overrides-favorite-tpl
div.mb-1
button.btn.btn-md.btn-outline-primary.overrides-name

View File

@@ -0,0 +1,11 @@
#possible-override
.card
.card-header
span.override-name
span.override
select.custom-select.custom-select-sm
.card-body
p.override-description
span.override-fav
button.btn.btn-sm.override-fav-button
span.override-fav-btn-icon.far.fa-star