bluez + pipewire: bluez5 encendido, y el bluetooth SUENA
`-Dbluez5=enabled` en pipewire, con `bluez` y `sbc` declarados. Resultado
medido en el artefacto: /usr/lib/spa-0.2/bluez5/ con los tres .so —
libspa-bluez5.so (21M), libspa-codec-bluez5-sbc.so y
libspa-codec-bluez5-faststream.so — y el plugin DEFINE 14 símbolos sbc_*, o
sea que el códec entró de verdad y no quedó un plugin que carga sin poder
negociar A2DP.
⚠ `libbluetooth.a` de bluez NO era PIC, y el enlace del plugin moría así:
ld.lld: error: relocation R_X86_64_64 cannot be used against local symbol;
recompile with -fPIC
>>> defined in /usr/lib/libbluetooth.a(bluetooth.o)
Se arregla en bluez y no en el consumidor porque **su radio es 1** —pipewire
es su único consumidor, medido— y la regla del repo es que el radio decide de
qué lado se toca (libuv radio 1 frente a util-linux radio 79). Y no añade
cascada: pipewire ya se rearmaba por la perilla. Hacen falta los DOS flags,
igual que en sbc: --with-pic solo no basta con --disable-shared.
Los dos guardianes nuevos usan la prueba autoritativa en vez de contar
reubicaciones: meter la .a dentro de una .so, que es lo que hace el
consumidor. Contar R_X86_64_32 da falsos positivos por las secciones .debug_*
(me rechazó un sbc correcto el mismo día).
Y el de pipewire comprueba lo que de verdad importaba: que libspa-bluez5.so
quede DONDE PipeWire lo busca y con los símbolos del códec definidos. Sin eso
el servidor arranca perfecto y el auricular no es una salida — el estado del
que venimos.
NEEDED del plugin verificados contra el store, sin colgantes:
libdbus-1.so.3 → dbus-shared · libffi.so.8 → libffi-shared · libz.so.1 →
zlib-rs. El vigía de sonames da 0 sin proveedor en los cinco escritorios.
⚠ Esto rearma 18 recetas más, las TRES de Gecko incluidas (cadena PGO). Se
hace a pedido explícito del usuario, que conocía el precio.
Semantic delta
No definition changed: this change is text, documentation or data.
2 files · +68 −5
- recipes/bluez.toml +30 −2
- recipes/pipewire.toml +38 −3
diff --git a/recipes/bluez.toml b/recipes/bluez.toml
index 3a5b5dd5..4c93a0dc 100644
--- a/recipes/bluez.toml
+++ b/recipes/bluez.toml
@@ -36,7 +36,21 @@ target = "x86_64-linux-musl"
link = "static"
[build.phases]
-configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libexecdir=/usr/lib --disable-shared --enable-static --enable-library --enable-threads --disable-systemd --disable-obex --disable-manpages --disable-cups --disable-udev --with-dbusconfdir=/usr/share'
+# ⚠ **`--with-pic` + `CFLAGS=-fPIC` desde el 2026-10-21, y NO es cosmético.** `libbluetooth.a`
+# tiene un consumidor nuevo: `libspa-bluez5.so`, el plugin que PipeWire carga con `dlopen` para
+# hablar A2DP. Una `.a` sin PIC no entra en una `.so`, y el enlace de pipewire moría así:
+# ld.lld: error: relocation R_X86_64_64 cannot be used against local symbol; recompile with -fPIC
+# >>> defined in /usr/lib/libbluetooth.a(bluetooth.o)
+# >>> referenced by bluetooth.o:(.rodata.bt_compidtostr+0x4ab8)
+#
+# Se arregla ACÁ y no en el consumidor porque **el radio de bluez es 1** —`pipewire` es su único
+# consumidor, medido— y la regla del repo es que el radio decide de qué lado se toca (igual que
+# `libuv` radio 1 frente a `util-linux` radio 79). Y no añade cascada: pipewire ya se rearma por
+# el `-Dbluez5=enabled`.
+#
+# Hacen falta LOS DOS flags: `--with-pic` solo no basta con `--disable-shared`, porque libtool lo
+# aplica a los objetos de la `.so` que no se construye. Mismo caso que en `sbc`.
+configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libexecdir=/usr/lib --disable-shared --enable-static --enable-library --enable-threads --disable-systemd --disable-obex --disable-manpages --disable-cups --disable-udev --with-dbusconfdir=/usr/share --with-pic CFLAGS="$CFLAGS -fPIC"'
# ⚠ `LDFLAGS=-all-static` EN COMPILE **Y** EN INSTALL. bluez enlaza con libtool, y libtool lee el
# `-static` que exporta el lab como «preferí los .a de libtool», no como flag al linker ⇒ el primer
# sello salió con los 11 ejecutables DINÁMICOS y `scripts/static-audit.sh` lo marcó. Lo de *install*
@@ -50,7 +64,21 @@ configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --sysconfdi
# pkg-config ⇒ la transitiva nunca entra. En dinámico no se nota (la resuelve el loader); en
# estático hay que nombrarla. Misma familia que las transitivas del `.pc` de glib en `[deps]`.
compile = 'make LDFLAGS=-all-static LIBS=-lncursesw'
-install = 'make LDFLAGS=-all-static LIBS=-lncursesw DESTDIR=/out install'
+install = '''
+set -e
+make LDFLAGS=-all-static LIBS=-lncursesw DESTDIR=/out install
+
+# ⚠ GUARDIÁN DE PIC — prueba AUTORITATIVA, no recuento de reubicaciones.
+# Contar `R_X86_64_32/32S` en el `.o` da falsos positivos: las secciones `.debug_*` llevan
+# reubicaciones absolutas SIEMPRE y no se cargan en ejecución (ese control me rechazó un build
+# CORRECTO de `sbc` por 321 de ésas, el mismo día). Lo que importa es si el consumidor puede
+# enlazarla, así que se hace exactamente eso: meter `libbluetooth.a` dentro de una `.so`.
+[ -f /out/usr/lib/libbluetooth.a ] || { echo "bluez: no quedó libbluetooth.a — --enable-library debería publicarla" >&2; exit 1; }
+printf 'extern int bt_bacmp(const void*,const void*);\nint envoltura(const void*a,const void*b){return bt_bacmp(a,b);}\n' > /tmp/bt.c
+/opt/zig/zig cc -mcpu=baseline -shared -fPIC -o /tmp/libbt.so /tmp/bt.c /out/usr/lib/libbluetooth.a 2>/tmp/bt.err \
+ || { echo "bluez: libbluetooth.a NO entra en una .so ⇒ libspa-bluez5.so de PipeWire no podría enlazarla y el bluetooth no sonaría" >&2; tail -8 /tmp/bt.err >&2; exit 1; }
+echo "bluez: libbluetooth.a es PIC (entra en una .so)"
+'''
[[service]]
label = "bluetoothd"
diff --git a/recipes/pipewire.toml b/recipes/pipewire.toml
index 4c9b48f7..75174358 100644
--- a/recipes/pipewire.toml
+++ b/recipes/pipewire.toml
@@ -77,7 +77,23 @@
# recetas entre colas, aplicada con la medición delante.
#
# Fuente del gitlab.freedesktop.org (como networkmanager/modemmanager).
-# Recortado al core + SPA: sin bluez5 (cadena de códecs aptx/ldac/aac/opus enorme), sin jack/v4l2/
+# Recortado al core + SPA: sin jack/v4l2/
+#
+# ══ ✅ BLUEZ5 ENCENDIDO EL 2026-10-21 — y la razón de apagarlo era exagerada ═══════════════════
+# Esta nota decía «sin bluez5 (cadena de códecs aptx/ldac/aac/opus enorme)». La cadena existe y es
+# OPCIONAL: leyendo `spa/meson.build:49-53`, las deps DURAS de `bluez5=enabled` son cinco —
+# `bluez >= 4.101`, `gio-2.0`, `gio-unix-2.0`, `glib-2.0` y `sbc`— y las cuatro primeras ya
+# estaban. Faltaba UNA receta, `sbc`, que es el único códec que A2DP exige (aptX/LDAC/AAC son
+# mejoras de calidad que se pueden añadir después sin tocar esto).
+#
+# **Qué se arregla:** el bluetooth EMPAREJABA y NO SONABA. `bluez` trae `bluetoothctl` y su daemon,
+# y los cuatro escritorios habilitan el servicio — pero sin el plugin SPA bluez5, PipeWire no ve el
+# auricular como salida. `/usr/lib/spa-0.2/` traía alsa, audioconvert, aec… y CERO bluez. Ninguno
+# de los dos paquetes estaba roto y la capacidad no existía.
+#
+# ⚠ **Esto rearma 19 recetas**, entre ellas las TRES de Gecko (firefox, firefox-instrumentado,
+# waterfox) ⇒ la cadena PGO entera. Era la razón de no hacerlo «de paso», y se hizo a pedido
+# explícito del usuario.
# libcamera/gstreamer/ffmpeg/vulkan/roc/lv2/avahi/x11/echo-cancel-webrtc/libusb/flatpak/snap.
# systemd/logind/selinux/rtkit off (no hay systemd acá). alsa=enabled + sndfile=enabled + libpulse=enabled
# (el emulador pulse: deja que los clientes libpulse hablen con pipewire) — las 3 ya selladas.
@@ -110,7 +126,7 @@ PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --
-Dsystemd=disabled -Dlogind=disabled -Dsystemd-system-service=disabled \
-Dsystemd-user-service=disabled -Dselinux=disabled -Dlegacy-rtkit=false \
-Dpipewire-jack=disabled -Dpipewire-v4l2=disabled -Djack=disabled -Dv4l2=disabled \
- -Dlibcamera=disabled -Dbluez5=disabled -Dffmpeg=disabled -Dvulkan=disabled \
+ -Dlibcamera=disabled -Dbluez5=enabled -Dffmpeg=disabled -Dvulkan=disabled \
-Droc=disabled -Dlv2=disabled -Davahi=disabled -Dx11=disabled -Dx11-xfixes=disabled \
-Decho-cancel-webrtc=disabled -Dlibusb=disabled -Dflatpak=disabled -Dsnap=disabled \
-Dlibmysofa=disabled -Dsdl2=disabled -Dopus=disabled -Dlibffado=disabled -Dcompress-offload=disabled \
@@ -138,11 +154,30 @@ PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson install -C outpu
mkdir -p /out/etc/alsa/conf.d
ln -sf /usr/share/alsa/alsa.conf.d/50-pipewire.conf /out/etc/alsa/conf.d/50-pipewire.conf
ln -sf /usr/share/alsa/alsa.conf.d/99-pipewire-default.conf /out/etc/alsa/conf.d/99-pipewire-default.conf
+
+# ── ⚠ GUARDIÁN DEL PLUGIN BLUEZ5 ──────────────────────────────────────────────────────────────
+# `-Dbluez5=enabled` hace que meson FALLE si le faltan las deps, pero no protege del caso que de
+# verdad importa acá: que el plugin se construya y **no se instale** donde PipeWire lo busca. Sin
+# el `.so` en `/usr/lib/spa-0.2/` el servidor arranca perfecto y el auricular no es una salida —
+# que es exactamente el estado que esta receta viene a dejar atrás, así que se comprueba.
+bl=$(find /out/usr/lib/spa-0.2 -name 'libspa-bluez5.so' 2>/dev/null | head -1)
+[ -n "$bl" ] || { echo "pipewire: NO quedó libspa-bluez5.so en /usr/lib/spa-0.2 — el bluetooth seguiría sin sonar" >&2; ls /out/usr/lib/spa-0.2/ >&2; exit 1; }
+# Y que traiga DENTRO el códec: el plugin enlaza sbc estáticamente, así que sus símbolos tienen que
+# estar definidos (sección ≠ UND). Pedir sólo el fichero dejaría pasar un plugin sin códec, que
+# cargaría y no podría negociar A2DP.
+command -v nm >/dev/null && {
+ n=$(nm -D --defined-only "$bl" 2>/dev/null | grep -c 'sbc_' || true)
+ [ "${n:-0}" -gt 0 ] || { echo "pipewire: libspa-bluez5.so no DEFINE ningún símbolo sbc_* — el códec no entró y A2DP no se podría negociar" >&2; exit 1; }
+ echo "pipewire: libspa-bluez5.so presente con $n símbolo(s) sbc_* definidos"
+}
'''
[deps]
build = ["meson", "samurai", "python3", "pkgconf", "dbus-shared", "expat", "alsa-lib", "libsndfile",
- "pulseaudio", "glib", "pcre2", "libffi", "libudev-zero", "zlib-shared", "gettext-tiny"]
+ "pulseaudio", "glib", "pcre2", "libffi", "libudev-zero", "zlib-shared", "gettext-tiny",
+ # `bluez` y `sbc` son las dos piezas de `-Dbluez5=enabled`. `bluez` aporta su `bluez.pc` (5.87) y
+ # `sbc` el códec A2DP obligatorio; `gio-2.0`/`gio-unix-2.0`/`glib-2.0` los publica ya `glib`.
+ "bluez", "sbc"]
# ── LOS SERVICIOS QUE ESTE PAQUETE TRAE (SDD 30) ────────────────────────────────────────────────
# `scope = "session"`, y eso es una afirmación con consecuencia: estos NO van en el `genesis` de la