File manifest MicroPython

Ringkasan

MicroPython memiliki fitur yang memungkinkan kode Python untuk "dibekukan" ke dalam firmware, sebagai alternatif dari memuat kode dari sistem file.

Ini memiliki manfaat berikut:

  • kode dikompilasi terlebih dahulu menjadi bytecode, sehingga tidak perlu mengompilasi sumber Python saat dimuat.

  • bytecode dapat dieksekusi langsung dari ROM (yaitu memori flash) alih-alih disalin ke RAM. Demikian pula, objek konstan apa pun (string, tuple, dll.) juga dimuat dari ROM. Ini dapat menyebabkan lebih banyak memori tersedia untuk aplikasi Anda.

  • pada perangkat yang tidak memiliki sistem file, ini adalah satu-satunya cara untuk memuat kode Python.

Selama pengembangan, pembekuan umumnya tidak disarankan karena akan sangat memperlambat siklus pengembangan Anda, karena setiap pembaruan akan memerlukan flashing ulang seluruh firmware. Namun, masih berguna untuk membekukan secara selektif beberapa dependensi yang jarang berubah (seperti perpustakaan pihak ketiga).

Cara untuk mendaftar file Python yang akan dibekukan ke dalam firmware adalah melalui "manifest", yang merupakan file Python yang akan diinterpretasikan oleh proses build. Biasanya Anda akan menulis file manifest sebagai bagian dari definisi board, tetapi Anda juga bisa menulis file manifest mandiri dan menggunakannya dengan definisi board yang sudah ada.

File manifest dapat mendefinisikan dependensi pada perpustakaan dari micropython-lib serta file Python di sistem file, dan juga pada file manifest lainnya.

Menulis file manifest

File manifest adalah file Python yang berisi serangkaian pemanggilan fungsi. Lihat fungsi-fungsi yang tersedia yang didefinisikan di bawah ini.

Setiap jalur yang digunakan dalam file manifest dapat menyertakan variabel-variabel berikut. Semuanya diselesaikan ke jalur absolut.

  • $(MPY_DIR) -- jalur ke repositori micropython.

  • $(MPY_LIB_DIR) -- jalur ke submodul micropython-lib. Lebih baik gunakan require().

  • $(PORT_DIR) -- jalur ke port saat ini (misalnya ports/stm32)

  • $(BOARD_DIR) -- jalur ke board saat ini (misalnya ports/stm32/boards/OPENMV4)

File manifest kustom tidak boleh berada di repositori utama MicroPython. Anda harus menyimpannya dalam version control bersama dengan proyek Anda.

Biasanya manifest yang digunakan untuk mengompilasi firmware perlu menyertakan manifest port, yang mungkin mencakup modul yang dibekukan yang diperlukan agar board dapat berfungsi. Jika Anda hanya ingin menambahkan modul tambahan ke board yang sudah ada, maka sertakan manifest board (yang pada gilirannya akan menyertakan manifest port).

Membangun dengan manifest kustom

Manifest Anda dapat ditentukan pada baris perintah make dengan:

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

Ini berlaku untuk semua port, termasuk yang berbasis CMake (misalnya rp2), karena wrapper Makefile akan meneruskannya ke build CMake.

Menambahkan manifest ke definisi board

Jika Anda memiliki definisi board kustom, Anda dapat membuatnya menyertakan manifest kustom Anda secara otomatis. Pada port berbasis make (sebagian besar port), di mpconfigboard.mk Anda, setel variabel FROZEN_MANIFEST.

FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py

Pada port berbasis CMake (misalnya rp2), gunakan mpconfigboard.cmake

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

Fungsi tingkat tinggi

Ini adalah fungsi-fungsi yang biasanya Anda gunakan. Fungsi-fungsi ini menambahkan kode ke set yang dikompilasi lebih dahulu menjadi bytecode dan dibekukan ke dalam citra firmware:

  • module dan package membekukan sumber lokal Anda sendiri -- satu file atau seluruh direktori paket masing-masing.

  • require membekukan paket yang diterbitkan (dan dependensinya) dari micropython-lib, berdasarkan nama.

  • include menarik manifest lain sehingga modul yang dibekukannya juga ditambahkan.

  • add_library dan metadata adalah fungsi pendukung (mendaftarkan jalur pencarian tambahan untuk require, dan mendeklarasikan metadata paket).

