MicroPython清单文件

总结

MicroPython有一个功能,可以将Python代码”冻结”到固件中,作为从文件系统加载代码的替代方案。

这有以下好处:

  • 代码被预编译为字节码,避免了在加载时编译Python源代码的需要。

  • 字节码可以直接从ROM(即闪存)执行,而不是复制到RAM。类似地,任何常量对象(字符串、元组等)也可以从ROM加载。这可能会为您的应用程序提供更多的可用内存。

  • 在没有文件系统的设备上,这是加载Python代码的唯一方法。

在开发过程中,通常不建议冻结,因为它会显著降低开发周期,因为每次更新都需要重新刷新整个固件。但是,有选择地冻结一些很少更改的依赖项(例如第三方库)仍然是有用的。

列出要冻结到固件中的Python文件的方法是通过”manifest”,这是一个Python文件,将由构建过程解释。通常,您将编写一个清单文件作为板定义的一部分,但您也可以编写一个独立的清单文件,并将其与现有的板定义一起使用。

清单文件可以定义对:term: micropython-lib 库的依赖,以及文件系统上的Python文件,也可以定义对其他清单文件的依赖。

编写清单文件

manifest文件是包含一系列函数调用的Python文件。请参阅下面定义的可用函数。

清单文件中使用的任何路径都可以包含以下变量。这些都解析为绝对路径。

  • $(MPY_DIR) – 指向micropython的路径。

  • $(MPY_LIB_DIR) – micropython-lib子模块的路径。建议使用 require()

  • $(PORT_DIR) – 当前移植版本的路径(例如:ports/stm32

  • $(BOARD_DIR) – 当前板的路径(例如:ports/stm32/boards/PYBV11

自定义清单文件不应该存在于主MicroPython存储库中。您应该将它们与项目的其余部分一起放在版本控制中。

通常用于编译固件的清单将需要包括移植清单,其中可能包括板运行所需的冻结模块。如果您只是想向现有的板添加额外的模块,那么包括板清单(这将依次包括移植清单)。

使用自定义清单构建

你的manifest可以在 make 命令行中指定:

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

这适用于所有移植版本,包括基于CMake的移植版本(例如esp32, rp2),因为Makefile包装器会将其传递到CMake构建中。

向板定义添加清单

如果您有一个自定义板定义,您可以使它自动包含您的自定义清单。在基于make的移植版本(大多数移植版本)上,在您的 mpconfigboard.mk 中设置 FROZEN_MANIFEST 变量。

FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py

在基于CMake的移植版本(例如esp32, rp2)上,请使用 mpconfigboard.cmake

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

高级功能

注意:opt 关键字参数可以在各种函数上设置,这控制了交叉编译器使用的优化级别。请参见:func:micropython.opt_level

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

这相当于将 “package_path” 目录复制到设备(冻结代码除外)。

在最简单的情况下,冻结当前目录中的包 “foo”:

package("foo")

将递归包含foo中的所有 .py 文件,并将冻结为 foo/**/*.py

如果包不在与manifest文件相同的目录中,请使用 base_path:

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

您可以使用上面的变量,例如 base_path 中的 $(PORT_DIR)

要限制包中的某些文件,请使用 files (注意:路径应该是相对于包的): package("foo", files=["bar/baz.py"])

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

包括一个Python文件作为模块。

如果文件在当前目录中:

module("foo.py")

否则,请使用base_path定位文件:

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

您可以使用上面的变量,例如 base_path 中的 $(PORT_DIR)

require(name, unix_ffi=False)

micropython-lib 中按名称(及其依赖项)要求一个包。

可选地指定unix_ffi=True以使用来自unix-ffi目录的模块。

include(manifest_path)

包括另一个清单。

通常用于编译固件的清单将需要包括移植清单,其中可能包括板运行所需的冻结模块。

*manifest*参数可以是字符串(filename)或字符串可迭代对象。

相对路径是根据当前清单文件解析的。

如果路径指向一个目录,那么它隐式地包含该目录中的manifest.py文件。

您可以使用上面的变量,例如 manifest_path 中的 $(PORT_DIR)

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

定义此清单文件的元数据。这对于micropython-lib包的清单很有用。

低级功能

为了完整起见,对这些函数进行了文档记录,但除了 freeze_as_str 之外,所有功能都可以通过高级函数访问。

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

冻结由*path*指定的输入,自动确定其类型。.py 脚本将首先被编译为 .mpy 后冻结,然后一个 .mpy 文件将被直接冻结。

*path*必须是一个目录,它是开始搜索文件的基目录。当导入结果冻结的模块时,模块的名称将以*path*开头,即*path*从模块名称中排除。

如果*path*是相对的,它被解析为当前的 manifest.py

如果*script*为None,*path*中的所有文件将被冻结。

如果*script*是一个可迭代对象,那么对可迭代对象的所有项调用 freeze() (传递相同的*path*和*opt*)。

如果*script*是一个字符串,那么它指定要冻结的文件或目录,并且可以在文件或最后一个目录之前包含额外的目录。文件或目录将在*path*中搜索。如果*script*是一个目录,那么该目录下的所有文件将被冻结。

*opt*是在编译 .py.mpy 时传递给mpy-cross的优化级别。这些级别在 micropython.opt_level() 中描述。

freeze_as_str(path)

冻结给定的*path*和其中的所有 .py 脚本作为字符串,该字符串将在导入时编译。

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

先将 .py 脚本编译为 .mpy 文件,然后冻结生成的 .mpy 文件。有关参数的更多详细信息,请参阅 freeze()

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

冻结输入,必须是直接冻结的 .mpy 文件。有关参数的更多详细信息,请参阅 freeze()

示例

要冻结当前目录中的单个文件,该文件将以 import mydriver 的形式可用,使用:

module("mydriver.py")

要冻结当前目录的子目录”mydriver”中的文件目录,该目录将以 import mydriver 的形式可用,使用:

package("mydriver")

要冻结 micropython-lib 中的”hmac”库,使用:

require("hmac")

PYBD_SF2 板的自定义 manifest.py 文件的一个更完整的例子是:

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

然后用板进行编译

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

请注意,大多数板没有自己的 manifest.py,而是直接使用一个移植版本,在这种情况下,你的清单应该只需 include("$(PORT_DIR)/boards/manifest.py")