This is an old revision of the document!


Extended Yocto Tutorial

Acest text vine ca supliment al laboratorului de Yocto și dorește să ofere informații pas cu pas despre îndeplinirea unor sarcini frecvente la generarea unei distribuții Linux (i.e., vă ajută la Tema 2 :P).

Revizii:

Mediul de lucru

Pentru început, aveți nevoie de mașina virtuală furnizată la Tema 2 descărcată.

Se recomandă să aveți ultima versiune de VMWare Workstation instalată, pe care o puteți descărca gratis de pe portalul https://vmware.pub.ro/ (vă autentificați cu contul unic al facultății).

Recomandat ar fi să faceți uz de funcționalitatea de VM snapshotting pe care o pune VMWare la dispoziție, astfel încât să puteți pleca de la imaginea de bază (pe care să o țineți nemodificată!) și să aveți mai multe instanțe separate (sub forma unor snapshot-uri) pe care le puteți comuta manual după caz. De exemplu, doriți să experimentați ceva cu Yocto: faceți un snapshot nou, vă jucați liberi în acea instanță apoi, dacă experimentul a eșuat, puteți reveni mereu la starea anterioară a snapshot-ului (cu fișierele de atunci).

Pentru un ghid de folosire a snapshot-urilor în VMWare, vedeți pagina aceasta. TLDR: Ctrl+M în VMWare pentru Snapshot Manager ;)

Un snapshot ocupă spațiu cât doar pentru modificările aduse la imaginea lui de bază (sau un alt snapshot pe care este bazat). Folosiți cu încredere, însă este posibil ca spațiul de modificări făcute la un build să aibe ~2 GB (puteți oricând combina snapshoturile reușite pentru a reduce spațiul).

Proiectul de VM al temei are deja un snapshot inițial făcut, dacă stricați ceva veți putea mereu reveni la varianta inițială a acestuia fără să-l re-descărcați ;)

O precizare aici: VM-ul inițial are 8GB RAM și 4vCPU-uri alocate (parcă). Le puteți înjumătăți fără probleme dacă nu dispuneți de aceste resurse hardware.

Errata: pentru cine întâmpină problema cu locale-ul la invocarea bitbake (LANG=en_US.UTF-8):

sudo locale-gen "en_US.UTF-8"
sudo localectl set-locale LANG=en_US.UTF-8
# ieșiți și reporniți shell-ul, sau dați:
export LANG=en_US.UTF-8

Starea / build-ul inițial, introducere kas

Considerăm starea inițială a fișierelor din VM cu următoarea structură (doar cele de interes pentru laboratorul de față, altele pot fi prezente, e.g. din toolbox-ul temei 2, însă le vom ignora momentan):

