MicroPython .mpy fájlok¶
A MicroPython bevezeti az .mpy fájl fogalmát, amely egy bináris konténer-fájlformátum, ami előfordított kódot tárol, és úgy importálható, mint egy normál .py modul. A foo.mpy fájl az import foo paranccsal importálható, amennyiben a foo.mpy a szokásos módon megtalálható az importáló mechanizmus számára. Általában a sys.path listában felsorolt könyvtárak mindegyikében sorban történik a keresés. Egy adott könyvtárban először a foo.py fájlt keresi a rendszer, és ha az nem található, akkor a foo.mpy fájlt, majd ha egyik sincs meg, a keresés a következő könyvtárban folytatódik. Ennek megfelelően a foo.py elsőbbséget élvez a foo.mpy fájllal szemben.
Ezek az .mpy fájlok bytecode kódot tartalmazhatnak, amelyet általában a mpy-cross programmal állítanak elő Python forrásfájlokból (.py fájlokból). Egyes architektúrák esetében az .mpy fájl natív gépi kódot is tartalmazhat, amely többféle módon, leginkább C forráskódból állítható elő.
Az mpy-cross fordító¶
A mpy-cross az a keresztfordító, amely egy .py forrásfájlt egy .mpy bináris konténerré alakít, amely készen áll a kamerára való importálásra. Ez a MicroPython forrásfa része (ugyanazé, amelyből a kamera firmware-ét építik), és pip csomagként is közzéteszik, hogy a gazdagép oldalán teljes firmware-letöltés nélkül használható legyen:
$ pip install --user mpy-cross
Vagy a pipx segítségével:
$ pipx install mpy-cross
A telepítés után hívd meg egyetlen forrásfájlra:
$ mpy-cross foo.py
Ez létrehozza a foo.mpy fájlt az aktuális könyvtárban, amely készen áll arra, hogy a többi modul mellé átmásold a kamera fájlrendszerébe, vagy egy ROMFS image-be illeszd.
A leghasznosabb parancssori kapcsolók:
-o <path>– a generált.mpykimeneti útvonala (alapértelmezés szerint a bemeneti fájlnév, lecserélt kiterjesztéssel; az-o -a stdout-ra ír).-O<n>– optimalizálási szint0és3között. Az alapértelmezett0megőrzi az asszerciókat és a teljes forráshelyeket; a3eltávolítja az asszerciókat és docstringeket, valamint átírja azif __debug__blokkokat. A szint ugyanazt amicropython.opt_levelfelületet vezérli, amelyet a futtatókörnyezet is elérhetővé tesz.-march=<arch>– a@nativeés@viperdekorátorral ellátott függvények cél natív architektúrája. Kötelező, ha a forrás ezeket a dekorátorokat használja. Az értéknek a kamera MCU-osztályához kell illeszkednie: válaszd ki ampy-cross --helpáltal kiírt listából, vagy olvasd ki a kameráról futásidőben asys.implementation._mpysegítségével.-s <path>– az.mpyhibakeresési információjába beágyazott forrásútvonal-karakterlánc. Hasznos, ha a lemezen lévő útvonal eltér attól az importálási útvonaltól, amely alatt a fájlnak meg kell jelennie a hibakövetésekben.-X emit=bytecode|native|viper– az egész modul alapértelmezett kibocsátójának kiválasztása (függvényenkénti alternatíva a@native/@viperdekorátorokhoz).--version– kiírja az.mpyformátum azon verzióját, amelyet ez a bináris kibocsát. Ennek a számnak meg kell egyeznie a kamera futtatókörnyezete által támogatott verzióval (lásd a lenti kiadási táblázatot), különben az importálásValueError('incompatible .mpy file')hibát vált ki.
A teljes kapcsolólistáért futtasd a mpy-cross --help parancsot.
A pip csomag egy kicsi Python modul API-t is elérhetővé tesz, így a build szkriptek a fordítót folyamaton belül vezérelhetik, ahelyett, hogy kézzel egy alfolyamatot forkolnának:
import mpy_cross
mpy_cross.compile('foo.py', dest='build/foo.mpy', opt=3,
march=mpy_cross.NATIVE_ARCH_ARMV7EMSP)
A mpy_cross.compile, mpy_cross.run és mpy_cross.mpy_version a három belépési pont; a mpy_cross.CrossCompileError hordozza a fordító stderr kimenetét, ha valami hibázik. Az architektúra-konstansok (NATIVE_ARCH_ARMV7EMSP, NATIVE_ARCH_ARMV7EMDP stb.) megfelelnek a -march kapcsoló által elfogadott karakterláncoknak.
Az .mpy fájlok verziókezelése és kompatibilitása¶
Egy adott .mpy fájl lehet, hogy kompatibilis egy adott MicroPython rendszerrel, de lehet, hogy nem. A kompatibilitás a következőkön alapul:
Az .mpy fájl verziója: a fájl verziójának meg kell egyeznie az azt betöltő rendszer által támogatott verzióval.
Az .mpy fájl alverziója: ha az .mpy fájl natív gépi kódot tartalmaz, akkor a fájl alverziójának meg kell egyeznie az azt betöltő rendszer által támogatott verzióval. Egyébként, ha az .mpy fájlban nincs natív gépi kód, akkor az alverziót a betöltéskor figyelmen kívül hagyja a rendszer.
Kis egész szám bitjei: az .mpy fájl egy minimális számú bitet igényel egy small integer típushoz, és az azt betöltő rendszernek legalább ennyi bitet kell támogatnia.
Natív architektúra: ha az .mpy fájl natív gépi kódot tartalmaz, akkor megadja ennek a gépi kódnak az architektúráját, és az azt betöltő rendszernek támogatnia kell az adott architektúra kódjának végrehajtását.
Ha egy MicroPython rendszer támogatja az .mpy fájlok importálását, akkor a sys.implementation._mpy mező létezik, és egy egész számot ad vissza, amely kódolja a verziót (alsó 8 bit), a jellemzőket és a natív architektúrát.
Egy olyan .mpy fájl importálásának megkísérlése, amely megbukik az első négy teszt valamelyikén, ValueError('incompatible .mpy file') hibát vált ki. Egy olyan .mpy fájl importálásának megkísérlése, amely megbukik a natív architektúra teszten (ha natív gépi kódot tartalmaz), ValueError('incompatible .mpy arch') hibát vált ki.
Ha egy .mpy fájl importálása sikertelen, próbáld meg a következőket:
Határozd meg a MicroPython rendszered által támogatott .mpy verziót és jelzőket a következő végrehajtásával:
import sys sys_mpy = sys.implementation._mpy arch = [None, 'x86', 'x64', 'armv6', 'armv6m', 'armv7m', 'armv7em', 'armv7emsp', 'armv7emdp', 'xtensa', 'xtensawin', 'rv32imc', 'rv64imc'][(sys_mpy >> 10) & 0x0F] print('mpy version:', sys_mpy & 0xff) print('mpy sub-version:', sys_mpy >> 8 & 3) print('mpy flags:', end='') if arch: print(' -march=' + arch, end='') if (sys_mpy >> 16) != 0: print(' -march-flags=' + (sys_mpy >> 16), end='') print()
Ellenőrizd az .mpy fájl érvényességét a fájl első két bájtjának megvizsgálásával. Az első bájtnak egy nagy «M» betűnek kell lennie, a második bájt pedig a verziószám, amelynek meg kell egyeznie a fenti rendszerverzióval. Ha nem egyezik, építsd újra az .mpy fájlt.
Ellenőrizd, hogy a rendszer .mpy verziója megegyezik-e az .mpy fájl építéséhez használt
mpy-crossáltal kibocsátott verzióval, amelyet ampy-cross --versionmutat meg. Ha nem egyezik, fordítsd újra ampy-crossprogramot ampy-cross --versionáltal jelentett tag-en (vagy hash-en) kicsekkolt Git repóból.Győződj meg róla, hogy a megfelelő
mpy-crossjelzőket használod, amelyeket a fenti kód mutat meg, vagy az általad használt porthoz tartozóMPY_CROSS_FLAGSMakefile változó megvizsgálásával.Ha az .mpy fájl harmadik bájtjának 6. bitje be van állítva, akkor ellenőrizd, hogy a kódolt architektúra-specifikus jelzőbitek vuint értéke kompatibilis-e azzal a céllal, amelyen a fájlt importálod.
Az alábbi táblázat a MicroPython kiadás és az .mpy verzió közötti megfelelést mutatja.
MicroPython kiadás |
.mpy verzió |
|---|---|
v1.23.0 és újabb |
6.3 |
v1.22.x |
6.2 |
v1.20 - v1.21.0 |
6.1 |
v1.19.x |
6 |
v1.12 - v1.18 |
5 |
v1.11 |
4 |
v1.9.3 - v1.10 |
3 |
v1.9 - v1.9.2 |
2 |
v1.5.1 - v1.8.7 |
0 |
A teljesség kedvéért a következő táblázat a fő MicroPython repó azon Git commitját mutatja, amelynél az .mpy verzió megváltozott.
.mpy verzióváltozás |
Git commit |
|---|---|
6.2 -> 6.3 |
bdbc869f9ea200c0d28b2bc7bfb60acd9d884e1b |
6.1 -> 6.2 |
6967ff3c581a66f73e9f3d78975f47528db39980 |
6 -> 6.1 |
d94141e1473aebae0d3c63aeaa8397651ad6fa01 |
5 -> 6 |
f2040bfc7ee033e48acef9f289790f3b4e6b74e5 |
4 -> 5 |
5716c5cf65e9b2cb46c2906f40302401bdd27517 |
3 -> 4 |
9a5f92ea72754c01cc03e5efcdfe94021120531e |
2 -> 3 |
ff93fd4f50321c6190e1659b19e64fef3045a484 |
1 -> 2 |
dd11af209d226b7d18d5148b239662e30ed60bad |
0 -> 1 |
6a11048af1d01c78bdacddadd1b72dc7ba7c6478 |
kezdeti 0 verzió |
d8c834c95d506db979ec871417de90b7951edc30 |
Az .mpy fájlok bináris kódolása¶
A MicroPython .mpy fájlok bináris konténerformátumúak, amelyben a kódobjektumok (bytecode és natív gépi kód) belsőleg egy beágyazott hierarchiában tárolódnak. A külső modul kódja tárolódik először, majd a gyermekei következnek. Minden gyermeknek lehetnek további gyermekei, például egy metódusokkal rendelkező osztály esetén, vagy egy lambdát vagy listaértelmezést definiáló függvény esetén. Hogy a fájlok kicsik maradjanak, miközben a lehetséges értékek széles tartományát biztosítják, sok helyen egy változó kódolású előjel nélküli egész szám (vuint) fogalmát használja. Az UTF-8 kódoláshoz hasonlóan ez a kódolás bájtonként 7 bitet tárol, a 8. bittel (MSB) beállítva, ha egy vagy több bájt következik. Az előjel nélküli egész szám bitjei a vuintben LSB sorrendben tárolódnak.
Egy .mpy fájl legfelső szintje három részből áll:
A fejléc.
A globális qstr és konstans táblák.
A modul külső hatóköréhez tartozó nyers kód. Ez a külső hatókör akkor hajtódik végre, amikor az .mpy fájlt importálják.
Egy .mpy fájl tartalmát a mpy-tool.py segítségével vizsgálhatod meg, például (a fő MicroPython repó gyökeréből futtatva):
$ ./tools/mpy-tool.py -xd myfile.mpy
A fejléc¶
Az .mpy fejléc a következő:
méret |
mező |
|---|---|
bájt |
0x4d érték (ASCII «M») |
bájt |
.mpy fő verziószám |
bájt |
jellemzőjelzők, natív arch, alverziószám (régebbi verziókban jellemzőjelzők volt) |
bájt |
bitek száma egy kis egész számban |
A harmadik bájt a következőképpen oszlik fel (MSB-vel kezdve):
bit |
jelentés |
|---|---|
7 |
fenntartott, 0-nak kell lennie |
6 |
egy architektúra-specifikus jelzőket tartalmazó vuint követi a fejlécet |
5..2 |
natív arch szám |
1..0 |
alverziószám |
Architektúra-specifikus jelzők¶
Ha a fejléc jellemzőjelző-bájtjának 6. bitje be van állítva, akkor egy opcionális architektúra-specifikus információt tartalmazó vuint követi a fejlécet. Ennek az egész számnak a tartalma attól függ, hogy a fájl melyik natív architektúrához készült.
Ezt jelenleg arra használják, hogy tárolja, mely RISC-V processzorkiterjesztésekre van szüksége az MPY fájlnak a helyes működéshez az I, M, C és Zicsr kiterjesztéseken kívül. Az ArmV7 különböző változatait a natív architektúra-számuk azonosítja, de ennek a mechanizmusnak az újrahasznosítása bonyolultabbá tenné a dolgokat az RV32 és RV64 esetében.
Az RV32-t vagy RV64-et célzó MPY fájloknak, amelyeknek nincs szükségük semmilyen különleges processzorkiterjesztésre, nem kell jelzőegész számot biztosítaniuk (a fejlécben a megfelelő bit beállítása mellett). Az RV32 és RV64 MPY fájlok esetében a jelzőérték hiánya azt jelzi, hogy nincs szükség specifikus kiterjesztésekre, és egy bájtot takarít meg a végső kimeneti binárisban.
Lásd még a -march-flags parancssori kapcsolót a mpy-tool.py és a mpy-cross programban egyaránt, valamint az --arch-flags parancssori kapcsolót a mpy_ld.py programban, amellyel ezt az értéket beállíthatod MPY fájlok létrehozásakor.
A globális qstr és konstans táblák¶
Egy .mpy fájl egyetlen qstr táblát és egyetlen konstansobjektum-táblát tartalmaz. Ezek globálisak az .mpy fájlra nézve, és minden beágyazott nyers kód objektum hivatkozik rájuk. A qstr tábla leképezi a belső qstr számot (amely az .mpy fájlon belüli) annak a futtatókörnyezetnek a feloldott qstr számára, amelybe az .mpy fájlt importálják. Ez összekapcsolja az .mpy fájlt a rendszer többi részével, amelyen belül végrehajtódik. A konstansobjektum-tábla az .mpy fájl által igényelt összes konstansobjektumra mutató hivatkozással van feltöltve.
méret |
mező |
|---|---|
vuint |
qstr-ek száma |
vuint |
konstansobjektumok száma |
… |
qstr adat |
… |
kódolt konstansobjektumok |
Nyers kód elemek¶
Egy nyers kód elem kódot tartalmaz, akár bytecode-ot, akár natív gépi kódot. A tartalma a következő:
méret |
mező |
|---|---|
vuint |
típus, méret, és hogy vannak-e al-nyers-kód elemek |
… |
kód (bytecode vagy gépi kód) |
vuint |
al-nyers-kód elemek száma (csak ha nem nulla) |
… |
al-nyers-kód elemek |
Egy nyers kód elem első vuintje kódolja az ebben az elemben tárolt kód típusát (a két legkevésbé jelentős bit), hogy van-e ennek a nyers kódnak gyermeke (a harmadik legkevésbé jelentős bit), és a következő kód hosszát (a számára lefoglalandó RAM mennyiségét).
A vuint után maga a kód következik. Hacsak a kódtípus nem áthelyezéseket tartalmazó viper kód, ez a kód konstans adat, és nem kell módosítani.
Ha ennek a nyers kódnak vannak gyermekei (amint azt az első vuint egy bitje jelzi), a kód után egy vuint következik, amely az al-nyers-kód elemek számát számlálja.
Végül az al-nyers-kód elemek tárolódnak, rekurzívan.