Manifest firmware yang umum pertama-tama includes manifest port atau board (sehingga modul yang dibutuhkan board tetap dibekukan), kemudian menambahkan entri module/package/require-nya sendiri.

Catatan: Argumen kata kunci opt dapat disetel pada berbagai fungsi, ini mengontrol tingkat optimisasi yang digunakan oleh kompiler silang. Lihat micropython.opt_level().

add_library(library, library_path, prepend=False)

Daftarkan jalur ke library bernama eksternal.

Gunakan ini ketika Anda ingin require menyelesaikan paket dari direktori selain micropython-lib -- misalnya koleksi driver Anda sendiri, atau checkout perpustakaan pihak ketiga.

Jalur library_path akan dicari secara otomatis saat menggunakan require. Secara default, perpustakaan yang ditambahkan ditambahkan ke akhir daftar perpustakaan yang akan dicari. Teruskan True ke prepend untuk menambahkannya ke awal daftar.

Selain itu, perpustakaan yang ditambahkan dapat diminta secara eksplisit menggunakan require("name", library="library").

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

Bekukan seluruh paket -- direktori file .py (opsional dengan sub-paket) -- sehingga dapat diimpor sebagai import <package>. Gunakan module sebagai gantinya untuk satu file mandiri.

Ini setara dengan menyalin direktori "package_path" ke perangkat (kecuali sebagai kode yang dibekukan).

Dalam kasus paling sederhana, untuk membekukan paket "foo" di direktori saat ini:

package("foo")

akan secara rekursif menyertakan semua file .py di foo, dan akan dibekukan sebagai foo/**/*.py.

Jika paket tidak ada di direktori yang sama dengan file manifest, gunakan base_path:

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

Anda dapat menggunakan variabel di atas, seperti $(PORT_DIR) di base_path.

Untuk membatasi file tertentu dalam paket, gunakan files (catatan: jalur harus relatif terhadap paket): package("foo", files=["bar/baz.py"]).

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

Bekukan satu file .py mandiri sehingga dapat diimpor dengan namanya (module("foo.py") membuat import foo berfungsi). Gunakan package untuk direktori/paket.

Jika file ada di direktori saat ini:

module("foo.py")

Jika tidak, gunakan base_path untuk menemukan file:

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

Anda dapat menggunakan variabel di atas, seperti $(PORT_DIR) di base_path.

require(name, library=None)

Membutuhkan paket berdasarkan nama (dan dependensinya) dari micropython-lib.

Inilah cara ekstensi perpustakaan standar dan driver komunitas dibekukan: paket yang disebutkan diambil dari submodul micropython-lib dan dibekukan bersama dengan semua yang bergantung padanya. Gunakan module atau package sebagai gantinya untuk membekukan sumber Anda sendiri daripada paket yang diterbitkan.

Secara opsional tentukan library (string) untuk merujuk paket dari perpustakaan yang sebelumnya telah didaftarkan dengan add_library. Jika tidak, daftar jalur perpustakaan akan digunakan.

include(manifest_path)

Sertakan manifest lain. Inilah cara manifest dikompilasi: manifest firmware kustom harus include manifest port (atau board) sehingga modul yang dibutuhkan board tetap dibekukan, kemudian menambahkan entri-entri miliknya sendiri.

Biasanya manifest yang digunakan untuk mengompilasi firmware perlu menyertakan manifest port, yang mungkin mencakup modul yang dibekukan yang diperlukan agar board dapat berfungsi.

Argumen manifest bisa berupa string (nama file) atau iterable berisi string.

Jalur relatif diselesaikan berdasarkan file manifest saat ini.

Jika jalur menuju ke direktori, maka secara implisit menyertakan file manifest.py di dalam direktori tersebut.

Anda dapat menggunakan variabel di atas, seperti $(PORT_DIR) di manifest_path.

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

Definisikan metadata untuk file manifest ini. Ini berguna untuk manifest paket micropython-lib.

