Tệp manifest MicroPython

Tóm tắt

MicroPython có tính năng cho phép mã Python được "đóng băng" vào firmware, thay thế cho việc tải mã từ hệ thống tệp.

Điều này mang lại các lợi ích sau:

  • mã được biên dịch trước thành bytecode, tránh việc phải biên dịch mã nguồn Python khi tải.

  • bytecode có thể được thực thi trực tiếp từ ROM (tức là bộ nhớ flash) thay vì phải sao chép vào RAM. Tương tự, các đối tượng hằng số (chuỗi, tuple, v.v.) cũng được tải từ ROM. Điều này có thể giúp tăng đáng kể lượng bộ nhớ có sẵn cho ứng dụng của bạn.

  • trên các thiết bị không có hệ thống tệp, đây là cách duy nhất để tải mã Python.

Trong quá trình phát triển, thông thường không nên đóng băng vì điều này sẽ làm chậm đáng kể chu kỳ phát triển của bạn, vì mỗi lần cập nhật sẽ yêu cầu nạp lại toàn bộ firmware. Tuy nhiên, bạn vẫn có thể đóng băng có chọn lọc một số phụ thuộc ít thay đổi (chẳng hạn như thư viện bên thứ ba).

Cách liệt kê các tệp Python cần đóng băng vào firmware là thông qua một "manifest", đây là một tệp Python sẽ được quá trình build thông dịch. Thông thường bạn sẽ viết tệp manifest như một phần của định nghĩa board, nhưng bạn cũng có thể viết tệp manifest độc lập và sử dụng nó với định nghĩa board hiện có.

Các tệp manifest có thể khai báo phụ thuộc vào các thư viện từ micropython-lib cũng như các tệp Python trên hệ thống tệp, và cả các tệp manifest khác.

Viết tệp manifest

Tệp manifest là một tệp Python chứa một loạt các lời gọi hàm. Xem các hàm có sẵn được định nghĩa bên dưới.

Bất kỳ đường dẫn nào được sử dụng trong tệp manifest đều có thể bao gồm các biến sau. Tất cả đều được phân giải thành đường dẫn tuyệt đối.

  • $(MPY_DIR) -- đường dẫn đến repo micropython.

  • $(MPY_LIB_DIR) -- đường dẫn đến submodule micropython-lib. Nên dùng require().

  • $(PORT_DIR) -- đường dẫn đến port hiện tại (ví dụ: ports/stm32)

  • $(BOARD_DIR) -- đường dẫn đến board hiện tại (ví dụ: ports/stm32/boards/OPENMV4)

Các tệp manifest tùy chỉnh không nên đặt trong kho lưu trữ MicroPython chính. Bạn nên quản lý chúng bằng hệ thống kiểm soát phiên bản cùng với phần còn lại của dự án.

Thông thường, một manifest dùng để biên dịch firmware sẽ cần bao gồm manifest của port, có thể bao gồm các module đóng băng cần thiết cho board hoạt động. Nếu bạn chỉ muốn thêm các module bổ sung vào một board hiện có, hãy bao gồm manifest của board (manifest này sẽ bao gồm manifest của port).

Build với manifest tùy chỉnh

Manifest của bạn có thể được chỉ định trên dòng lệnh make với:

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

Điều này áp dụng cho tất cả các port, bao gồm cả các port dựa trên CMake (ví dụ: rp2), vì wrapper Makefile sẽ truyền thông tin này vào bản build CMake.

Thêm manifest vào định nghĩa board

Nếu bạn có định nghĩa board tùy chỉnh, bạn có thể làm cho nó tự động bao gồm manifest tùy chỉnh của bạn. Trên các port dựa trên make (hầu hết các port), trong mpconfigboard.mk của bạn, hãy đặt biến FROZEN_MANIFEST.

FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py

Trên các port dựa trên CMake (ví dụ: rp2), thay vào đó hãy dùng mpconfigboard.cmake

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

Các hàm cấp cao

Đây là những hàm bạn thường dùng. Chúng thêm mã vào tập hợp được biên dịch trước thành bytecode và đóng băng vào image firmware:

  • modulepackage đóng băng mã nguồn cục bộ của bạn -- một tệp đơn lẻ hoặc toàn bộ thư mục package tương ứng.

  • require đóng băng một package đã được công bố (và các phụ thuộc của nó) từ micropython-lib, theo tên.

  • include kéo vào một manifest khác để các module đóng băng của nó cũng được thêm vào.

  • add_librarymetadata là các hàm hỗ trợ (đăng ký các đường dẫn tìm kiếm thêm cho require, và khai báo metadata của package).

