Soubory manifestu MicroPython

Shrnutí

MicroPython má funkci, která umožňuje „zmrazit“ (freeze) Python kód přímo do firmwaru jako alternativu k načítání kódu ze souborového systému.

To má následující výhody:

  • kód je předem zkompilován do bytecode, čímž odpadá nutnost kompilovat zdrojový kód Python při načítání.

  • bytecode lze spustit přímo z ROM (tj. flash paměti), místo aby byl kopírován do RAM. Podobně se z ROM načítají i veškeré konstantní objekty (řetězce, n-tice atd.). Díky tomu může být pro vaši aplikaci k dispozici výrazně více paměti.

  • na zařízeních, která nemají souborový systém, je to jediný způsob, jak načíst Python kód.

Během vývoje se zmrazování obecně nedoporučuje, protože výrazně zpomalí váš vývojový cyklus, neboť každá aktualizace bude vyžadovat opětovné nahrání celého firmwaru. Přesto může být užitečné selektivně zmrazit některé zřídka se měnící závislosti (například knihovny třetích stran).

Soubory Python, které se mají zmrazit do firmwaru, se vypisují pomocí „manifestu“, což je soubor Python interpretovaný procesem sestavení. Manifest obvykle napíšete jako součást definice desky, ale můžete také napsat samostatný soubor manifestu a použít jej s existující definicí desky.

Soubory manifestu mohou definovat závislosti na knihovnách z micropython-lib, na souborech Python v souborovém systému a také na jiných souborech manifestu.

Psaní souborů manifestu

Soubor manifestu je soubor Python obsahující sérii volání funkcí. Viz dostupné funkce definované níže.

Jakékoli cesty použité v souborech manifestu mohou obsahovat následující proměnné. Všechny se rozkládají na absolutní cesty.

  • $(MPY_DIR) – cesta k repozitáři micropython.

  • $(MPY_LIB_DIR) – cesta k submodulu micropython-lib. Upřednostněte použití require().

  • $(PORT_DIR) – cesta k aktuálnímu portu (např. ports/stm32)

  • $(BOARD_DIR) – cesta k aktuální desce (např. ports/stm32/boards/OPENMV4)

Vlastní soubory manifestu by neměly být umístěny v hlavním repozitáři MicroPython. Měli byste je uchovávat ve verzovacím systému spolu se zbytkem vašeho projektu.

Manifest používaný pro kompilaci firmwaru obvykle bude muset zahrnovat manifest portu, který může obsahovat zmrazené moduly potřebné pro fungování desky. Pokud chcete pouze přidat další moduly k existující desce, zahrňte manifest desky (který následně zahrne manifest portu).

Sestavení s vlastním manifestem

Váš manifest lze zadat na příkazové řádce make pomocí:

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

To platí pro všechny porty, včetně těch založených na CMake (např. rp2), protože obal Makefile to předá do sestavení CMake.

Přidání manifestu k definici desky

Pokud máte vlastní definici desky, můžete zařídit, aby automaticky zahrnovala váš vlastní manifest. Na portech založených na make (většina portů) nastavte ve svém mpconfigboard.mk proměnnou FROZEN_MANIFEST.

FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py

Na portech založených na CMake (např. rp2) místo toho použijte mpconfigboard.cmake

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

Funkce vysoké úrovně

Toto jsou funkce, které budete běžně používat. Přidávají kód do sady, která je předkompilována do bytecode a zmrazena do obrazu firmwaru:

  • module a package zmrazí váš vlastní lokální zdrojový kód — jeden soubor, respektive celý adresář balíčku.

  • require zmrazí publikovaný balíček (a jeho závislosti) z micropython-lib podle názvu.

  • include vtáhne další manifest, takže se přidají i jeho zmrazené moduly.

  • add_library a metadata jsou pomocné funkce (registrace dalších cest pro vyhledávání pro require a deklarace metadat balíčku).

Typický manifest firmwaru nejprve pomocí include zahrne manifest portu nebo desky (aby moduly, které deska potřebuje, zůstaly zmrazené) a poté přidá vlastní řádky module/package/require.

Poznámka: U různých funkcí lze nastavit pojmenovaný argument opt, který řídí úroveň optimalizace použitou křížovým kompilátorem. Viz micropython.opt_level().

add_library(library, library_path, prepend=False)

Zaregistruje cestu k externí pojmenované library.

Použijte toto, když chcete, aby require rozlišoval balíčky z jiného adresáře než micropython-lib — například z vaší vlastní kolekce ovladačů nebo z lokální kopie knihovny třetí strany.

Cesta library_path bude automaticky prohledána při použití require. Ve výchozím nastavení se přidaná knihovna přidá na konec seznamu knihoven k prohledání. Předáním True ji předřadíte na začátek seznamu.

Navíc lze přidanou knihovnu explicitně vyžádat pomocí require("name", library="library").

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

Zmrazí celý balíček — adresář souborů .py (volitelně s podbalíčky) — aby jej bylo možné importovat jako import <package>. Pro jeden samostatný soubor použijte místo toho module.

To je ekvivalentní zkopírování adresáře „package_path“ do zařízení (s výjimkou toho, že jde o zmrazený kód).

V nejjednodušším případě, pro zmrazení balíčku „foo“ v aktuálním adresáři:

package("foo")

rekurzivně zahrne všechny soubory .py ve foo a zmrazí je jako foo/**/*.py.

Pokud balíček není ve stejném adresáři jako soubor manifestu, použijte base_path:

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

Ve base_path můžete použít výše uvedené proměnné, například $(PORT_DIR).

Pro omezení na určité soubory v balíčku použijte files (poznámka: cesty by měly být relativní k balíčku): package("foo", files=["bar/baz.py"]).

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

