const EventEmitter = require('events'); const path = require('path'); const crossSpawn = require('cross-spawn'); const Log = require('../log'); const Ps = require('../ps'); const Channel = require('../const/channel'); const EE = require('../ee'); const Helper = require('../utils/helper'); const UtilsIs = require('../utils/is'); const UtilsPargv = require('../utils/pargv'); class SpawnProcess { constructor(host, opt = {}) { this.emitter = new EventEmitter(); this.host = host; this.child = undefined; this.pid = 0; this.port = 0; this.name = ""; this.config = {}; this._init(opt); } /** * 初始化子进程 */ _init(options = {}) { const { targetConf, port } = options; this.config = targetConf; this.port = port; // 该名称如果在childrenMap重复,会被重写 this.name = targetConf.name; // Launch executable program let cmdPath = ''; let cmdArgs = targetConf.args; let execDir = Ps.getExtraResourcesDir(); let standardOutput = ['inherit', 'inherit', 'inherit', 'ipc']; if (Ps.isPackaged()) { standardOutput = ['ignore', 'ignore', 'ignore', 'ipc']; } if (targetConf.stdio) { standardOutput = targetConf.stdio; } const { cmd, directory } = targetConf; // use cmd first if (cmd) { if (!directory) { throw new Error(`[ee-core] [cross] The config [directory] attribute does not exist`); } cmdPath = cmd; if (!path.isAbsolute(cmd) && !Ps.isDev()) { cmdPath = path.join(Ps.getExtraResourcesDir(), cmd); } } else { cmdPath = path.join(Ps.getExtraResourcesDir(), targetConf.name); } // windows if (UtilsIs.windows() && path.extname(cmdPath) != '.exe') { // Complete the executable program extension // notice: python.exe may bring up the App Store if (targetConf.windowsExtname === true || !Ps.isDev()) { cmdPath += ".exe"; } } // executable program directory if (directory && path.isAbsolute(directory)) { execDir = directory; } else if (directory && !path.isAbsolute(directory)) { if (Ps.isDev()) { execDir = path.join(Ps.getHomeDir(), directory); } else { execDir = path.join(Ps.getExtraResourcesDir(), directory); } } else { execDir = Ps.getExtraResourcesDir(); } Log.coreLogger.info(`[ee-core] [cross/run] cmd: ${cmdPath}, args: ${cmdArgs}`); const coreProcess = crossSpawn(cmdPath, cmdArgs, { stdio: standardOutput, detached: false, cwd: execDir, maxBuffer: 1024 * 1024 * 1024 }); this.child = coreProcess; this.pid = coreProcess.pid; coreProcess.on('exit', (code, signal) => { let data = { pid: this.pid } this.host.emitter.emit(Channel.events.childProcessExit, data); Log.coreLogger.info(`[ee-core] [corss/process] received a exit from child-process, code:${code}, signal:${signal}, pid:${this.pid}`); this._exitElectron(); }); coreProcess.on('error', (err) => { let data = { pid: this.pid } this.host.emitter.emit(Channel.events.childProcessError, data); Log.coreLogger.error(`[ee-core] [corss/process] received a error from child-process, error: ${err}, pid:${this.pid}`); this._exitElectron(); }); } /** * kill */ kill(timeout = 1000) { this.child.kill('SIGINT'); setTimeout(() => { if (this.child.killed) return; this.child.kill('SIGKILL'); }, timeout) } // send(message) { // return this.sendByType(message, 'message'); // } // close() { // return this.sendByType('close', 'close'); // } // async sendByType(message, type) { // const msg = typeof message === 'string' ? message : JSON.stringify(message); // const id = this._generateId(); // this.child.send({ // id, // type, // data: msg, // }); // return; // } getUrl() { const ssl = Helper.getValueFromArgv(this.config.args, 'ssl'); let hostname = Helper.getValueFromArgv(this.config.args, 'hostname') let protocol = 'http://'; if (ssl && (ssl == 'true' || ssl == '1')) { protocol = 'https://'; } hostname = hostname ? hostname : '127.0.0.1'; const url = protocol + hostname + ":" + this.port; return url; } getArgsObj() { const obj = UtilsPargv(this.config.args); return obj; } setPort(port) { this.port = parseInt(port); } _generateId() { const rid = Helper.getRandomString(); return `node:${this.pid}:${rid}`; } /** * exit electron */ _exitElectron(timeout = 1000) { const { CoreApp } = EE; if (this.config.appExit) { setTimeout(() => { // 进程退出前的一些清理工作 CoreApp.appQuit(); }, timeout) } } } module.exports = SpawnProcess;