Một manifest firmware điển hình trước tiên includes manifest của port hoặc board (để các module board cần vẫn được đóng băng), sau đó thêm các mục module/package/require của riêng nó.

Lưu ý: Đối số từ khóa opt có thể được đặt trên các hàm khác nhau, điều này kiểm soát mức độ tối ưu hóa được sử dụng bởi cross-compiler. Xem micropython.opt_level().

add_library(library, library_path, prepend=False)

Đăng ký đường dẫn đến một library có tên bên ngoài.

Dùng khi bạn muốn require phân giải các package từ một thư mục khác với micropython-lib -- ví dụ: bộ sưu tập driver riêng của bạn, hoặc một checkout thư viện bên thứ ba.

Đường dẫn library_path sẽ được tự động tìm kiếm khi sử dụng require. Mặc định, thư viện được thêm vào cuối danh sách các thư viện cần tìm kiếm. Truyền True vào prepend để thêm vào đầu danh sách.

Ngoài ra, thư viện được thêm có thể được yêu cầu rõ ràng bằng cách sử dụng require("name", library="library").

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

Đóng băng toàn bộ một package -- một thư mục các tệp .py (tùy chọn có sub-package) -- để nó có thể được import dưới dạng import <package>. Dùng module thay thế cho một tệp độc lập đơn lẻ.

Điều này tương đương với việc sao chép thư mục "package_path" vào thiết bị (ngoại trừ dưới dạng mã đóng băng).

Trong trường hợp đơn giản nhất, để đóng băng một package "foo" trong thư mục hiện tại:

package("foo")

sẽ đệ quy bao gồm tất cả các tệp .py trong foo, và sẽ được đóng băng dưới dạng foo/**/*.py.

Nếu package không ở cùng thư mục với tệp manifest, hãy dùng base_path:

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

Bạn có thể sử dụng các biến ở trên, chẳng hạn như $(PORT_DIR) trong base_path.

Để giới hạn ở một số tệp nhất định trong package, hãy dùng files (lưu ý: đường dẫn phải tương đối với package): package("foo", files=["bar/baz.py"]).

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

Đóng băng một tệp .py độc lập để nó có thể được import theo tên (module("foo.py") làm cho import foo hoạt động). Dùng package cho thư mục/package.

Nếu tệp ở trong thư mục hiện tại:

module("foo.py")

Nếu không, hãy dùng base_path để xác định vị trí tệp:

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

Bạn có thể sử dụng các biến ở trên, chẳng hạn như $(PORT_DIR) trong base_path.

require(name, library=None)

Yêu cầu một package theo tên (và các phụ thuộc của nó) từ micropython-lib.

Đây là cách các tiện ích mở rộng thư viện chuẩn và driver cộng đồng được đóng băng: package được đặt tên sẽ được lấy từ submodule micropython-lib và đóng băng cùng với tất cả những gì nó phụ thuộc vào. Dùng module hoặc package thay thế để đóng băng mã nguồn của riêng bạn thay vì một package đã được công bố.

Tùy chọn chỉ định library (một chuỗi) để tham chiếu đến một package từ thư viện đã được đăng ký trước đó với add_library. Nếu không, danh sách các đường dẫn thư viện sẽ được sử dụng.

include(manifest_path)

Bao gồm một manifest khác. Đây là cách các manifest được kết hợp: một manifest firmware tùy chỉnh nên include manifest của port (hoặc board) để các module board cần vẫn được đóng băng, sau đó thêm các mục của riêng nó.

Thông thường, một manifest dùng để biên dịch firmware sẽ cần bao gồm manifest của port, có thể bao gồm các module đóng băng cần thiết cho board hoạt động.

Đối số manifest có thể là một chuỗi (tên tệp) hoặc một iterable của các chuỗi.

Các đường dẫn tương đối được phân giải so với tệp manifest hiện tại.

Nếu đường dẫn trỏ đến một thư mục, thì nó ngầm bao gồm tệp manifest.py bên trong thư mục đó.

Bạn có thể sử dụng các biến ở trên, chẳng hạn như $(PORT_DIR) trong manifest_path.

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

