Yocto extended content

Yocto este o colecție de utilitare, metadate și șabloane ce permit construirea/compilarea distribuțiilor de Linux pentru platforme embedded. Acest proiect este dezvoltat de Linux Foundation și condus de către Richard Purdie.

Proiectul Yocto se adresează atât utilizatorilor experimentați cât și utilizatorilor noi. Pentru utilizatorii experimentați, Yocto oferă posibilitatea de a crea distribuții personalizate pornind de la 0 sau de la imagini scheletice. Utilizatorii noi au la dispoziție o serie de exemple și un kernel ce poate fi folosit ca punct de pornire. De asemenea, aceste imagini de bază sunt disponibile pentru diferite platforme: ARM, PPC, MIPS, x86 etc.

Pentru a-și ajuta utilizatorii, Yocto vine cu o serie de aplicații: un sistem de build numit Bitbake, o interfață grafică numită Hob (we don't do that here :D) cât și alte unelte de dezvoltare, și, cel mai important, o documentație stufoasă.

Sursele Yocto pot fi găsite atât la adresa oficială http://git.yoctoproject.org/cgit/cgit.cgi/poky, cât și la un mirror pe GitHub (pentru cine preferă).

Descriere componente / unelte Yocto

Pentru început, vom face o descriere componentelor și utilitarelor folosite în cadrul laboratoarelor de Yocto Linux: Reveniți asupra acestora oricând aveți nelămuriri despre sintaxă / variabile pe parcursul exercițiilor.

Bitbake

Bitbake este sistemul de build folosit de Yocto. Acesta este asemănător sistemului folosit uzual pe distribuțiile Linux, make. Ca și make, bitbake trebuie să determine ce acțiuni trebuie executate și apoi să le lanseze efectiv în execuție. Aceste acțiuni se determină în funcție de: comenzile date de către utilizator, datele proiectului și starea curentă a build-ului. Toate operațiile ce trebuie executate, dependențele dintre acestea, variabilele și instrucțiunile sunt ținute și citite din fișiere scrise în sintaxa specifică bitbake:

HelloWorld.bb
DESCRIPTION = "Hello World"
LICENSE = "CLOSED"
 
PR = "r0"
SRC_URI = "file://myhelloworld.c \
           file://README.txt"
 
TARGET_CC_ARCH += "${LDFLAGS}" 
 
do_compile() {
        ${CC} ${CFLAGS} ${LDFLAGS} ${WORKDIR}/myhelloworld.c -o ${WORKDIR}/myhelloworld
}
 
do_install() {
        install -d ${D}${bindir}
 
        install -m 0755 -t ${D}${bindir} ${WORKDIR}/myhelloworld
}

Asemănător cu un Makefile, putem folosi variabile de sistem pentru a specifica flag-uri, executabile, căi etc. Principala diferență constă în modul de organizare a fișierului. În aceste fișiere de configurare ne este permis să definim task-uri pentru fiecare etapă a procesului de build ce trebuie executată. Astfel, codul este mult mai bine organizat, mai ușor de urmărit și de depanat.

Un alt avantaj al lui bitbake este organizarea sa ierarhică. Atunci când se pornește un build, bitbake are o viziune de ansamblu asupra distribuției. Se vor citi mai întâi toate fișierele de configurare (rețetele) ce au legătură cu acea distribuție și abia apoi se va decide care sunt task-urile ce trebuie executate și în ce ordine se vor executa.

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).

Fișiere speciale și variabile de sistem

Observați că fișierul din exemplul de mai sus are extensia .bb. Aceasta este o extensie specială specifică unei categorii de fișiere de configurare pentru bitbake. Există mai multe categorii de astfel de fișiere, fiecare exprimând un set diferit de metadate (dependențe, patch-uri, instrucțiuni) pentru o anumită componentă. Categoriile importante (în funcție de extensie):

  • .bb: fișiere concise ce definesc operațiile ce trebuie executate. Poartă numele de rețete. O rețetă poate include alte rețete și poate moșteni dintr-o altă rețetă
  • .bbclass: fișiere folosite pentru a preciza metadatele folosite uzual pentru operațiile comune de build sau împachetare. Aceste fișiere sunt de obicei moștenite de către rețete
  • .inc: fișiere ce conțin un anumit set de definiții. Sunt folosite atunci când vrem să particularizăm procesul de build pentru o anumită versiune, arhitectură etc.
  • .bbappend: fișiere ce conțin adăugiri sau componente opționale pentru rețete
  • .conf: fișierul local de configurare al unui proiect

