MicroPython manifest fájlok

Összefoglaló

A MicroPython rendelkezik egy olyan funkcióval, amely lehetővé teszi, hogy a Python kódot „befagyasszuk” a firmware-be, alternatívaként a kód fájlrendszerből történő betöltéséhez képest.

Ennek a következő előnyei vannak:

  • a kód előre bájtkóddá van fordítva, így nincs szükség a Python forrás betöltéskori fordítására.

  • a bájtkód közvetlenül a ROM-ból (azaz a flash memóriából) futtatható ahelyett, hogy a RAM-ba másolnánk. Hasonlóképpen minden konstans objektum (sztringek, tuple-ök stb.) is a ROM-ból töltődik be. Ez jelentősen több memóriát tehet elérhetővé az alkalmazásod számára.

  • olyan eszközökön, amelyeken nincs fájlrendszer, ez az egyetlen módja a Python kód betöltésének.

A fejlesztés során a befagyasztás általában nem ajánlott, mivel jelentősen lelassítja a fejlesztési ciklust, hiszen minden egyes frissítés a teljes firmware újraflashelését igényli. Ennek ellenére hasznos lehet bizonyos, ritkán változó függőségek (például harmadik féltől származó könyvtárak) szelektív befagyasztása.

A firmware-be befagyasztandó Python fájlok felsorolásának módja egy „manifest”, amely egy Python fájl, amelyet az építési folyamat értelmez. Jellemzően egy manifest fájlt egy lap definíciójának részeként írnál, de írhatsz önálló manifest fájlt is, és használhatod azt egy meglévő lap definícióval.

A manifest fájlok meghatározhatnak függőségeket a micropython-lib könyvtáraira, valamint a fájlrendszeren lévő Python fájlokra, és más manifest fájlokra is.

Manifest fájlok írása

A manifest fájl egy Python fájl, amely függvényhívások sorozatát tartalmazza. Lásd az alább definiált elérhető függvényeket.

A manifest fájlokban használt bármely útvonal tartalmazhatja a következő változókat. Ezek mind abszolút útvonalakra oldódnak fel.

  • $(MPY_DIR) – a micropython repó útvonala.

  • $(MPY_LIB_DIR) – a micropython-lib almodul útvonala. Inkább a require() használatát részesítsd előnyben.

  • $(PORT_DIR) – az aktuális port útvonala (pl. ports/stm32)

  • $(BOARD_DIR) – az aktuális lap útvonala (pl. ports/stm32/boards/OPENMV4)

Az egyéni manifest fájlok ne a fő MicroPython repóban legyenek. A projekted többi részével együtt verziókövetésben kell tartanod őket.

A firmware fordításához használt manifest jellemzően tartalmaznia kell a port manifestet, amely olyan befagyasztott modulokat foglalhat magában, amelyek a lap működéséhez szükségesek. Ha csak további modulokat szeretnél hozzáadni egy meglévő laphoz, akkor a lap manifestjét vond be (amely viszont magában foglalja a port manifestet).

Fordítás egyéni manifesttel

A manifested a make parancssorban a következővel adhatod meg:

$ make BOARD=MYBOARD FROZEN_MANIFEST=/path/to/my/project/manifest.py

Ez minden portra vonatkozik, beleértve a CMake-alapúakat is (pl. rp2), mivel a Makefile-burkoló ezt továbbítja a CMake buildbe.

Manifest hozzáadása egy lap definícióhoz

Ha van egyéni lap definíciód, beállíthatod, hogy automatikusan tartalmazza az egyéni manifestedet. A make-alapú portokon (a legtöbb porton) a mpconfigboard.mk fájlban állítsd be a FROZEN_MANIFEST változót.

FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py

A CMake-alapú portokon (pl. rp2) ehelyett az mpconfigboard.cmake fájlt használd

set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)

Magas szintű függvények

Ezek azok a függvények, amelyeket általában használni fogsz. Ezek kódot adnak ahhoz a halmazhoz, amely előre bájtkóddá van fordítva és befagyasztva a firmware-képbe:

  • A module és a package a saját helyi forrásodat fagyasztja be — egyetlen fájlt, illetve egy egész csomagkönyvtárat.

  • A require egy közzétett csomagot (és annak függőségeit) fagyaszt be a micropython-lib-ből, név szerint.

  • Az include egy másik manifestet húz be, így annak befagyasztott moduljai is hozzáadódnak.

  • Az add_library és a metadata támogató függvények (extra keresési útvonalak regisztrálása a require számára, illetve csomag-metaadatok deklarálása).

Egy tipikus firmware manifest először include-olja a port vagy lap manifestjét (így a lapnak szükséges modulok befagyasztva maradnak), majd hozzáadja a saját module/package/require sorait.

Megjegyzés: Az opt kulcsszó argumentum a különböző függvényeken beállítható, ez vezérli a keresztfordító által használt optimalizálási szintet. Lásd: micropython.opt_level().

add_library(library, library_path, prepend=False)

Regisztrálja egy külső, megnevezett library útvonalát.

Akkor használd ezt, amikor azt szeretnéd, hogy a require a micropython-lib-től eltérő könyvtárból oldja fel a csomagokat — például a saját illesztőprogram-gyűjteményedből vagy egy harmadik féltől származó könyvtár checkout-jából.

A library_path útvonal automatikusan kerül átkutatásra a require használatakor. Alapértelmezés szerint a hozzáadott könyvtár a keresendő könyvtárak listájának végére kerül. Adj át True értéket, hogy a lista elejére fűzd, és így a lista elejére kerüljön.

Ezenkívül a hozzáadott könyvtár kifejezetten kérhető a require("name", library="library") használatával.

package(package_path, files=None, base_path='.', opt=None)

Befagyaszt egy egész csomagot — egy .py fájlokat tartalmazó könyvtárat (opcionálisan alcsomagokkal) — hogy az import <package> módon importálható legyen. Egyetlen önálló fájlhoz használd helyette a module függvényt.

Ez egyenértékű a „package_path” könyvtár eszközre másolásával (azzal a különbséggel, hogy befagyasztott kódként).

A legegyszerűbb esetben egy „foo” csomag befagyasztásához az aktuális könyvtárban:

package("foo")

rekurzívan beemeli az összes .py fájlt a foo-ból, és foo/**/*.py néven lesz befagyasztva.

Ha a csomag nem ugyanabban a könyvtárban van, mint a manifest fájl, használd a base_path-t:

package("foo", base_path="path/to/libraries")

Használhatod a fenti változókat, például a $(PORT_DIR)-t a base_path-ban.

Ha bizonyos fájlokra szeretnéd korlátozni a csomagban, használd a files-t (megjegyzés: az útvonalaknak a csomaghoz képest relatívnak kell lenniük): package("foo", files=["bar/baz.py"]).

module(module_path, base_path='.', opt=None)

Befagyaszt egyetlen önálló .py fájlt, hogy a nevén importálható legyen (a module("foo.py") az import foo-t teszi működőképessé). Könyvtárhoz/csomaghoz használd a package függvényt.

Ha a fájl az aktuális könyvtárban van:

module("foo.py")

Egyébként a fájl megtalálásához használd a base_path-t:

module("foo.py", base_path="src/drivers")

Használhatod a fenti változókat, például a $(PORT_DIR)-t a base_path-ban.

require(name, library=None)

Megkövetel egy csomagot név szerint (és annak függőségeit) a micropython-lib-ből.

Így kerülnek befagyasztásra a standard könyvtári kiterjesztések és a közösségi illesztőprogramok: a megnevezett csomag a micropython-lib almodulból kerül lehívásra, és mindennel együtt befagyasztásra kerül, amitől függ. A saját forrásod befagyasztásához egy közzétett csomag helyett használd a module vagy a package függvényt.

Opcionálisan add meg a library-t (egy sztringet), hogy egy korábban az add_library függvénnyel regisztrált könyvtárból hivatkozz egy csomagra. Egyébként a könyvtár-útvonalak listája lesz használva.

include(manifest_path)

Beemel egy másik manifestet. Így állnak össze a manifestek: egy egyéni firmware manifestnek include-olnia kell a port (vagy lap) manifestjét, hogy a lapnak szükséges modulok befagyasztva maradjanak, majd hozzá kell adnia a saját bejegyzéseit.

A firmware fordításához használt manifest jellemzően tartalmaznia kell a port manifestet, amely olyan befagyasztott modulokat foglalhat magában, amelyek a lap működéséhez szükségesek.

A manifest argumentum lehet egy sztring (fájlnév) vagy sztringek iterálható gyűjteménye.

A relatív útvonalak az aktuális manifest fájlhoz képest oldódnak fel.

Ha az útvonal egy könyvtárra mutat, akkor implicit módon az adott könyvtáron belüli manifest.py fájlt foglalja magában.

Használhatod a fenti változókat, például a $(PORT_DIR)-t a manifest_path-ban.

metadata(description=None, version=None, license=None, author=None)

Metaadatokat definiál ehhez a manifest fájlhoz. Ez a micropython-lib csomagokhoz tartozó manifestek esetén hasznos.

Ezeket a mezőket akkor használja a rendszer, amikor egy csomagot közzétesznek a micropython-lib-be / telepítenek onnan a mip segítségével; ezekre nincs szükség egy lap firmware manifestjében.

