Skip to content

Commit 9fb7d0c

Browse files
committed
fix(build): sur Linux, ne vendoriser que le SDK ffmpeg
Deux collisions dans electron/native/bin/linux-x64/, introduites en branchant fetch:ffmpeg sur build:linux. La première a cassé le build : le CLI statique est vendorisé sous le nom `ffmpeg` (un FICHIER), alors que build-linux-pipewire-helper.mjs y crée un RÉPERTOIRE `ffmpeg/` — le nom contre lequel le RUNPATH $ORIGIN/ffmpeg du helper est compilé. D'où `EEXIST: mkdir .../linux-x64/ffmpeg`. La seconde n'avait pas encore frappé et aurait été pire à diagnostiquer : les copies runtime des .so partaient elles aussi dans ce répertoire, NON renommées, sous les mêmes noms que les copies à symboles renommés (osff_*) qu'y place build-linux-compositor-addon.mjs pour que l'addon ne se lie pas au ffmpeg de Chromium. L'une aurait écrasé l'autre, et l'addon serait mort au chargement. Ce répertoire appartient donc aux deux scripts natifs ; fetch-ffmpeg n'a rien à y faire sur Linux. La copie runtime est restreinte à Windows, où le loader cherche bien les DLL à côté de l'exécutable, et `--sdk-only` (nouveau script fetch:ffmpeg:sdk) saute le CLI statique. Le SDK, lui, reste nécessaire : pipewire-capture et l'addon compositeur linkent contre lui. Le CLI n'est pas une perte : assertLgpl note que plus rien dans l'app ne lance ffmpeg, et v1.7.0 livrait AppImage/deb/pacman sans. Vérifié depuis un état vierge sur Linux : les cinq bibliothèques que build.rs demande sont résolvables, les en-têtes sont en place, et le chemin linux-x64/ffmpeg est libre pour le mkdir du helper.
1 parent ca13518 commit 9fb7d0c

2 files changed

Lines changed: 46 additions & 18 deletions

File tree

