electron-builder
electron-builder copied to clipboard
Build error "EEXIST: file already exists" caused by `app-builder-bin`
- Electron-Builder Version: 23.3.3
- Node Version: v16.16.0
- Electron Version: 20.0.2
- Electron Type (current, beta, nightly):
- Target: dir for debugging
Abstract
Met the same build error at Github actions like #3039 / #3179 , after some console.log debugging and I found that the root cause of it was app-builder
.
Project structure
~/PROJECTS/electron/bug-root: tree -L 2 .
├── app-root
│ ├── dist
│ ├── index.js
│ ├── node_modules
│ ├── package.json
│ └── package-lock.json
├── node_modules
│ ├── fs-extra
│ ├── graceful-fs
│ ├── jsonfile
│ └── universalify
├── package.json
└── package-lock.json
8 directories, 5 files
package.json in /bug-root:
{
"name": "bug-root",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"fs-extra": "^10.1.0"
},
"keywords": [],
"author": "",
"license": "ISC"
}
package.json in /bug-root/app-root/:
{
"name": "test",
"version": "0.0.1",
"dependencies": {
"fs-extra": "^10.1.0"
},
"devDependencies": {
"electron": "20.0.2"
},
"build": {
"files": [
"**/*"
],
"linux": {
"target": "dir",
"asar": false
}
}
}
Both the root and the app folder has fs-extra
as dependency.
Run app-builder
Run app-builder node-dep-tree --dir=./bug-root/app-root | jq
got:
[
{
"dir": "/home/zc/PROJECTS/electron/bug-root/node_modules",
"deps": [
{
"name": "graceful-fs",
"version": "4.2.10",
"optional": true
},
{
"name": "universalify",
"version": "2.0.0"
}
]
},
{
"dir": "/home/zc/PROJECTS/electron/bug-root/app-root/node_modules",
"deps": [
{
"name": "fs-extra",
"version": "10.1.0"
},
{
"name": "graceful-fs",
"version": "4.2.10"
},
{
"name": "jsonfile",
"version": "6.1.0"
},
{
"name": "universalify",
"version": "2.0.0"
}
]
}
]
Seems like graceful-fs
& universalify
would be copied from both /bug-root/node_modules
& /bug-root/app-root/node_modules.
After I move /app-root out of /bug-root to /tmp dir, the build process is done suceessfully, and the output of app-builder
after then was like:
[
{
"dir": "/tmp/app-root/node_modules",
"deps": [
{
"name": "fs-extra",
"version": "10.1.0"
},
{
"name": "graceful-fs",
"version": "4.2.10"
},
{
"name": "jsonfile",
"version": "6.1.0"
},
{
"name": "universalify",
"version": "2.0.0"
}
]
}
]
Some printf log proof of work
Add some console.log to app-builder-lib/src/util/appFileCopier.ts
export async function copyAppFiles(fileSet: ResolvedFileSet, packager: Packager, transformer: FileTransformer) {
const metadata = fileSet.metadata
// search auto unpacked dir
const taskManager = new AsyncTaskManager(packager.cancellationToken)
const createdParentDirs = new Set<string>()
const fileCopier = new FileCopier(file => {
// https://github.com/electron-userland/electron-builder/issues/3038
return !(isLibOrExe(file) || file.endsWith(".node"))
}, transformer)
const links: Array<Link> = []
for (let i = 0, n = fileSet.files.length; i < n; i++) {
const sourceFile = fileSet.files[i]
const stat = metadata.get(sourceFile)
if (stat == null) {
// dir
continue
}
const destinationFile = getDestinationPath(sourceFile, fileSet)
console.log("=== copy", sourceFile, destinationFile) // <------------
if (stat.isSymbolicLink()) {
links.push({ file: destinationFile, link: await readlink(sourceFile) })
continue
}
(...)
}
And we could find we did copy graceful-fs
twice.
console.log
=== copy /home/runner/work/electron-asar-differential-builder-debug/electron-asar-differential-builder-debug/node_modules/graceful-fs/LICENSE /home/runner/work/electron-asar-differential-builder-debug/electron-asar-differential-builder-debug/test/dist/unit-nle9lzyn/linux_includesBinaryDir-etwggc0g/linux-unpacked/resources/app/node_modules/graceful-fs/LICENSE
at Object.copyAppFiles (../node_modules/electron-builder/node_modules/app-builder-lib/src/util/appFileCopier.ts:65:7)
console.log
=== copy /home/runner/work/electron-asar-differential-builder-debug/electron-asar-differential-builder-debug/test/dist/unit-nle9lzyn/app-root-0.0.1/node_modules/graceful-fs/LICENSE /home/runner/work/electron-asar-differential-builder-debug/electron-asar-differential-builder-debug/test/dist/unit-nle9lzyn/linux_includesBinaryDir-etwggc0g/linux-unpacked/resources/app/node_modules/graceful-fs/LICENSE
at Object.copyAppFiles (../node_modules/electron-builder/node_modules/app-builder-lib/src/util/appFileCopier.ts:65:7)
The error log:
Error #1 --------------------------------------------------------------------------------
Error: EEXIST: file already exists, link '/home/runner/work/electron-asar-differential-builder-debug/electron-asar-differential-builder-debug/test/dist/unit-nle9lzyn/app-root-0.0.1/node_modules/graceful-fs/LICENSE' -> '/home/runner/work/electron-asar-differential-builder-debug/electron-asar-differential-builder-debug/test/dist/unit-nle9lzyn/linux_includesBinaryDir-etwggc0g/linux-unpacked/resources/app/node_modules/graceful-fs/LICENSE'
Error #2 --------------------------------------------------------------------------------
Error: EEXIST: file already exists, link '/home/runner/work/electron-asar-differential-builder-debug/electron-asar-differential-builder-debug/test/dist/unit-nle9lzyn/app-root-0.0.1/node_modules/graceful-fs/clone.js' -> '/home/runner/work/electron-asar-differential-builder-debug/electron-asar-differential-builder-debug/test/dist/unit-nle9lzyn/linux_includesBinaryDir-etwggc0g/linux-unpacked/resources/app/node_modules/graceful-fs/clone.js'
(...)
Final
As folks in #3179 point out that we could mitigate this with USE_HARD_LINKS=false
, but to fix this I think we could add a deduplicate step before file copy.
Happy to review a PR for your suggestion! I actually am very unfamiliar with the file-copying code, I just haven't been able to wrap my head around it :/