# BlueZ 5.87 — la pila Bluetooth de Linux (`bluetoothd` + `bluetoothctl`). El otro pendiente del # punto 8 de PUBLICABLE, junto con `cups`. # # ── QUÉ ENTRA Y QUÉ NO, Y POR QUÉ ───────────────────────────────────────────────────────────── # Todas las perillas salen del `configure --help` del tarball PINEADO, no de un APKBUILD ajeno: # # --disable-obex obexd (transferencia de ficheros) pide `libical`, y libical existe SÓLO en # `incoming-gnome`. Una receta del corpus resuelve sibling-first y después el # catálogo PADRE, **nunca una cola hermana** ⇒ desde acá no se alcanza. # Encenderlo exigiría promover libical al corpus, que es otra unidad de # trabajo. Se apaga por declaración para que el configure no la busque y la # tome del sysroot del lab (el lab NO entra en `hash_inputs`). # --disable-manpages las man pages se generan con `rst2man` (docutils, Python). No es receta. # --disable-systemd el init es arje. # --disable-udev sólo afecta a `hid2hci` y a las reglas; `bluetoothd` no lo necesita. # --disable-cups el backend de impresión por Bluetooth. `cups` YA es receta del corpus, así # que esto se puede encender — pero acopla las dos recetas y la primera # versión de cada una conviene tenerla sellada por separado. Queda anotado # como lo próximo, no como olvido. # --disable-shared el lab exporta LDFLAGS=-static: un .so no cierra el enlace. # # ⚠ ESTO NO ENCIENDE EL BLUETOOTH DE NADIE. `bluetoothd` necesita el bus de sistema (dbus) vivo y # el perfil que lo habilite (SDD 30), y el kernel con BT compilado. La receta es la pieza, no la # capacidad — la distinción que este repo ya pagó con `foot` y con las fuentes. name = "bluez" version = "5.87" license = "GPL-2.0-or-later AND LGPL-2.1-or-later" [source] tarball = "https://www.kernel.org/pub/linux/bluetooth/bluez-5.87.tar.xz" sha256 = "26bdcf2cebd7310c6f598850606b037ef0c515fe6608ebc54d22c50c4c32b35f" [build] compiler = "zig-cc" target = "x86_64-linux-musl" link = "static" [build.phases] # ⚠ **`--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* # no es redundancia: **libtool RELINKEA al instalar** y sin el flag ahí devuelve el binario a # dinámico. Se puede hacer estático porque el artefacto NO trae ningún plugin `.so` —comprobado, # 0 ficheros— o sea que nadie hace `dlopen` (a diferencia de `cmus`, que por eso va dinámico). # ⚠ Y `LIBS=-lncursesw`, que es la segunda mitad y no se ve venir. Con `-all-static` el enlace de # `bluetoothctl` corta con `undefined symbol: tgoto ... in archive /usr/lib/libreadline.a`: readline # usa termcap y su `readline.pc` lo declara en **`Requires.private: ncurses`**, que sólo se expande # con `pkg-config --static`. El configure de bluez busca readline con `AC_CHECK_LIB`, no con # 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 = ''' 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" id = "01HQAR53D4M2NBV8KZTYXFQA2A" exec = "/usr/lib/bluetooth/bluetoothd --nodetach" scope = "system" restart = { initial_ms = 500, max_ms = 20000 } # ⚠ `pcre2`/`libffi`/`zlib` NO son caprichos ni deps de bluez: son lo que pide el `.pc` de glib. # Con `glib` a secas el configure corta con «Package 'libpcre2-8', required by 'glib-2.0', not # found» — un enlace estático resuelve la cadena ENTERA en el punto de enlace, así que las # transitivas de pkg-config hay que materializarlas igual. Es el mismo juego de cuatro que declaran # cairo, appstream y adwaita-hello. `expat` entra por el `.pc` de dbus. [deps] build = ["glib", "dbus", "readline", "ncurses", "pkgconf", "pcre2", "libffi", "zlib", "expat"] # (Hubo aquí un `runtime = ["readline-shared"]`, y se quitó el mismo día: hacía falta mientras # `bluetoothctl` salía DINÁMICO pidiendo `libreadline.so.8`. Con el enlace estático de arriba el # artefacto no pide ni un soname —los 11 ejecutables salen `statically linked`, comprobado— así que # esa dep pasó a ser una mentira en la otra dirección: declarar lo que no se usa. Queda la nota # porque el reparto era el peor posible y conviene recordarlo: `bluetoothd` salía con `libc.so` a # secas y habría arrancado, y la herramienta con la que se emparejan los dispositivos NO.)