‎package.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"build:native:linux": "node scripts/build-linux-pipewire-helper.mjs",
3737
"build:win": "npm run build:native:win && npm run fetch:ffmpeg && npm run build:native:compositor && tsc && vite build && electron-builder --win --config.npmRebuild=false",
3838
"build:win:store": "npm run build:native:win && npm run fetch:ffmpeg && npm run build:native:compositor && tsc && vite build && electron-builder --win appx --config.npmRebuild=false",
39-
"build:linux": "npm run fetch:ffmpeg && npm run build:native:linux && npm run build:native:compositor:linux && tsc && vite build && electron-builder --linux AppImage deb pacman --config.npmRebuild=false",
39+
"build:linux": "npm run fetch:ffmpeg:sdk && npm run build:native:linux && npm run build:native:compositor:linux && tsc && vite build && electron-builder --linux AppImage deb pacman --config.npmRebuild=false",
4040
"build:whisper-binaries": "bash scripts/build-whisper-stt.sh",
4141
"test:whisper-stt": "node scripts/test-whisper-stt.mjs",
4242
"test": "vitest --run",
@@ -66,7 +66,8 @@
6666
"test:e2e:windows-native-checklist": "playwright test tests/e2e/windows-native-checklist.spec.ts",
6767
"prepare": "husky",
6868
"fetch:ffmpeg": "node scripts/fetch-ffmpeg.mjs",
69-
"fetch:ffmpeg:mac": "node scripts/fetch-ffmpeg-macos.mjs"
69+
"fetch:ffmpeg:mac": "node scripts/fetch-ffmpeg-macos.mjs",
70+
"fetch:ffmpeg:sdk": "node scripts/fetch-ffmpeg.mjs --sdk-only"
7071
},
7172
"dependencies": {
7273
"@fix-webm-duration/fix": "^1.0.1",

‎scripts/fetch-ffmpeg.mjs‎

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -387,9 +387,11 @@ async function fetchSharedDlls(tag, binDir) {
387387

388388
// probe for any previously vendored DLL by name; re-download is driven by
389389
// --force same as the static exe, checked once we know what we'd extract.
390-
const alreadyVendored = fs
391-
.readdirSync(binDir, { withFileTypes: true })
392-
.some((e) => e.isFile() && isSharedLib(e.name) && /^(lib)?av/i.test(e.name));
390+
const alreadyVendored =
391+
process.platform === "win32" &&
392+
fs
393+
.readdirSync(binDir, { withFileTypes: true })
394+
.some((e) => e.isFile() && isSharedLib(e.name) && /^(lib)?av/i.test(e.name));
393395
// The build-time SDK comes out of this same archive, so a tree that has the
394396
// DLLs but not the SDK must still re-download — otherwise we skip here and
395397
// the compositor build fails afterwards on the missing FFMPEG_DIR.
@@ -426,21 +428,33 @@ async function fetchSharedDlls(tag, binDir) {
426428
const libs = findSharedLibs(tmp);
427429
if (libs.length === 0) throw new Error(`No shared ffmpeg libraries found inside ${spec.asset}`);
428430

429-
fs.mkdirSync(binDir, { recursive: true });
430-
for (const lib of libs) {
431-
// Linux ships symlink chains (libavcodec.so -> .so.62 -> .so.62.x). Follow
432-
// them: copyFileSync would dereference into three identical large files, and
433-
// a dangling link would break the loader outright.
434-
const dest = path.join(binDir, path.basename(lib));
435-
const st = fs.lstatSync(lib);
436-
if (st.isSymbolicLink()) {
437-
fs.rmSync(dest, { force: true });
438-
fs.symlinkSync(fs.readlinkSync(lib), dest);
439-
} else {
440-
fs.copyFileSync(lib, dest);
431+
// Windows only. There, the loader finds a DLL next to the .exe, so the runtime
432+
// copies belong in binDir. On Linux that same directory is owned by the two
433+
// native build scripts: build-linux-compositor-addon.mjs puts SYMBOL-RENAMED
434+
// (osff_*) copies there so the addon cannot bind to Chromium's ffmpeg, and
435+
// build-linux-pipewire-helper.mjs stages unrenamed ones in `binDir/ffmpeg/`
436+
// for the helper's `$ORIGIN/ffmpeg` RUNPATH. Dropping a third, unrenamed set
437+
// in binDir would overwrite the renamed ones under identical filenames and
438+
// break the addon at load time. Linux takes the SDK below and nothing else.
439+
if (process.platform !== "win32") {
440+
console.log(`Skipping runtime copies into ${binDir} (owned by the native build scripts).`);
441+
} else {
442+
fs.mkdirSync(binDir, { recursive: true });
443+
for (const lib of libs) {
444+
// Linux ships symlink chains (libavcodec.so -> .so.62 -> .so.62.x). Follow
445+
// them: copyFileSync would dereference into three identical large files, and
446+
// a dangling link would break the loader outright.
447+
const dest = path.join(binDir, path.basename(lib));
448+
const st = fs.lstatSync(lib);
449+
if (st.isSymbolicLink()) {
450+
fs.rmSync(dest, { force: true });
451+
fs.symlinkSync(fs.readlinkSync(lib), dest);
452+
} else {
453+
fs.copyFileSync(lib, dest);
454+
}
441455
}
456+
console.log(`Vendored ${libs.length} shared librar(ies) -> ${binDir}`);
442457
}
443-
console.log(`Vendored ${libs.length} shared librar(ies) -> ${binDir}`);
444458
if (sdkDest) vendorFfmpegSdk(tmp, sdkDest);
445459
console.log("LGPL verified: safe to ship with an MIT app.");
446460
} finally {
@@ -469,6 +483,19 @@ async function main() {
469483
const binDir = path.join(ROOT, "electron", "native", "bin", tag);
470484
const dest = path.join(binDir, spec.exe);
471485

486+
// `--sdk-only` skips the standalone ffmpeg CLI and vendors just the build-time
487+
// SDK. Linux needs it: the CLI lands at `<binDir>/ffmpeg` as a FILE, while
488+
// build-linux-pipewire-helper.mjs stages the helper's libraries into
489+
// `<binDir>/ffmpeg/` as a DIRECTORY — the name its `$ORIGIN/ffmpeg` RUNPATH is
490+
// compiled against. One clobbers the other (`EEXIST: mkdir .../linux-x64/ffmpeg`).
491+
// Nothing in the app spawns the CLI any more (see assertLgpl's note), and v1.7.0
492+
// shipped Linux packages without it, so on Linux it is dead weight AND a conflict.
493+
if (process.argv.includes("--sdk-only")) {
494+
console.log(`Skipping the standalone ffmpeg CLI (--sdk-only).`);
495+
await fetchSharedDlls(tag, binDir);
496+
return;
497+
}
498+
472499
if (fs.existsSync(dest) && !process.argv.includes("--force")) {
473500
console.log(`Already present: ${dest}`);
474501
console.log(assertLgpl(dest));

0 commit comments

Comments
 (0)