/home/student/yocto:
|-- build   # (generat de kas + Yocto bitbake)
|   |-- cache
|   |-- conf
|   |-- conf  # generate automat din kas.yml
|   |   |-- bblayers.conf
|   |   |-- local.conf
|   |   `-- templateconf.cfg
|   `-- tmp
|-- layers  # (descărcate prin kas)
|   |-- meta-raspberrypi
|   `-- poky
|-- kas.yml

Per parcursul acestui document, vom considera strict acest director de bază (neavând treabă în alte locuri).

În VM a fost deja invocat utilitarul kas (vedeți și documentația oficială), care a avut următoarele efecte:

  • a clonat (descărcat) de pe git-ul oficial proiectele poky (ce conține sursele și scripturile principale ale Yocto) și meta-raspberrypi (Layer oficial pentru plăcuțele RaspberryPi); toate acestea pe baza descrierii din secțiunea repos a fișierului kas.yml.
  • a generat fișierele de configurare ale build-ului Yocto: build/conf/{bblayers.conf,local.conf,templateconf.cfg}.
  • a făcut un bitbake build inițial la imaginea de bază a distribuției Poky (core-image-base), lucru ce poate dura chiar și câteva ore (pentru compilarea atât a toolchain-ului, cât și ale kernelului și ale tuturor pachetelor instalate în distribuția de bază); din fericire, o dată compilate aceste pachete, vor rămâne în cache și vor fi refolosite la toate build-urile ulterioare (decât dacă schimbați versiunea layerelor descărcate sau doriți să upgradați pachetele – we don't do that here!).

Deși acest mic tool ne-a automatizat din munca de setup a unui proiect nou Yocto, acesta vine cu inconvenientul că, dacă dorim să modificăm unul dintre fișierele generate (din build/conf/), va trebui s-o facem prin intermediul fișierului de configurare kas.yml (more on this later).

De menționat ar fi faptul că imaginea de bază conține strict pachetele prevăzute de către fișierele implicite de configurare ale Poky și atât. Nu este nici măcar folosibilă în starea aceasta inițială (neavând parolă la contul de root). Pentru a schimba aceste lucruri avem nevoie să ne construim propriul layer, ceea ce vom și face în secțiunea următoare.

Observați faptul că nu puteți rula bitbake (dă command not found). Acesta un executabil care nu se află de obicei în $PATH-ul normal al sistemelor Linux, putând fi accesat doar după inițializarea mediului Yocto. Reamintim că utilitarul kas ne-a descărcat codul sursă al poky în calea layers/poky/.

Tot aici găsim și scriptul oe-init-build-env responsabil de inițializarea. Astfel, dacă încărcăm acest script în shell-ul curent:

student@vm:~/yocto$ source layers/poky/oe-init-build-env
### Shell environment set up for builds. ###
You can now run 'bitbake <target>'
...

… utilitarele Yocto devin accesibile (însă doar în shell-ul actual, dacă utilizați mai multe terminale sau îl re-deschideți pe acesta, va trebui să repetați comanda în fiecare). Atenție! Comanda ne schimbă directorul nostru actual în build/, dăm cd .. pentru a reveni în rădăcina proiectului nostru.

1. Layere și rețete

Un layer este, practic, o colecție de sine stătătoare de scripturi, metadate și alte resurse construcție a unui sistem Linux. O distribuție Yocto este, practic, compusă din unul sau mai multe straturi așezate unul peste altul (unde Poky, desigur, stă la baza acestora) ce aplică operații de build într-o anumită ordine pentru a obține rezultatul dorit.

Pentru a crea un layer nou, putem folosi comanda bitbake-layers:

student@vm:~/yocto/build$ cd ../  # navigăm în afara directorului build/
student@vm:~/yocto$ bitbake-layers create-layer meta-tutorial-si
NOTE: Starting bitbake server...
Add your new layer with 'bitbake-layers add-layer meta-tutorial-si'

Nu recomandăm utilizarea directorului build/ pentru a stoca codul layerelor, deoarece va fi ignorat de scriptul care împachetează arhiva cu sursele. La fel și cu directorul layers/ (aici sunt stocate layerele third-party ce au fost / vor fi descărcate automat prin kas).

Această comandă va genera scheletul pentru noul nostru layer într-un director cu numele furnizat ca parametru. Structura layer-ului nou creat va fi următoarea:

meta-tutorial-si/
|-- conf
|   `-- layer.conf   # variabile de configurație utilizate de layer
|-- COPYING.MIT
|-- README
`-- recipes-example  # ce drăguț, avem și exemplu de o rețetă ;)
    `-- example
        `-- example_0.1.bb

Un ghid oficial pentru creare layere puteți găsi aici (să îl puteți consulta ulterior).

Recipes (rețete)

Pentru a fi util, un layer conține una sau mai multe rețete. O rețetă este un fișier .bb cu sintaxă specială Bitbake ce descriu pașii de urmat de către sistemul de build pentru a genera pachete sau altera configurația unei distribuții. Apoi mai avem fișiere .bbappend care ne permit să re-configurăm rețetele din alte layere fără a le rescrie. Ghid oficial aici.

Rețetele se află, de obicei, într-un director cu prefixul recipes- în denumirea lor, însă comportamentul real este definit în fișierul layer.conf:

# We have recipes-* directories, add to BBFILES
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
            ${LAYERDIR}/recipes-*/*/*.bbappend"

A se observa regula de globbing: se caută recursiv în orice director ce urmează această convenție, precum și sufixul de versionare (obligatoriu). Desigur, nu avem de ce să o schimbăm, așa că trecem mai departe ;)

O rețetă are o sintaxă relativ similară cu o combinație de Makefile și script de shell (e.g., bash). Putem vedea asta prin exemplul generat de create-layer:

SUMMARY = "bitbake-layers recipe"
DESCRIPTION = "Recipe created by bitbake-layers"
...
python do_display_banner() {
    # ...
    bb.plain("*  Example recipe created by bitbake-layers   *");
}
addtask display_banner before do_build

A se observa că este posibilă definirea de funcții în alte limbaje integrate (SHell sau chiar Python) ce se vor executa într-un anumit pas al procesului de build (vedeți diagrama din documentația oficială pentru o idee).

Pentru a folosi layer-ul nou creat, este necesar ca acesta să fie introdus în fișierul (global) de configurație al Yocto referitor la acestea: bblayers.conf. Ne reamintim, însă, faptul că tool-ul nostru helper de build va suprascrie orice modificări vom face. Defapt, ne generează lista de straturi pe baza unui fișier de configurație kas.yml, deci vom adăuga layer-ul nostru acolo:

# ...
repos:
  # adăugăm, pur și simplu, o cheie nouă (opțional, putem să modificăm calea către layer)
  meta-tutorial-si:
    path: './meta-tutorial-si'
    # atenție la identare! contează... yaml s-a inspirat din python :(
 
  # ... restul de layere au fost descărcate prin URL de git...
  meta-raspberrypi:
    url: git://git.yoctoproject.org/meta-raspberrypi
  # ...

Rulăm apoi kas build kas.yml. Observăm că, în build/conf/bblayers.conf ne-a apărut layer-ul:

BBLAYERS ?= " \
    /home/student/yocto/layers/meta-raspberrypi \
    ...
    /home/student/yocto/meta-tutorial-si"  # <-- here it is

Doar că trebuia să ne printeze mesajul acela din rețeta Example la build, ceea ce nu se întâmplă… de ce oare? (heheee) Să facem altfel:

# REMINDER: să aveți oe-init-build-env activat!
student@vm:~/yocto$ bitbake example  # să aveți oe-init-build-env activat!
Loading cache: 100% |##########| Time: 0:00:00
...
NOTE: Executing Tasks
***********************************************
*                                             *
*  Example recipe created by bitbake-layers   *
*                                             *
***********************************************

Now we're getting somewhere! Mai rămâne, deci, să adăugăm această rețetă undeva să fie executată la build-ul imaginii principale (reminder: denumită core-image-base:

student@vm:~/yocto$ cd meta-tutorial-si/
student@vm:~/yocto/meta-tutorial-si$ mkdir -p recipes-core/images/
student@vm:~/yocto/meta-tutorial-si$ vim recipes-core/images/core-image-base.bbappend
# iar în fișier vom scrie: (suntem în vim, deci apăsăm un 'i' înainte pentru insert mode)
IMAGE_INSTALL += " example"
# :wq ca să nu rămânem blocați :P
student@vm:~/yocto$ cd ../
student@vm:~/yocto$ kas build kas.yml

et voila!

Cum funcționează: am extins rețeta imaginii de bază printr-un fișier .bbappend (pentru asta au fost concepute). Codul nostru va fi procesat după fișierul .bb original, astfel putând modifica variabila IMAGE_INSTALL pentru a ne fi incluse pachete custom de către task-urile rețetei generatoare de imagine. Also check these goodies (chestii gata făcute incluzabile printr-o linie de cod).

Ca alternativă, puteam construi o nouă imagine (e.g., tutorial-si-image) care să pornească de la prima. Doar că trebuia să build-uim noua noastră imagine (bitbake tutorial-si-image în loc de core-image-base).

Am fi putut să facem același lucru prin modificarea conf/local.conf și adăugarea unei linii:

IMAGE_INSTALL_append = " example"

… dar este, desigur, anti-practică! Vrem să avem toată funcționalitatea încapsulată în layer bine definit, păi ce facem noi aici… :P

2. Construirea unei distribuții utilizabile

Înainte de a continua, să aveți un layer creat (e.g., meta-tutorial-si de la pasul anteror sau, desigur, puteți să folosiți ce altă denumire doriți) pe care îl vom extinde.

Pentru a avea o distribuție Linux utilizabilă va trebui, desigur, să avem creat un utilizator ;) Apoi vom demonstra adăugarea de resurse (fișiere) în imaginea de bază.

2.1. Adăugare utilizator nou

Se dă tutorialul acesta. Cum se procedează mai departe?

“In your image recipe:”. Deci vim meta-tutorial-si/recipes-core/images/core-image-base.bbappend:

# vechiul conținut:
IMAGE_INSTALL += "example"
# mai adăugăm:
inherit extrausers
# pentru a schimba parola la root:
EXTRA_USERS_PARAMS += "usermod -P student root;"
# pentru a adăuga un utilizator nou:
EXTRA_USERS_PARAMS += "useradd -P parolalastudent student;"
# atenție: trebuie să muncim în plus pentru a-i da drept de sudo utilizatorului, deci recomand root-ul

Apoi construim imaginea din nou: kas build kas.yml. Și rulăm:

export LANG=en_US.UTF-8  # dacă runqemu se plânge că nu-l avem setat
# nu uitați să faceți source la oe-init-build-env dacă nu aveți utiltarele în PATH
runqemu qemuarm nographic
# pentru a ieși, trebuie să apăsăm combinația Ctrl+A, c
# va apărea un terminal qemu unde puteți scrie "quit"

Dacă doriți să folosiți scriptul launch.sh, va trebui mai întâi să rulați sudo create-bridge.sh din consola VMWare (conexiunea la rețea va pica temporar). Apoi, rulați make bin_archive pentru a copia ultima imagine în cache-ul de arhivă (pentru distribuirea binarelor), deoarece scriptul folosește ultima imaginea salvată astfel!

2.2. Inspectare rootfs

Următoarea întrebare care se pune este: cum vedem conținutul unei imagini generate? Din fericire, suntem pe un sistem Linux, așadar putem folosi mount pentru a inspecta local partiția ext3/4 generată:

student@vm:~/yocto$ sudo mount -o loop build/tmp/deploy/images/qemuarm/core-image-base-qemuarm.ext4 /mnt
student@vm:~/yocto$ ls -l /mnt
student@vm:~/yocto$ cat /etc/passwd | grep student
student:x:1000:1000::/home/student:/bin/sh
# YEAHHH!
# NU UITAȚI: să de-montați partiția la final!!!
# (altfel s-ar putea să se corupă imaginea de la dublă utilizare)
student@vm:~/yocto$ sudo umount /mnt

2.3. Instalare pachete de bază

Deseori vom dori să instalăm pachete standard în imaginea noastră.

Ca un prim pas, trebuie să aflăm denumirile pachetelor disponibile în distribuția standard. Pentru acesta, folosim bitbake show-recipes pentru a vedea lista întreagă de rețete. Mod de folosire:

student@vm:~/yocto$ bitbake-layers show-recipes | grep -A 1 dhcp
dhcp:
  meta                 4.4.2
# (conține utilitarul dhclient, pentru a primi adresă pe interfață)
 
student@vm:~/yocto$ bitbake-layers show-recipes | grep -A 1 python3
python3:
  meta                 3.8.12
# ... și multe altele

Problema este că acestea sunt denumiri de rețete, însă o rețetă poate defini mai multe pachete (de obicei, se păstrează o convenție de nume prin prefixare). Din păcate, va trebui să ne uităm pe codul sursă al rețetelor de oricâte ori nu există pachetele cu numele lor. Spre exemplu, pentru dhcp putem vedea în fișierul sursă toate pachetele generate:

# pe linia ~100:
PACKAGES += "dhcp-libs dhcp-server dhcp-server-config dhcp-client dhcp-relay dhcp-omshell"

Desigur, noi avem nevoie doar de un client DHCP, deci numele pachetului va fi dhcp-client. Pentru pachetele standard din Poky (versiunea dunfell), puteți explora rețetele aici (a se observa categorizarea acestora).

Pentru a instala un pachet într-o imagine, trebuie să edităm rețeta imaginii (fișierul .bb dacă este imaginea noastră, altfel prin .bbappend în rețeta imaginii modificate):

student@labsi-vm:~/yocto$ vim meta-tutorial-si/recipes-core/images/core-image-base.bbappend
# adăugăm pachetele prin variabila IMAGE_INSTALL:
IMAGE_INSTALL += "dhcp python3"

Desigur, putem face același lucru prin folosirea variabilei IMAGE_INSTALL_append a lui local.conf, dar vrem să păstrăm gruparea corectă a funcționalității în layere ;)

Pentru fiecare pachet nou adăugat, bitbake va porni un proces de compilare care s-ar putea să dureze câteva minute (posibil și zeci). Să țineți cont acest timp de așteptare când vă planificați activitatea!

Finally, dorim să testăm noua imagine:

student@vm:~/yocto$ kas build kas.yml
# (sudo make coffee ...)
NOTE: Tasks Summary: ... all succeeded.  # doamne-ajută!
 
student@vm:~/yocto$ runqemu qemuarm nographic
# ...
qemuarm login: root
Password:
root@qemuarm:~# ip a sh eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 52:54:00:12:34:02 brd ff:ff:ff:ff:ff:ff
    inet 192.168.7.2/24 brd 192.168.7.255 scope global eth0
       valid_lft forever preferred_lft forever
# ...
root@qemuarm:~# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=127 time=30.050 ms
64 bytes from 8.8.8.8: seq=1 ttl=127 time=20.487 ms
# https://www.youtube.com/watch?v=ITRBsXrPOn8
# dacă rămâneți blocați în qemu console, căutați mai sus cum ieșiți :)

2.4. Adăugare resurse în imagine

O dată instalate pachetele necesare (e.g., servicii de rețea), vom dori să configurăm sau să instalăm scripturi custom-made.

Putem face asta prin intermediul rețetelor. Reamintim că acestea execută anumite acțiuni într-o ordine bine definită. Printre primele task-uri efectuate se numără cel de fetch source files, ce de multe ori constă în descărcarea codului de pe un server. Într-o rețetă, se face asta prin intermediul variabilei de configurare SRC_URI care ne permite inclusiv prin căi locale.

De exemplu, edităm rețeta exemplu (meta-tutorial-si/recipes-example/example/example_0.1.bb):

# ... (după descrierea din antet)
LICENSE = "MIT"
 
# adăugăm fișiere sursă
SRC_URI = "file://hello.py \
           file://example.conf"
# sau pe linii separate:
#SRC_URI += "file://example.conf"
# fișierele sursă vor fi copiate / descărcate în ${WORKDIR}!
 
# va trebui să intervenim cu un pas de instalare pentru a le copia în imagine:
do_install() {
    # atenție: ${D} represintă directorul destinație în procesul de împachetare!
    #          orice generați în afara acestuia nu va fi inclus în pachet!
    # copiem scriptul în bin:
    install -D -m 0755 -t ${D}${bindir} ${WORKDIR}/hello.py
    # copiem conf-ul în /etc:
    install -D -m 0644 -t ${D}/etc ${WORKDIR}/example.conf
}
 
python do_display_banner() {
# ... restul rămâne

Dacă încercați să build-uiți pachetul sau imaginea (bitbake example), veți obține o eroare cum că nu poate găsi fișierele incluse (și vă dă și căile unde le puteți crea). O convenție foarte bună ar fi subdirectorul files, deci:

student@vm:~/yocto$ mkdir -p meta-tutorial-si/recipes-example/example/files/
 
student@vm:~/yocto$ vim meta-tutorial-si/recipes-example/example/files/hello.py
#!/usr/bin/python3
with open("/etc/example.conf", "r") as f:
  name = f.readline().strip()
print("Hello, I am " + name)
 
# <esc>:wq pentru a ieși din vim ;)
 
student@vm:~/yocto$ echo "TUTORIAL" > meta-tutorial-si/recipes-example/example/files/example.conf
student@vm:~/yocto$ bitbake example

Desigur, obținem iar o eroare, plângându-se că fișierele adăugate nu au precizată licența și checksum-urile (pentru securitate sporită). Modificăm iar fișierul example_0.1.bb:

# pe la început, să fie cu restul de variabile: 
# 1. dezactivăm checksum-urile definitiv pentru rețetă
BB_STRICT_CHECKSUM = "0"
# 2. fișierul licenței alese (MIT) e partajat și are acest checksum:
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

Iar bitbake example, iar o erorare despre RDEPENDS la python3, iar rezolvăm (example_0.1.bb):

# Yocto detectează automat (prin shebang-ul pus la script) că avem nevoie de python3
RDEPENDS_${PN} = "python3-core"

Generăm imaginea: kas build kas.yml apoi o rulăm (prin runqemu).

În qemu, rulăm scriptul:

root@qemuarm:~# hello.py
Hello, I am TUTORIAL

2.5. Pornire servicii de sistem

Acum că avem posibilitatea de a încărca programe noi, dorim ca acestea să ruleze automat la boot.

TODO

si/laboratoare/yocto-extra1.1642247336.txt.gz · Last modified: 2022/01/15 13:48 by florin.stancu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0