Kolom-kolom ini digunakan saat paket diterbitkan ke / dipasang dari micropython-lib melalui mip; kolom-kolom ini tidak diperlukan dalam manifest firmware board.

Fungsi tingkat rendah

Fungsi-fungsi ini didokumentasikan untuk kelengkapan, tetapi dengan pengecualian freeze_as_str semua fungsionalitas dapat diakses melalui fungsi tingkat tinggi.

Fungsi-fungsi freeze* berbeda hanya dalam cara kode disimpan:

  • freeze_as_mpy / freeze_mpy menyimpan bytecode yang dikompilasi lebih dahulu (.mpy) di flash. Kode berjalan langsung dari flash, menggunakan RAM minimal, dan diimpor dengan cepat. Inilah yang digunakan module, package, dan require secara internal.

  • freeze_as_str sebaliknya membekukan sumber Python, yang dikompilasi menjadi bytecode saat impor (menggunakan RAM, dan memerlukan kompiler di perangkat). Ini adalah satu kemampuan yang tidak diekspos oleh fungsi tingkat tinggi, itulah mengapa ini merupakan pengecualian yang disebutkan di atas.

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

Primitif dasar tempat fungsi tingkat tinggi dibangun; lebih baik gunakan fungsi-fungsi tersebut. Bekukan input yang ditentukan oleh path, secara otomatis menentukan jenisnya. Skrip .py akan dikompilasi terlebih dahulu ke .mpy lalu dibekukan, dan file .mpy akan dibekukan langsung.

path harus berupa direktori, yang merupakan direktori dasar untuk mulai mencari file. Saat mengimpor modul yang dibekukan yang dihasilkan, nama modul akan dimulai setelah path, yaitu path dikecualikan dari nama modul.

Jika path relatif, maka diselesaikan ke manifest.py saat ini.

Jika script adalah None, semua file di path akan dibekukan.

Jika script adalah iterable, maka freeze() dipanggil pada semua item iterable (dengan path dan opt yang sama diteruskan).

Jika script adalah string maka itu menentukan file atau direktori yang akan dibekukan, dan dapat menyertakan direktori tambahan sebelum file atau direktori terakhir. File atau direktori akan dicari di path. Jika script adalah direktori, maka semua file di direktori tersebut akan dibekukan.

opt adalah tingkat optimisasi yang akan diteruskan ke mpy-cross saat mengompilasi .py ke .mpy. Tingkat-tingkat ini dijelaskan dalam micropython.opt_level().

freeze_as_str(path)

Bekukan path yang diberikan dan semua skrip .py di dalamnya sebagai string, yang akan dikompilasi saat impor. Gunakan ini hanya jika kode yang dibekukan harus tetap berupa sumber Python; ini membutuhkan RAM saat impor dibandingkan dengan varian .mpy.

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

Bekukan input dengan terlebih dahulu mengompilasi skrip .py ke file .mpy, kemudian membekukan file .mpy yang dihasilkan. Inilah yang dilakukan module dan package di balik layar. Lihat freeze() untuk detail lebih lanjut tentang argumen.

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

Bekukan input, yang harus berupa file .mpy yang dibekukan langsung (tanpa langkah kompilasi). Lihat freeze() untuk detail lebih lanjut tentang argumen.

Contoh

Untuk membekukan satu file dari direktori saat ini yang akan tersedia sebagai import mydriver, gunakan:

module("mydriver.py")

Untuk membekukan direktori file dalam subdirektori "mydriver" dari direktori saat ini yang akan tersedia sebagai import mydriver, gunakan:

package("mydriver")

Untuk membekukan perpustakaan "hmac" dari micropython-lib, gunakan:

require("hmac")

Contoh yang lebih lengkap dari file manifest.py kustom (untuk board yang memiliki manifest defaultnya sendiri) adalah:

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

Kemudian board dapat dikompilasi dengan

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

Perhatikan bahwa sebagian besar board tidak memiliki manifest.py sendiri, melainkan menggunakan manifest port secara langsung, dalam hal ini manifest Anda hanya perlu include("$(PORT_DIR)/boards/manifest.py") sebagai gantinya.