Alacsony szintű függvények

Ezek a függvények a teljesség kedvéért vannak dokumentálva, de a freeze_as_str kivételével minden funkció elérhető a magas szintű függvényeken keresztül.

A freeze* függvények csak abban különböznek, hogy hogyan tárolódik a kód:

  • A freeze_as_mpy / freeze_mpy előre fordított bájtkódot (.mpy) tárol a flash memóriában. A kód közvetlenül a flash memóriából fut, minimális RAM-ot használ, és gyorsan importálódik. Ezt használja belsőleg a module, a package és a require.

  • A freeze_as_str ezzel szemben a Python forrást fagyasztja be, amely importáláskor kerül bájtkóddá fordításra (RAM-ot használva, és az eszközön lévő fordítót igényelve). Ez az egyetlen képesség, amelyet a magas szintű függvények nem tesznek elérhetővé, ezért szerepel a fent említett kivételként.

freeze(path, script=None, opt=0)

Az alapprimitív, amelyre a magas szintű függvények épülnek; inkább azokat részesítsd előnyben. Befagyasztja a path által megadott bemenetet, automatikusan meghatározva annak típusát. Egy .py szkript előbb .mpy-vé lesz fordítva, majd befagyasztva, egy .mpy fájl pedig közvetlenül befagyasztva.

A path-nak könyvtárnak kell lennie, amely a fájlok keresésének kiindulási alapkönyvtára. A létrejövő befagyasztott modulok importálásakor a modul neve a path után kezdődik, azaz a path ki van zárva a modul nevéből.

Ha a path relatív, akkor az aktuális manifest.py-hoz képest oldódik fel.

Ha a script None, akkor a path összes fájlja befagyasztásra kerül.

Ha a script egy iterálható gyűjtemény, akkor a freeze() az iterálható gyűjtemény összes elemén meghívásra kerül (ugyanazzal a path-szal és opt-tal átadva).

Ha a script egy sztring, akkor a befagyasztandó fájlt vagy könyvtárat adja meg, és tartalmazhat extra könyvtárakat a fájl vagy az utolsó könyvtár előtt. A fájl vagy könyvtár a path-ban lesz keresve. Ha a script egy könyvtár, akkor az adott könyvtár összes fájlja befagyasztásra kerül.

Az opt az az optimalizálási szint, amelyet a mpy-cross-nak kell átadni a .py .mpy-vé fordításakor. Ezeket a szinteket a micropython.opt_level() ismerteti.

freeze_as_str(path)

Befagyasztja a megadott path-ot és az abban lévő összes .py szkriptet sztringként, amely importáláskor kerül lefordításra. Csak akkor használd ezt, ha a befagyasztott kódnak Python forrásnak kell maradnia; a .mpy változatokhoz képest importáláskori RAM-ba kerül.

freeze_as_mpy(path, script=None, opt=0)

Befagyasztja a bemenetet úgy, hogy először a .py szkripteket .mpy fájlokká fordítja, majd befagyasztja a létrejövő .mpy fájlokat. Ezt teszi a háttérben a module és a package. Az argumentumokkal kapcsolatos további részletekért lásd a freeze()-t.

freeze_mpy(path, script=None, opt=0)

Befagyasztja a bemenetet, amelynek .mpy fájloknak kell lenniük, amelyek közvetlenül kerülnek befagyasztásra (fordítási lépés nélkül). Az argumentumokkal kapcsolatos további részletekért lásd a freeze()-t.

Példák

Egyetlen fájl befagyasztásához az aktuális könyvtárból, amely import mydriver módon lesz elérhető, használd:

module("mydriver.py")

Egy fájlokat tartalmazó könyvtár befagyasztásához az aktuális könyvtár „mydriver” alkönyvtárában, amely import mydriver módon lesz elérhető, használd:

package("mydriver")

A „hmac” könyvtár befagyasztásához a micropython-lib-ből, használd:

require("hmac")

Egy egyéni manifest.py fájl teljesebb példája (egy olyan laphoz, amelynek saját alapértelmezett manifestje van):

# Include the board's default manifest.
include("$(BOARD_DIR)/manifest.py")
# Add a custom driver
module("mydriver.py")
# Add aiorepl from micropython-lib
require("aiorepl")

Ekkor a lap a következővel fordítható

$ cd ports/stm32
$ make BOARD=MYBOARD FROZEN_MANIFEST=~/src/myproject/manifest.py

Vedd figyelembe, hogy a legtöbb lapnak nincs saját manifest.py-ja, hanem közvetlenül a port-félét használja, mely esetben a manifestednek csak include("$(PORT_DIR)/boards/manifest.py")-t kell tartalmaznia helyette.