Pe lângă aceste fișiere, pentru a personaliza un proiect/build, avem la dispoziție și o serie de variabile de sistem:

  • BBFILES: variabila ce spune sistemului bitbake care sunt rețetele disponibile
  • SRC_URI: identifică fișierele care trebuiesc incluse în directorul de lucru special făcut pentru rețeta respectivă
  • BB_NUMBER_THREADS: denotă numărul de thread-uri de bitbake care să ruleze
  • PARALLEL_MAKE: modul în care va fi făcută compilarea și numărul de thread-uri de compilare care vor porni. Valoarea acestei variabile este aceeași ca în cazul sistemului make: -j <num_threads>
  • MACHINE: denumirea sistemului (mașinii) pentru care se realizează compilarea
  • BBMASK: lista de pachete ce vor fi ignorate în momentul compilării

Rețete

Fișierele de configurare prezentate anterior poartă denumirea de rețete și au uzual extensia .bb. În cadrul unui proiect (o distribuție) acestea definesc operațiile ce trebuie executate. În funcție de complexitatea și dimensiunea proiectului, acesta va parcurge un anumit număr de etape. Dacă proiectul este mic și presupune, spre exemplu, doar compilarea unor surse, atunci este suficientă o rețetă ce conține un task do_compile. Un proiect mai mare va trece însă cel puțin prin etapele de fetch, configure, compile și install.

Pentru un astfel de proiect va exista câte o rețetă ce conține un task de tip do_ pentru fiecare operație. Folosind apoi proprietatea rețetelor de a include sau a specifica dependențe față de alte rețete, se creează o ierarhie ce pornește de la o rețetă top level și parcurge și execută în ordine toate operațiile.

Directivele ce pot fi folosite într-o rețetă pentru a include sau moșteni alte fișiere de configurare sunt:

  • include <file_name>: include fișierul cu numele <file_name>. Este folosită variabila BBPATH pentru a căuta fișierul
  • require [<path>]<file_name>: un tip special de includere a fișierului <file_name>. Se va încerca includerea fișierului ca și în cazul lui include, dar operația va eșua dacă nu există fișierul în locația specificată
  • inherit <file_name>: include definițiile din fișierul <file_name>.bbclass, dacă acesta există

Layer

Un set de rețete și alte fișiere specifice bitbake ce au legătura cu o anumită funcționalitate sau o anumită componentă pot fi organizate într-un layer. Layer-ul este un fișier ce conține referința către o configurație și setul de rețete ce trebuiesc executate pentru a obține o anumită distribuție.

LayerExample
meta-layer: 
  - conf
  - recipes-core
      - important_recipe
          - x.bb
  - recipes-category1
      - recipe-1a
          - y.bb
          - t.bbappend
      - recipe-1b
          - ...
  - recipes-category2
      - recipe-2a
          - ... 
      - recipe-2b
          - ...

Structura unui layer este următoarea: denumirea layer-ului urmată de o serie de categorii de rețete. Fiecare categorie conține lista rețetelor componente și fișierele de configurare aferente fiecarei dintre acestea.

Atunci când parsează un fișier de tip bblayer, bitbake va configura variabila de sistem BBPATH cu locațiile în care se găsesc rețetele necesare. În cadrul acestui proces de construire a variabilei BBPATH, dacă ierarhia conține rețete cu nume duplicat, pot apărea conflincte sau situații neașteptate în timpul compilării, deoarece fișierele de configurare pentru o rețetă pot fi incărcate dintr-o locație diferită față de cea dorită.

Pentru a automatiza procesul de creare a unui layer se poate folosi comanda:

# pentru kas, intrați în shell mai întâi:
# apoi rulați:
bitbake-layers create-layer meta-mylayer

Specificarea layer-elor ce se doresc a fi incluse într-un proiect se face prin scrierea unui fișier numit bblayers.conf. Structura acestuia este următoarea:

BBLAYERS ?= " \
<path_to>/poky-rasp/meta \
<path_to>/poky-rasp/meta-yocto \
<path_to>/poky-rasp/meta-yocto-bsp \
<path_to>/poky-rasp/meta-raspberrypi \
"

Acest fișier pune în variabila de sistem BBLAYERS căile către fiecare locație în care se găsește fișierul bblayer al layer-ului dorit.

De ținut cont: modalitățile de personalizare a parametrilor în fișiere precum bblayers.conf și local.conf diferă atunci când folosim utilitarul kas, deoarece acestea vor fi generate automat!

Mediul de lucru

