diff --git a/lib/core/engine.js b/lib/core/engine.js index efef0f3a..618323fb 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -1,6 +1,7 @@ const async = require('async'); const utils = require('../utils/utils'); +const IPC = require('./ipc'); class Engine { constructor(options) { @@ -182,7 +183,8 @@ class Engine { compiler.compile_contracts(contractFiles, cb); }); - this.registerModule('solidity'); + this.ipc = new IPC({logger: this.logger, ipcRole: options.ipcRole}); + this.registerModule('solidity', {ipc: this.ipc}); this.registerModule('vyper'); this.registerModule('profiler'); this.registerModule('deploytracker'); diff --git a/lib/core/ipc.js b/lib/core/ipc.js new file mode 100644 index 00000000..e84e88ab --- /dev/null +++ b/lib/core/ipc.js @@ -0,0 +1,90 @@ +let fs = require('./fs.js'); +let ipc = require('node-ipc'); + +class IPC { + + constructor(options) { + this.logger = options.logger; + this.socketPath = options.socketPath || fs.dappPath(".embark/embark.ipc"); + this.ipcRole = options.ipcRole || "server"; + ipc.config.silent = true; + this.connected = false; + } + + connect(done) { + const self = this; + function connecting(_socket) { + let connectedBefore = false, alreadyDisconnected = false; + ipc.of['embark'].on('connect',function() { + connectedBefore = true; + if (!alreadyDisconnected) { + self.connected = true; + done(); + } + }); + ipc.of['embark'].on('disconnect',function() { + self.connected = false; + ipc.disconnect('embark'); + + // we only want to trigger the error callback the first time + if (!connectedBefore && !alreadyDisconnected) { + alreadyDisconnected = true; + done(new Error("no connection found")); + } + }); + } + + ipc.connectTo('embark', this.socketPath, connecting); + } + + serve() { + ipc.serve(this.socketPath, () => {}); + ipc.server.start(); + + this.logger.info(`pid ${process.pid} listening on ${this.socketPath}`); + } + + on(action, done) { + const self = this; + ipc.server.on('message', function(data, socket) { + if (data.action !== action) { + return; + } + let reply = function(replyData) { + self.reply(socket, 'compile', replyData); + }; + done(data.message, reply, socket); + }); + } + + reply(client, action, data) { + ipc.server.emit(client, 'message', {action: action, message: data}); + } + + once(action, cb) { + ipc.of['embark'].once('message', function(msg) { + if (msg.action !== action) { + return; + } + cb(msg.message); + }); + } + + request(action, data, cb) { + if (cb) { + this.once(action, cb); + } + ipc.of['embark'].emit('message', {action: action, message: data}); + } + + isClient() { + return this.ipcRole === 'client'; + } + + isServer() { + return this.ipcRole === 'server'; + } + +} + +module.exports = IPC; diff --git a/lib/modules/solidity/index.js b/lib/modules/solidity/index.js index c0d77188..f665144b 100644 --- a/lib/modules/solidity/index.js +++ b/lib/modules/solidity/index.js @@ -3,9 +3,10 @@ let SolcW = require('./solcW.js'); class Solidity { - constructor(embark, _options) { + constructor(embark, options) { this.logger = embark.logger; this.events = embark.events; + this.ipc = options.ipc; this.contractDirectories = embark.config.contractDirectories; this.solcAlreadyLoaded = false; this.solcW = null; @@ -48,7 +49,7 @@ class Solidity { if (self.solcAlreadyLoaded) { return callback(); } - self.solcW = new SolcW({logger: self.logger, events: self.events}); + self.solcW = new SolcW({logger: self.logger, events: self.events, ipc: self.ipc}); self.logger.info(__("loading solc compiler") + ".."); self.solcW.load_compiler(function (err) { diff --git a/lib/modules/solidity/solcW.js b/lib/modules/solidity/solcW.js index f13d2490..510ef98f 100644 --- a/lib/modules/solidity/solcW.js +++ b/lib/modules/solidity/solcW.js @@ -9,11 +9,27 @@ class SolcW { constructor(options) { this.logger = options.logger; this.events = options.events; + this.ipc = options.ipc; this.compilerLoaded = false; this.solcProcess = null; } load_compiler(done) { + const self = this; + if (!self.ipc.isClient()) { + return self.load_compiler_internally(done); + } + + self.ipc.connect((err) => { + if (err) { + return self.load_compiler_internally(done); + } + self.compilerLoaded = true; + done(); + }); + } + + load_compiler_internally(done) { const self = this; if (this.compilerLoaded) { return done(); @@ -30,6 +46,11 @@ class SolcW { done(); }); + if (this.ipc.isServer()) { + this.ipc.serve(); + this.ipc.on('compile', self.compile.bind(this)); + } + this.events.request("version:get:solc", function(solcVersion) { if (solcVersion === currentSolcVersion) { self.solcProcess.send({action: 'loadCompiler', requirePath: 'solc'}); @@ -51,6 +72,11 @@ class SolcW { compile(jsonObj, done) { const id = uuid(); + + if (this.ipc.isClient() && this.ipc.connected) { + return this.ipc.request('compile', jsonObj, done); + } + this.solcProcess.once('result', 'compilation-' + id, (msg) => { done(JSON.parse(msg.output)); }); diff --git a/lib/tests/test.js b/lib/tests/test.js index a935327f..eabb6812 100644 --- a/lib/tests/test.js +++ b/lib/tests/test.js @@ -64,7 +64,8 @@ class Test { web3: this.web3 }); this.engine.startService("deployment", { - trackContracts: false + trackContracts: false, + ipcRole: 'client' }); this.engine.startService("codeGenerator"); } diff --git a/package.json b/package.json index 6d013652..cd660c0a 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "ipfs-api": "17.2.4", "live-plugin-manager": "https://github.com/iurimatias/live-plugin-manager.git", "merge": "^1.2.0", + "node-ipc": "^9.1.1", "os-locale": "^2.1.0", "p-iteration": "^1.1.7", "parse-json": "^4.0.0", diff --git a/test/compiler.js b/test/compiler.js index 4bc48160..7f9a8649 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -2,6 +2,7 @@ let SolidityCompiler = require('../lib/modules/solidity'); let TestLogger = require('../lib/tests/test_logger.js'); let File = require('../lib/core/file.js'); +let Ipc = require('../lib/core/ipc.js'); let assert = require('assert'); let readFile = function(file) { @@ -16,6 +17,10 @@ var TestEvents = { } }; +let ipcObject = new Ipc({ + ipcRole: 'none' +}); + var apiObject = { registerCompiler: function() {}, logger: new TestLogger({}), @@ -27,7 +32,7 @@ var apiObject = { describe('embark.Compiler', function() { //let compiler = new Compiler({logger: new TestLogger({})}); - let compiler = new SolidityCompiler(apiObject); + let compiler = new SolidityCompiler(apiObject, {ipc: ipcObject}); describe('#compile_solidity', function() { this.timeout(0); diff --git a/test/contracts.js b/test/contracts.js index b8452b80..9662fa8b 100644 --- a/test/contracts.js +++ b/test/contracts.js @@ -5,6 +5,7 @@ let Logger = require('../lib/core/logger.js'); let File = require('../lib/core/file.js'); let TestLogger = require('../lib/tests/test_logger.js'); let Events = require('../lib/core/events'); +let Ipc = require('../lib/core/ipc.js'); let assert = require('assert'); //let SolidityCompiler = require('../lib/modules/solidity'); @@ -31,7 +32,10 @@ describe('embark.Contracts', function() { contractDirectories: ['app/contracts/'] } }); - plugins.loadInternalPlugin('solidity'); + let ipcObject = new Ipc({ + ipcRole: 'none' + }); + plugins.loadInternalPlugin('solidity', {ipc: ipcObject}); let compiler = new Compiler({plugins: plugins, logger: plugins.logger}); let events = new Events(); @@ -123,7 +127,10 @@ describe('embark.Contracts', function() { contractDirectories: ['app/contracts/'] } }); - plugins.loadInternalPlugin('solidity'); + let ipcObject = new Ipc({ + ipcRole: 'none' + }); + plugins.loadInternalPlugin('solidity', {ipc: ipcObject}); let compiler = new Compiler({plugins: plugins, logger: plugins.logger}); let events = new Events();