refactor(compiler): run only once with all files configured

Fixes #13
This commit is contained in:
Jonathan Rainville 2018-11-27 15:46:07 -05:00
parent 5f79db8020
commit f83465c641
1 changed files with 159 additions and 140 deletions

View File

@ -3,43 +3,10 @@ const shelljs = require('shelljs');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
function compileSolcContract(logger, file, allowedDirectories, remappings, solcConfig, callback) { function compileSolcContract(logger, compileSettings, allowedDirectories, callback) {
let input = {};
const filename = file.pluginPath ? path.join(file.pluginPath, file.filename) : file.filename;
input[filename] = {content: file.parsedContent.replace(/\r\n/g, '\n'), path: filename};
let jsonObj = {
language: 'Solidity',
sources: input,
settings: {
optimizer: {
enabled: solcConfig['optimize'],
runs: solcConfig['optimize-runs']
},
remappings,
outputSelection: {
'*': {
'': ['ast'],
'*': [
'abi',
'devdoc',
'evm.bytecode',
'evm.deployedBytecode',
'evm.gasEstimates',
'evm.legacyAssembly',
'evm.methodIdentifiers',
'metadata',
'userdoc'
]
}
}
}
};
const command = `solc --standard-json --allow-paths ${allowedDirectories.join(',')}`; const command = `solc --standard-json --allow-paths ${allowedDirectories.join(',')}`;
shelljs.ShellString(JSON.stringify(jsonObj)).exec(command, {silent: true}, (code, stdout, stderr) => { shelljs.ShellString(JSON.stringify(compileSettings)).exec(command, {silent: true}, (code, stdout, stderr) => {
if (stderr) { if (stderr) {
logger.warn(stderr); logger.warn(stderr);
} }
@ -75,9 +42,9 @@ function getSolcVersion(logger, callback) {
}); });
} }
function compileSolc(embark, contractFiles, contractDirectories, cb) { function compileSolc(embark, contractFiles, contractDirectories, callback) {
if (!contractFiles || !contractFiles.length) { if (!contractFiles || !contractFiles.length) {
return cb(); return callback();
} }
const logger = embark.logger; const logger = embark.logger;
@ -85,113 +52,165 @@ function compileSolc(embark, contractFiles, contractDirectories, cb) {
const outputDir = embark.config.buildDir + embark.config.contractDirectories[0]; const outputDir = embark.config.buildDir + embark.config.contractDirectories[0];
const solcConfig = embark.config.embarkConfig.options.solc; const solcConfig = embark.config.embarkConfig.options.solc;
const solc = shelljs.which('solc'); let allowedDirectories;
if (!solc) { const remappings = [];
logger.error('solc is not installed on your machine'); const compilationSettings = {
logger.info('You can install it by following the instructions on: http://solidity.readthedocs.io/en/latest/installing-solidity.html'); language: 'Solidity',
return cb('Compiler not installed'); sources: {},
} settings: {
optimizer: {
logger.info("compiling solidity contracts with command line solc..."); enabled: solcConfig['optimize'],
runs: solcConfig['optimize-runs']
const allowedDirectories = contractFiles.map((contractFile) => path.join(process.cwd(), path.dirname(contractFile.path) + '/'))
.filter((x, i, a) => a.indexOf(x) === i);
// Add default contract paths
allowedDirectories.push(
path.join(process.cwd(), 'node_modules/'),
path.join(process.cwd(), '.embark/contracts/')
);
const remappings = []; // Will get populated by compilations
// Get content and remappings
async.each(contractFiles, (file, eachCb) => {
file.content(content => {
file.parsedContent = content;
const newRemappings = file.importRemappings.map((mapping) => `${mapping.prefix}=${mapping.target}`);
newRemappings.forEach(newRemapping => {
if (!remappings.includes(newRemapping)) {
remappings.push(newRemapping);
}
});
eachCb();
});
}, (err) => {
if (err) {
return cb(err);
}
let compiled_object = {};
async.each(contractFiles,
function(file, callback) {
compileSolcContract(logger, file, allowedDirectories, remappings, solcConfig, (err, compileString) => {
if (err) {
return callback(err);
}
let json;
try {
json = JSON.parse(compileString);
} catch (e) {
logger.error(e.message || e);
return callback(`Compiling ${file} returned an unreadable result`);
}
const contracts = json.contracts;
if (json.errors) {
let isError = false;
json.errors.forEach(error => {
if (error.severity === 'error') {
isError = true;
logger.error(error.formattedMessage);
} else {
logger.warn(error.formattedMessage);
}
});
if (isError) {
return callback(`Error while compiling ${file.filename}`);
}
}
for (let contractFile in contracts) {
for (let contractName in contracts[contractFile]) {
let contract = contracts[contractFile][contractName];
let filename = contractFile;
const originalFilename = filename;
for (let directory of contractDirectories) {
let match = new RegExp("^" + directory);
filename = filename.replace(match, '');
}
const className = contractName;
compiled_object[className] = {};
compiled_object[className].code = contract.evm.bytecode.object;
compiled_object[className].runtimeBytecode = contract.evm.deployedBytecode.object;
compiled_object[className].realRuntimeBytecode = contract.evm.deployedBytecode.object.slice(0, -68);
compiled_object[className].swarmHash = contract.evm.deployedBytecode.object.slice(-68).slice(0, 64);
compiled_object[className].gasEstimates = contract.evm.gasEstimates;
compiled_object[className].functionHashes = contract.evm.methodIdentifiers;
compiled_object[className].abiDefinition = contract.abi;
compiled_object[className].filename = filename;
compiled_object[className].originalFilename = originalFilename;
}
}
callback();
});
}, },
function(err) { remappings,
cb(err, compiled_object); outputSelection: {
if (outputBinary) { '*': {
embark.events.on("outputDone", function() { '': ['ast'],
Object.keys(compiled_object).map(function(className, _index) { '*': [
fs.writeFile(path.join(outputDir, className + ".bin"), compiled_object[className].code, (err) => { 'abi',
if (err) { 'devdoc',
logger.error("Error writing binary file: " + JSON.stringify(err)); 'evm.bytecode',
} 'evm.deployedBytecode',
}); 'evm.gasEstimates',
}); 'evm.legacyAssembly',
'evm.methodIdentifiers',
'metadata',
'userdoc'
]
}
}
}
};
async.waterfall([
function checkSolc(next) {
const solc = shelljs.which('solc');
if (!solc) {
logger.error('solc is not installed on your machine');
logger.info('You can install it by following the instructions on: http://solidity.readthedocs.io/en/latest/installing-solidity.html');
return next('Compiler not installed');
}
logger.info("compiling solidity contracts with command line solc...");
next();
},
function getAllowedDirectories(next) {
allowedDirectories = contractFiles.map((contractFile) => path.join(process.cwd(), path.dirname(contractFile.path) + '/'))
.filter((x, i, a) => a.indexOf(x) === i);
// Add default contract paths
allowedDirectories.push(
path.join(process.cwd(), 'node_modules/'),
path.join(process.cwd(), '.embark/contracts/')
);
next();
},
function getContentAndRemappings(next) {
async.each(contractFiles, (file, eachCb) => {
file.content(content => {
file.parsedContent = content;
const newRemappings = file.importRemappings.map((mapping) => `${mapping.prefix}=${mapping.target}`);
newRemappings.forEach(newRemapping => {
if (!remappings.includes(newRemapping)) {
remappings.push(newRemapping);
}
}); });
eachCb();
});
}, next);
},
function getCompilationSettings(next) {
contractFiles.forEach(file => {
const filename = file.pluginPath ? path.join(file.pluginPath, file.filename) : file.filename;
compilationSettings.sources[filename] = {
content: file.parsedContent.replace(/\r\n/g, '\n'),
path: filename
};
});
next();
},
function compile(next) {
compileSolcContract(logger, compilationSettings, allowedDirectories, (err, compileString) => {
if (err) {
return next(err);
}
let json;
try {
json = JSON.parse(compileString);
} catch (e) {
logger.error(e.message || e);
return callback(`Compiling returned an unreadable result`);
}
const contracts = json.contracts;
// Check for errors
if (json.errors) {
let isError = false;
json.errors.forEach(error => {
if (error.severity === 'error') {
isError = true;
logger.error(error.formattedMessage);
} else {
logger.warn(error.formattedMessage);
}
});
if (isError) {
return next(`Error while compiling`);
}
next(null, contracts);
} }
}); });
},
function populateCompiledObject(contracts, next) {
const compiledObject = {};
for (let contractFile in contracts) {
for (let contractName in contracts[contractFile]) {
let contract = contracts[contractFile][contractName];
let filename = contractFile;
const originalFilename = filename;
for (let directory of contractDirectories) {
let match = new RegExp("^" + directory);
filename = filename.replace(match, '');
}
const className = contractName;
compiledObject[className] = {};
compiledObject[className].code = contract.evm.bytecode.object;
compiledObject[className].runtimeBytecode = contract.evm.deployedBytecode.object;
compiledObject[className].realRuntimeBytecode = contract.evm.deployedBytecode.object.slice(0, -68);
compiledObject[className].swarmHash = contract.evm.deployedBytecode.object.slice(-68).slice(0, 64);
compiledObject[className].gasEstimates = contract.evm.gasEstimates;
compiledObject[className].functionHashes = contract.evm.methodIdentifiers;
compiledObject[className].abiDefinition = contract.abi;
compiledObject[className].filename = filename;
compiledObject[className].originalFilename = originalFilename;
}
}
next(null, compiledObject);
}
], (err, compiledObject) => {
callback(err, compiledObject);
if (outputBinary) {
embark.events.once("outputDone", function() {
Object.keys(compiledObject).map(function(className, _index) {
fs.writeFile(path.join(outputDir, className + ".bin"), compiledObject[className].code, (err) => {
if (err) {
logger.error("Error writing binary file: " + JSON.stringify(err));
}
});
});
});
}
}); });
} }