Định nghĩa metadata cho tệp manifest này. Điều này hữu ích cho các manifest của package micropython-lib.

Các trường này được sử dụng khi một package được công bố lên / cài đặt từ micropython-lib thông qua mip; chúng không cần thiết trong manifest firmware của board.

Các hàm cấp thấp

Các hàm này được ghi lại đầy đủ, nhưng ngoại trừ freeze_as_str, tất cả các chức năng đều có thể truy cập thông qua các hàm cấp cao.

Các hàm freeze* chỉ khác nhau ở cách mã được lưu trữ:

  • freeze_as_mpy / freeze_mpy lưu trữ bytecode đã được biên dịch trước (.mpy) trong flash. Mã chạy trực tiếp từ flash, sử dụng RAM tối thiểu và import nhanh. Đây là những gì module, packagerequire sử dụng nội bộ.

  • freeze_as_str thay vào đó đóng băng mã nguồn Python, được biên dịch thành bytecode tại thời điểm import (sử dụng RAM và yêu cầu trình biên dịch trên thiết bị). Đây là khả năng duy nhất không được các hàm cấp cao hiển thị, đó là lý do tại sao nó là ngoại lệ được đề cập ở trên.

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

Hàm nguyên thủy cơ bản mà các hàm cấp cao xây dựng trên; nên ưu tiên dùng những hàm đó. Đóng băng đầu vào được chỉ định bởi path, tự động xác định loại của nó. Tập lệnh .py sẽ được biên dịch thành .mpy trước rồi mới đóng băng, và tệp .mpy sẽ được đóng băng trực tiếp.

path phải là một thư mục, đây là thư mục gốc để bắt đầu tìm kiếm các tệp. Khi import các module đóng băng kết quả, tên của module sẽ bắt đầu sau path, tức là path bị loại trừ khỏi tên module.

Nếu path là tương đối, nó sẽ được phân giải theo manifest.py hiện tại.

Nếu script là None, tất cả các tệp trong path sẽ được đóng băng.

Nếu script là một iterable thì freeze() sẽ được gọi trên tất cả các mục của iterable (với cùng pathopt được truyền qua).

Nếu script là một chuỗi thì nó chỉ định tệp hoặc thư mục cần đóng băng, và có thể bao gồm các thư mục thêm trước tệp hoặc thư mục cuối cùng. Tệp hoặc thư mục sẽ được tìm kiếm trong path. Nếu script là một thư mục thì tất cả các tệp trong thư mục đó sẽ được đóng băng.

opt là mức độ tối ưu hóa để truyền cho mpy-cross khi biên dịch .py thành .mpy. Các mức này được mô tả trong micropython.opt_level().

freeze_as_str(path)

Đóng băng path đã cho và tất cả các tập lệnh .py bên trong nó dưới dạng chuỗi, sẽ được biên dịch khi import. Chỉ dùng điều này khi mã đóng băng phải vẫn là mã nguồn Python; nó tốn RAM khi import so với các biến thể .mpy.

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

Đóng băng đầu vào bằng cách trước tiên biên dịch các tập lệnh .py thành các tệp .mpy, sau đó đóng băng các tệp .mpy kết quả. Đây là những gì modulepackage thực hiện bên trong. Xem freeze() để biết thêm chi tiết về các đối số.

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

Đóng băng đầu vào, phải là các tệp .mpy được đóng băng trực tiếp (không có bước biên dịch). Xem freeze() để biết thêm chi tiết về các đối số.

Ví dụ

Để đóng băng một tệp đơn từ thư mục hiện tại có thể truy cập dưới dạng import mydriver, hãy dùng:

module("mydriver.py")

Để đóng băng một thư mục các tệp trong thư mục con "mydriver" của thư mục hiện tại có thể truy cập dưới dạng import mydriver, hãy dùng:

package("mydriver")

Để đóng băng thư viện "hmac" từ micropython-lib, hãy dùng:

require("hmac")

Một ví dụ đầy đủ hơn về tệp manifest.py tùy chỉnh (cho một board có manifest mặc định riêng) là:

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

Sau đó board có thể được biên dịch với

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

Lưu ý rằng hầu hết các board không có manifest.py riêng, thay vào đó chúng sử dụng trực tiếp manifest của port, trong trường hợp đó manifest của bạn chỉ cần include("$(PORT_DIR)/boards/manifest.py") thay thế.