mirror of
https://github.com/fluxerapp/fluxer.git
synced 2026-09-02 21:04:06 +03:00
fix(desktop): build with compatible PipeWire headers (#2043)
This commit is contained in:
+239
-19
@@ -33,6 +33,9 @@ const PUBLIC_DL_BASE: &str = "https://api.fluxer.app/dl";
|
||||
const PNPM_VERSION: &str = "10.29.3";
|
||||
const RUST_TOOLCHAIN: &str = "1.93.0";
|
||||
const DEFAULT_DESKTOP_VARIANT: &str = "default";
|
||||
const LINUX_PIPEWIRE_VERSION: &str = "0.3.65";
|
||||
const LINUX_PIPEWIRE_SOURCE_SHA256: &str =
|
||||
"bb76f938136d0ce8c35bffa99e002dc2dbaeab5e14c6c34154e7f750013d1d6b";
|
||||
const LINUX_LIBFIDO2_VERSION: &str = "1.16.0";
|
||||
const LINUX_LIBFIDO2_SOURCE_SHA256: &str =
|
||||
"8c2b6fb279b5b42e9ac92ade71832e485852647b53607c43baaafbbcecea04e4";
|
||||
@@ -812,6 +815,8 @@ DPkg::Lock::Timeout "120";
|
||||
"build-essential",
|
||||
"binutils",
|
||||
"cmake",
|
||||
"meson",
|
||||
"ninja-build",
|
||||
"nasm",
|
||||
"rpm",
|
||||
"desktop-file-utils",
|
||||
@@ -836,10 +841,217 @@ DPkg::Lock::Timeout "120";
|
||||
"clang",
|
||||
"libpulse-dev",
|
||||
])?;
|
||||
install_linux_pipewire_headers().await?;
|
||||
install_linux_libfido2().await?;
|
||||
run_command(CommandSpec::new("sudo").args(["gem", "install", "--no-document", "fpm"]))
|
||||
}
|
||||
|
||||
async fn install_linux_pipewire_headers() -> Result<()> {
|
||||
let temp = TempDir::new().context("Failed to create PipeWire header build directory")?;
|
||||
let archive_name = format!("pipewire-{LINUX_PIPEWIRE_VERSION}.tar.gz");
|
||||
let archive_path = temp.path().join(&archive_name);
|
||||
let source_dir = temp.path().join("source");
|
||||
let build_dir = temp.path().join("build");
|
||||
let staging_dir = temp.path().join("stage");
|
||||
let source_url = format!(
|
||||
"https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/{LINUX_PIPEWIRE_VERSION}/{archive_name}"
|
||||
);
|
||||
let multiarch = linux_multiarch()?;
|
||||
let install_library_dir = Path::new("/usr/local/lib").join(&multiarch);
|
||||
let install_pkgconfig_dir = install_library_dir.join("pkgconfig");
|
||||
let libdir_arg = format!("--libdir=lib/{multiarch}");
|
||||
let system_pipewire_library = fs::canonicalize(linux_loader_path("libpipewire-0.3.so.0")?)
|
||||
.context("Failed to resolve the system PipeWire library")?;
|
||||
ensure!(
|
||||
!system_pipewire_library.starts_with("/usr/local"),
|
||||
"Expected the system PipeWire runtime, got {}",
|
||||
system_pipewire_library.display()
|
||||
);
|
||||
|
||||
download_file(&source_url, &archive_path).await?;
|
||||
let archive_sha256 = sha256_file(&archive_path)?;
|
||||
ensure!(
|
||||
archive_sha256 == LINUX_PIPEWIRE_SOURCE_SHA256,
|
||||
"PipeWire source checksum mismatch: expected {}, got {}",
|
||||
LINUX_PIPEWIRE_SOURCE_SHA256,
|
||||
archive_sha256
|
||||
);
|
||||
|
||||
fs::create_dir_all(&source_dir)
|
||||
.with_context(|| format!("Failed to create {}", source_dir.display()))?;
|
||||
run_command(CommandSpec::new("tar").args([
|
||||
"-xzf",
|
||||
archive_path.to_string_lossy().as_ref(),
|
||||
"-C",
|
||||
source_dir.to_string_lossy().as_ref(),
|
||||
"--strip-components=1",
|
||||
]))?;
|
||||
run_command(CommandSpec::new("meson").args([
|
||||
"setup",
|
||||
build_dir.to_string_lossy().as_ref(),
|
||||
source_dir.to_string_lossy().as_ref(),
|
||||
"--buildtype=release",
|
||||
"--default-library=shared",
|
||||
"--prefix=/usr/local",
|
||||
&libdir_arg,
|
||||
"--auto-features=disabled",
|
||||
"-Dexamples=disabled",
|
||||
"-Dtests=disabled",
|
||||
"-Dinstalled_tests=disabled",
|
||||
"-Ddocs=disabled",
|
||||
"-Dman=disabled",
|
||||
"-Dspa-plugins=enabled",
|
||||
"-Dpipewire-alsa=disabled",
|
||||
"-Dpipewire-jack=disabled",
|
||||
"-Dpipewire-v4l2=disabled",
|
||||
"-Dsystemd=disabled",
|
||||
"-Ddbus=disabled",
|
||||
"-Dflatpak=disabled",
|
||||
"-Dsession-managers=[]",
|
||||
"-Dlegacy-rtkit=false",
|
||||
]))?;
|
||||
run_command(CommandSpec::new("meson").args([
|
||||
"compile",
|
||||
"-C",
|
||||
build_dir.to_string_lossy().as_ref(),
|
||||
]))?;
|
||||
run_command(
|
||||
CommandSpec::new("meson")
|
||||
.args([
|
||||
"install",
|
||||
"-C",
|
||||
build_dir.to_string_lossy().as_ref(),
|
||||
"--no-rebuild",
|
||||
])
|
||||
.env("DESTDIR", staging_dir.as_os_str()),
|
||||
)?;
|
||||
|
||||
let staged_prefix = staging_dir.join("usr/local");
|
||||
let staged_pipewire_headers = staged_prefix.join("include/pipewire-0.3");
|
||||
let staged_spa_headers = staged_prefix.join("include/spa-0.2");
|
||||
let staged_pkgconfig_dir = staged_prefix.join(format!("lib/{multiarch}/pkgconfig"));
|
||||
let install_include_dir = Path::new("/usr/local/include");
|
||||
let install_pipewire_headers = install_include_dir.join("pipewire-0.3");
|
||||
let install_spa_headers = install_include_dir.join("spa-0.2");
|
||||
ensure!(
|
||||
!install_pipewire_headers.exists() && !install_spa_headers.exists(),
|
||||
"PipeWire header overlay already exists under {}",
|
||||
install_include_dir.display()
|
||||
);
|
||||
run_command(CommandSpec::new("sudo").args([
|
||||
"install",
|
||||
"-d",
|
||||
install_include_dir.to_string_lossy().as_ref(),
|
||||
install_pkgconfig_dir.to_string_lossy().as_ref(),
|
||||
]))?;
|
||||
run_command(CommandSpec::new("sudo").args([
|
||||
"cp",
|
||||
"-a",
|
||||
staged_pipewire_headers.to_string_lossy().as_ref(),
|
||||
staged_spa_headers.to_string_lossy().as_ref(),
|
||||
install_include_dir.to_string_lossy().as_ref(),
|
||||
]))?;
|
||||
for pkgconfig_name in ["libpipewire-0.3.pc", "libspa-0.2.pc"] {
|
||||
run_command(
|
||||
CommandSpec::new("sudo").args([
|
||||
"install",
|
||||
"-m",
|
||||
"0644",
|
||||
staged_pkgconfig_dir
|
||||
.join(pkgconfig_name)
|
||||
.to_string_lossy()
|
||||
.as_ref(),
|
||||
install_pkgconfig_dir
|
||||
.join(pkgconfig_name)
|
||||
.to_string_lossy()
|
||||
.as_ref(),
|
||||
]),
|
||||
)?;
|
||||
}
|
||||
|
||||
let installed_version =
|
||||
output_text(CommandSpec::new("pkg-config").args(["--modversion", "libpipewire-0.3"]))?;
|
||||
ensure!(
|
||||
installed_version == LINUX_PIPEWIRE_VERSION,
|
||||
"Expected PipeWire headers {}, got {}",
|
||||
LINUX_PIPEWIRE_VERSION,
|
||||
installed_version
|
||||
);
|
||||
for (package, expected_include_flag) in [
|
||||
("libpipewire-0.3", "-I/usr/local/include/pipewire-0.3"),
|
||||
("libspa-0.2", "-I/usr/local/include/spa-0.2"),
|
||||
] {
|
||||
let pkgconfig_dir =
|
||||
output_text(CommandSpec::new("pkg-config").args(["--variable=pcfiledir", package]))?;
|
||||
ensure!(
|
||||
Path::new(&pkgconfig_dir) == install_pkgconfig_dir,
|
||||
"Expected {package} metadata in {}, got {}",
|
||||
install_pkgconfig_dir.display(),
|
||||
pkgconfig_dir
|
||||
);
|
||||
let include_dir =
|
||||
output_text(CommandSpec::new("pkg-config").args(["--variable=includedir", package]))?;
|
||||
ensure!(
|
||||
Path::new(&include_dir) == install_include_dir,
|
||||
"Expected {package} headers in {}, got {}",
|
||||
install_include_dir.display(),
|
||||
include_dir
|
||||
);
|
||||
let include_flags =
|
||||
output_text(CommandSpec::new("pkg-config").args(["--cflags-only-I", package]))?;
|
||||
ensure!(
|
||||
include_flags
|
||||
.split_whitespace()
|
||||
.any(|flag| flag == expected_include_flag),
|
||||
"Expected {package} include flag {expected_include_flag}, got {include_flags}"
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
install_pipewire_headers
|
||||
.join("pipewire/pipewire.h")
|
||||
.is_file()
|
||||
&& install_spa_headers.join("spa/buffer/meta.h").is_file()
|
||||
&& install_spa_headers.join("spa/param/video/raw.h").is_file(),
|
||||
"PipeWire {} header overlay is incomplete",
|
||||
LINUX_PIPEWIRE_VERSION
|
||||
);
|
||||
let mut local_pipewire_library = None;
|
||||
for entry in fs::read_dir(&install_library_dir)
|
||||
.with_context(|| format!("Failed to read {}", install_library_dir.display()))?
|
||||
{
|
||||
let entry = entry
|
||||
.with_context(|| format!("Failed to inspect {}", install_library_dir.display()))?;
|
||||
if entry
|
||||
.file_name()
|
||||
.to_string_lossy()
|
||||
.starts_with("libpipewire-0.3.")
|
||||
{
|
||||
local_pipewire_library = Some(entry.path());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(path) = local_pipewire_library {
|
||||
bail!(
|
||||
"PipeWire header overlay found unexpected library {}",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
let loaded_pipewire_library = fs::canonicalize(linux_loader_path("libpipewire-0.3.so.0")?)
|
||||
.context("Failed to resolve the selected PipeWire library")?;
|
||||
ensure!(
|
||||
loaded_pipewire_library == system_pipewire_library,
|
||||
"PipeWire header overlay changed the runtime from {} to {}",
|
||||
system_pipewire_library.display(),
|
||||
loaded_pipewire_library.display()
|
||||
);
|
||||
|
||||
println!(
|
||||
"Installed PipeWire {LINUX_PIPEWIRE_VERSION} headers for {}.",
|
||||
system_pipewire_library.display()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn install_linux_libfido2() -> Result<()> {
|
||||
let temp = TempDir::new().context("Failed to create libfido2 build directory")?;
|
||||
let archive_name = format!("libfido2-{LINUX_LIBFIDO2_VERSION}.tar.gz");
|
||||
@@ -847,16 +1059,8 @@ async fn install_linux_libfido2() -> Result<()> {
|
||||
let source_dir = temp.path().join("source");
|
||||
let build_dir = temp.path().join("build");
|
||||
let source_url = format!("https://developers.yubico.com/libfido2/Releases/{archive_name}");
|
||||
let multiarch = output_text(CommandSpec::new("gcc").arg("-print-multiarch"))?;
|
||||
let multiarch = multiarch.trim();
|
||||
ensure!(
|
||||
!multiarch.is_empty()
|
||||
&& multiarch
|
||||
.bytes()
|
||||
.all(|byte| { byte.is_ascii_alphanumeric() || byte == b'-' || byte == b'_' }),
|
||||
"gcc returned invalid multiarch tuple: {multiarch:?}"
|
||||
);
|
||||
let install_library_dir = Path::new("/usr/local/lib").join(multiarch);
|
||||
let multiarch = linux_multiarch()?;
|
||||
let install_library_dir = Path::new("/usr/local/lib").join(&multiarch);
|
||||
let install_library_arg = format!("-DCMAKE_INSTALL_LIBDIR=lib/{multiarch}");
|
||||
|
||||
download_file(&source_url, &archive_path).await?;
|
||||
@@ -945,15 +1149,7 @@ async fn install_linux_libfido2() -> Result<()> {
|
||||
);
|
||||
let installed_library = fs::canonicalize(&installed_soname)
|
||||
.with_context(|| format!("Failed to resolve {}", installed_soname.display()))?;
|
||||
let loader_cache = output_text(CommandSpec::new("ldconfig").arg("-p"))?;
|
||||
let loader_path = loader_cache
|
||||
.lines()
|
||||
.find_map(|line| {
|
||||
let (library, path) = line.trim().split_once("=>")?;
|
||||
(library.split_whitespace().next() == Some("libfido2.so.1"))
|
||||
.then(|| PathBuf::from(path.trim()))
|
||||
})
|
||||
.context("ldconfig did not resolve libfido2.so.1")?;
|
||||
let loader_path = linux_loader_path("libfido2.so.1")?;
|
||||
let loaded_library = fs::canonicalize(&loader_path)
|
||||
.with_context(|| format!("Failed to resolve {}", loader_path.display()))?;
|
||||
ensure!(
|
||||
@@ -967,6 +1163,30 @@ async fn install_linux_libfido2() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn linux_multiarch() -> Result<String> {
|
||||
let multiarch = output_text(CommandSpec::new("gcc").arg("-print-multiarch"))?;
|
||||
ensure!(
|
||||
!multiarch.is_empty()
|
||||
&& multiarch
|
||||
.bytes()
|
||||
.all(|byte| byte.is_ascii_alphanumeric() || byte == b'-' || byte == b'_'),
|
||||
"gcc returned invalid multiarch tuple: {multiarch:?}"
|
||||
);
|
||||
Ok(multiarch)
|
||||
}
|
||||
|
||||
fn linux_loader_path(library_name: &str) -> Result<PathBuf> {
|
||||
let loader_cache = output_text(CommandSpec::new("ldconfig").arg("-p"))?;
|
||||
loader_cache
|
||||
.lines()
|
||||
.find_map(|line| {
|
||||
let (library, path) = line.trim().split_once("=>")?;
|
||||
(library.split_whitespace().next() == Some(library_name))
|
||||
.then(|| PathBuf::from(path.trim()))
|
||||
})
|
||||
.with_context(|| format!("ldconfig did not resolve {library_name}"))
|
||||
}
|
||||
|
||||
fn rewrite_ubuntu_ports_sources() -> Result<()> {
|
||||
let apt = Path::new("/etc/apt");
|
||||
if !apt.exists() {
|
||||
|
||||
Reference in New Issue
Block a user