Cos'è?
Il mondo digitale gira grazie a Linux.
Circa. Ok su desktop è ancora una cosa un po’ di nicchia (fintanto che Windows non tira gli ultimi respiri), ma nell’ ambito server Linux DOMINA. Nell’ ambito dei sistemi embedded ha cominciato a prendere piede molto velocemente, visto che permette di realizzare sistemi compatti utilizzando gli stessi software disponibili su desktop e server. Con embedded intendo tutto quei dispositivi che ufficialmente sono dei computer, ma a primo occhio non lo sembrano. Tipo quasi tutti gli elettrodomestici smart (frigoriferi, tv, …), molte centraline di automobili e macchinari industriali, insomma possiamo dire quasi tutti gli apparecchi elettronici.
Non tutti tutti, perchè in realtà su quelli che hanno meno necessità di collegamento in rete, più necessità di un preciso controllo hardware e non hanno grandi richieste di parallelismo di processi sono basati su microcontrollori o addirittura circuiti digitali/analogici.
Linux non è la soluzione a tutti i mali (coff coff.. Windows non è manco una soluzione), però la sua modularità, configurabilità e accessibilità lo hanno reso uno strumento potente e adatto a risolvere un grande scenario di problemi.
Tant’è che esistono una infinità di varianti di sistemi opeativi basati su Linux. Pure Android è basato su Linux e ne segue ancora lo sviluppo. Per essere precisi, con “Linux” si intende il componente principale di un sistema operativo, il Kernel. Il programma principale con il compito di gestire le risorse hardware disponibili. Poi sopra a questo abbiamo le valangate di applicazioni/software e programmi vari. Di questi, i principali sono quelli nati dal progetto GNU, ovvero le principali utility che troviamo in ogni sistema basato su Linux, tipo i comandi:
cp --> copy
mv --> move
ls --> list
dd --> data duplicator
grep --> global regular expression printe così via… Questi sono talmente indispensabili che di solito si parla di GNU/Linux, invece che Linux e basta.
E poi ovviamente ci sono tutto il resto di comandi e applicativi vari.
Ogni assieme di kernel + applicativi prende il nome di distribuzione.
Tra le più famose distribuzioni Linux troviamo:
- Debian
- Fedora
- Alpine
- Android
- Tuamadre?
Insomma, prova a cercare su internet e vedi quante te ne compaiono. Fai però attenzione, perchè è un rabbit-hole senza fine, letteralmente.
Ok quindi Yocto che minchia centra in tutto questo? Creare una distribuzione è un casino. Richiede un sacco di tempo ed energie per fare in modo che tutto sia configurato a dovere. Poi se devi fare modifiche devi anche ricontrollare che tutto funzioni ancora come deve. Cosa potrebbe mai andare storto ti chiedi?
I CONFLITTI TRA PACCHETTI AD ESEMPIO.
Ogni software si porta dietro delle dipendenze e spesso molteplici software condividono delle dipendenze. Però quando uno di questi si aggiorna è probabile che cambi le versioni software richieste delle sue dipendenze. E se alcune nuove versioni hanno smesso di essere compatibili con le vecchie? E se devi tenere una dipendenza vecchia per un certo software e una più recente per un altro software? E se scopri che una di queste dipendenze non la potevi usare per motivi di licenze? E se tipo molli tutto e ti dai all’ agricoltura? Tranquillo/a, ci siamo passati tutti.
Poi tra gli altri problemi c’è il grande fatto che alcuni software richiedono pure un determinato hardware. Tipo che te ne fai di un controller HDMI, se il tuo pc degli anni 2000 ha solo il VGA? Oppure come fanno i software a capire che hai un monitor touchscreen? E se il monitor manco ce l’hai?
Sono tantissimi i fattori variabili che possono mandare a puttane l’esperienza di utilizzo di un PC. Pensa che nell’ ambito embedded le variabili sono ancora di più perchè esistono molte più tipologie di periferiche e spesso necessitano di driver appositi. Periferiche complesse che su PC e server non troviamo di solito, tipo il CAN, I2C, ADC solo per dirne alcuni.
Yocto si pone come una valida soluzione per creare distribuzioni basate sul kernel linux altamente specifiche per le tue necessità. Non è una singola distribuzione magica, è un più un configuratore. Il termine tecnico è “build system”, ovvero un sistema che a partire da istruzioni ben definite realizza un prodotto ben definito. E ti assicuro che il livello di definizione è estremamente alto. Non è l’unico tool di questo genere, esistono anche Buildroot e poi boh non so cosa altro. Per l’embedded sono principalmente questi due. Per l’ambito desktop e server ce ne sono altri, ma spesso non sono utilizzabili per quello che vogliamo fare qui.
Ok adesso pausa caffè e poi riprendiamo. Ti stai divertendo, no?
Come funziona Yocto
Yocto è un progetto che deriva dal build system
openembedded,
basato attorno al tool
bitbake,
il quale è sostanzialmente un insieme di script
Python.
Generalmente i termini Yocto, Openembedded, Bitbake sono usati interscambiabilmente. Per farla breve, Yocto è il sistema che ha soppiantato il sistema openembedded e entrambi comprendono:
- dei layer di riferimento per poter fare cose utili
- il tool bitbake
Bitbake definisce la sintassi dei file .bb,.bbappend, .conf
e i meccanismi di base per gestire le interazioni tra questi file
e le dipendenze tra i task definiti all’interno di questi.
Yocto sfrutta questi meccanismi per descrivere quelle che chiama ricette,
ovvero i task di base per poter compilare un determinato codice sorgente,
muovere determinati output della build in determinate cartelle,
definire dei metadati associati a tale software e molto altro.
Poi definisce i layer, che sono dei raggruppamenti di ricette
e contengono ulteriori metadati.
Oltre a questi, definisce anche tutte una serie di meccanismi
specifici per facilitare le operazioni più comuni per lo sviluppo
di kernel, bootloader, partizioni di schede di memoria e altro.
Infine raggruppa tutti questi strumenti in layer di base ,
che appunto sono quelli che devono essere inclusi in ogni progetto basato su Yocto.
Ricordiamo che alla base ci sono molti script python, quindi tutti questi meccanismi aggiuntivi sono sempre ispezionabili. Cosa che torna molto utile quando la documentazione non basta, o quando vogliamo aggiungere ulteriori funzionalità.
Anzi, per aggiungere funzionalità, non andremo direttamente a modificare questi script, ma utilizzeremo i meccanismi che ci vengono forniti per poter “fare le cose esattamente come vogliamo noi”.
Un’altra cosa molto importante da tenere a mente è che gran parte della configurazione di Yocto è gestita tramite variabili più o meno globali, quindi spesso ci troveremo a navigare codice per capire come e dove certe variabili vengono usate e modificate.
Bene, questa era la premessa, adesso facciamo qualche passo in più.
Struttura di un progetto
Adesso che abbiamo all’incirca una idea di quali sono i componenti in gioco, cerchiamo di capire come sono effettivamente usati.
Ogni progetto Yocto parte con l’inclusione del layer di base,
chiamato solitamente
meta.
Questo perchè non contiene delle cose utili di per sè,
ma fornisce solamente istruzioni su come utilizzare qualcosa di già esistente.
Infatti vedremo che le ricette di Yocto non contengono
il codice sorgente vero e proprio di un software,
ma solamente dei riferimenti al dove trovarlo
(ad esempio su quale repository GitHub),
a che strumenti servono per compilarlo (Make, Meson, …),
che comandi lanciare per compilarlo e configurarlo
e cose così.
Quindi un layer Yocto corrisponde alla definizione generale di metadata,
da cui l’utilizzo della convenzione di dare un nome che inizi per meta-
a qualsiasi layer Yocto.
Ad esempio:
- meta-arm
- meta-arduino
- meta-qt5
- meta-python
Insomma e via così. Non è una regola fissa, ma solo una convenzione.
Il layer di base è stato quindi chiamato semplicemente meta.
Contiene i principali recipe (ricette) per poter buildare
i software più comuni, ad esempio:
E veramente tanti, tanti altri. A questo poi vanno aggiunti altri componenti, come bitbake e alcuni script. Esistono varie repository che racchiudono tutto quello che serve per cominciare e sono sostanzialmente le stesse:
Anzi, nelle ultime versioni hanno cambiato un po’ le cose
e la repository poky non contiene più nulla,
in favore dell’utilizzo del comando
bitbake-setup
Quindi nelle versioni più recenti basta clonare la repository di bitbake
e utilizzare il comando
./bin/bitbake-setup init
Sì è un delirio e le cose possono cambiare molto di “versione in versione”. Per questo è il momento di parlare appunto delle varie versioni di Yocto. Tranquilli, poi nei prossimi articoli vedremo come fare il setup partendo dal nulla cosmico, quindi in qualche modo ce la faremo ad usare Yocto.
Versioni Yocto
Le versioni pubblicamente disponibili partono dall’anno 2010 e solamente dal 2022 iniziano ad esistere le prime versioni LTS (Long Term Support), con l’obiettivo di rendere più stabile l’ecosistema Yocto. Questo perchè il rilascio di una nuova versione avviene circa 2 volte l’anno e spesso crea problemi di retrocompatibilità. Alcune delle versioni:
- Zeus (2019)
- Dunfell (2020)
- Kirkstone (2022, LTS fino 2026)
- Mickledore (2023)
- Scarthgap (2024, LTS fino 2028)
- Styhead (2024)
Scarthgap è la versione LTS (Long Term Support) più recente e rimarrà tale fino ad Aprile 2028, come descritto sul sito ufficiale. Per un elenco di tutte le versioni, vedere il sito ufficiale. Scarthgap è la versione che utilizzeremo in questi articoli, almeno fino al 2028.
Chissà quante cose cambieranno fino ad allora. Il setup di un progetto Yocto di sicuro.
Struttura di un progetto, di nuovo
Quindi, tornando al discorso principale, un progetto Yocto è composto da una caterba di cartelle e file, perciò stiamo attento che perdersi è estremamente facile.
Piccola nota, esistono molte variabili che ci permettono di configurare la posizione e nome delle cartelle utilizzate in Yocto, per il momento cerchiamo di capire il layout standard.
Al piano più alto, abbiamo la cartella layers e la cartella build.
La prima conterrà tutti i layer che abbiamo intenzione di usare,
o anche non usare, nel nostro progetto.
La seconda è dove bitbake genererà tutti i file necessari.
layers
layers
├── meta-arm
│ └── ...
└── meta
├── conf
├── recipes-bsp
│ ├── u-boot
│ │ └── u-boot.bb
│ ├── usbutils
│ │ └── usbutils.bb
│ └── ...
├── recipes-kernel
│ ├── linux
│ │ ├── linux-yocto.bb
│ │ └── linux-yocto-tiny.bb
│ ├── kern-tools
│ │ └── kern-tools.bb
│ └── ...
└── ...Dentro layers troveremo una cartella per ogni layer
che abbiamo intenzione di utilizzare.
Questa ha l’intento di essere una panoramica semplicistica
e non un elenco dettagliato di cosa troviamo all’interno del layer meta,
infatti nella gerarchia delle cartella mostrata,
molte cose sono state tralasciate e altre semplificate,
ma ci aiuta comunque a capire come orientarci.
La panoramica dettagliata la lasciamo
ad un prossimo articolo dedicato solo a quella.
All’interno di un singolo layer troviamo:
La cartella conf contiene sottocartelle
e file per definire i metadati associati al layer.
La cartelle recipes- raggruppano molteplici recipe
e all’interno di questi troviamo i file .bb
che definiscono effettivamente un recipe.
build
Dentro build le cose si complicano.
build
├── cache
├── conf
│ ├── bblayers.conf
│ └── local.conf
├── downloads
├── sstate-cache
└── tmpIn cache troviamo i file temporanei di bitbake,
come ad esempio il database degli hash e delle ricette trovate.
In conf troviamo i file di configurazione del nostro progetto.
Sono sostanzialmente due e sono:
bblayers.confdefinisce i layer effettivamente utilizzati nel progettolocal.confdefinisce delle variabili di base e permette di fare un override di qualsiasi altra variabile
In downloads troviamo i file tar delle repository
utilizzate dai vari recipe del nostro progetto.
Questa cartella funge da cache locale per evitare di dover
riscaricare le repository ad ogni avvio della build.
In sstate-cache troviamo una miriade di cartelle che nel loro ultimo livello
contengono i file di output generati da determinati task.
Questa cartella funge da cache locale per evitare di dover
rieseguire task di cui bitbake conosce già l’effetto finale.
In tmp troviamo i tutti file generati durante la build.
Questi possono essere
le repository dei recipe estratte ed ispezionabili,
i file intermedi generati durante una compilazione di un recipe,
i file di output di un recipe,
statistiche sulle tempistiche delle build eseguite
e molto molto altro.
Nel caso volessimo pulire una build,
ci basterà cancellare questa cartella
e l’obiettivo di Yocto è proprio quello di riuscire
a ricrearla ESATTAMENTE uguale (byte per byte)
solamente a partire dalle informazioni contenute
nei layer e nella cartella conf.
Build workflow
Per arrivare ad ottenere un output che descrive un intero sistema operativo, Yocto segue queste fasi:

