MicroPython-Manifest-Dateien¶
Zusammenfassung¶
MicroPython bietet die Möglichkeit, Python-Code in die Firmware „einzufrieren“ („freeze“), als Alternative zum Laden von Code aus dem Dateisystem.
Dies bietet die folgenden Vorteile:
Der Code wird vorab zu Bytecode kompiliert, sodass der Python-Quellcode nicht zur Ladezeit kompiliert werden muss.
Der Bytecode kann direkt aus dem ROM (d.h. dem Flash-Speicher) ausgeführt werden, anstatt in den RAM kopiert zu werden. Ebenso werden alle konstanten Objekte (Strings, Tupel usw.) ebenfalls aus dem ROM geladen. Dadurch kann deutlich mehr Speicher für Ihre Anwendung zur Verfügung stehen.
Auf Geräten ohne Dateisystem ist dies die einzige Möglichkeit, Python-Code zu laden.
Während der Entwicklung wird das Einfrieren im Allgemeinen nicht empfohlen, da es Ihren Entwicklungszyklus erheblich verlangsamt, weil bei jeder Aktualisierung die gesamte Firmware neu geflasht werden muss. Es kann jedoch dennoch nützlich sein, selektiv einige sich selten ändernde Abhängigkeiten (etwa Bibliotheken von Drittanbietern) einzufrieren.
Die Python-Dateien, die in die Firmware eingefroren werden sollen, werden über ein „Manifest“ aufgelistet. Dabei handelt es sich um eine Python-Datei, die vom Build-Prozess interpretiert wird. Normalerweise schreiben Sie eine Manifest-Datei als Teil einer Board-Definition, Sie können aber auch eine eigenständige Manifest-Datei schreiben und sie mit einer vorhandenen Board-Definition verwenden.
Manifest-Dateien können Abhängigkeiten von Bibliotheken aus micropython-lib definieren, ebenso wie von Python-Dateien im Dateisystem und auch von anderen Manifest-Dateien.
Manifest-Dateien schreiben¶
Eine Manifest-Datei ist eine Python-Datei, die eine Reihe von Funktionsaufrufen enthält. Siehe die unten definierten verfügbaren Funktionen.
Alle in Manifest-Dateien verwendeten Pfade können die folgenden Variablen enthalten. Diese werden alle zu absoluten Pfaden aufgelöst.
$(MPY_DIR)– Pfad zum micropython-Repository.$(MPY_LIB_DIR)– Pfad zum micropython-lib-Submodul. Verwenden Sie vorzugsweiserequire().$(PORT_DIR)– Pfad zum aktuellen Port (z.B.ports/stm32)$(BOARD_DIR)– Pfad zum aktuellen Board (z.B.ports/stm32/boards/OPENMV4)
Benutzerdefinierte Manifest-Dateien sollten nicht im Haupt-Repository von MicroPython liegen. Sie sollten sie zusammen mit dem Rest Ihres Projekts in der Versionsverwaltung aufbewahren.
Normalerweise muss ein zum Kompilieren von Firmware verwendetes Manifest das Port-Manifest einbinden, das eingefrorene Module enthalten kann, die für das Funktionieren des Boards erforderlich sind. Wenn Sie lediglich zusätzliche Module zu einem vorhandenen Board hinzufügen möchten, binden Sie das Board-Manifest ein (das wiederum das Port-Manifest einbindet).
Mit einem benutzerdefinierten Manifest bauen¶
Ihr Manifest kann in der make-Befehlszeile wie folgt angegeben werden:
$ make BOARD=MYBOARD FROZEN_MANIFEST=/path/to/my/project/manifest.py
Dies gilt für alle Ports, einschließlich CMake-basierter Ports (z.B. rp2), da der Makefile-Wrapper dies an den CMake-Build weitergibt.
Ein Manifest zu einer Board-Definition hinzufügen¶
Wenn Sie eine benutzerdefinierte Board-Definition haben, können Sie dafür sorgen, dass Ihr benutzerdefiniertes Manifest automatisch eingebunden wird. Setzen Sie auf make-basierten Ports (den meisten Ports) in Ihrer mpconfigboard.mk die Variable FROZEN_MANIFEST.
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
Verwenden Sie auf CMake-basierten Ports (z.B. rp2) stattdessen mpconfigboard.cmake
set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
Höherstufige Funktionen¶
Dies sind die Funktionen, die Sie normalerweise verwenden werden. Sie fügen Code zu der Menge hinzu, die zu Bytecode vorkompiliert und in das Firmware-Image eingefroren wird:
moduleundpackagefrieren Ihren eigenen lokalen Quellcode ein — eine einzelne Datei beziehungsweise ein ganzes Paketverzeichnis.requirefriert ein veröffentlichtes Paket (und seine Abhängigkeiten) aus micropython-lib anhand des Namens ein.includezieht ein weiteres Manifest hinzu, sodass dessen eingefrorene Module ebenfalls hinzugefügt werden.add_libraryundmetadatasind Hilfsfunktionen (Registrieren zusätzlicher Suchpfade fürrequireund Deklarieren von Paket-Metadaten).
Ein typisches Firmware-Manifest bindet zunächst mit include das Port- oder Board-Manifest ein (damit die vom Board benötigten Module eingefroren bleiben) und fügt dann seine eigenen module/package/require-Zeilen hinzu.
Hinweis: Das Schlüsselwortargument opt kann bei den verschiedenen Funktionen gesetzt werden; es steuert die vom Cross-Compiler verwendete Optimierungsstufe. Siehe micropython.opt_level().
- add_library(library, library_path, prepend=False)¶
Registriert den Pfad zu einer externen benannten library.
Verwenden Sie dies, wenn
requirePakete aus einem anderen Verzeichnis als micropython-lib auflösen soll — zum Beispiel aus Ihrer eigenen Sammlung von Treibern oder aus einem ausgecheckten Drittanbieter-Bibliotheksverzeichnis.Der Pfad library_path wird bei Verwendung von
requireautomatisch durchsucht. Standardmäßig wird die hinzugefügte Bibliothek an das Ende der Liste der zu durchsuchenden Bibliotheken angefügt. Übergeben SieTrue, um sie voranzustellen und an den Anfang der Liste zu setzen.Zusätzlich kann die hinzugefügte Bibliothek explizit über
require("name", library="library")angefordert werden.
- package(package_path, files=None, base_path='.', opt=None)¶
Friert ein gesamtes Paket ein — ein Verzeichnis mit
.py-Dateien (optional mit Unterpaketen) —, sodass es alsimport <package>importiert werden kann. Verwenden Sie stattdessenmodulefür eine einzelne eigenständige Datei.Dies entspricht dem Kopieren des Verzeichnisses „package_path“ auf das Gerät (allerdings als eingefrorener Code).
Im einfachsten Fall, um ein Paket „foo“ im aktuellen Verzeichnis einzufrieren:
package("foo")Dies bindet rekursiv alle .py-Dateien in foo ein, die als
foo/**/*.pyeingefroren werden.Wenn sich das Paket nicht im selben Verzeichnis wie die Manifest-Datei befindet, verwenden Sie
base_path:package("foo", base_path="path/to/libraries")Sie können die obigen Variablen wie
$(PORT_DIR)inbase_pathverwenden.Um die Einbindung auf bestimmte Dateien im Paket zu beschränken, verwenden Sie
files(Hinweis: Pfade sollten relativ zum Paket sein):package("foo", files=["bar/baz.py"]).
- module(module_path, base_path='.', opt=None)¶
Friert eine einzelne eigenständige
.py-Datei ein, sodass sie unter ihrem Namen importiert werden kann (module("foo.py")ermöglichtimport foo). Verwenden Siepackagefür ein Verzeichnis/Paket.Wenn sich die Datei im aktuellen Verzeichnis befindet:
module("foo.py")Andernfalls verwenden Sie base_path, um die Datei zu lokalisieren:
module("foo.py", base_path="src/drivers")Sie können die obigen Variablen wie
$(PORT_DIR)inbase_pathverwenden.
- require(name, library=None)¶
Bindet ein Paket anhand des Namens (und seine Abhängigkeiten) aus micropython-lib ein.
So werden Erweiterungen der Standardbibliothek und Community-Treiber eingefroren: Das benannte Paket wird aus dem micropython-lib-Submodul abgerufen und zusammen mit allem, wovon es abhängt, eingefroren. Verwenden Sie stattdessen
moduleoderpackage, um Ihren eigenen Quellcode statt eines veröffentlichten Pakets einzufrieren.Optional kann library (ein String) angegeben werden, um ein Paket aus einer Bibliothek zu referenzieren, die zuvor mit
add_libraryregistriert wurde. Andernfalls wird die Liste der Bibliothekspfade verwendet.
- include(manifest_path)¶
Bindet ein weiteres Manifest ein. So werden Manifeste zusammengesetzt: Ein benutzerdefiniertes Firmware-Manifest sollte mit
includedas Port- (oder Board-)Manifest einbinden, damit die vom Board benötigten Module eingefroren bleiben, und dann seine eigenen Einträge hinzufügen.Normalerweise muss ein zum Kompilieren von Firmware verwendetes Manifest das Port-Manifest einbinden, das eingefrorene Module enthalten kann, die für das Funktionieren des Boards erforderlich sind.
Das Argument manifest kann ein String (Dateiname) oder ein iterierbares Objekt von Strings sein.
Relative Pfade werden relativ zur aktuellen Manifest-Datei aufgelöst.
Wenn der Pfad auf ein Verzeichnis verweist, wird implizit die darin enthaltene Datei manifest.py eingebunden.
Sie können die obigen Variablen wie
$(PORT_DIR)inmanifest_pathverwenden.
- metadata(description=None, version=None, license=None, author=None)¶
Definiert Metadaten für diese Manifest-Datei. Dies ist nützlich für Manifeste von micropython-lib-Paketen.
Diese Felder werden verwendet, wenn ein Paket über mip zu micropython-lib veröffentlicht bzw. von dort installiert wird; in einem Board-Firmware-Manifest werden sie nicht benötigt.
Tieferstufige Funktionen¶
Diese Funktionen sind der Vollständigkeit halber dokumentiert, aber mit Ausnahme von freeze_as_str kann die gesamte Funktionalität über die höherstufigen Funktionen erreicht werden.
Die freeze*-Funktionen unterscheiden sich nur darin, wie der Code gespeichert wird:
freeze_as_mpy/freeze_mpyspeichern vorkompilierten Bytecode (.mpy) im Flash. Der Code läuft direkt aus dem Flash, verwendet minimal RAM und wird schnell importiert. Dies ist das, wasmodule,packageundrequireintern verwenden.freeze_as_strfriert stattdessen den Python-Quellcode ein, der beim Import zu Bytecode kompiliert wird (unter Verwendung von RAM und mit dem On-Device-Compiler). Dies ist die einzige Fähigkeit, die nicht von den höherstufigen Funktionen bereitgestellt wird, weshalb es die oben erwähnte Ausnahme darstellt.
- freeze(path, script=None, opt=0)¶
Das zugrunde liegende Primitiv, auf dem die höherstufigen Funktionen aufbauen; bevorzugen Sie diese. Friert die durch path angegebene Eingabe ein und bestimmt deren Typ automatisch. Ein
.py-Skript wird zuerst zu einer.mpy-Datei kompiliert und dann eingefroren, eine.mpy-Datei wird direkt eingefroren.path muss ein Verzeichnis sein, das als Basisverzeichnis für die Dateisuche dient. Beim Importieren der resultierenden eingefrorenen Module beginnt der Modulname nach path, d.h. path wird aus dem Modulnamen ausgeschlossen.
Wenn path relativ ist, wird er relativ zur aktuellen
manifest.pyaufgelöst.Wenn script None ist, werden alle Dateien in path eingefroren.
Wenn script ein iterierbares Objekt ist, wird
freeze()für alle Elemente des iterierbaren Objekts aufgerufen (wobei dieselben Werte für path und opt durchgereicht werden).Wenn script ein String ist, gibt er die einzufrierende Datei oder das einzufrierende Verzeichnis an und kann zusätzliche Verzeichnisse vor der Datei oder dem letzten Verzeichnis enthalten. Die Datei oder das Verzeichnis wird in path gesucht. Wenn script ein Verzeichnis ist, werden alle Dateien in diesem Verzeichnis eingefroren.
opt ist die Optimierungsstufe, die beim Kompilieren von
.pyzu.mpyan mpy-cross übergeben wird. Diese Stufen werden inmicropython.opt_level()beschrieben.
- freeze_as_str(path)¶
Friert den angegebenen path und alle darin enthaltenen
.py-Skripte als String ein, der beim Import kompiliert wird. Verwenden Sie dies nur, wenn der eingefrorene Code Python-Quellcode bleiben muss; es kostet im Vergleich zu den.mpy-Varianten RAM zur Importzeit.
- freeze_as_mpy(path, script=None, opt=0)¶
Friert die Eingabe ein, indem zuerst die
.py-Skripte zu.mpy-Dateien kompiliert werden und dann die resultierenden.mpy-Dateien eingefroren werden. Dies ist das, wasmoduleundpackageim Hintergrund tun. Siehefreeze()für weitere Details zu den Argumenten.
- freeze_mpy(path, script=None, opt=0)¶
Friert die Eingabe ein, die
.mpy-Dateien sein müssen, die direkt eingefroren werden (kein Kompilierungsschritt). Siehefreeze()für weitere Details zu den Argumenten.
Beispiele¶
Um eine einzelne Datei aus dem aktuellen Verzeichnis einzufrieren, die als import mydriver verfügbar sein wird, verwenden Sie:
module("mydriver.py")
Um ein Verzeichnis von Dateien in einem Unterverzeichnis „mydriver“ des aktuellen Verzeichnisses einzufrieren, das als import mydriver verfügbar sein wird, verwenden Sie:
package("mydriver")
Um die Bibliothek „hmac“ aus micropython-lib einzufrieren, verwenden Sie:
require("hmac")
Ein vollständigeres Beispiel einer benutzerdefinierten manifest.py-Datei (für ein Board, das ein eigenes Standard-Manifest hat) lautet:
# 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")
Dann kann das Board kompiliert werden mit
$ cd ports/stm32
$ make BOARD=MYBOARD FROZEN_MANIFEST=~/src/myproject/manifest.py
Beachten Sie, dass die meisten Boards keine eigene manifest.py haben, sondern direkt die des Ports verwenden; in diesem Fall sollte Ihr Manifest einfach include("$(PORT_DIR)/boards/manifest.py") ausführen.