blob: b9509cdd31566a0314e81440c9c4a3ac9c4fa5b9 [file] [log] [blame] [raw]
// Copyright (c) 2018, 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 'path';
import PromClient from 'prom-client';
import _ from 'underscore';
import * as exec from '../exec';
import {logger} from '../logger';
import * as utils from '../utils';
const countersPerTool = {};
export class BaseTool {
constructor(toolInfo, env) {
this.tool = toolInfo;
this.env = env;
this.tool.exclude = this.tool.exclude ? this.tool.exclude.split(':') : [];
this.addOptionsToToolArgs = true;
this.parseOutput = utils.parseOutput;
const toolName = this.tool.name;
if (toolName && !(toolName in countersPerTool)) {
countersPerTool[toolName] = new PromClient.Counter({
name: `${toolName.replaceAll(/\W/g, '_').toLowerCase()}_compilation_total`,
help: `Number of compilations with ${toolName} active`,
labelNames: ['language', 'compiler'],
});
}
}
getId() {
return this.tool.id;
}
getType() {
return this.tool.type || 'independent';
}
getUniqueFilePrefix() {
const timestamp = process.hrtime();
const timestamp_str = '_' + timestamp[0] * 1000000 + timestamp[1] / 1000;
return this.tool.id.replace(/[^\da-z]/gi, '_') + timestamp_str + '_';
}
isCompilerExcluded(compilerId, compilerProps) {
if (this.tool.includeKey) {
// If the includeKey is set, we only support compilers that have a truthy 'includeKey'.
if (!compilerProps(this.tool.includeKey)) {
return true;
}
// Even if the include key is truthy, we fall back to the exclusion list.
}
return this.tool.exclude.find(excl => compilerId.includes(excl));
}
exec(toolExe, args, options) {
return exec.execute(toolExe, args, options);
}
getDefaultExecOptions() {
return {
timeoutMs: this.env.ceProps('compileTimeoutMs', 7500),
maxErrorOutput: this.env.ceProps('max-error-output', 5000),
wrapper: this.env.compilerProps('compiler-wrapper'),
};
}
createErrorResponse(message) {
return {
id: this.tool.id,
name: this.tool.name,
code: -1,
languageId: 'stderr',
stdout: [],
stderr: utils.parseOutput(message),
};
}
// mostly copy&paste from base-compiler.js
findLibVersion(selectedLib, supportedLibraries) {
const foundLib = _.find(supportedLibraries, (o, libId) => libId === selectedLib.id);
if (!foundLib) return false;
return _.find(foundLib.versions, (o, versionId) => versionId === selectedLib.version);
}
// mostly copy&paste from base-compiler.js
getIncludeArguments(libraries, supportedLibraries) {
const includeFlag = '-I';
return _.flatten(
_.map(libraries, selectedLib => {
const foundVersion = this.findLibVersion(selectedLib, supportedLibraries);
if (!foundVersion) return false;
return _.map(foundVersion.path, path => includeFlag + path);
}),
);
}
getLibraryOptions(libraries, supportedLibraries) {
return _.flatten(
_.map(libraries, selectedLib => {
const foundVersion = this.findLibVersion(selectedLib, supportedLibraries);
if (!foundVersion) return false;
return foundVersion.options;
}),
);
}
async runTool(compilationInfo, inputFilepath, args, stdin /*, supportedLibraries*/) {
if (this.tool.name) {
countersPerTool[this.tool.name].inc({
language: compilationInfo.compiler.lang,
compiler: compilationInfo.compiler.name,
});
}
let execOptions = this.getDefaultExecOptions();
if (inputFilepath) execOptions.customCwd = path.dirname(inputFilepath);
execOptions.input = stdin;
args = args ? args : [];
if (this.addOptionsToToolArgs) args = this.tool.options.concat(args);
if (inputFilepath) args.push(inputFilepath);
const exeDir = path.dirname(this.tool.exe);
try {
const result = await this.exec(this.tool.exe, args, execOptions);
return this.convertResult(result, inputFilepath, exeDir);
} catch (e) {
logger.error('Error while running tool: ', e);
return this.createErrorResponse('Error while running tool');
}
}
convertResult(result, inputFilepath, exeDir) {
const transformedFilepath = result.filenameTransform(inputFilepath);
return {
id: this.tool.id,
name: this.tool.name,
code: result.code,
languageId: this.tool.languageId,
stderr: this.parseOutput(result.stderr, transformedFilepath, exeDir),
stdout: this.parseOutput(result.stdout, transformedFilepath, exeDir),
};
}
}