Comandiamo a bitbake di buildare un determinato recipe.
Bitbake procede a leggere la configurazione del progetto,
applicando gli override descritti in local.conf
e attivando i layer descritti in bblayers.conf.
Procede quindi a fare il parse di tutti i recipe
disponibili dentro i layer selezionati e
cerca il recipe che gli abbiamo comando di buildare.

Prima di iniziare la build del recipe voluto,
bitbake ne controlla le dipendenze,
andando quindi a generare una lista ordinata
di task da eseguire per poter arrivare a
buildare il recipe desiderato.
Se riscontra che nel nostro progetto mancano
dei recipe, la build fallisce.

Inizia la vera e propria fase esecutiva.
Bitbake inizia a eseguire tutti i task necessari,
cercando di parallelizzare al massimo l’esecuzione
ed utilizzando la sstate-cache e i downloads
per evitare di rieseguire task di cui conosce già
l’output finale, velocizzando quindi la build.
Per ogni recipe buildato, viene creato
un fake-root filesystem che simula un
vero filesystem linux e viene utilizzato
per capire dove posizionare determinati
file di output del nostro recipe all’interno
del filesystem finale.
Questo viene utilizzato anche per poi
generare un package che,
quando installato, applicherà tali modifiche
ad un generico filesystem.

La generazione dei package è necessaria,
perchè per costruire
il root filesystem finale,
Yocto andrà creare un filesystem pulito
e poi ci andrà a installare tutti i
package necessari,
garantendo quindi una build
perfettamente ricreabile.
Dopo ogni build di un recipe,
il relativo package viene
quindi salvato in un package feed.
Il package feed torna utile
anche nel caso volessimo
installare dei pacchetti aggiuntivi
sul sistema una volta completata la build.
Ci basta trasferire il package sul
nostro dispositivo ed installarlo.

Una volta assicurata la presenza di tutti
i componenti necessari, bitbake procede
con la creazione del recipe richiesto.
Nel caso il recipe definisca una intera distribuzione,
verranno generati dei file che permettono di
flashare l’intero firmware sul dispositivo.
Bene, questo era il processo a grandi linee. Per maggiori dettagli, provate a capirci qualcosa dal sito ufficiale.
Cose finali
Si conclude così la nostra infarinatura su Yocto.
Adesso siamo pronti per iniziare a entrare più un dettaglio
su come si creano e gestiscono
i recipe, i layer e tutto quello che ci va dietro.