Pliki manifestu MicroPython¶
Podsumowanie¶
MicroPython udostępnia funkcję pozwalającą na „zamrożenie” kodu Python w oprogramowaniu układowym, jako alternatywę dla ładowania kodu z systemu plików.
Daje to następujące korzyści:
kod jest wstępnie skompilowany do kodu bajtowego, dzięki czemu źródło Python nie musi być kompilowane w czasie ładowania.
kod bajtowy może być wykonywany bezpośrednio z pamięci ROM (tj. pamięci flash), zamiast być kopiowany do pamięci RAM. Podobnie wszelkie stałe obiekty (łańcuchy znaków, krotki itp.) są również ładowane z pamięci ROM. Dzięki temu dla Twojej aplikacji może być dostępne znacznie więcej pamięci.
na urządzeniach, które nie mają systemu plików, jest to jedyny sposób na załadowanie kodu Python.
W trakcie programowania zamrażanie zazwyczaj nie jest zalecane, ponieważ znacznie spowalnia cykl programowania – każda aktualizacja wymaga ponownego wgrania całego oprogramowania układowego. Mimo to nadal może być przydatne wybiórcze zamrażanie niektórych rzadko zmieniających się zależności (takich jak biblioteki firm trzecich).
Sposobem na wymienienie plików Python, które mają zostać zamrożone w oprogramowaniu układowym, jest „manifest”, czyli plik Python interpretowany przez proces budowania. Zazwyczaj manifest pisze się jako część definicji płytki, ale można też napisać samodzielny plik manifestu i użyć go z istniejącą definicją płytki.
Pliki manifestu mogą definiować zależności od bibliotek z micropython-lib, a także od plików Python w systemie plików oraz od innych plików manifestu.
Pisanie plików manifestu¶
Plik manifestu to plik Python zawierający serię wywołań funkcji. Zobacz dostępne funkcje zdefiniowane poniżej.
Wszelkie ścieżki używane w plikach manifestu mogą zawierać następujące zmienne. Wszystkie one rozwijają się do ścieżek bezwzględnych.
$(MPY_DIR)– ścieżka do repozytorium micropython.$(MPY_LIB_DIR)– ścieżka do submodułu micropython-lib. Preferuj użycierequire().$(PORT_DIR)– ścieżka do bieżącego portu (np.ports/stm32)$(BOARD_DIR)– ścieżka do bieżącej płytki (np.ports/stm32/boards/OPENMV4)
Niestandardowe pliki manifestu nie powinny znajdować się w głównym repozytorium MicroPython. Powinieneś przechowywać je w systemie kontroli wersji razem z resztą swojego projektu.
Zazwyczaj manifest używany do kompilowania oprogramowania układowego musi dołączać manifest portu, który może zawierać zamrożone moduły wymagane do działania płytki. Jeśli chcesz jedynie dodać dodatkowe moduły do istniejącej płytki, dołącz manifest płytki (który z kolei dołączy manifest portu).
Budowanie z niestandardowym manifestem¶
Twój manifest można podać w wierszu poleceń make za pomocą:
$ make BOARD=MYBOARD FROZEN_MANIFEST=/path/to/my/project/manifest.py
Dotyczy to wszystkich portów, w tym tych opartych na CMake (np. rp2), ponieważ nakładka Makefile przekaże to do procesu budowania CMake.
Dodawanie manifestu do definicji płytki¶
Jeśli masz niestandardową definicję płytki, możesz sprawić, by automatycznie dołączała Twój niestandardowy manifest. W portach opartych na make (większość portów) ustaw zmienną FROZEN_MANIFEST w pliku mpconfigboard.mk.
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
W portach opartych na CMake (np. rp2) użyj zamiast tego mpconfigboard.cmake
set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
Funkcje wysokiego poziomu¶
To są funkcje, których będziesz zazwyczaj używać. Dodają one kod do zestawu, który jest wstępnie kompilowany do kodu bajtowego i zamrażany w obrazie oprogramowania układowego:
moduleipackagezamrażają Twoje własne lokalne źródło – odpowiednio pojedynczy plik lub cały katalog pakietu.requirezamraża opublikowany pakiet (i jego zależności) z micropython-lib, według nazwy.includedołącza inny manifest, dzięki czemu dodawane są również jego zamrożone moduły.add_libraryimetadatato funkcje pomocnicze (rejestrowanie dodatkowych ścieżek wyszukiwania dlarequireoraz deklarowanie metadanych pakietu).
Typowy manifest oprogramowania układowego najpierw includeuje manifest portu lub płytki (aby moduły potrzebne płytce pozostały zamrożone), a następnie dodaje własne linie module/package/require.
Uwaga: argument słowa kluczowego opt można ustawić w różnych funkcjach – kontroluje on poziom optymalizacji używany przez kompilator krzyżowy. Zobacz micropython.opt_level().
- add_library(library, library_path, prepend=False)¶
Rejestruje ścieżkę do zewnętrznej nazwanej library.
Użyj tego, gdy chcesz, aby
requirerozwiązywał pakiety z katalogu innego niż micropython-lib – na przykład z Twojej własnej kolekcji sterowników lub z pobranej biblioteki firmy trzeciej.Ścieżka library_path będzie automatycznie przeszukiwana podczas używania
require. Domyślnie dodana biblioteka jest dodawana na koniec listy bibliotek do przeszukania. PrzekażTrue, aby dodać na początku listy.Dodatkowo dodaną bibliotekę można jawnie wskazać, używając
require("name", library="library").
- package(package_path, files=None, base_path='.', opt=None)¶
Zamraża cały pakiet – katalog plików
.py(opcjonalnie z podpakietami) – tak aby można było go zaimportować jakoimport <package>. Dla pojedynczego, samodzielnego pliku użyj zamiast tegomodule.Jest to równoważne skopiowaniu katalogu „package_path” na urządzenie (z tą różnicą, że jako zamrożony kod).
W najprostszym przypadku, aby zamrozić pakiet „foo” w bieżącym katalogu:
package("foo")rekurencyjnie dołączy wszystkie pliki .py w foo i zostanie on zamrożony jako
foo/**/*.py.Jeśli pakiet nie znajduje się w tym samym katalogu co plik manifestu, użyj
base_path:package("foo", base_path="path/to/libraries")Możesz używać powyższych zmiennych, takich jak
$(PORT_DIR)wbase_path.Aby ograniczyć się do określonych plików w pakiecie, użyj
files(uwaga: ścieżki powinny być względne wobec pakietu):package("foo", files=["bar/baz.py"]).
- module(module_path, base_path='.', opt=None)¶
Zamraża pojedynczy, samodzielny plik
.py, tak aby można było go zaimportować po jego nazwie (module("foo.py")sprawia, żeimport foodziała). Dla katalogu/pakietu użyjpackage.Jeśli plik znajduje się w bieżącym katalogu:
module("foo.py")W przeciwnym razie użyj base_path, aby zlokalizować plik:
module("foo.py", base_path="src/drivers")Możesz używać powyższych zmiennych, takich jak
$(PORT_DIR)wbase_path.
- require(name, library=None)¶
Wymaga pakietu według nazwy (i jego zależności) z micropython-lib.
W ten sposób zamrażane są rozszerzenia biblioteki standardowej oraz sterowniki społeczności: nazwany pakiet jest pobierany z submodułu micropython-lib i zamrażany wraz ze wszystkim, od czego zależy. Aby zamrozić własne źródło, a nie opublikowany pakiet, użyj zamiast tego
modulelubpackage.Opcjonalnie podaj library (łańcuch znaków), aby odwołać się do pakietu z biblioteki wcześniej zarejestrowanej za pomocą
add_library. W przeciwnym razie zostanie użyta lista ścieżek bibliotek.
- include(manifest_path)¶
Dołącza inny manifest. W ten sposób komponuje się manifesty: niestandardowy manifest oprogramowania układowego powinien
includemanifest portu (lub płytki), aby moduły potrzebne płytce pozostały zamrożone, a następnie dodać własne wpisy.Zazwyczaj manifest używany do kompilowania oprogramowania układowego musi dołączać manifest portu, który może zawierać zamrożone moduły wymagane do działania płytki.
Argument manifest może być łańcuchem znaków (nazwą pliku) lub iterowalnym zbiorem łańcuchów znaków.
Ścieżki względne są rozwiązywane względem bieżącego pliku manifestu.
Jeśli ścieżka wskazuje na katalog, to niejawnie dołączany jest plik manifest.py znajdujący się w tym katalogu.
Możesz używać powyższych zmiennych, takich jak
$(PORT_DIR)wmanifest_path.
- metadata(description=None, version=None, license=None, author=None)¶
Definiuje metadane dla tego pliku manifestu. Jest to przydatne w przypadku manifestów dla pakietów micropython-lib.
Pola te są wykorzystywane, gdy pakiet jest publikowany do / instalowany z micropython-lib za pomocą mip; nie są one potrzebne w manifeście oprogramowania układowego płytki.
Funkcje niskiego poziomu¶
Funkcje te są udokumentowane dla kompletności, ale z wyjątkiem freeze_as_str cała funkcjonalność jest dostępna poprzez funkcje wysokiego poziomu.
Funkcje freeze* różnią się jedynie sposobem przechowywania kodu:
freeze_as_mpy/freeze_mpyprzechowują wstępnie skompilowany kod bajtowy (.mpy) w pamięci flash. Kod działa bezpośrednio z pamięci flash, zużywa minimalną ilość pamięci RAM i szybko się importuje. To właśnie wykorzystują wewnętrzniemodule,packageirequire.freeze_as_strzamraża natomiast źródło Python, które jest kompilowane do kodu bajtowego w czasie importu (zużywając pamięć RAM i wymagając kompilatora na urządzeniu). Jest to jedyna funkcjonalność nieudostępniana przez funkcje wysokiego poziomu, co stanowi wspomniany wyżej wyjątek.
- freeze(path, script=None, opt=0)¶
Podstawowa prymitywna funkcja, na której opierają się funkcje wysokiego poziomu; preferuj te ostatnie. Zamraża dane wejściowe określone przez path, automatycznie określając ich typ. Skrypt
.pyzostanie najpierw skompilowany do.mpy, a następnie zamrożony, natomiast plik.mpyzostanie zamrożony bezpośrednio.path musi być katalogiem, który jest katalogiem bazowym do rozpoczęcia wyszukiwania plików. Podczas importowania powstałych zamrożonych modułów nazwa modułu zaczyna się po path, tj. path jest wykluczany z nazwy modułu.
Jeśli path jest względna, jest rozwiązywana względem bieżącego
manifest.py.Jeśli script ma wartość None, zamrożone zostaną wszystkie pliki w path.
Jeśli script jest obiektem iterowalnym, to
freeze()jest wywoływana na wszystkich elementach tego obiektu (z przekazaniem tych samych path i opt).Jeśli script jest łańcuchem znaków, to określa plik lub katalog do zamrożenia i może zawierać dodatkowe katalogi przed plikiem lub ostatnim katalogiem. Plik lub katalog będzie wyszukiwany w path. Jeśli script jest katalogiem, to zamrożone zostaną wszystkie pliki w tym katalogu.
opt to poziom optymalizacji przekazywany do mpy-cross podczas kompilowania
.pydo.mpy. Poziomy te są opisane wmicropython.opt_level().
- freeze_as_str(path)¶
Zamraża podaną path i wszystkie skrypty
.pyw niej zawarte jako łańcuch znaków, który zostanie skompilowany podczas importu. Używaj tego tylko wtedy, gdy zamrożony kod musi pozostać źródłem Python; w porównaniu z wariantami.mpykosztuje to pamięć RAM w czasie importu.
- freeze_as_mpy(path, script=None, opt=0)¶
Zamraża dane wejściowe, najpierw kompilując skrypty
.pydo plików.mpy, a następnie zamrażając powstałe pliki.mpy. To właśnie robią pod maskąmoduleipackage. Zobaczfreeze(), aby uzyskać dalsze szczegóły dotyczące argumentów.
- freeze_mpy(path, script=None, opt=0)¶
Zamraża dane wejściowe, którymi muszą być pliki
.mpyzamrażane bezpośrednio (bez etapu kompilacji). Zobaczfreeze(), aby uzyskać dalsze szczegóły dotyczące argumentów.
Przykłady¶
Aby zamrozić pojedynczy plik z bieżącego katalogu, który będzie dostępny jako import mydriver, użyj:
module("mydriver.py")
Aby zamrozić katalog plików w podkatalogu „mydriver” bieżącego katalogu, który będzie dostępny jako import mydriver, użyj:
package("mydriver")
Aby zamrozić bibliotekę „hmac” z micropython-lib, użyj:
require("hmac")
Pełniejszy przykład niestandardowego pliku manifest.py (dla płytki, która ma swój własny domyślny manifest) jest następujący:
# 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")
Następnie płytkę można skompilować za pomocą
$ cd ports/stm32
$ make BOARD=MYBOARD FROZEN_MANIFEST=~/src/myproject/manifest.py
Zwróć uwagę, że większość płytek nie ma własnego manifest.py, lecz używa bezpośrednio manifestu portu; w takim przypadku Twój manifest powinien po prostu zawierać include("$(PORT_DIR)/boards/manifest.py").