mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 07:04:04 -05:00
181 lines
6.6 KiB
TypeScript
181 lines
6.6 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 path from 'node:path';
|
|
|
|
import fs from 'fs-extra';
|
|
|
|
import type {ExecutionOptionsWithEnv} from '../../types/compilation/compilation.interfaces.js';
|
|
import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
|
|
import {UnprocessedExecResult} from '../../types/execution/execution.interfaces.js';
|
|
import {BaseCompiler} from '../base-compiler.js';
|
|
import {CompilationEnvironment} from '../compilation-env.js';
|
|
import * as exec from '../exec.js';
|
|
import {logger} from '../logger.js';
|
|
import {TurboCAsmParser} from '../parsers/asm-parser-turboc.js';
|
|
|
|
export class DosboxCompiler extends BaseCompiler {
|
|
private readonly dosbox: string;
|
|
private readonly root: string;
|
|
|
|
constructor(compilerInfo: PreliminaryCompilerInfo, env: CompilationEnvironment) {
|
|
super(compilerInfo, env);
|
|
|
|
this.dosbox = this.compilerProps<string>(`compiler.${this.compiler.id}.dosbox`);
|
|
this.root = this.compilerProps<string>(`compiler.${this.compiler.id}.root`);
|
|
this.asm = new TurboCAsmParser(this.compilerProps);
|
|
}
|
|
|
|
protected override async writeMultipleFiles(files: any[], dirPath: string): Promise<any[]> {
|
|
const filesToWrite: any[] = [];
|
|
|
|
for (const file of files) {
|
|
if (!file.filename) throw new Error('One of more files do not have a filename');
|
|
|
|
const fullpath = this.getExtraFilepath(dirPath, file.filename);
|
|
const contents = file.contents.replaceAll('\n', '\r\n');
|
|
filesToWrite.push(fs.outputFile(fullpath, contents));
|
|
}
|
|
|
|
return Promise.all(filesToWrite);
|
|
}
|
|
|
|
protected override async writeAllFiles(dirPath: string, source: string, files: any[], filters: object) {
|
|
if (!source) throw new Error(`File ${this.compileFilename} has no content or file is missing`);
|
|
|
|
const inputFilename = path.join(dirPath, this.compileFilename);
|
|
await fs.writeFile(inputFilename, source.replaceAll('\n', '\r\n'));
|
|
|
|
if (files && files.length > 0) {
|
|
await this.writeMultipleFiles(files, dirPath);
|
|
}
|
|
|
|
return {
|
|
inputFilename,
|
|
};
|
|
}
|
|
|
|
private getDosboxArgs(tempDir: string, compileArgs: string[]) {
|
|
const binPath = path.relative(this.root, path.dirname(this.compiler.exe));
|
|
const exeName = path.basename(this.compiler.exe).replace(/\.exe$/i, '');
|
|
return [
|
|
'-c',
|
|
`mount c ${this.root}`,
|
|
'-c',
|
|
`mount d ${tempDir}`,
|
|
'-c',
|
|
`PATH=%PATH%;C:\\${binPath}`,
|
|
'-c',
|
|
'd:',
|
|
'-c',
|
|
`${exeName} ${compileArgs.join(' ')} > STDOUT.TXT`,
|
|
'-c',
|
|
'exit',
|
|
];
|
|
}
|
|
|
|
private getDosboxEnv() {
|
|
return {
|
|
SDL_VIDEODRIVER: 'dummy',
|
|
};
|
|
}
|
|
|
|
public override async execCompilerCached(
|
|
compiler: string,
|
|
args: string[],
|
|
options?: ExecutionOptionsWithEnv,
|
|
): Promise<UnprocessedExecResult> {
|
|
if (this.mtime === null) {
|
|
throw new Error('Attempt to access cached compiler before initialise() called');
|
|
}
|
|
if (!options) {
|
|
options = this.getDefaultExecOptions();
|
|
options.timeoutMs = 0;
|
|
options.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths([]);
|
|
}
|
|
|
|
const key = this.getCompilerCacheKey(compiler, args, options);
|
|
let result: UnprocessedExecResult = await this.env.compilerCacheGet(key as any);
|
|
if (!result) {
|
|
result = await (this.env.enqueue(async () =>
|
|
this.exec(compiler, args, options),
|
|
) as Promise<UnprocessedExecResult>);
|
|
if (result?.okToCache) {
|
|
this.env
|
|
.compilerCachePut(key as any, result, undefined)
|
|
.then(() => {
|
|
// Do nothing, but we don't await here.
|
|
})
|
|
.catch(e => {
|
|
logger.info('Uncaught exception caching compilation results', e);
|
|
});
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public override async exec(filepath: string, args: string[], execOptions: any) {
|
|
if (!execOptions) {
|
|
execOptions = this.getDefaultExecOptions();
|
|
}
|
|
|
|
execOptions.env = this.getDosboxEnv();
|
|
|
|
if (!execOptions.customCwd) {
|
|
execOptions.customCwd = await this.newTempDir();
|
|
}
|
|
|
|
const tempDir = execOptions.customCwd;
|
|
const fullArgs = this.getDosboxArgs(tempDir, args);
|
|
|
|
const result = await exec.executeDirect(this.dosbox, fullArgs, execOptions);
|
|
|
|
const stdoutFilename = path.join(tempDir, 'STDOUT.TXT');
|
|
const stdout = await fs.readFile(stdoutFilename);
|
|
result.stdout = stdout.toString('utf8');
|
|
|
|
return result;
|
|
}
|
|
|
|
public override async runCompiler(
|
|
compiler: string,
|
|
options: string[],
|
|
inputFilename: string,
|
|
execOptions: ExecutionOptionsWithEnv,
|
|
) {
|
|
return super.runCompiler(
|
|
compiler,
|
|
options.map(option => {
|
|
if (option === inputFilename) {
|
|
return path.basename(option);
|
|
}
|
|
return option;
|
|
}),
|
|
inputFilename,
|
|
execOptions,
|
|
);
|
|
}
|
|
}
|