32blogby Studio Mitsu

Cómo escribir recetas Yocto: Guía de archivos .bb

Escribe recetas Yocto desde cero. Cubre Hello World, obtención de fuentes desde GitHub, CMake y Autotools con clases inherit.

by omitsu11 min read

This article contains affiliate links.

Contenido

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:

text
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

text
# 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

VariableDescripciónEjemplo
SUMMARYDescripción corta de una línea"Hello World sample"
DESCRIPTIONDescripción detallada"A minimal learning example..."
LICENSEIdentificador de licencia (SPDX)"MIT", "GPL-2.0-only", "Apache-2.0"
LIC_FILES_CHKSUMChecksum del archivo de licenciafile://LICENSE;md5=xxx
SRC_URIDónde obtener las fuentes"file://", "git://", "https://"
SDónde se desempaquetan las fuentes$/git

Variables de compilación e instalación

VariableDescripciónSe 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 cruzadoarm-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.

bash
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

bash
mkdir -p ../meta-mylayer/recipes-example/hello-world/files

Escribe files/hello.c:

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:

text
# 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

bash
# 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:

text
IMAGE_INSTALL:append = " hello-world"

Recompila y prueba en QEMU:

bash
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

text
# 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 fuenteFormato de SRC_URI
Archivo localfile://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/HTTPShttps://example.com/file.tar.gz
Archivo de parchefile://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.

ClaseCaso de usoQué automatiza
cmakeProyectos CMakecmake → make → make install
autotoolsProyectos Autoconfconfigure → make → make install
mesonProyectos Mesonmeson → ninja → install
setuptools3Paquetes Pythoninstalación de setuptools / wheel
systemdServicios systemdHabilitación de servicios

Ejemplo de proyecto CMake

text
# 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

text
# 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

text
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:

bash
md5sum LICENSE

do_fetch falló

text
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'

text
ERROR: Nothing PROVIDES 'hello-world'

Causa: La capa que contiene tu receta no está en bblayers.conf.

Solución: Añade la capa:

bash
bitbake-layers add-layer ../meta-mylayer

Discrepancia de checksum de SRC_URI

text
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:

bash
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

VariableCuándo importaEjemplo
DEPENDSCompilación — bibliotecas/headers necesariosDEPENDS = "openssl zlib"
RDEPENDS:${PN}Ejecución — paquetes necesarios en el targetRDEPENDS:${PN} = "libssl"
text
# 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 existe
  • include — incluye otro archivo; se omite silenciosamente si no existe
text
# 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:

text
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():

text
# 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:

bash
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:

text
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 .bb que 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 inherit para manejar automáticamente proyectos CMake/Autotools/Meson
  • Añade paquetes a una imagen con IMAGE_INSTALL:append
  • Siempre fija SRCREV a un hash de commit específico para reproducibilidad
  • Usa DEPENDS para compilación y RDEPENDS para dependencias en tiempo de ejecución

El Yocto Development Tasks Manual es la referencia oficial para escribir recetas. Estos son los siguientes pasos:

Para profundizar en Linux embebido, estos libros son excelentes referencias.

Artículos relacionados: