This is an old revision of the document!
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:
LANG=en_US…
) (la secțiunea 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 ;)
~2 GB
(puteți oricând combina snapshoturile reușite pentru a reduce spațiul).
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
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:
repos
a fișierului kas.yml
.build/conf/{bblayers.conf,local.conf,templateconf.cfg}
.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.
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'
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).
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
).
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
Î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ă.
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"
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!
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
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-client 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 ;)
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 :)
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
Acum că avem posibilitatea de a încărca programe noi, dorim ca acestea să ruleze automat la boot. Well, let's keep it short.
Implicit, Yocto folosește sistemul sysvinit
(cel cu scripturi în /etc/rc.d
și symlink-uri în foldere /etc/rc<X>
pentru fiecare runlevel). Pentru a instala un serviciu, pur și simplu instalați scripturile / ln
la aceste locații (pentru runlevel-ul dorit). Vedeți exemplu aici.
Dacă sunteți fani SystemD, vedeți aici un tutorial cum înlocuiți sysvinit cu systemd (+ adăugare servicii).