File manifest di MicroPython¶
Riepilogo¶
MicroPython dispone di una funzionalità che consente di «congelare» il codice Python all’interno del firmware, come alternativa al caricamento del codice dal filesystem.
Questo offre i seguenti vantaggi:
il codice viene precompilato in bytecode, evitando la necessità di compilare il sorgente Python al momento del caricamento.
il bytecode può essere eseguito direttamente dalla ROM (ovvero dalla memoria flash) invece di essere copiato nella RAM. Allo stesso modo, anche eventuali oggetti costanti (stringhe, tuple, ecc.) vengono caricati dalla ROM. Questo può rendere disponibile una quantità significativamente maggiore di memoria per la tua applicazione.
sui dispositivi che non dispongono di un filesystem, questo è l’unico modo per caricare il codice Python.
Durante lo sviluppo, il congelamento non è generalmente consigliato perché rallenta significativamente il ciclo di sviluppo, dato che ogni aggiornamento richiede di riprogrammare l’intero firmware. Tuttavia, può comunque essere utile congelare selettivamente alcune dipendenze che cambiano raramente (come le librerie di terze parti).
Il modo per elencare i file Python da congelare nel firmware è tramite un «manifest», ovvero un file Python che viene interpretato dal processo di build. Tipicamente si scrive un file manifest come parte di una definizione di scheda, ma è anche possibile scrivere un file manifest autonomo e utilizzarlo con una definizione di scheda esistente.
I file manifest possono definire dipendenze su librerie di micropython-lib, nonché su file Python presenti nel filesystem e anche su altri file manifest.
Scrivere file manifest¶
Un file manifest è un file Python che contiene una serie di chiamate di funzione. Vedi le funzioni disponibili definite di seguito.
Qualsiasi percorso usato nei file manifest può includere le seguenti variabili. Queste vengono tutte risolte in percorsi assoluti.
$(MPY_DIR)– percorso del repository di micropython.$(MPY_LIB_DIR)– percorso del sottomodulo micropython-lib. Preferisci usarerequire().$(PORT_DIR)– percorso della porta corrente (ad es.ports/stm32)$(BOARD_DIR)– percorso della scheda corrente (ad es.ports/stm32/boards/OPENMV4)
I file manifest personalizzati non dovrebbero risiedere nel repository principale di MicroPython. Dovresti mantenerli sotto controllo di versione insieme al resto del tuo progetto.
Tipicamente un manifest usato per compilare il firmware dovrà includere il manifest della porta, che potrebbe includere moduli congelati necessari al funzionamento della scheda. Se vuoi semplicemente aggiungere moduli extra a una scheda esistente, includi il manifest della scheda (che a sua volta includerà il manifest della porta).
Build con un manifest personalizzato¶
Il tuo manifest può essere specificato sulla riga di comando di make con:
$ make BOARD=MYBOARD FROZEN_MANIFEST=/path/to/my/project/manifest.py
Questo vale per tutte le porte, comprese quelle basate su CMake (ad es. rp2), poiché il wrapper Makefile lo passerà alla build CMake.
Aggiungere un manifest a una definizione di scheda¶
Se disponi di una definizione di scheda personalizzata, puoi fare in modo che includa automaticamente il tuo manifest personalizzato. Sulle porte basate su make (la maggior parte delle porte), nel tuo mpconfigboard.mk imposta la variabile FROZEN_MANIFEST.
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
Sulle porte basate su CMake (ad es. rp2), usa invece mpconfigboard.cmake
set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
Funzioni di alto livello¶
Queste sono le funzioni che userai normalmente. Aggiungono codice all’insieme che viene precompilato in bytecode e congelato nell’immagine del firmware:
moduleepackagecongelano il tuo codice sorgente locale — rispettivamente un singolo file o un’intera directory di pacchetto.requirecongela un pacchetto pubblicato (e le sue dipendenze) da micropython-lib, per nome.includeincorpora un altro manifest in modo che vengano aggiunti anche i suoi moduli congelati.add_libraryemetadatasono funzioni di supporto (registrano percorsi di ricerca aggiuntivi perrequiree dichiarano i metadati del pacchetto).
Un tipico manifest di firmware prima fa include del manifest della porta o della scheda (in modo che i moduli necessari alla scheda restino congelati), quindi aggiunge le proprie righe module/package/require.
Nota: l’argomento per parola chiave opt può essere impostato sulle varie funzioni; controlla il livello di ottimizzazione usato dal cross-compilatore. Vedi micropython.opt_level().
- add_library(library, library_path, prepend=False)¶
Registra il percorso di una library esterna con nome.
Usa questo quando vuoi che
requirerisolva i pacchetti da una directory diversa da micropython-lib — per esempio la tua collezione personale di driver o il checkout di una libreria di terze parti.Il percorso library_path verrà cercato automaticamente quando si usa
require. Per impostazione predefinita la libreria aggiunta viene inserita alla fine dell’elenco delle librerie da cercare. PassaTrueper anteporla e aggiungerla all’inizio dell’elenco.Inoltre, la libreria aggiunta può essere richiesta esplicitamente usando
require("name", library="library").
- package(package_path, files=None, base_path='.', opt=None)¶
Congela un intero pacchetto — una directory di file
.py(opzionalmente con sotto-pacchetti) — in modo che possa essere importato comeimport <package>. Usa invecemoduleper un singolo file autonomo.Questo equivale a copiare la directory «package_path» sul dispositivo (tranne per il fatto che il codice è congelato).
Nel caso più semplice, per congelare un pacchetto «foo» nella directory corrente:
package("foo")includerà ricorsivamente tutti i file .py in foo e verrà congelato come
foo/**/*.py.Se il pacchetto non si trova nella stessa directory del file manifest, usa
base_path:package("foo", base_path="path/to/libraries")Puoi usare le variabili sopra indicate, come
$(PORT_DIR)inbase_path.Per limitare a determinati file del pacchetto usa
files(nota: i percorsi devono essere relativi al pacchetto):package("foo", files=["bar/baz.py"]).
- module(module_path, base_path='.', opt=None)¶
Congela un singolo file
.pyautonomo in modo che possa essere importato col suo nome (module("foo.py")fa funzionareimport foo). Usapackageper una directory/pacchetto.Se il file si trova nella directory corrente:
module("foo.py")Altrimenti usa base_path per localizzare il file:
module("foo.py", base_path="src/drivers")Puoi usare le variabili sopra indicate, come
$(PORT_DIR)inbase_path.
- require(name, library=None)¶
Richiede un pacchetto per nome (e le sue dipendenze) da micropython-lib.
È così che le estensioni della libreria standard e i driver della comunità vengono congelati: il pacchetto indicato viene recuperato dal sottomodulo micropython-lib e congelato insieme a tutto ciò da cui dipende. Usa invece
moduleopackageper congelare il tuo codice sorgente anziché un pacchetto pubblicato.Opzionalmente specifica library (una stringa) per riferirti a un pacchetto di una libreria precedentemente registrata con
add_library. Altrimenti verrà usato l’elenco dei percorsi delle librerie.
- include(manifest_path)¶
Include un altro manifest. È così che i manifest vengono composti: un manifest di firmware personalizzato dovrebbe fare
includedel manifest della porta (o della scheda), in modo che i moduli necessari alla scheda restino congelati, e quindi aggiungere le proprie voci.Tipicamente un manifest usato per compilare il firmware dovrà includere il manifest della porta, che potrebbe includere moduli congelati necessari al funzionamento della scheda.
L’argomento manifest può essere una stringa (nome file) o un iterabile di stringhe.
I percorsi relativi vengono risolti rispetto al file manifest corrente.
Se il percorso punta a una directory, allora include implicitamente il file manifest.py presente in quella directory.
Puoi usare le variabili sopra indicate, come
$(PORT_DIR)inmanifest_path.
- metadata(description=None, version=None, license=None, author=None)¶
Definisce i metadati per questo file manifest. Questo è utile per i manifest dei pacchetti micropython-lib.
Questi campi vengono utilizzati quando un pacchetto viene pubblicato su / installato da micropython-lib tramite mip; non sono necessari in un manifest di firmware di una scheda.
Funzioni di basso livello¶
Queste funzioni sono documentate per completezza, ma ad eccezione di freeze_as_str tutte le funzionalità sono accessibili tramite le funzioni di alto livello.
Le funzioni freeze* differiscono solo nel modo in cui il codice viene memorizzato:
freeze_as_mpy/freeze_mpymemorizzano bytecode precompilato (.mpy) nella flash. Il codice viene eseguito direttamente dalla flash, usa una quantità minima di RAM e viene importato rapidamente. È ciò chemodule,packageerequireusano internamente.freeze_as_strcongela invece il sorgente Python, che viene compilato in bytecode al momento dell’importazione (usando la RAM e richiedendo il compilatore sul dispositivo). Questa è l’unica capacità non esposta dalle funzioni di alto livello, motivo per cui è l’eccezione segnalata sopra.
- freeze(path, script=None, opt=0)¶
La primitiva sottostante su cui si basano le funzioni di alto livello; preferisci queste ultime. Congela l’input specificato da path, determinandone automaticamente il tipo. Uno script
.pyverrà prima compilato in.mpye poi congelato, mentre un file.mpyverrà congelato direttamente.path deve essere una directory, ovvero la directory di base da cui iniziare la ricerca dei file. Quando si importano i moduli congelati risultanti, il nome del modulo inizierà dopo path, cioè path è escluso dal nome del modulo.
Se path è relativo, viene risolto rispetto al
manifest.pycorrente.Se script è None, tutti i file in path verranno congelati.
Se script è un iterabile, allora
freeze()viene chiamata su tutti gli elementi dell’iterabile (con gli stessi path e opt passati attraverso).Se script è una stringa, allora specifica il file o la directory da congelare e può includere directory aggiuntive prima del file o dell’ultima directory. Il file o la directory verranno cercati in path. Se script è una directory, allora tutti i file in quella directory verranno congelati.
opt è il livello di ottimizzazione da passare a mpy-cross durante la compilazione da
.pya.mpy. Questi livelli sono descritti inmicropython.opt_level().
- freeze_as_str(path)¶
Congela il path indicato e tutti gli script
.pyal suo interno come stringa, che verrà compilata al momento dell’importazione. Usa questo solo quando il codice congelato deve rimanere sorgente Python; comporta un consumo di RAM al momento dell’importazione rispetto alle varianti.mpy.
- freeze_as_mpy(path, script=None, opt=0)¶
Congela l’input compilando prima gli script
.pyin file.mpye poi congelando i file.mpyrisultanti. È ciò chemoduleepackagefanno dietro le quinte. Vedifreeze()per ulteriori dettagli sugli argomenti.
- freeze_mpy(path, script=None, opt=0)¶
Congela l’input, che deve essere costituito da file
.mpycongelati direttamente (senza passaggio di compilazione). Vedifreeze()per ulteriori dettagli sugli argomenti.
Esempi¶
Per congelare un singolo file dalla directory corrente che sarà disponibile come import mydriver, usa:
module("mydriver.py")
Per congelare una directory di file in una sottodirectory «mydriver» della directory corrente che sarà disponibile come import mydriver, usa:
package("mydriver")
Per congelare la libreria «hmac» da micropython-lib, usa:
require("hmac")
Un esempio più completo di un file manifest.py personalizzato (per una scheda che ha il proprio manifest predefinito) è:
# 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")
La scheda può quindi essere compilata con
$ cd ports/stm32
$ make BOARD=MYBOARD FROZEN_MANIFEST=~/src/myproject/manifest.py
Nota che la maggior parte delle schede non ha il proprio manifest.py, ma usa direttamente quello della porta, nel qual caso il tuo manifest dovrebbe semplicemente fare include("$(PORT_DIR)/boards/manifest.py").