Skip to content

Commit

Permalink
electron: fix backend forking
Browse files Browse the repository at this point in the history
This commit fixes 3 issues:

1. 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.

2. The generated application code also used to not fork 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.

3. When running a bundled application, the backend was forked. But any
command line parameter passed to the Electron application wasn't passing
any argument down to the backend. This commit makes sure that relevant
arguments are passed to the forked backend process.

Signed-off-by: Paul Maréchal <paul.marechal@ericsson.com>
  • Loading branch information
paul-marechal committed Mar 23, 2020
1 parent ae5591e commit 97e5e66
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 14 deletions.
17 changes: 17 additions & 0 deletions dev-packages/application-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@

</div>

## README

This package contains the generators used when creating a Theia application through `@theia/cli`.

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, or set an environment variable like `THEIA_ELECTRON_NO_BACKEND_FORK=1`.

```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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export class BackendGenerator extends AbstractGenerator {
protected compileServer(backendModules: Map<string, string>): 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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 user wants to run everything as one process.
const noBackendFork = Boolean(process.env['THEIA_ELECTRON_NO_BACKEND_FORK']) || process.argv.includes('--no-cluster');
const mainWindow = createNewWindow();
if (isSingleInstance) {
Expand Down Expand Up @@ -327,17 +329,14 @@ 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.
// 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);
Expand All @@ -346,7 +345,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) => {
Expand Down
6 changes: 2 additions & 4 deletions dev-packages/application-package/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ class ElectronEnv {
/**
* `true` if running in electron. Otherwise, `false`.
*
* Can be called from both the `main` and the render process. Also works for forked cluster workers.
* Can be called from both the `main` and the render process.
*/
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();
}

/**
Expand Down

0 comments on commit 97e5e66

Please sign in to comment.