aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpartouf <partouf@gmail.com>2024-04-27 21:24:04 +0200
committerpartouf <partouf@gmail.com>2024-04-27 21:24:04 +0200
commit6ce79d25954196e1a38ce7aec6fe508cbd8e02bf (patch)
tree4d2f6179db304482b4c0d4592a30f1dfba8d6ba2
parent8d3ba37fec3b074cd5657766e5d67258e53c57d2 (diff)
downloadcompiler-explorer-gh-11529.tar.gz
compiler-explorer-gh-11529.zip
sketch for localexecution classgh-11529
-rw-r--r--lib/execution-env.ts101
-rw-r--r--lib/handlers/api.ts44
2 files changed, 145 insertions, 0 deletions
diff --git a/lib/execution-env.ts b/lib/execution-env.ts
new file mode 100644
index 000000000..31f81d895
--- /dev/null
+++ b/lib/execution-env.ts
@@ -0,0 +1,101 @@
+import os from 'os';
+import path from 'path';
+
+import fs from 'fs-extra';
+import temp from 'temp';
+
+import {ExecutionOptions, ExecutionParams} from '../types/compilation/compilation.interfaces.js';
+import {UnprocessedExecResult} from '../types/execution/execution.interfaces.js';
+
+import {unwrap} from './assert.js';
+import * as exec from './exec.js';
+import {logger} from './logger.js';
+import {Packager} from './packager.js';
+import * as utils from './utils.js';
+
+export interface IExecutionEnvironment {
+ downloadExecutablePackage(hash: string): Promise<void>;
+ execute(params: ExecutionParams): Promise<UnprocessedExecResult>;
+}
+
+export class LocalExecutionEnvironment implements IExecutionEnvironment {
+ protected packager: Packager;
+ protected executableCache: any;
+ protected dirPath: string;
+ protected buildResult: any;
+
+ constructor() {
+ this.packager = new Packager();
+ this.dirPath = 'not initialized';
+ }
+
+ protected async executableGet(key: string, destinationFolder: string) {
+ const result = await this.executableCache.get(key);
+ if (!result.hit) return null;
+ const filepath = destinationFolder + '/' + key;
+ await fs.writeFile(filepath, unwrap(result.data));
+ return filepath;
+ }
+
+ protected async loadPackageWithExecutable(hash: string, dirPath: string) {
+ const compilationResultFilename = 'compilation-result.json';
+ try {
+ const startTime = process.hrtime.bigint();
+ const outputFilename = await this.executableGet(hash, dirPath);
+ if (outputFilename) {
+ logger.debug(`Using cached package ${outputFilename}`);
+ await this.packager.unpack(outputFilename, dirPath);
+ const buildResultsBuf = await fs.readFile(path.join(dirPath, compilationResultFilename));
+ const buildResults = JSON.parse(buildResultsBuf.toString('utf8'));
+ const endTime = process.hrtime.bigint();
+
+ // todo: get inputFilename and executableFilename from somewhere else?
+
+ // let inputFilename = path.join(dirPath, this.compileFilename);
+ // if (buildResults.inputFilename) {
+ // inputFilename = path.join(dirPath, path.basename(buildResults.inputFilename));
+ // }
+
+ return Object.assign({}, buildResults, {
+ code: 0,
+ // inputFilename: inputFilename,
+ dirPath: dirPath,
+ // executableFilename: this.getExecutableFilename(dirPath, this.outputFilebase),
+ packageDownloadAndUnzipTime: ((endTime - startTime) / BigInt(1000000)).toString(),
+ });
+ }
+ logger.debug('Tried to get executable from cache, but got a cache miss');
+ } catch (err) {
+ logger.error('Tried to get executable from cache, but got an error:', {err});
+ }
+ return false;
+ }
+
+ async downloadExecutablePackage(hash: string): Promise<void> {
+ this.dirPath = await temp.mkdir({prefix: utils.ce_temp_prefix, dir: os.tmpdir()});
+
+ this.buildResult = await this.loadPackageWithExecutable(hash, this.dirPath);
+ }
+
+ private getDefaultExecOptions(): ExecutionOptions & {env: Record<string, string>} {
+ // const env = this.env.getEnv(this.compiler.needsMulti);
+ const env: Record<string, string> = {};
+ if (!env.PATH) env.PATH = '';
+ // env.PATH = [...this.getExtraPaths(), env.PATH].filter(Boolean).join(path.delimiter);
+
+ return {
+ // timeoutMs: this.env.ceProps('compileTimeoutMs', 7500),
+ // maxErrorOutput: this.env.ceProps('max-error-output', 5000),
+ env,
+ // wrapper: this.compilerWrapper,
+ };
+ }
+
+ async execute(params: ExecutionParams): Promise<UnprocessedExecResult> {
+ return await exec.execute(
+ this.buildResult.executableFilename,
+ typeof params.args === 'string' ? utils.splitArguments(params.args) : params.args || [],
+ this.getDefaultExecOptions(),
+ );
+ }
+}
diff --git a/lib/handlers/api.ts b/lib/handlers/api.ts
index c64f8c269..e48e01304 100644
--- a/lib/handlers/api.ts
+++ b/lib/handlers/api.ts
@@ -31,6 +31,7 @@ import {CompilerInfo} from '../../types/compiler.interfaces.js';
import {Language, LanguageKey} from '../../types/languages.interfaces.js';
import {assert, unwrap} from '../assert.js';
import {ClientStateNormalizer} from '../clientstate-normalizer.js';
+import {IExecutionEnvironment, LocalExecutionEnvironment} from '../execution-env.js';
import {logger} from '../logger.js';
import {ClientOptionsHandler} from '../options-handler.js';
import {PropertyGetter} from '../properties.interfaces.js';
@@ -107,6 +108,8 @@ export class ApiHandler {
.post(compileHandler.handleCmake.bind(compileHandler))
.all(methodNotAllowed);
+ this.handle.route('/localexecution/:hash').post(this.handleLocalExecution.bind(this)).all(methodNotAllowed);
+
this.handle
.route('/popularArguments/:compiler')
.post(compileHandler.handlePopularArguments.bind(compileHandler))
@@ -277,6 +280,47 @@ export class ApiHandler {
}
}
+ handleLocalExecution(req: express.Request, res: express.Response, next: express.NextFunction) {
+ if (!req.params.hash) {
+ next({statusCode: 404, message: 'No hash supplied'});
+ return;
+ }
+
+ if (!req.body.ExecutionParams) {
+ next({statusCode: 404, message: 'No ExecutionParams'});
+ return;
+ }
+
+ /*
+
+export type ExecutionOptions = {
+ timeoutMs?: number;
+ maxErrorOutput?: number;
+ env?: Record<string, string>;
+ wrapper?: any;
+ maxOutput?: number;
+ ldPath?: string[];
+ appHome?: string;
+ customCwd?: string;
+ createAndUseTempDir?: boolean;
+ // Stdin
+ input?: any;
+ killChild?: () => void;
+};
+
+export type ExecutionParams = {
+ args?: string[] | string;
+ stdin?: string;
+ runtimeTools?: ConfiguredRuntimeTools;
+};
+
+*/
+
+ const env: IExecutionEnvironment = new LocalExecutionEnvironment();
+ env.downloadExecutablePackage(req.params.hash);
+ env.execute(req.body.ExecutionParams);
+ }
+
handleAllLibraries(req: express.Request, res: express.Response, next: express.NextFunction) {
if (this.options) {
res.send(this.options.options.libs);