Pentru început, aveți nevoie de mașina virtuală de laborator sau ceva cu pachete similare instalate.

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).

Yocto beneficiază de viteză cu cât are mai multe resurse hardware disponibile. De aceea, este recomandat să utilizați VM-ul de pe un SSD și să-i configurați ~8GB ram și 4 nuclee (este important dacă doriți să recompilați kernelul sau alte componente mai mari).

Mai jos aveți instrucțiunile manuale de instalare dacă nu aveți VM-ul sau doriți să vedeți cum se face asta (atenție: compilarea inițială poate dura câteva ore bune!)

Click to display ⇲

Click to hide ⇱

Este recomandat să folosiți VM-ul pus la dispoziție pentru a nu fi nevoiți să recompilați toată distribuția de la zero. De asemenea, kas poate rula majoritatea pașilor de descărcare + compilare automat pentru noi!

Pentru început, trebuie să ne asigurăm că utilitarele folosite de Yocto / bitbake sunt instalate în sistem ca să putem compila imaginea. În cazul în care acestea nu există, se pot instala folosind una din comenzile de mai jos, în funcție de distribuție:

Ubuntu 22.04
sudo apt-get -y install gawk wget git sed diffstat unzip texinfo gcc build-essential \
	chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils \
	iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev \
	python3-pylint-common python3-subunit mesa-common-dev zstd liblz4-tool
Fedora
sudo yum groupinstall "development tools"

Apoi, sursele proiectului Yocto pot fi descărcate urmărind instrucțiunile de aici sau clonate direct din repository-urile mentionate la începutul laboratorului.

Următorul pas este să obținem layer-ele și configurațiile necesare pentru a compila imaginea corespunzătoare pentru sistemul dorit. În cazul nostru, acest sistem este RaspberryPi. Acest layer poate fi obținut clonând repository-ul de la adresa: https://github.com/djwillis/meta-raspberrypi.git

După ce avem sursele Yocto și layer-ul necesar pentru dezvoltarea unei imagini de RaspberryPi, trebuie să inițializăm mediul de compilare și directorul de lucru. Pentru aceasta, trebuie rulată următoarea comandă în directorul ce conține sursele Yocto:

source oe-init-build-env <path_to_working_directory>/<rpi_build_folder>/

Apoi vom adăuga intrări în fișierele de configurare a mediului pentru a ne asigura că parametrii folosiți la compilare sunt corecți și că va fi inclus layer-ul pentru RaspberryPi. În directorul inițializat mai devreme, <path_to_working_directory>/<rpi_build_folder>/, se găsește directorul conf ce conține fișierul de configurare local.conf. Trebuie să ne asigurăm că:

  1. BB_NUMBER_THREADS are valoarea egală cu numărul de thread-uri de build ce dorim să fie create. Valoarea inițială este 4
  2. PARALLEL_MAKE are valoarea egală cu numărul de procesoare disponibile.

Valoarea trebuie să aibă forma -j x, unde x este numărul de procesoare disponibile.

  1. MACHINE are valoarea “raspberrypi”
  2. BBMASK va exclude pachetele ce nu sunt suportate de core-ul Yocto. Spre exemplu, în unele versiuni mai vechi ale layer-ului de RaspberryPi, acestea erau: “meta-raspberrypi/recipes-multimedia/libav|meta-raspberrypi/recipes-core/systemd”

Toate valorile acestor variabile sunt șiruri de caractere! O atribuire precum MACHINE = my_machine_name va genera eroare la parsare. Atribuirea corectă este MACHINE = “my_machine_name” (i.e., ghilimelele sunt obligatorii)

Tot în directorul conf există și fișierul bblayers.conf ce conține informația legată de layer-ele ce vor fi compilate. Trebuie să adăugăm la variabila BBLAYERS o cale către layer-ul de RaspberryPi. Revedeți secțiunea Layers și exemplul de acolo.

Compilarea unei imagini inițiale pentru o arhitectură poate dura de la cateva ore la zeci de ore, în funcție de performanța sistemului pe care se face compilarea. Procesul este unul îndelungat deoarece sistemul de build Yocto își va construi singur toate componentele necesare precum biblioteci, cross-compiler, etc. Spre exemplu, observați că nu mai este necesară instalarea unui cross-compiler. Acesta va fi creat la începutul build-ului, în două etape: mai întâi va fi compilată biblioteca glibc pentru platforma aleasă și apoi se va crea cross-compiler-ul propriu-zis.