Zmrazí jeden samostatný soubor .py, aby jej bylo možné importovat podle jeho názvu (module("foo.py") umožní fungování import foo). Pro adresář/balíček použijte package.

Pokud je soubor v aktuálním adresáři:

module("foo.py")

Jinak použijte base_path k lokalizaci souboru:

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

Ve base_path můžete použít výše uvedené proměnné, například $(PORT_DIR).

require(name, library=None)

Vyžádá balíček podle názvu (a jeho závislosti) z micropython-lib.

Takto se zmrazují rozšíření standardní knihovny a komunitní ovladače: pojmenovaný balíček se získá ze submodulu micropython-lib a zmrazí se spolu se vším, na čem závisí. Pro zmrazení vlastního zdrojového kódu místo publikovaného balíčku použijte module nebo package.

Volitelně lze zadat library (řetězec) pro odkaz na balíček z knihovny, která byla dříve zaregistrována pomocí add_library. Jinak se použije seznam cest ke knihovnám.

include(manifest_path)

Zahrne další manifest. Takto se manifesty skládají: vlastní manifest firmwaru by měl pomocí include zahrnout manifest portu (nebo desky), aby moduly, které deska potřebuje, zůstaly zmrazené, a poté přidat své vlastní položky.

Manifest používaný pro kompilaci firmwaru obvykle bude muset zahrnovat manifest portu, který může obsahovat zmrazené moduly potřebné pro fungování desky.

Argument manifest může být řetězec (název souboru) nebo iterovatelný objekt řetězců.

Relativní cesty se rozlišují vzhledem k aktuálnímu souboru manifestu.

Pokud cesta vede k adresáři, implicitně zahrnuje soubor manifest.py uvnitř tohoto adresáře.

Ve manifest_path můžete použít výše uvedené proměnné, například $(PORT_DIR).

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

Definuje metadata pro tento soubor manifestu. To je užitečné pro manifesty balíčků micropython-lib.

Tato pole se zpracovávají, když je balíček publikován do / instalován z micropython-lib přes mip; v manifestu firmwaru desky nejsou potřeba.

Funkce nízké úrovně

Tyto funkce jsou zde zdokumentovány pro úplnost, ale s výjimkou freeze_as_str je veškerá funkcionalita přístupná přes funkce vysoké úrovně.

Funkce freeze* se liší pouze v tom, jak je kód uložen:

  • freeze_as_mpy / freeze_mpy ukládají do flash předkompilovaný bytecode (.mpy). Kód běží přímo z flash, využívá minimum RAM a rychle se importuje. To je to, co module, package a require používají interně.

  • freeze_as_str místo toho zmrazí zdrojový kód Python, který se kompiluje do bytecode při importu (s využitím RAM a vyžadující kompilátor na zařízení). To je jediná schopnost, kterou funkce vysoké úrovně nevystavují, a proto je výše uvedenou výjimkou.

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

Základní primitivum, na kterém funkce vysoké úrovně staví; upřednostněte je. Zmrazí vstup zadaný pomocí path a automaticky určí jeho typ. Skript .py se nejprve zkompiluje do .mpy a poté zmrazí, soubor .mpy se zmrazí přímo.

path musí být adresář, který je základním adresářem pro zahájení vyhledávání souborů. Při importu výsledných zmrazených modulů začne název modulu za path, tj. path je z názvu modulu vyloučena.

Pokud je path relativní, rozlišuje se vzhledem k aktuálnímu manifest.py.

Pokud je script None, zmrazí se všechny soubory v path.

Pokud je script iterovatelný objekt, pak se freeze() zavolá na všechny položky iterovatelného objektu (se stejnými předanými hodnotami path a opt).

Pokud je script řetězec, pak specifikuje soubor nebo adresář ke zmrazení a může před souborem nebo posledním adresářem obsahovat další adresáře. Soubor nebo adresář se bude hledat v path. Pokud je script adresář, pak se zmrazí všechny soubory v tomto adresáři.

opt je úroveň optimalizace předávaná do mpy-cross při kompilaci .py do .mpy. Tyto úrovně jsou popsány v micropython.opt_level().

freeze_as_str(path)

Zmrazí zadanou cestu path a všechny skripty .py v ní jako řetězec, který bude zkompilován při importu. Použijte toto pouze tehdy, když zmrazený kód musí zůstat zdrojovým kódem Python; oproti variantám .mpy to stojí RAM při importu.

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

Zmrazí vstup tak, že nejprve zkompiluje skripty .py do souborů .mpy a poté zmrazí výsledné soubory .mpy. To je to, co module a package dělají pod kapotou. Další podrobnosti o argumentech viz freeze().

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

Zmrazí vstup, kterým musí být soubory .mpy zmrazené přímo (bez kroku kompilace). Další podrobnosti o argumentech viz freeze().

Příklady

Pro zmrazení jednoho souboru z aktuálního adresáře, který bude dostupný jako import mydriver, použijte:

module("mydriver.py")

Pro zmrazení adresáře souborů v podadresáři „mydriver“ aktuálního adresáře, který bude dostupný jako import mydriver, použijte:

package("mydriver")

Pro zmrazení knihovny „hmac“ z micropython-lib použijte:

require("hmac")

Úplnější příklad vlastního souboru manifest.py (pro desku, která má svůj vlastní výchozí manifest) je:

# 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")

Poté lze desku zkompilovat pomocí

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

Všimněte si, že většina desek nemá svůj vlastní manifest.py, místo toho používají přímo manifest portu; v takovém případě by váš manifest měl jednoduše použít include("$(PORT_DIR)/boards/manifest.py").