Skip to content

Commit 9edfee6

Browse files
authored
feat: yarn 3 support for native modules via new electron/rebuild compilation (#8112)
1 parent 3d4cc7a commit 9edfee6

14 files changed

Lines changed: 743 additions & 30 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"app-builder-lib": minor
3+
---
4+
5+
feat: implementing electron/rebuild with config option `useLegacyRebuilder` default: `true` to support Yarn 3

.github/actions/pretest/action.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ runs:
2020
with:
2121
version: ${{ inputs.version }}
2222

23+
- name: Setup python
24+
uses: actions/setup-python@v5
25+
with:
26+
python-version: '3.11'
27+
2328
- name: Setup node
2429
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4
2530
with:

docs/configuration/configuration.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ Env file `electron-builder.env` in the current dir ([example](https://github.com
9393
<p><code id="Configuration-npmRebuild">npmRebuild</code> = <code>true</code> Boolean - Whether to <a href="https://docs.npmjs.com/cli/rebuild">rebuild</a> native dependencies before starting to package the app.</p>
9494
</li>
9595
<li>
96+
<p><code id="Configuration-nativeRebuilder">nativeRebuilder</code> = <code>legacy</code> “legacy” | “sequential” | “parallel” | “undefined” - Use <code>legacy</code> app-builder binary for installing native dependencies, or @electron/rebuild in <code>sequential</code> or <code>parallel</code> compilation modes.</p>
97+
</li>
98+
<li>
9699
<p><code id="Configuration-buildNumber">buildNumber</code> String | “undefined” - The build number. Maps to the <code>--iteration</code> flag for builds using FPM on Linux. If not defined, then it will fallback to <code>BUILD_NUMBER</code> or <code>TRAVIS_BUILD_NUMBER</code> or <code>APPVEYOR_BUILD_NUMBER</code> or <code>CIRCLE_BUILD_NUM</code> or <code>BUILD_BUILDNUMBER</code> or <code>CI_PIPELINE_IID</code> env.</p>
97100
</li>
98101
</ul>

packages/app-builder-lib/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"@develar/schema-utils": "~2.6.5",
5050
"@electron/notarize": "2.2.1",
5151
"@electron/osx-sign": "1.0.5",
52+
"@electron/rebuild": "3.6.0",
5253
"@electron/universal": "1.5.1",
5354
"@malept/flatpak-bundler": "^0.4.0",
5455
"@types/fs-extra": "9.0.13",

packages/app-builder-lib/scheme.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7463,6 +7463,23 @@
74637463
}
74647464
]
74657465
},
7466+
"nativeRebuilder": {
7467+
"anyOf": [
7468+
{
7469+
"enum": [
7470+
"legacy",
7471+
"parallel",
7472+
"sequential"
7473+
],
7474+
"type": "string"
7475+
},
7476+
{
7477+
"type": "null"
7478+
}
7479+
],
7480+
"default": "legacy",
7481+
"description": "Use `legacy` app-builder binary for installing native dependencies, or @electron/rebuild in `sequential` or `parallel` compilation modes."
7482+
},
74667483
"nodeGypRebuild": {
74677484
"default": false,
74687485
"description": "Whether to execute `node-gyp rebuild` before starting to package the app.\n\nDon't [use](https://github.com/electron-userland/electron-builder/issues/683#issuecomment-241214075) [npm](http://electron.atom.io/docs/tutorial/using-native-node-modules/#using-npm) (neither `.npmrc`) for configuring electron headers. Use `electron-builder node-gyp-rebuild` instead.",

packages/app-builder-lib/src/configuration.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ export interface Configuration extends PlatformSpecificBuildOptions {
131131
* @default true
132132
*/
133133
readonly npmRebuild?: boolean
134+
/**
135+
* Use `legacy` app-builder binary for installing native dependencies, or @electron/rebuild in `sequential` or `parallel` compilation modes.
136+
* @default legacy
137+
*/
138+
readonly nativeRebuilder?: "legacy" | "sequential" | "parallel" | null
134139

135140
/**
136141
* The build number. Maps to the `--iteration` flag for builds using FPM on Linux.

packages/app-builder-lib/src/electron/electronVersion.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { getProjectRootPath } from "@electron/rebuild/lib/search-module"
2+
13
import { InvalidConfigurationError, log } from "builder-util"
24
import { parseXml } from "builder-util-runtime"
35
import { httpExecutor } from "builder-util/out/nodeHttpExecutor"
@@ -56,8 +58,15 @@ export async function computeElectronVersion(projectDir: string): Promise<string
5658
return result
5759
}
5860

59-
const metadata = await orNullIfFileNotExist(readJson(path.join(projectDir, "package.json")))
60-
const dependency = metadata ? findFromPackageMetadata(metadata) : null
61+
const potentialRootDirs = [projectDir, await getProjectRootPath(projectDir)]
62+
let dependency: NameAndVersion | null = null
63+
for await (const dir of potentialRootDirs) {
64+
const metadata = await orNullIfFileNotExist(readJson(path.join(dir, "package.json")))
65+
dependency = metadata ? findFromPackageMetadata(metadata) : null
66+
if (dependency) {
67+
break
68+
}
69+
}
6170
if (dependency?.name === "electron-nightly") {
6271
log.info("You are using a nightly version of electron, be warned that those builds are highly unstable.")
6372
const feedXml = await httpExecutor.request({

packages/app-builder-lib/src/util/packageMetadata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export function checkMetadata(metadata: Metadata, devMetadata: any | null, appPa
7070
const devDependencies = (metadata as any).devDependencies
7171
if (devDependencies != null && ("electron-rebuild" in devDependencies || "@electron/rebuild" in devDependencies)) {
7272
log.info(
73-
'@electron/rebuild not required if you use electron-builder, please consider to remove excess dependency from devDependencies\n\nTo ensure your native dependencies are always matched electron version, simply add script `"postinstall": "electron-builder install-app-deps" to your `package.json`'
73+
'@electron/rebuild already used by electron-builder, please consider to remove excess dependency from devDependencies\n\nTo ensure your native dependencies are always matched electron version, simply add script `"postinstall": "electron-builder install-app-deps" to your `package.json`'
7474
)
7575
}
7676

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import * as cp from "child_process"
2+
import * as path from "path"
3+
import { RebuildOptions } from "@electron/rebuild"
4+
import { log } from "builder-util"
5+
6+
export const rebuild = async (options: RebuildOptions): Promise<void> => {
7+
const { arch } = options
8+
log.info({ arch }, `installing native dependencies`)
9+
10+
const child = cp.fork(path.resolve(__dirname, "remote-rebuild.js"), [JSON.stringify(options)], {
11+
stdio: ["pipe", "pipe", "pipe", "ipc"],
12+
})
13+
14+
let pendingError: Error
15+
16+
child.stdout?.on("data", chunk => {
17+
log.info(chunk.toString())
18+
})
19+
child.stderr?.on("data", chunk => {
20+
log.error(chunk.toString())
21+
})
22+
23+
child.on("message", (message: { msg: string; moduleName: string; err: { message: string; stack: string } }) => {
24+
const { moduleName, msg } = message
25+
switch (msg) {
26+
case "module-found": {
27+
log.info({ moduleName, arch }, "preparing")
28+
break
29+
}
30+
case "module-done": {
31+
log.info({ moduleName, arch }, "finished")
32+
break
33+
}
34+
case "module-skip": {
35+
log.debug?.({ moduleName, arch }, "skipped. set ENV=electron-rebuild to determine why")
36+
break
37+
}
38+
case "rebuild-error": {
39+
pendingError = new Error(message.err.message)
40+
pendingError.stack = message.err.stack
41+
break
42+
}
43+
case "rebuild-done": {
44+
log.info("completed installing native dependencies")
45+
break
46+
}
47+
}
48+
})
49+
50+
await new Promise<void>((resolve, reject) => {
51+
child.on("exit", code => {
52+
if (code === 0 && !pendingError) {
53+
resolve()
54+
} else {
55+
reject(pendingError || new Error(`Rebuilder failed with exit code: ${code}`))
56+
}
57+
})
58+
})
59+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { rebuild, RebuildOptions } from "@electron/rebuild"
2+
3+
if (!process.send) {
4+
console.error("The remote rebuilder expects to be spawned with an IPC channel")
5+
process.exit(1)
6+
}
7+
8+
const options: RebuildOptions = JSON.parse(process.argv[2])
9+
10+
const rebuilder = rebuild(options)
11+
12+
rebuilder.lifecycle.on("module-found", (moduleName: string) => process.send?.({ msg: "module-found", moduleName }))
13+
rebuilder.lifecycle.on("module-done", (moduleName: string) => process.send?.({ msg: "module-done", moduleName }))
14+
rebuilder.lifecycle.on("module-skip", (moduleName: string) => process.send?.({ msg: "module-skip", moduleName }))
15+
16+
rebuilder
17+
.then(() => {
18+
process.send?.({ msg: "rebuild-done" })
19+
return process.exit(0)
20+
})
21+
.catch(err => {
22+
process.send?.({
23+
msg: "rebuild-error",
24+
err: {
25+
message: err.message,
26+
stack: err.stack,
27+
},
28+
})
29+
process.exit(0)
30+
})

0 commit comments

Comments
 (0)