Avantajul acestui mod de lucru este că efortul necesar pentru pregătirea și inițializarea unui build este mic și nu există dependențe numeroase față de alte biblioteci externe sau alte utilitare. Dezavantajul major este timpul necesar pentru o compilare.


Exerciții (tutorial)

0. Starea / build-ul inițial, introducere kas

Considerăm starea inițială a distribuției descărcată / precompilată în VM cu următoarea structură:

/home/student/yocto:
|-- build/   # (generat de Yocto bitbake)
|   |-- conf/  # generate automat din kas.yml
|   |   |-- bblayers.conf
|   |   |-- local.conf
|   |   `-- templateconf.cfg
|   `-- tmp/
|   |   |-- deploy/  # resurse generate
|   |       |-- images/  # aici se află imaginile generate
|   |       |-- ipk/     # aici se află pachetele generate
|   |-- cache/  # nimic important aici ;)
|-- layers/  # (descărcate de `kas`)
|   |-- meta-raspberrypi/  # layer-ul de la RPI
|   `-- poky/  # distribuția poky
|-- kas.yml     # fișierul de configurare kas
|-- Makefile    # un makefile ajutător (poate)
|-- runqemu.sh  # experiment eșuat, puteți să-l ștergeți :D a fost uitat acolo

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 dispozitivele 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 cross 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 într-un exercițiu viitor.

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>'
...
# sau, alternativa prin kas:
student@vm:~/yocto$ kas shell kas.yml
# (se pornește un subshell unde puteți da comenzi yocto)

După aceasta, 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/, dați cd .. pentru a reveni în rădăcina proiectului nostru.

În final, dorim să testăm distribuția precompilată din VM:

# atenție: rulați într-un shell inițializat prin Yocto ca mai sus:
student@vm:~/yocto$ runqemu qemuarm slirp nographic

Argumentele ce vor fi pasate (ușor modificate) comenzii qemu: slirp - activează un tunel cu gazda prin care se furnizează acces la Internet, și nographic - folosește consola doar (nu avem server grafic în VM, mai adăuga câțiva GB în plus + mai multă memorie consumată :D).

Pentru a ieși din qemu, folosiți combinația Ctrl+a, x (adică ctrl+a care va intra într-un mod invizibil de comandă a monitorului qemu, apoi, separat, tasta x).

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 (iar 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:

# asigurațivă că sunteți într-un shell Yocto, citiți mai sus ^^ ;)
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 ;)

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"
# <esc>: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. Configurarea distribuției + pachete adiționale

Î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

Momentan, imaginea este configurată în modul de debugging prin această linie din local.conf-ul generat (vedeți în kas.yml):

EXTRA_IMAGE_FEATURES = "debug-tweaks"

Aceasta ne setează parola goală la contul root. Pentru a spori securitatea imaginii implicite, se pot crea utilizatori noi / schimba parolele, vedeți tutorialul acesta. Cum se citește:

“uses this class in an 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 \$(openssl passwd -6 student) root;"
# pentru a adăuga un utilizator nou:
EXTRA_USERS_PARAMS += "useradd -p \$(openssl passwd -6 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:

# nu uitați să faceți source la oe-init-build-env dacă nu aveți utiltarele în PATH
runqemu qemuarm slirp nographic
# pentru a ieși, trebuie să apăsăm combinația Ctrl+a, apoi separat tasta `x`
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
# cool!
# 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 python3
python3:
  meta                 3.10.7
# ... și multe alte subpachete ale bibliotecilor populare

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. Pentru pachetele standard din Poky (versiunea kirkstone), 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 să modificăm imaginea standard, ca mai sus):

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

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 slirp 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:~# echo "nameserver 8.8.8.8" > /etc/resolv.conf
root@qemuarm:~# ping google.com
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
root@qemuarm:~# pip3 install youtube-dl
root@qemuarm:~# python3
Python 3.10.7 (main, Sep  5 2022, 13:12:31) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from youtube_dl import YoutubeDL
>>> ydl = YoutubeDL()
>>> ydl.add_default_info_extractors()
>>> info = ydl.extract_info('https://www.youtube.com/watch?v=ITRBsXrPOn8')
>>> info['title']
# mulțumiți, putem ieși ;)
# dacă rămâneți blocați în qemu console, căutați mai sus cum ieșiți :P
2.4. Adăugare resurse în imagine

O dată instalate pachetele necesare (e.g., interpretorul python), 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 "STUDENT" > 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:

LICENSE = "CLOSED"

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 STUDENT
si/res/yocto-tutorial.txt · Last modified: 2023/12/10 17:22 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