Soubory .mpy v MicroPythonu¶
MicroPython definuje koncept souboru .mpy, což je binární kontejnerový formát, který uchovává předkompilovaný kód a který lze importovat stejně jako běžný modul .py. Soubor foo.mpy lze importovat pomocí import foo za předpokladu, že importovací mechanismus dokáže foo.mpy najít obvyklým způsobem. Obvykle se postupně prohledává každý adresář uvedený v sys.path. Při prohledávání konkrétního adresáře se nejprve hledá foo.py a pokud není nalezen, hledá se foo.mpy; pokud není nalezen ani jeden, prohledávání pokračuje v dalším adresáři. Z toho plyne, že foo.py má přednost před foo.mpy.
Tyto soubory .mpy mohou obsahovat bytecode, který se obvykle generuje ze zdrojových souborů Pythonu (souborů .py) pomocí programu mpy-cross. U některých architektur může soubor .mpy obsahovat také nativní strojový kód, který lze generovat různými způsoby, zejména ze zdrojového kódu v jazyce C.
Kompilátor mpy-cross¶
mpy-cross je křížový kompilátor, který převádí zdrojový soubor .py na binární kontejner .mpy připravený k importu na kameře. Je součástí zdrojového stromu MicroPythonu (téhož, který se používá k sestavení firmwaru kamery) a je rovněž publikován jako balíček pip pro použití na straně hostitele bez nutnosti kompletního stažení firmwaru:
$ pip install --user mpy-cross
Nebo pomocí pipx:
$ pipx install mpy-cross
Po instalaci jej spusťte na jednom zdrojovém souboru:
$ mpy-cross foo.py
Tím vznikne foo.mpy v aktuálním adresáři, připravený ke zkopírování do souborového systému kamery vedle ostatních modulů nebo k vložení do obrazu ROMFS.
Nejužitečnější volby příkazového řádku:
-o <path>– výstupní cesta pro vygenerovaný.mpy(výchozí je název vstupního souboru s nahrazenou příponou;-o -zapisuje na stdout).-O<n>– úroveň optimalizace0až3. Výchozí hodnota0zachovává asserce a úplné informace o umístění ve zdroji;3odstraňuje asserce a docstringy a přepisuje blokyif __debug__. Tato úroveň ovládá tutéž vrstvumicropython.opt_level, kterou zpřístupňuje runtime.-march=<arch>– cílová nativní architektura pro funkce dekorované@nativea@viper. Vyžaduje se, pokud zdroj tyto dekorátory používá. Hodnota musí odpovídat třídě MCU kamery: vyberte ji ze seznamu, který vypíšempy-cross --help, nebo ji zjistěte přímo z kamery za běhu pomocísys.implementation._mpy.-s <path>– řetězec se zdrojovou cestou vloženou do ladicích informací souboru.mpy. Užitečné, když se cesta na disku liší od importovací cesty, pod kterou se má soubor zobrazovat ve výpisech zásobníku.-X emit=bytecode|native|viper– volba výchozího emitoru pro celý modul (alternativa k dekorátorům@native/@viperna úrovni jednotlivých funkcí).--version– vypíše verzi formátu.mpy, kterou tato binárka emituje. Toto číslo musí odpovídat verzi, kterou podporuje runtime kamery (viz tabulka verzí níže), jinak import vyvoláValueError('incompatible .mpy file').
Úplný seznam přepínačů zobrazíte spuštěním mpy-cross --help.
Balíček pip rovněž poskytuje malé API modulu pro Python, takže sestavovací skripty mohou ovládat kompilátor přímo v procesu místo ručního forkování podprocesu:
import mpy_cross
mpy_cross.compile('foo.py', dest='build/foo.mpy', opt=3,
march=mpy_cross.NATIVE_ARCH_ARMV7EMSP)
mpy_cross.compile, mpy_cross.run a mpy_cross.mpy_version jsou tři vstupní body; mpy_cross.CrossCompileError nese stderr kompilátoru, když se něco pokazí. Konstanty architektury (NATIVE_ARCH_ARMV7EMSP, NATIVE_ARCH_ARMV7EMDP atd.) odpovídají řetězcům, které přijímá přepínač -march.
Verzování a kompatibilita souborů .mpy¶
Daný soubor .mpy může, ale nemusí být kompatibilní s daným systémem MicroPython. Kompatibilita je založena na následujícím:
Verze souboru .mpy: verze souboru musí odpovídat verzi podporované systémem, který jej načítá.
Podverze souboru .mpy: pokud soubor .mpy obsahuje nativní strojový kód, pak podverze souboru musí odpovídat verzi podporované systémem, který jej načítá. V opačném případě, pokud soubor .mpy neobsahuje žádný nativní strojový kód, se podverze při načítání ignoruje.
Bity malého celého čísla: soubor .mpy bude vyžadovat minimální počet bitů v small integer a systém, který jej načítá, musí podporovat alespoň tento počet bitů.
Nativní architektura: pokud soubor .mpy obsahuje nativní strojový kód, pak určí architekturu tohoto strojového kódu a systém, který jej načítá, musí podporovat provádění kódu této architektury.
Pokud systém MicroPython podporuje import souborů .mpy, pak bude existovat pole sys.implementation._mpy a vrátí celé číslo, které kóduje verzi (spodních 8 bitů), funkce a nativní architekturu.
Pokus o import souboru .mpy, který neprojde jedním z prvních čtyř testů, vyvolá ValueError('incompatible .mpy file'). Pokus o import souboru .mpy, který neprojde testem nativní architektury (pokud obsahuje nativní strojový kód), vyvolá ValueError('incompatible .mpy arch').
Pokud import souboru .mpy selže, zkuste následující:
Zjistěte verzi a příznaky .mpy podporované vaším systémem MicroPython spuštěním:
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()
Zkontrolujte platnost souboru .mpy prozkoumáním prvních dvou bajtů souboru. První bajt by měl být velké písmeno ‚M‘ a druhý bajt bude číslo verze, které by mělo odpovídat verzi systému zjištěné výše. Pokud neodpovídá, soubor .mpy znovu sestavte.
Zkontrolujte, zda verze .mpy systému odpovídá verzi emitované programem
mpy-cross, který byl použit k sestavení souboru .mpy a kterou zjistíte pomocímpy-cross --version. Pokud neodpovídá, znovu zkompilujtempy-crossz Git repozitáře staženého na značce (nebo hashi) nahlášeném pomocímpy-cross --version.Ujistěte se, že používáte správné přepínače
mpy-cross, zjištěné výše uvedeným kódem nebo prozkoumáním proměnné MakefileMPY_CROSS_FLAGSpro port, který používáte.Pokud má třetí bajt souboru .mpy nastavený bit č. 6, zkontrolujte, zda je zakódovaný vuint s příznakovými bity specifickými pro architekturu kompatibilní s cílem, na který soubor importujete.
Následující tabulka ukazuje vztah mezi vydáním MicroPythonu a verzí .mpy.
Vydání MicroPythonu |
verze .mpy |
|---|---|
v1.23.0 a vyšší |
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 |
Pro úplnost další tabulka ukazuje Git commit hlavního repozitáře MicroPythonu, ve kterém došlo ke změně verze .mpy.
změna verze .mpy |
Git commit |
|---|---|
6.2 na 6.3 |
bdbc869f9ea200c0d28b2bc7bfb60acd9d884e1b |
6.1 na 6.2 |
6967ff3c581a66f73e9f3d78975f47528db39980 |
6 na 6.1 |
d94141e1473aebae0d3c63aeaa8397651ad6fa01 |
5 na 6 |
f2040bfc7ee033e48acef9f289790f3b4e6b74e5 |
4 na 5 |
5716c5cf65e9b2cb46c2906f40302401bdd27517 |
3 na 4 |
9a5f92ea72754c01cc03e5efcdfe94021120531e |
2 na 3 |
ff93fd4f50321c6190e1659b19e64fef3045a484 |
1 na 2 |
dd11af209d226b7d18d5148b239662e30ed60bad |
0 na 1 |
6a11048af1d01c78bdacddadd1b72dc7ba7c6478 |
počáteční verze 0 |
d8c834c95d506db979ec871417de90b7951edc30 |
Binární kódování souborů .mpy¶
Soubory .mpy v MicroPythonu jsou binární kontejnerový formát s objekty kódu (bytecode a nativní strojový kód) uloženými interně ve vnořené hierarchii. Kód pro vnější modul je uložen jako první a poté následují jeho potomci. Každý potomek může mít další potomky, například v případě třídy s metodami nebo funkce definující lambdu či komprehenzi. Aby soubory zůstaly malé a přitom poskytovaly široký rozsah možných hodnot, používá se na mnoha místech koncept proměnně kódovaného celého čísla bez znaménka (vuint, variably-encoded-unsigned-integer). Podobně jako kódování UTF-8 ukládá toto kódování 7 bitů na bajt s nastaveným 8. bitem (MSB), pokud následuje jeden nebo více dalších bajtů. Bity celého čísla bez znaménka jsou ve vuint uloženy ve tvaru LSB.
Nejvyšší úroveň souboru .mpy se skládá ze tří částí:
Hlavička.
Globální tabulky qstr a konstant.
Surový kód (raw-code) pro vnější rozsah modulu. Tento vnější rozsah se provede při importu souboru .mpy.
Obsah souboru .mpy můžete prozkoumat pomocí mpy-tool.py, například (spuštěno z kořene hlavního repozitáře MicroPythonu):
$ ./tools/mpy-tool.py -xd myfile.mpy
Hlavička¶
Hlavička .mpy je:
velikost |
pole |
|---|---|
bajt |
hodnota 0x4d (ASCII ‚M‘) |
bajt |
hlavní číslo verze .mpy |
bajt |
příznaky funkcí, nativní arch, vedlejší číslo verze (ve starších verzích to byly příznaky funkcí) |
bajt |
počet bitů v malém celém čísle (small int) |
Třetí bajt je rozdělen následovně (MSB jako první):
bit |
význam |
|---|---|
7 |
rezervováno, musí být 0 |
6 |
za hlavičkou následuje vuint s příznaky specifickými pro architekturu |
5..2 |
číslo nativní arch |
1..0 |
vedlejší číslo verze |
Příznaky specifické pro architekturu¶
Pokud je nastaven bit č. 6 bajtu příznaků funkcí v hlavičce, pak za hlavičkou bude následovat vuint obsahující volitelné informace specifické pro architekturu. Obsah tohoto celého čísla závisí na tom, pro kterou nativní architekturu je soubor určen.
To se v současnosti používá k uložení informace o tom, která rozšíření procesoru RISC-V soubor MPY potřebuje ke správné činnosti kromě I, M, C a Zicsr. Různé varianty ArmV7 se rozlišují podle čísla nativní architektury, ale opětovné použití tohoto mechanismu by zkomplikovalo věci pro RV32 a RV64.
Soubory MPY cílené na RV32 nebo RV64, které nepotřebují žádná konkrétní rozšíření procesoru, nemusí poskytovat celé číslo s příznaky (spolu s nastavením příslušného bitu v hlavičce). Absence hodnoty příznaků u souborů MPY pro RV32 a RV64 se používá k označení, že nejsou potřeba žádná konkrétní rozšíření, a ušetří jeden bajt ve výsledné binárce.
Viz také volba příkazového řádku -march-flags v programech mpy-tool.py i mpy-cross a volba příkazového řádku --arch-flags v programu mpy_ld.py, které tuto hodnotu nastavují při vytváření souborů MPY.
Globální tabulky qstr a konstant¶
Soubor .mpy obsahuje jednu tabulku qstr a jednu tabulku objektů konstant. Tyto tabulky jsou globální pro celý soubor .mpy, odkazují na ně všechny vnořené objekty surového kódu (raw-code). Tabulka qstr mapuje interní číslo qstr (interní vůči souboru .mpy) na rozřešené číslo qstr runtime, do kterého je soubor .mpy importován. Tím se soubor .mpy propojuje se zbytkem systému, v němž se provádí. Tabulka objektů konstant je naplněna odkazy na všechny objekty konstant, které soubor .mpy potřebuje.
velikost |
pole |
|---|---|
vuint |
počet qstr |
vuint |
počet objektů konstant |
… |
data qstr |
… |
zakódované objekty konstant |
Prvky surového kódu (raw code)¶
Prvek surového kódu obsahuje kód, buď bytecode, nebo nativní strojový kód. Jeho obsah je následující:
velikost |
pole |
|---|---|
vuint |
typ, velikost a zda existují podřízené prvky surového kódu |
… |
kód (bytecode nebo strojový kód) |
vuint |
počet podřízených prvků surového kódu (pouze pokud je nenulový) |
… |
podřízené prvky surového kódu |
První vuint v prvku surového kódu kóduje typ kódu uloženého v tomto prvku (dva nejméně významné bity), zda má tento surový kód nějaké potomky (třetí nejméně významný bit) a délku kódu, který následuje (množství RAM, jež se pro něj má alokovat).
Za vuint následuje samotný kód. Pokud typem kódu není viper kód s relokacemi, jde o konstantní data, která není třeba měnit.
Pokud má tento surový kód nějaké potomky (jak udává bit v prvním vuint), následuje za kódem vuint udávající počet podřízených prvků surového kódu.
Nakonec jsou rekurzivně uloženy případné podřízené prvky surového kódu.