Una receta Yocto (archivo .bb) le dice a BitBake de dónde obtener el código fuente, cómo compilarlo y dónde instalarlo — es el mecanismo central para añadir cualquier software a tu imagen de Linux embebido.
Esta guía te lleva paso a paso por la creación de recetas Yocto desde cero, con ejemplos de código funcional en cada paso. Al terminar, sabrás escribir recetas para archivos locales, proyectos de GitHub y compilaciones con CMake/Autotools.
¿Qué es una receta Yocto?
Una receta es un archivo .bb que describe cómo compilar e instalar una pieza específica de software. El Yocto Development Tasks Manual cubre la especificación completa, pero en esencia, una receta responde cuatro preguntas:
- Dónde obtener el código fuente (
SRC_URI) - Cómo compilarlo (
do_compile) - Dónde instalarlo (
do_install) - Qué licencia usa (
LICENSE)
Nomenclatura de archivos de recetas
Los nombres de archivo de recetas siguen el patrón nombrePaquete_versión.bb:
hello-world_1.0.bb # hello-world versión 1.0
myapp_2.3.1.bb # myapp versión 2.3.1
busybox_1.36.1.bb # busybox versión 1.36.1
BitBake extrae automáticamente el nombre del paquete (PN) y la versión (PV) del nombre de archivo. Consulta el Yocto Reference Manual — Variables Glossary para la lista completa de variables integradas.
Estructura básica de una receta
# hello-world_1.0.bb
# ===== Metadatos =====
SUMMARY = "Hello World sample"
DESCRIPTION = "A minimal example recipe for learning Yocto"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
# ===== Adquisición de fuentes =====
SRC_URI = "file://hello.c"
# ===== Directorio de fuentes =====
S = "${WORKDIR}"
# ===== Compilación =====
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} -o hello hello.c
}
# ===== Instalación =====
do_install() {
install -d ${D}${bindir}
install -m 0755 hello ${D}${bindir}
}
Variables clave
Metadatos del paquete
| Variable | Descripción | Ejemplo |
|---|---|---|
SUMMARY | Descripción corta de una línea | "Hello World sample" |
DESCRIPTION | Descripción detallada | "A minimal learning example..." |
LICENSE | Identificador de licencia (SPDX) | "MIT", "GPL-2.0-only", "Apache-2.0" |
LIC_FILES_CHKSUM | Checksum del archivo de licencia | file://LICENSE;md5=xxx |
SRC_URI | Dónde obtener las fuentes | "file://", "git://", "https://" |
S | Dónde se desempaquetan las fuentes | $/git |
Variables de compilación e instalación
| Variable | Descripción | Se expande a |
|---|---|---|
${WORKDIR} | Directorio de trabajo para esta compilación | /tmp/work/.../hello-world/1.0-r0/ |
${D} | Raíz de staging para instalación | .../image/ |
${bindir} | Ruta /usr/bin | /usr/bin |
${libdir} | Ruta /usr/lib | /usr/lib |
${sysconfdir} | Ruta /etc | /etc |
${CC} | Compilador cruzado | arm-poky-linux-gnueabi-gcc |
Compilación de una receta Hello World
Compilemos la receta más simple posible y confirmemos que funciona de extremo a extremo.
Crear una capa personalizada
Siempre pon tus recetas en tu propia capa — nunca directamente en las capas de Poky.
cd ~/yocto/poky
source oe-init-build-env build
# Crear una nueva capa
cd ..
bitbake-layers create-layer meta-mylayer
# Añadirla a la compilación
cd build
bitbake-layers add-layer ../meta-mylayer
Consulta la guía de creación de capas para detalles sobre la gestión de capas.
Crear la estructura de directorios y el código fuente
mkdir -p ../meta-mylayer/recipes-example/hello-world/files
Escribe files/hello.c:
// files/hello.c
#include <stdio.h>
int main(void) {
printf("Hello from Yocto!\n");
printf("This app was built with a custom recipe.\n");
return 0;
}
Escribir la receta
Crea hello-world_1.0.bb:
# hello-world_1.0.bb
SUMMARY = "Hello World - Yocto recipe learning example"
DESCRIPTION = "A minimal application demonstrating how to write a custom Yocto recipe"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://hello.c"
S = "${WORKDIR}"
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} -o hello hello.c
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello ${D}${bindir}
}
Compilar e incluir en tu imagen
# Compilar la receta individualmente
bitbake hello-world
# El éxito se ve así:
NOTE: Tasks Summary: Attempted 123 tasks of which 120 didn't need to be rerun
Añade a conf/local.conf para incluirlo en la imagen:
IMAGE_INSTALL:append = " hello-world"
Recompila y prueba en QEMU:
bitbake core-image-minimal
runqemu qemux86-64 nographic
# Después del arranque:
root@qemux86-64:~# hello
Hello from Yocto!
This app was built with a custom recipe.
Obtener fuentes desde GitHub
En proyectos reales, normalmente obtendrás las fuentes de un repositorio externo en lugar de incluirlas localmente.
Ejemplo: Añadir nlohmann/json
# nlohmann-json_3.11.3.bb
SUMMARY = "JSON for Modern C++"
DESCRIPTION = "A header-only JSON library for C++"
HOMEPAGE = "https://github.com/nlohmann/json"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE.MIT;md5=f969127d7b7ed0a8a63c2bbeae002588"
# Obtener de GitHub usando HTTPS
SRC_URI = "git://github.com/nlohmann/json.git;protocol=https;branch=develop"
# Fijar a un commit específico — siempre haz esto para reproducibilidad
SRCREV = "9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03"
S = "${WORKDIR}/git"
inherit cmake
EXTRA_OECMAKE = "-DJSON_BuildTests=OFF"
Patrones de SRC_URI
| Tipo de fuente | Formato de SRC_URI |
|---|---|
| Archivo local | file://hello.c |
| Git (HTTPS) | git://github.com/user/repo.git;protocol=https;branch=main |
| Git (SSH) | git://git@github.com/user/repo.git;protocol=ssh;branch=main |
| Tarball HTTP/HTTPS | https://example.com/file.tar.gz |
| Archivo de parche | file://fix-build.patch |
Uso de clases Inherit
Yocto incluye clases que encapsulan patrones de compilación comunes. Usar inherit te ahorra escribir tareas repetitivas de do_compile y do_install.
| Clase | Caso de uso | Qué automatiza |
|---|---|---|
cmake | Proyectos CMake | cmake → make → make install |
autotools | Proyectos Autoconf | configure → make → make install |
meson | Proyectos Meson | meson → ninja → install |
setuptools3 | Paquetes Python | instalación de setuptools / wheel |
systemd | Servicios systemd | Habilitación de servicios |
Ejemplo de proyecto CMake
# myapp-cmake_1.0.bb
SUMMARY = "My CMake App"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "git://github.com/user/myapp.git;protocol=https;branch=main"
SRCREV = "abc123def456..."
S = "${WORKDIR}/git"
inherit cmake
EXTRA_OECMAKE = "-DBUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=Release"
Ejemplo de proyecto Autotools
# myapp-autotools_1.0.bb
SUMMARY = "My Autotools App"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://COPYING;md5=..."
SRC_URI = "https://example.com/myapp-1.0.tar.gz"
SRC_URI[sha256sum] = "def456..."
inherit autotools
EXTRA_OECONF = "--disable-docs"
Servicios systemd
Para iniciar automáticamente tu aplicación en el arranque, usa inherit systemd. Para configuración detallada, consulta la guía de servicios systemd.
Errores comunes y soluciones
Discrepancia de LIC_FILES_CHKSUM
ERROR: License checksum mismatch...
Causa: El hash MD5 en tu receta no coincide con el archivo de licencia real.
Solución: Recalcula el hash correcto:
md5sum LICENSE
do_fetch falló
ERROR: Fetcher failure: Unable to find file...
Causa: Ruta incorrecta en SRC_URI, o problema de red al obtener de Git.
Solución: Verifica tu SRC_URI y SRCREV. Asegúrate de que el hash de commit existe en el repositorio.
Nothing PROVIDES 'hello-world'
ERROR: Nothing PROVIDES 'hello-world'
Causa: La capa que contiene tu receta no está en bblayers.conf.
Solución: Añade la capa:
bitbake-layers add-layer ../meta-mylayer
Discrepancia de checksum de SRC_URI
ERROR: SRC_URI checksum mismatch...
Causa: El checksum de una descarga HTTP/HTTPS no está configurado o no coincide.
Solución: Calcula el checksum correcto:
sha256sum downloaded-file.tar.gz
Gestión de dependencias
Las recetas reales suelen depender de otros paquetes. Yocto distingue entre dependencias en tiempo de compilación y en tiempo de ejecución.
DEPENDS vs RDEPENDS
| Variable | Cuándo importa | Ejemplo |
|---|---|---|
DEPENDS | Compilación — bibliotecas/headers necesarios | DEPENDS = "openssl zlib" |
RDEPENDS:${PN} | Ejecución — paquetes necesarios en el target | RDEPENDS:${PN} = "libssl" |
# myapp_1.0.bb — con dependencias
SUMMARY = "My App with Dependencies"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://myapp.c"
S = "${WORKDIR}"
# Compilación: necesita headers y bibliotecas de OpenSSL
DEPENDS = "openssl"
# Ejecución: necesita bibliotecas compartidas de OpenSSL en el target
RDEPENDS:${PN} = "openssl"
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} -o myapp myapp.c -lssl -lcrypto
}
do_install() {
install -d ${D}${bindir}
install -m 0755 myapp ${D}${bindir}
}
Uso de require e include
Puedes dividir la lógica de una receta en múltiples archivos:
require— incluye otro archivo; falla si no existeinclude— incluye otro archivo; se omite silenciosamente si no existe
# myapp_1.0.bb
require myapp-common.inc
SRC_URI = "https://example.com/myapp-1.0.tar.gz"
SRC_URI[sha256sum] = "abc123..."
Este patrón se usa comúnmente en las recetas oficiales de Poky para compartir metadatos entre versiones.
FAQ
¿Cuál es la diferencia entre SRCREV y AUTOREV?
SRCREV fija tu receta a un hash de commit Git específico, garantizando compilaciones reproducibles en cualquier máquina o entorno CI. AUTOREV (SRCREV = "${AUTOREV}") obtiene el último commit en cada compilación. El BitBake User Manual desaconseja explícitamente AUTOREV en producción — hace que las compilaciones sean no deterministas y puede introducir cambios no probados.
¿Cómo añado múltiples archivos fuente en SRC_URI?
Enuméralos separados por espacios o usa continuación de línea con barras invertidas:
SRC_URI = "file://main.c \
file://utils.c \
file://utils.h \
file://config.json \
"
Los archivos file:// deben colocarse en un directorio files/ junto a la receta, o en un directorio con el mismo nombre de la receta.
¿Puedo escribir lógica de recetas en Python?
Sí. Las recetas BitBake soportan Python de dos formas: expresiones inline con ${@expression} y funciones completas con python do_taskname():
# Establecer una variable con Python
DATEVAR = "${@time.strftime('%Y%m%d', time.gmtime())}"
python do_configure:prepend() {
bb.note("Configuring with custom Python logic")
}
El BitBake User Manual cubre las tareas Python en detalle.
¿Cuál es la diferencia entre inherit y require?
inherit carga un archivo .bbclass, que añade comportamiento de compilación estandarizado (como cmake o autotools). require / include carga otro archivo .bb o .inc para compartir metadatos. Piensa en inherit como "usar este sistema de compilación" y require como "importar configuración compartida."
¿Cómo depuro una receta que falla al compilar?
Usa devshell para abrir un shell interactivo dentro del entorno de compilación de la receta:
bitbake -c devshell hello-world
Se abre una terminal con todas las variables de entorno (compilador cruzado, rutas sysroot) ya configuradas. Puedes ejecutar make manualmente, inspeccionar archivos y probar correcciones. Consulta la guía de depuración de errores de compilación para un enfoque sistemático.
¿Cómo añado archivos de configuración a /etc?
Usa ${sysconfdir} (que se expande a /etc) en do_install:
do_install() {
install -d ${D}${sysconfdir}/myapp
install -m 0644 ${WORKDIR}/myapp.conf ${D}${sysconfdir}/myapp/
}
No olvides incluir el archivo de configuración también en SRC_URI.
¿Cuál es la diferencia entre do_install y do_deploy?
do_install coloca archivos en el área de staging del rootfs del target (${D}). do_deploy los coloca en el directorio de despliegue — se usa para imágenes de arranque, binarios del bootloader u otros artefactos que no forman parte del rootfs pero son necesarios para flashear. La mayoría de recetas solo necesitan do_install.
Conclusión
Esto es lo que hay que recordar sobre escribir recetas Yocto:
- Las recetas son archivos
.bbque describen el proceso completo: obtención, compilación e instalación - Variables fundamentales:
SRC_URI,do_compile,do_install,DEPENDS,RDEPENDS - Siempre pon tus recetas en una capa personalizada, no en Poky
- Usa
inheritpara manejar automáticamente proyectos CMake/Autotools/Meson - Añade paquetes a una imagen con
IMAGE_INSTALL:append - Siempre fija
SRCREVa un hash de commit específico para reproducibilidad - Usa
DEPENDSpara compilación yRDEPENDSpara dependencias en tiempo de ejecución
El Yocto Development Tasks Manual es la referencia oficial para escribir recetas. Estos son los siguientes pasos:
- Crear y gestionar capas — arquitectura de capas para tus recetas
- Guía práctica de bbappend — personalizar recetas existentes
- Compilar Linux para Raspberry Pi — desplegar en hardware real
- Gestión de SBOM y CVE — seguridad de la cadena de suministro
Para profundizar en Linux embebido, estos libros son excelentes referencias.
Artículos relacionados:
- Yocto bbappend: Guía práctica con 5 patrones
- Capas en Yocto: Cómo crear y gestionar capas personalizadas
- Primeros pasos con Yocto: Compila tu primera imagen Linux
- Errores de compilación en Yocto: Guía de depuración por tarea
- Inicio automático con systemd en Yocto: Guía con 7 errores comunes
- Compilaciones Yocto 10x más rápidas
- Raspberry Pi 5 con Yocto Scarthgap