MicroPython .mpy ファイル

MicroPython は .mpy ファイルの概念を定義しています。これはプリコンパイルされたコードを保持するバイナリコンテナファイル形式であり、通常の .py モジュールのようにインポートできます。ファイル foo.mpy は、foo.mpy をインポート機構が通常の方法で見つけることができる限り import foo でインポートできます。通常 sys.path にリストされている各ディレクトリが順番に検索されます。特定のディレクトリを検索で foo.py が最初に検索され、それが見つからなければ foo.mpy が検索されます。どちらも見つからない場合は次のディレクトリで検索が続行されます。そのため foo.pyfoo.mpy に優先します。

これらの .mpy ファイルには、通常 mpy-cross プログラムで Python ソースファイル(.py ファイル)から生成されるバイトコードを含めることができます。一部のアーキテクチャでは .mpy ファイルにネイティブマシンコードを含めることもできます。ネイティブコードはマシンコードはさまざまな方法で生成できますが、C 言語ソースコードから生成することが特に多いです。

.mpy ファイルのバージョンと互換性

利用しようとする .mpy ファイルは、特定の MicroPython システムと互換性がある場合と互換性がない場合があります。互換性は以下に基づいています:

  • .mpy ファイルのバージョン: ファイルのバージョンは、それをロードするシステムでサポートされているバージョンと一致する必要があります。
  • .mpy ファイルで使用されるバイトコード機能: ファイルとシステムの間で一致する必要がある2つのバイトコード機能があります。ユニコードサポートとバイトコードでのマップ検索のインラインキャッシュです。
  • スモール整数ビット: .mpy ファイルにはスモール整数の最小ビット数の要求があり、.mpy をロードするシステムは少なくともこの数のビットをサポートする必要があります。
  • qstr 圧縮ウィンドウサイズ: .mpy ファイルには qstr 解凍のための最小ウィンドウサイズの要求があり、.mpy をロードするシステムにはこのサイズ以上のウィンドウが必要です。
  • ネイティブアーキテクチャ: .mpyファイルにネイティブマシンコードが含まれている場合、そのマシンコードのアーキテクチャの指定があり、.mpy をロードするシステムはそのアーキテクチャのコードの実行をサポートする必要があります。

MicroPython システムが .mpy ファイルのインポートをサポートしている場合には、 sys.implementation._mpy フィールドが存在し、バージョン(下位8ビット)、機能、ネイティブアーキテクチャをエンコードした整数を返します。

最初の4つのテストの1つに失敗した .mpy ファイルをインポートしようとすると ValueError('incompatible .mpy file') が発生します。(ネイティブマシンコードが含まれている場合)ネイティブアーキテクチャのテストに失敗した .mpy ファイルをインポートしようとすると ValueError('incompatible .mpy arch') が発生します。

.mpy ファイルのインポートに失敗した場合は、次を試してください:

  • 以下を実行して、MicroPython システムでサポートされている .mpy バージョンとフラグを確認します:

    import sys
    sys_mpy = sys.implementation._mpy
    arch = [None, 'x86', 'x64',
        'armv6', 'armv6m', 'armv7m', 'armv7em', 'armv7emsp', 'armv7emdp',
        'xtensa', 'xtensawin'][sys_mpy >> 10]
    print('mpy version:', sys_mpy & 0xff)
    print('mpy flags:', end='')
    if arch:
        print(' -march=' + arch, end='')
    if not sys_mpy & 0x200:
        print(' -mno-unicode', end='')
    print()
    
  • ファイルの先頭2バイトを調べて、.mpy ファイルの妥当性を確認します。先頭バイトは大文字の 'M' で、2バイト目はバージョン番号です。このバージョン番号は、上記のシステムバージョンと一致する必要があります。一致しない場合は .mpy ファイルを再作成してください。

  • .mpy ファイルを作成するのに使った mpy-cross の出した .mpy バーションがシステムの .mpy バージョンと一致するかを確認します。 mpy-cross の出すバージョンは mpy-cross --version でわかります。一致しない場合は mpy-cross --version によって報告されたタグ(またはハッシュ)でチェックアウトした Git リポジトリにある mpy-cross で再コンパイルします。

  • mpy-cross フラグが正しいかを確認します。フラグは上記のコードで見つけるか、使っているポートの MPY_CROSS_FLAGS Makefile 変数を調べてください。

次の表は、MicroPythonリリースと.mpyバージョンの対応を示しています。

MicroPython リリース .mpy バージョン
v1.12 以上 5
v1.11 4
v1.9.3 - v1.10 3
v1.9 - v1.9.2 2
v1.5.1 - v1.8.7 0

完全を期すため、次の表は .mpy バージョンが変更されたメイン MicroPython リポジトリの Git コミットを示しています。

.mpy バージョン変更 Git コミット
4 から 5 5716c5cf65e9b2cb46c2906f40302401bdd27517
3 から 4 9a5f92ea72754c01cc03e5efcdfe94021120531e
2 から 3 ff93fd4f50321c6190e1659b19e64fef3045a484
1 から 2 dd11af209d226b7d18d5148b239662e30ed60bad
0 から 1 6a11048af1d01c78bdacddadd1b72dc7ba7c6478
初期バージョン 0 d8c834c95d506db979ec871417de90b7951edc30

.mpy ファイルのバイナリエンコーディング

MicroPython .mpy ファイルは、ネストされた階層内にコードオブジェクトを格納したバイナリコンテナー形式です。大きな範囲の値を提供しながらもファイルを小さく保つために、多くの場所で可変エンコード符号なし整数(variably-encoded-unsigned-integer: vuint)の概念を使用します。utf-8 エンコーディングと同様に、このエンコーディングは1バイトあたり7ビットを格納し、1つ以上のバイトが続く場合は8ビット目(MSB)が設定されます。符号なし整数のビットは、LSB 形式で vuint に格納されます。

.mpyファイルのトップレベルは2つの部分で構成されています:

  • ヘッダー
  • モジュールの外部スコープの raw コード。この外部スコープは .mpy ファイルがインポートされるときに実行されます。

ヘッダー

.mpy のヘッダーは次のとおりです:

サイズ フォールド
バイト 値 0x4d (ASCII の "M')
バイト .mpy バージョン番号
バイト 機能フラグ
バイト スモール整数のビット数
vuint qstr ウィンドウのサイズ

raw コードの要素

raw コードの要素には、バイトコードまたはネイティブマシンコードのコードが含まれます。その内容は次のとおりです:

サイズ フォールド
vuint タイプとサイズ
... コード(バイトコードまたはマシンコード)
vuint 定数オブジェクトの数
vuint 副 raw コード要素の数
... 定数オブジェクト
... 副 raw コード要素

raw コード要素の最初の vuint は、この要素に格納されているコードのタイプ(最下位2ビット)と、圧縮解除されたコードの長さ(割り当てられるRAMの量)をエンコードしています。

vuint に続いてコード自体が続きます。バイトコードの場合、圧縮された qstr 値も含まれます。

コードに続いて、定数オブジェクトの数を数える vuint と、副 raw コード要素数の vuint が続きます。

定数オブジェクトは次に保存されます。

最後に 副 raw コード要素が再帰的に保存されます。