fix(embedded-mpv): require bundled Linux runtime

This commit is contained in:
4gray committed 2026-07-17 22:42:36 +02:00
1 parent 0fb9ee2789
commit 84ff8de04a
3 files changed
+119 -19

No files matched your search

+68 -10
View File
@@ -20,6 +20,7 @@ const {
const LINUX_PACKAGE_RUNTIME_MODES = Object.freeze(['system', 'bundled']);
const LINUX_STAGED_RUNTIME_ORIGIN = 'vendored-lgpl';
const LINUX_SOURCE_RUNTIME_ORIGIN = 'vendored-lgpl-source-build';
const VERSIONED_LINUX_LIBMPV_PATTERN = /^libmpv\.so\.\d+(?:\.\d+)*$/;
const workspaceRoot = process.cwd();
const addonRoot = path.join(
@@ -233,7 +234,7 @@ function validatedLinuxSourceRuntime(runtimeRoot) {
return {
buildInputMode,
sourceRuntimeManifest,
sourceRuntimeValidated: true,
sourceRuntimeValidated: buildInputMode === 'bundled-runtime',
};
}
@@ -381,12 +382,6 @@ function resolveRuntime() {
const systemIncludeDir =
process.env.LIBMPV_INCLUDE_DIR || '/usr/include';
if (fs.existsSync(path.join(systemIncludeDir, 'mpv', 'client.h'))) {
if (embeddedMpvRequired) {
throw new Error(
'Required Linux builds must use an explicit staged runtime manifest.'
);
}
const sourceRuntimeManifest = {
linuxBackend: 'process-isolated mpv --wid',
warning: 'Development-only unmanaged system libmpv toolchain.',
@@ -431,6 +426,61 @@ function resolveRuntime() {
return null;
}
function hasStagedLinuxLibMpvLinkerInput(runtime) {
return (
Array.isArray(runtime.sourceRuntimeManifest?.runtimeFiles) &&
runtime.sourceRuntimeManifest.runtimeFiles.some(
(runtimeFile) => runtimeFile.name === 'libmpv.so'
)
);
}
function assertRequiredLinuxFrameCopyRuntime(runtime) {
if (targetPlatform !== 'linux' || !embeddedMpvRequired) {
return;
}
if (
runtime.buildInputMode !== 'bundled-runtime' ||
runtime.sourceRuntimeValidated !== true ||
!hasStagedLinuxLibMpvLinkerInput(runtime)
) {
cleanOutput();
throw new Error(
'Required Linux builds must use the validated bundled source runtime containing staged libmpv.'
);
}
}
function deriveLinuxLibMpvSoname(runtime) {
if (
runtime.buildInputMode !== 'bundled-runtime' ||
runtime.sourceRuntimeValidated !== true ||
!Array.isArray(runtime.sourceRuntimeManifest?.runtimeFiles)
) {
return null;
}
const candidates = runtime.sourceRuntimeManifest.runtimeFiles
.map((runtimeFile) => runtimeFile.name)
.filter((fileName) => VERSIONED_LINUX_LIBMPV_PATTERN.test(fileName))
.sort((left, right) => {
const leftVersionParts = left
.slice('libmpv.so.'.length)
.split('.').length;
const rightVersionParts = right
.slice('libmpv.so.'.length)
.split('.').length;
return (
leftVersionParts - rightVersionParts ||
left.length - right.length ||
left.localeCompare(right)
);
});
return candidates[0] ?? null;
}
function copyLinuxRuntimeClosureToNativeBuild(runtime) {
fs.rmSync(outputLibDir, { recursive: true, force: true });
@@ -490,6 +540,12 @@ function copyLinuxRuntimeClosureToNativeBuild(runtime) {
function writeLinuxFrameCopyBuildManifest(runtime) {
const copiedRuntimeFiles = copyLinuxRuntimeClosureToNativeBuild(runtime);
const libmpvSoname = deriveLinuxLibMpvSoname(runtime);
const packageRuntimeAvailable =
runtime.sourceRuntimeValidated === true &&
runtime.buildInputMode === 'bundled-runtime' &&
copiedRuntimeFiles.length > 0 &&
libmpvSoname !== null;
const manifest = {
schemaVersion: 1,
origin: 'linux-frame-copy-build',
@@ -500,8 +556,8 @@ function writeLinuxFrameCopyBuildManifest(runtime) {
sourceRuntimeValidated: runtime.sourceRuntimeValidated,
allowedPackageRuntimeModes: [...LINUX_PACKAGE_RUNTIME_MODES],
packageRuntimeAvailability: {
system: true,
bundled: copiedRuntimeFiles.length > 0,
system: packageRuntimeAvailable,
bundled: packageRuntimeAvailable,
},
artifacts: {
addon: 'embedded_mpv.node',
@@ -514,7 +570,7 @@ function writeLinuxFrameCopyBuildManifest(runtime) {
helperRunpath: ['$ORIGIN/lib'],
},
nativeViewFallback: 'process-isolated mpv --wid',
libmpvSoname: 'libmpv.so.2',
libmpvSoname,
runtimeFiles: copiedRuntimeFiles,
runtimeTotalBytes: copiedRuntimeFiles.reduce(
(total, runtimeFile) => total + runtimeFile.size,
@@ -685,6 +741,8 @@ function main() {
return;
}
assertRequiredLinuxFrameCopyRuntime(runtime);
const runtimeManifest =
targetPlatform === 'darwin'
? copyRuntimeToNativeBuild({
+1 -1
View File
@@ -163,7 +163,7 @@
],
"libraries": [
"-L<!(node -p \"process.env.LINUX_NATIVE_LIBRARY_DIR || '/usr/lib'\")",
"-lmpv",
"<!(node -e \"const path = require('path'); const dir = process.env.LINUX_NATIVE_LIBRARY_DIR || '/usr/lib'; process.stdout.write(path.join(dir, 'libmpv.so'))\")",
"-lEGL",
"-lOpenGL",
"-lgbm",
@@ -617,6 +617,48 @@ describe('Embedded MPV native source recording invariants', () => {
);
});
it('requires a validated bundled source runtime for required Linux builds', () => {
expect(buildScriptSource).toContain(
"sourceRuntimeValidated: buildInputMode === 'bundled-runtime'"
);
expect(buildScriptSource).toContain(
'assertRequiredLinuxFrameCopyRuntime(runtime);'
);
expect(buildScriptSource).toContain(
"runtime.buildInputMode !== 'bundled-runtime' ||"
);
expect(buildScriptSource).toContain(
'runtime.sourceRuntimeValidated !== true'
);
expect(buildScriptSource).toContain("runtimeFile.name === 'libmpv.so'");
expect(buildScriptSource).toContain(
'Required Linux builds must use the validated bundled source runtime containing staged libmpv.'
);
});
it('derives packaged Linux libmpv identity from the validated runtime', () => {
expect(buildScriptSource).toContain(
'function deriveLinuxLibMpvSoname(runtime)'
);
expect(buildScriptSource).toContain(
'runtime.sourceRuntimeManifest.runtimeFiles'
);
expect(buildScriptSource).toContain('VERSIONED_LINUX_LIBMPV_PATTERN');
expect(buildScriptSource).toContain('libmpvSoname,');
expect(buildScriptSource).not.toContain("libmpvSoname: 'libmpv.so.2'");
});
it('marks both package modes available only for a validated bundled build', () => {
expect(buildScriptSource).toContain(
'runtime.sourceRuntimeValidated === true &&\n' +
" runtime.buildInputMode === 'bundled-runtime' &&\n" +
' copiedRuntimeFiles.length > 0 &&\n' +
' libmpvSoname !== null'
);
expect(buildScriptSource).toContain('system: packageRuntimeAvailable');
expect(buildScriptSource).toContain('bundled: packageRuntimeAvailable');
});
it('uses platform-specific embedded MPV runtime cache key inputs in CI', () => {
expect(buildAndMakeWorkflowSource).toContain(
"const targetPlatform = '${{ matrix.embedded_mpv_platform }}';"
@@ -672,7 +714,7 @@ describe('Embedded MPV native source recording invariants', () => {
"if (!embeddedMpvRequired && runtime.origin === 'system-dev')"
);
expect(buildScriptSource).toContain(
'Required Linux builds must use an explicit staged runtime manifest.'
'Required Linux builds must use the validated bundled source runtime containing staged libmpv.'
);
expect(buildScriptSource).toContain(
'removeStaleFrameCopyArtifacts(outputDir);'
@@ -858,14 +900,14 @@ describe('Embedded MPV native build configuration', () => {
expect(linuxAddonConfig?.libraries).not.toContain('-lmpv');
expect(JSON.stringify(linuxAddonConfig)).not.toContain('-lmpv');
expect(linuxHelperConfig?.libraries).toEqual(
expect.arrayContaining([
'-lmpv',
'-lEGL',
'-lOpenGL',
'-lgbm',
'-ldl',
])
expect.arrayContaining(['-lEGL', '-lOpenGL', '-lgbm', '-ldl'])
);
expect(linuxHelperConfig?.libraries).not.toContain('-lmpv');
expect(
linuxHelperConfig?.libraries.find((library: string) =>
library.includes("path.join(dir, 'libmpv.so')")
)
).toContain('LINUX_NATIVE_LIBRARY_DIR');
const runtimeLinkerFlags = linuxHelperConfig?.ldflags.filter(
(flag: string) => flag.startsWith('-Wl,')