Na primeira parte dessa série apresentamos os prós e contras ao se utilizar distribuições Linux prontas e customizadas, uma breve introdução sobre o Yocto Project, bem como suas características principais e seus diferenciais.
Nessa segunda parte veremos em detalhes a arquitetura desse sistema de build assim como alguns conceitos e definições importantes que nos darão a base necessária para desenvolvermos distribuições Linux embarcado customizadas a serem utilizadas em produtos eletrônicos.
Yocto Project – Arquitetura
A figura 1 apresenta um panorama da arquitetura do ambiente de desenvolvimento do Yocto Project:
Basicamente podemos considerar que temos 6 entidades principais no sistema:
- Código fonte upstream (Upstream sources);
- Metadados e entradas (Metada/Inputs);
- Sistema de Build propriamente dito (Build System);
- Pacotes resultantes (Output Packages);
- Tarefas (Process steps – tasks);
- Imagem resultante (Output Data Image).
De posse das configurações de usuário, BSP (board support package) e políticas, juntamente com os metadados, o Bitbake irá processar tais informações e criar uma cadeia de execução de tarefas ordenadas de acordo com suas interdependências. O workflow comumente encontrado das tarefas (tasks) é o representado na figura 1, onde:
- O código fonte é baixado;
- Aplicação de patches se necessários;
- Configuração e compilação;
- Análise do resultado para a divisão do pacote e suas relações de dependência;
- Geração de pacotes (rpm, deb ou ipk);
- Testes de garantia de qualidade dos pacotes gerados (ex.: verificação se todos os binários esperados estão presentes no pacote);
- Geração do feed de pacotes;
- Geração da imagem ou do SDK.
Yocto Project – Conceitos e Definições
Layer (camada)
Grupo de metadados (metadata) que podem ser incorporados ao sistema de build de forma a estendê-lo (ex.: Software, BSP e Distro).
Distro (distribuição)
Configurações, políticas e regras para geração da imagem do sistema.
Machine (máquina)
Plataforma de hardware alvo da distribuição a ser gerada, implementada através de uma camada de BSP.
Image (imagem)
Imagem final do rootfs do sistema gerado para determinada máquina, implementado através de uma receita de imagem.
Task (tarefa)
Procedimentos executados pelo sistema de build ao processar as receitas.
Package (pacote)
Resultado do processamento da receita de um componente de software, agregado em algum formato popular de empacotamento (.ipk, .deb, .rpm).
Tipos de Metadados
Recipes – Receitas (*.bb)
- Descrevem instruções de como se constrói um pacote;
- Podem ser estendidas em outras camadas (.bbappend);
- Podem compartilhar “dados” através de arquivos “.inc”.
PackageGroups – Grupos de Pacotes (*.bb)
Usado para agrupar pacotes para serem inclusos em uma imagem.
Classes (*.bbclass)
Mecanismo de herança para prover funcionalidades comuns.
Configuration – Configuração (*.conf)
Dita o comportamento do processo de build de maneira global.
Detalharemos a diante cada um desses conceitos apresentados.
Layer
Estrutura típica de diretórios (ex.: meta-yocto-bsp):
conf lib recipes-bsp recipes-core recipes-graphics recipes-kernel
Possui um arquivo de configuração a ser usado pelo Bitbake para processamento dos metadados (ex.: meta-yocto-bsp/conf/layer.conf):
# We have a conf and classes directory, add to BBPATH
BBPATH .= ":${LAYERDIR}"
# We have recipes-* directories, add to BBFILES
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
${LAYERDIR}/recipes-*/*/*.bbappend"
BBFILE_COLLECTIONS += "yoctobsp"
BBFILE_PATTERN_yoctobsp = "^${LAYERDIR}/"
BBFILE_PRIORITY_yoctobsp = "5"
LAYERVERSION_yoctobsp = "3"
As camadas devem ser adicionadas no arquivo “build/conf/bblayers.conf“. Exemplo:
# LAYER_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
LCONF_VERSION = "6"
BBPATH = "${TOPDIR}"
BBFILES ?= ""
BBLAYERS ?= " \
${TOPDIR}/../poky-krogoth/meta-embarcados \
${TOPDIR}/../poky-krogoth/meta-embarcados/meta-rpi \
${TOPDIR}/../poky-krogoth/meta-qt5 \
${TOPDIR}/../poky-krogoth/meta-raspberrypi \
${TOPDIR}/../poky-krogoth/meta-openembedded/meta-oe \
${TOPDIR}/../poky-krogoth/meta-openembedded/meta-ruby \
${TOPDIR}/../poky-krogoth/meta \
${TOPDIR}/../poky-krogoth/meta-yocto \
"
Distro
Possui um arquivo de configuração a ser usado pelo Bitbake para definição de regras e políticas da imagem a ser gerada (ex.: meta-poky/conf/distro/poky.conf):
DISTRO = "poky"
DISTRO_NAME = "Poky (Yocto Project Reference Distro)"
DISTRO_VERSION = "2.1+snapshot-${DATE}"
DISTRO_CODENAME = "master"
SDK_VENDOR = "-pokysdk"
SDK_VERSION := "${@'${DISTRO_VERSION}'.replace('snapshot-${DATE}','snapshot')}"
MAINTAINER = "Poky <poky@yoctoproject.org>"
TARGET_VENDOR = "-poky"
LOCALCONF_VERSION = "1"
DISTRO_VERSION[vardepsexclude] = "DATE"
SDK_VERSION[vardepsexclude] = "DATE"
# Override these in poky based distros
POKY_DEFAULT_DISTRO_FEATURES = "largefile opengl ptest multiarch wayland"
POKY_DEFAULT_EXTRA_RDEPENDS = "packagegroup-core-boot"
POKY_DEFAULT_EXTRA_RRECOMMENDS = "kernel-module-af-packet"
DISTRO_FEATURES ?= "${DISTRO_FEATURES_DEFAULT} ${DISTRO_FEATURES_LIBC} ${POKY_DEFAULT_DISTRO_FEATURES}"
PREFERRED_VERSION_linux-yocto ?= "4.4%"
PREFERRED_VERSION_linux-yocto_qemux86 ?= "4.4%"
PREFERRED_VERSION_linux-yocto_qemux86-64 ?= "4.4%"
PREFERRED_VERSION_linux-yocto_qemuarm ?= "4.4%"
PREFERRED_VERSION_linux-yocto_qemumips ?= "4.4%"
PREFERRED_VERSION_linux-yocto_qemumips64 ?= "4.4%"
PREFERRED_VERSION_linux-yocto_qemuppc ?= "4.4%"
SDK_NAME = "${DISTRO}-${TCLIBC}-${SDK_ARCH}-${IMAGE_BASENAME}-${TUNE_PKGARCH}"
SDKPATH = "/opt/${DISTRO}/${SDK_VERSION}"
DISTRO_EXTRA_RDEPENDS += " ${POKY_DEFAULT_EXTRA_RDEPENDS}"
DISTRO_EXTRA_RRECOMMENDS += " ${POKY_DEFAULT_EXTRA_RRECOMMENDS}"
POKYQEMUDEPS = "${@bb.utils.contains("INCOMPATIBLE_LICENSE", "GPL-3.0", "", "packagegroup-core-device-devel",d)}"
DISTRO_EXTRA_RDEPENDS_append_qemuarm = " ${POKYQEMUDEPS}"
DISTRO_EXTRA_RDEPENDS_append_qemuarm64 = " ${POKYQEMUDEPS}"
DISTRO_EXTRA_RDEPENDS_append_qemumips = " ${POKYQEMUDEPS}"
DISTRO_EXTRA_RDEPENDS_append_qemuppc = " ${POKYQEMUDEPS}"
DISTRO_EXTRA_RDEPENDS_append_qemux86 = " ${POKYQEMUDEPS}"
DISTRO_EXTRA_RDEPENDS_append_qemux86-64 = " ${POKYQEMUDEPS}"
TCLIBCAPPEND = ""
QEMU_TARGETS ?= "arm aarch64 i386 mips mipsel mips64 ppc x86_64"
# Other QEMU_TARGETS "mips64el sh4"
PREMIRRORS ??= "\
bzr://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n \
cvs://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n \
git://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n \
gitsm://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n \
hg://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n \
osc://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n \
p4://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n \
svn://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n"
MIRRORS =+ "\
ftp://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n \
https://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n \
https://.*/.* https://downloads.yoctoproject.org/mirror/sources/ \n"
# The CONNECTIVITY_CHECK_URI's are used to test whether we can succesfully
# fetch from the network (and warn you if not). To disable the test set
# the variable to be empty.
# Git example url: git://git.yoctoproject.org/yocto-firewall-test;protocol=git;rev=master
CONNECTIVITY_CHECK_URIS ?= "https://www.example.com/"
SANITY_TESTED_DISTROS ?= " \
poky-1.7 \n \
poky-1.8 \n \
poky-2.0 \n \
Ubuntu-14.04 \n \
Ubuntu-14.10 \n \
Ubuntu-15.04 \n \
Ubuntu-15.10 \n \
Ubuntu-16.04 \n \
Fedora-22 \n \
Fedora-23 \n \
CentOS-7.* \n \
Debian-8.* \n \
openSUSE-project-13.2 \n \
"
#
# OELAYOUT_ABI allows us to notify users when the format of TMPDIR changes in
# an incompatible way. Such changes should usually be detailed in the commit
# that breaks the format and have been previously discussed on the mailing list
# with general agreement from the core team.
#
OELAYOUT_ABI = "11"
# add poky sanity bbclass
INHERIT += "poky-sanity"
# QA check settings - a little stricter than the OE-Core defaults
WARN_TO_ERROR_QA = "already-stripped compile-host-path install-host-path \
installed-vs-shipped ldflags pn-overrides rpaths staticdev \
useless-rpaths"
WARN_QA_remove = "${WARN_TO_ERROR_QA}"
ERROR_QA_append = " ${WARN_TO_ERROR_QA}"
require conf/distro/include/poky-world-exclude.inc
require conf/distro/include/no-static-libs.inc
require conf/distro/include/yocto-uninative.inc
INHERIT += "uninative"
Basicamente define toolchain, libc, sistema de init e versões de determinados pacotes. Possui a variável DISTRO_FEATURES que pode receber os seguintes valores:
alsa bluetooth cramfs directfb ext2 ipsec ipv6 irda keyboard nfs opengl pci pcmcia ppp ptest smbfs systemd usbgadget usbhost wayland wifi x11
A figura 2 ilustra as principais distribuições disponíveis.
Para configurarmos qual distribuição queremos gerar devemos atribuir o nome da distribuição à variável DISTRO no arquivo build/conf/local.conf.
Documentação sobre DISTRO e DISTRO_FEATURES.
Machine
A Machine é a nossa plataforma alvo, ou seja, a placa para a qual desejamos gerar a nossa distribuição customizada. Ela é definida através de um arquivo de configuração a ser usado pelo Bitbake que armazena as features de hardware que vão influenciar a compilação e instalação pacotes, bem como quais receitas de bootloader e kernel a serem consideradas. Vejamos o exemplo da Beaglebone Black (arquivo meta-yocto-bsp/conf/machine/beaglebone.conf):
#@TYPE: Machine
#@NAME: Beaglebone machine
#@DESCRIPTION: Machine configuration for https://beagleboard.org/bone and https://beagleboard.org/black boards
PREFERRED_PROVIDER_virtual/xserver ?= "xserver-xorg"
XSERVER ?= "xserver-xorg \
xf86-input-evdev \
xf86-input-mouse \
xf86-video-fbdev \
xf86-input-keyboard"
MACHINE_EXTRA_RRECOMMENDS = " kernel-modules kernel-devicetree"
EXTRA_IMAGEDEPENDS += "u-boot"
DEFAULTTUNE ?= "cortexa8hf-neon"
include conf/machine/include/tune-cortexa8.inc
IMAGE_FSTYPES += "tar.bz2 jffs2"
EXTRA_IMAGECMD_jffs2 = "-lnp "
SERIAL_CONSOLE = "115200 ttyO0"
PREFERRED_PROVIDER_virtual/kernel ?= "linux-yocto"
PREFERRED_VERSION_linux-yocto ?= "4.4%"
KERNEL_IMAGETYPE = "zImage"
KERNEL_DEVICETREE = "am335x-bone.dtb am335x-boneblack.dtb"
KERNEL_EXTRA_ARGS += "LOADADDR=${UBOOT_ENTRYPOINT}"
SPL_BINARY = "MLO"
UBOOT_SUFFIX = "img"
UBOOT_MACHINE = "am335x_evm_config"
UBOOT_ENTRYPOINT = "0x80008000"
UBOOT_LOADADDRESS = "0x80008000"
MACHINE_FEATURES = "usbgadget usbhost vfat alsa"
IMAGE_BOOT_FILES ?= "u-boot.${UBOOT_SUFFIX} MLO"
A Machine possui uma variável (MACHINE_FEATURES) para definição de suas características que pode receber os seguintes valores:
acpi alsa apm bluetooth efi ext2 irda keyboard pcbios pci pcmcia phone qvga rtc screen serial touchscreen usbgadget usbhost vfat wifi
Para configurarmos para qual Machine queremos gerar a distribuição devemos atribuir o nome da Machine à variável MACHINE no arquivo build/conf/local.conf.
Documentação sobre MACHINE e MACHINE_FEATURES.
Receitas
As Receitas (recipes) disponibilizam os “ingredientes” e as instruções de “culinária” a serem consumidas pelo Bitbake para se construir um determinado pacote ou imagem. São nomeadas da seguinte maneira: basename_version.bb. Onde: basename representa o nome do pacote e version a versão do pacote em questão. As recipes sempre terão a extensão “.bb” como identificação.
Dentro de uma receita as informações comumente encontradas são:
DESCRIPTION
HOMEPAGE
LICENSE
SECTION
DEPENDS
LIC_FILES_CHKSUM
SRC_URI
Documentação sobre as variáveis que podem ser utilizadas em uma receita.
Vejamos o exemplo da receita para o pacote libogg na versão 1.3.2 (meta/recipes-multimedia/libogg/libogg_1.3.2.bb):
SUMMARY = "Ogg bitstream and framing libary"
DESCRIPTION = "libogg is the bitstream and framing library \
for the Ogg project. It provides functions which are \
necessary to codec libraries like libvorbis."
HOMEPAGE = "https://xiph.org/"
BUGTRACKER = "https://trac.xiph.org/newticket"
SECTION = "libs"
LICENSE = "BSD"
LIC_FILES_CHKSUM = "file://COPYING;md5=db1b7a668b2a6f47b2af88fb008ad555 \
file://include/ogg/ogg.h;beginline=1;endline=11;md5=eda812856f13a3b1326eb8f020cc3b0b"
SRC_URI = "https://downloads.xiph.org/releases/ogg/${BP}.tar.xz"
SRC_URI[md5sum] = "5c3a34309d8b98640827e5d0991a4015"
SRC_URI[sha256sum] = "3f687ccdd5ac8b52d76328fbbfebc70c459a40ea891dbf3dccb74a210826e79b"
inherit autotools pkgconfig
As receitas podem ser modificadas através de arquivos “.bbappend” (e.: libogg_1.3.2.bbappend) localizados em outras camadas. Dessa maneira não precisamos editar a receita original e podemos concentrar todas as customizações necessárias em uma camada específica, facilitando a manutenção e controle de mudanças.
De posse dos ingredientes e instruções básicas o Bitbake irá executar tarefas (tasks) para construção da receita. A seguir listamos as tasks comumentes executadas para uma receita:
- do_fetch: Localiza e baixa o código fonte;
- do_unpack: desempacota o fonte no diretório de trabalho;
- do_patch: aplica patches se necessários;
- do_configure: executa se necessário alguma configuração pré-build;
- do_compile: compila o código fonte;
- do_install: instalação dos artefatos de software resultantes do build em um local temporário;
- do_populate_sysroot: copia os artefatos para o sysroot (área comum a ser usada para satisfação de dependência);
- do_package_*: cria os pacotes de instalação.
Tudo começa no local.conf
O arquivo “build/conf/local.conf” irá ditar “as regras do jogo”. A partir dele com configurações mínimas da DISTRO e MACHINE que iremos utilizar o Bitbake será capaz de construir imagens e pacotes presentes nas camadas definidas no arquivo”build/conf/bblayers.conf“. Exemplo de um arquivo”local.conf“:
MACHINE = "raspberrypi2"
DISTRO = "poky"
DL_DIR = "${TOPDIR}/../dl"
PACKAGE_CLASSES = "package_ipk"
DISTRO_FEATURES_remove = "x11 wayland"
LICENSE_FLAGS_WHITELIST = "commercial license"
Conclusão
Na segunda parte dessa série vimos em mais detalhes a arquitetura e conceitos e definições do Yocto Project. Na terceira parte faremos um Quick Start e demonstraremos na prática o uso dos conceitos aqui apresentados.









Oi Diego. Muito obrigado por essa série do Yocto! Muito claro e detalhes no ponto pra ler se perder. Valeu mesmo!