ligo/tools/webide/packages/server/src/ligo-compiler.ts

241 lines
5.3 KiB
TypeScript

import fs from 'fs';
import path from 'path';
import tmp from 'tmp';
import { logger } from './logger';
const { spawn } = require('child_process');
const dataDir = process.env['DATA_DIR'] || path.join(__dirname, 'tmp');
const JOB_TIMEOUT = 10000;
export class CompilerError extends Error {
constructor(message: string) {
super(message);
}
}
export class LigoCompiler {
private ligoCmd = process.env['LIGO_CMD'] || [
'docker',
'run',
'-t',
'--rm',
'-v',
`${dataDir}:${dataDir}`,
'-w',
dataDir,
'ligolang/ligo:next'
];
private execPromise(cmd: string | string[], args: string[]): Promise<string> {
let command: string[] = [];
if (Array.isArray(cmd)) {
command = cmd;
} else {
command = cmd.split(' ');
}
let program = command[0];
const argument = [...command.slice(1), ...args];
return new Promise((resolve, reject) => {
try {
const result = spawn(program, argument, { shell: false, cwd: dataDir });
let finalResult = '';
let finalError = '';
result.stdout.on('data', (data: Buffer) => {
finalResult += data.toString();
});
result.stderr.on('data', (data: Buffer) => {
finalError += data.toString();
});
result.on('close', (code: any) => {
if (code === 0) {
resolve(finalResult);
} else {
reject(new CompilerError(finalError));
}
});
} catch (ex) {
logger.error(`Unexpected compiler error ${ex}`);
reject(ex);
}
setTimeout(() => {
reject(new Error(`command: ${cmd} Timed out after ${JOB_TIMEOUT} ms`));
}, JOB_TIMEOUT);
});
}
private createTemporaryFile(fileContent: string) {
return new Promise<{ name: string; remove: () => void }>(
(resolve, reject) => {
tmp.file(
{ dir: dataDir, postfix: '.ligo' },
(err, name, fd, remove) => {
if (err) {
reject(err);
return;
}
fs.write(fd, Buffer.from(fileContent), err => {
if (err) {
reject(err);
return;
}
resolve({
name,
remove: () => {
try {
remove();
} catch (ex) {
logger.error(`Unable to remove file ${name}`);
}
const ppFile = name.replace('.ligo', '.pp.ligo');
try {
if (fs.existsSync(ppFile)) {
fs.unlinkSync(ppFile);
}
} catch (ex) {
logger.error(`Unable to remove file ${ppFile}`);
}
}
});
});
}
);
}
);
}
async compileContract(
syntax: string,
code: string,
entrypoint: string,
format: string
) {
const { name, remove } = await this.createTemporaryFile(code);
try {
const result = await this.execPromise(this.ligoCmd, [
'compile-contract',
'--michelson-format',
format,
'-s',
syntax,
name,
entrypoint
]);
return result;
} finally {
remove();
}
}
async compileExpression(syntax: string, expression: string, format: string) {
const result = await this.execPromise(this.ligoCmd, [
'compile-expression',
'--michelson-format',
format,
syntax,
expression
]);
return result;
}
async compileStorage(
syntax: string,
code: string,
entrypoint: string,
format: string,
storage: string
) {
const { name, remove } = await this.createTemporaryFile(code);
try {
const result = await this.execPromise(this.ligoCmd, [
'compile-storage',
'--michelson-format',
format,
'-s',
syntax,
name,
entrypoint,
storage
]);
return result;
} finally {
remove();
}
}
async dryRun(
syntax: string,
code: string,
entrypoint: string,
parameter: string,
storage: string
) {
const { name, remove } = await this.createTemporaryFile(code);
try {
const result = await this.execPromise(this.ligoCmd, [
'dry-run',
'-s',
syntax,
name,
entrypoint,
parameter,
storage
]);
return result;
} finally {
remove();
}
}
async evaluateValue(syntax: string, code: string, entrypoint: string) {
const { name, remove } = await this.createTemporaryFile(code);
try {
const result = await this.execPromise(this.ligoCmd, [
'evaluate-value',
'-s',
syntax,
name,
entrypoint
]);
return result;
} finally {
remove();
}
}
async runFunction(
syntax: string,
code: string,
entrypoint: string,
parameter: string
) {
const { name, remove } = await this.createTemporaryFile(code);
try {
const result = await this.execPromise(this.ligoCmd, [
'run-function',
'-s',
syntax,
name,
entrypoint,
parameter
]);
return result;
} finally {
remove();
}
}
}