From 962c46977064e41f2faaa4cd4573aa7218c81ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Mar=C3=A9chal?= Date: Mon, 23 Mar 2020 16:27:25 -0400 Subject: [PATCH 1/3] electron: pass arguments to backend process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running a bundled application, the backend is forked. But any command line parameter passed to the Electron application isn't passed down to the backend. This commit makes sure that relevant arguments are passed to the forked backend process. Signed-off-by: Paul Maréchal --- .../application-manager/src/generator/frontend-generator.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index 6556fa4d3fc88..ad432e51e0b3a 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -346,7 +346,10 @@ app.on('ready', () => { app.exit(1); }); } else { - const cp = fork(mainPath, [], { env: Object.assign({ + // We want to pass flags passed to the Electron app to the backend process. + // Quirk: When developing from sources, we execute Electron as \`electron.exe electron-main.js ...args\`, but when bundled, + // the command looks like \`bundled-application.exe ...args\`. + const cp = fork(mainPath, process.argv.slice(devMode ? 2 : 1), { env: Object.assign({ [ElectronSecurityToken]: JSON.stringify(electronSecurityToken), }, process.env) }); cp.on('message', (address) => { From 5b6afb44438b268881af26378620537dfbccd293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Mar=C3=A9chal?= Date: Mon, 23 Mar 2020 16:30:45 -0400 Subject: [PATCH 2/3] electron: patch `process.versions.electron` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When forking, Electron 4 forgets to set `process.versions.electron` in the sub-process, making some packages wrongly guess their environment. This commit manually patches this variable. Signed-off-by: Paul Maréchal --- .../src/generator/backend-generator.ts | 6 ++++++ .../src/generator/frontend-generator.ts | 8 ++------ dev-packages/application-package/src/environment.ts | 4 +--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dev-packages/application-manager/src/generator/backend-generator.ts b/dev-packages/application-manager/src/generator/backend-generator.ts index d82b797f839d8..62304ebaa5637 100644 --- a/dev-packages/application-manager/src/generator/backend-generator.ts +++ b/dev-packages/application-manager/src/generator/backend-generator.ts @@ -27,6 +27,12 @@ export class BackendGenerator extends AbstractGenerator { protected compileServer(backendModules: Map): string { return `// @ts-check require('reflect-metadata'); + +// Patch electron version if missing, see https://github.com/eclipse-theia/theia/pull/7361#pullrequestreview-377065146 +if (typeof process.versions.electron === 'undefined' && typeof process.env.THEIA_ELECTRON_VERSION === 'string') { + process.versions.electron = process.env.THEIA_ELECTRON_VERSION; +} + const path = require('path'); const express = require('express'); const { Container } = require('inversify'); diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index ad432e51e0b3a..fb28c07c90420 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -327,12 +327,8 @@ app.on('ready', () => { // Set the electron version for both the dev and the production mode. (https://github.com/eclipse-theia/theia/issues/3254) // Otherwise, the forked backend processes will not know that they're serving the electron frontend. - const { versions } = process; - // @ts-ignore - if (versions && typeof versions.electron !== 'undefined') { - // @ts-ignore - process.env.THEIA_ELECTRON_VERSION = versions.electron; - } + // The forked backend should patch its \`process.versions.electron\` with this value if it is missing. + process.env.THEIA_ELECTRON_VERSION = process.versions.electron; const mainPath = join(__dirname, '..', 'backend', 'main'); // We need to distinguish between bundled application and development mode when starting the clusters. diff --git a/dev-packages/application-package/src/environment.ts b/dev-packages/application-package/src/environment.ts index a6439ce4a5eec..a15135d08cbc0 100644 --- a/dev-packages/application-package/src/environment.ts +++ b/dev-packages/application-package/src/environment.ts @@ -32,9 +32,7 @@ class ElectronEnv { * Can be called from both the `main` and the render process. Also works for forked cluster workers. */ is(): boolean { - // When forking a new process from the cluster, we can rely neither on `process.versions` nor `process.argv`. - // Se we look into the `process.env` as well. `is-electron` does not do it for us. - return isElectron() || typeof process !== 'undefined' && typeof process.env === 'object' && !!process.env.THEIA_ELECTRON_VERSION; + return isElectron(); } /** From 17142226eed8c12866475aab76d79e5ec31a8506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Mar=C3=A9chal?= Date: Mon, 23 Mar 2020 16:33:27 -0400 Subject: [PATCH 3/3] electron: always fork except if `--no-cluster` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated application code is not forking the backend when working from sources. Running the backend in the Electron main process was only meant to help with debugging. This commit makes the generated code fork the backend unless a `--no-cluster` flag is specified. Signed-off-by: Paul Maréchal --- .../src/generator/frontend-generator.ts | 9 ++++++--- dev-packages/cli/README.md | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index fb28c07c90420..b3e7c79176391 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -290,6 +290,8 @@ app.on('ready', () => { // Check whether we are in bundled application or development mode. // @ts-ignore const devMode = process.defaultApp || /node_modules[\/]electron[\/]/.test(process.execPath); + // Check if we should run everything as one process. + const noBackendFork = process.argv.includes('--no-cluster'); const mainWindow = createNewWindow(); if (isSingleInstance) { @@ -331,9 +333,10 @@ app.on('ready', () => { process.env.THEIA_ELECTRON_VERSION = process.versions.electron; const mainPath = join(__dirname, '..', 'backend', 'main'); - // We need to distinguish between bundled application and development mode when starting the clusters. - // See: https://github.com/electron/electron/issues/6337#issuecomment-230183287 - if (devMode) { + // We spawn a separate process for the backend for Express to not run in the Electron main process. + // See: https://github.com/eclipse-theia/theia/pull/7361#issuecomment-601272212 + // But when in debugging we want to run everything in the same process to make things easier. + if (noBackendFork) { process.env[ElectronSecurityToken] = JSON.stringify(electronSecurityToken); require(mainPath).then(address => { loadMainWindow(address.port); diff --git a/dev-packages/cli/README.md b/dev-packages/cli/README.md index b9bea05e4c3a2..e95f14c4ec342 100644 --- a/dev-packages/cli/README.md +++ b/dev-packages/cli/README.md @@ -34,6 +34,7 @@ - [**Inspecting Tests**](#inspecting-tests) - [**Reporting Test Coverage**](#reporting-test-coverage) - [**Downloading Plugins**](#downloading-plugins) +- [**Autogenerated Application**](#autogenerated-application) ## Description @@ -270,6 +271,20 @@ The property `theiaPlugins` describes the list of plugins to download, for examp } ``` +## Autogenerated Application + +This package can auto-generate application code for both the backend and frontend, as well as webpack configuration files. + +When targeting Electron, the `electron-main.js` script will spawn the backend process in a Node.js sub-process, where Electron's API won't be available. To prevent the generated application from forking the backend, you can pass a `--no-cluster` flag. This flag is mostly useful/used for debugging. + +```sh +# when developing a Theia application with @theia/cli: +yarn theia start --no-cluster + +# when starting a bundled application made using @theia/cli: +bundled-application.exe --no-cluster +``` + ## Additional Information - [Theia - GitHub](https://github.com/eclipse-theia/theia)