メインコンテンツまでスキップ

パッケージ

パッケージを使用すると、Moveプログラマーはより簡単にコードを再利用し、プロジェクト間で共有できます。Moveパッケージシステムを使用すると、プログラマーは以下を簡単に実行出来ます。

  • Moveコードを含むパッケージを定義します。
  • 名前付きアドレスでパッケージをパラメータ化します。
  • 他のMoveコードでパッケージをインポートして使用し、名前付きアドレスをインスタンス化します。
  • パッケージを構築し、パッケージから関連するコンパイル成果物を生成します。
  • コンパイルされたMove成果物の周囲の共通のインターフェースで操作します。

パッケージレイアウトとマニフェスト構文

MoveパッケージソースディレクトリにはMove.toml、パッケージマニフェストファイルと一連のサブディレクトリが含まれています。

a_move_package
├── Move.toml
├── sources (必須)
├ ├── module.move
├ └── *.move
├── examples (オプション、テスト及び開発モード)
├── scripts (オプション、ソースへ入れる事も出来ます)
├── doc_templates (オプション)
└── tests (オプション、テストモード)

requiredとマークされたディレクトリは、ディレクトリがMoveパッケージとみなされ、コンパイルされるために存在する 必要 が有ります。オプションのディレクトリが存在出来、その場合はコンパイルプロセスへ含められます。パッケージがビルドされるモード(testdev)に応じてtestsexamplesディレクトリも含まれます。

sourcesディレクトリは、Moveモジュールと Moveスクリプト(Moveスクリプトとスクリプト関数を含むモジュールの両方)の両方を含む事が出来ます。examplesディレクトリは、開発やチュートリアルの目的でのみ使用される追加のコードを収容する事が出来ます。これらのコードは、testdevモード外でコンパイルされた時は含まれません。

scriptsディレクトリがサポートされているため、パッケージ作成者が希望する場合は、Moveスクリプトをモジュールから分離できます。

scriptsディレクトリが存在する場合は、常にコンパイルへ含められます。ドキュメントは、doc_templatesディレクトリ内に存在するドキュメント テンプレートを使用して構築されます。

Move.toml

MoveパッケージマニフェストはMove.tomlファイル内で定義され、以下の構文を持ちます。オプションフィールドは*+でマークされ、ひとつ以上の要素を表します。

//Move.toml

[package]
name = <string> # e.g., "MoveStdlib"
version = "<uint>.<uint>.<uint>" # e.g., "0.1.1"
license* = <string> # e.g., "MIT", "GPL", "Apache 2.0"
authors* = [<string>] # e.g., ["Joe Smith (joesmith@noemail.com)", "Jane Smith (janesmith@noemail.com)"]

[addresses]  
#(オプションのセクション)のパッケージで名前付きアドレスを宣言し、パッケージグラフで名前付きアドレスをインスタンス化します。
#以下の形式で1行以上で名前付きアドレスを宣言します。
<addr_name> = "_" | "<hex_address>" # e.g., std = "_" or my_addr = "0xC0FFEECAFE"

[dependencies]
#(オプションのセクション)依存関係へのパスと、各依存関係からの名前付きアドレスのインスタンス化もしくは名前変更。
#以下の形式で1行以上で依存関係を宣言します。
<string> = { local = <string>, addr_subst* = { (<string> = (<string> | "<hex_address>"))+ } } # local dependencies
<string> = { git = <URL ending in .git>, subdir=<path to dir containing Move.toml inside git repo>, rev=<git commit hash>, addr_subst* = { (<string> = (<string> | "<hex_address>"))+ } } # git dependencies

[dev-addresses]
#(オプションのセクション)[addresses]セクションと同じですが「dev」モードと 「test」モードへのみ含まれます。
#以下の形式で開発名前付きアドレスを1行以上で宣言します。
<addr_name> = "_" | "<hex_address>" # e.g., std = "_" or my_addr = "0xC0FFEECAFE"

[dev-dependencies]
#(オプションのセクション)[dependencies]セクションと同じですが「devモードと「test」モードへのみ含まれます。
#以下の形式で開発依存関係を1行以上で宣言します。
<string> = { local = <string>, addr_subst* = { (<string> = (<string> | <address>))+ } }

1つのローカル依存関係と1つのgit依存関係を持つ最小限のパッケージマニフェストの例:

[package]
name = "AName"
version = "0.0.0"

より標準的なパッケージマニフェストの例。Move標準ライブラリも含まれ、そこから名前付きアドレスStdをアドレス値0x1でインスタンス化します。

[package]
name = "AName"
version = "0.0.0"
license = "Apache 2.0"

[addresses]
address_to_be_filled_in = "_"
specified_address = "0xB0B"

[dependencies]
   #ローカルの依存関係
LocalDep = { local = "projects/move-awesomeness", addr_subst = { "std" = "0x1" } }
#Gitの依存関係
MoveStdlib = { git = "https://github.com/diem/diem.git", subdir="language/move-stdlib", rev = "56ab033cc403b489e891424a629e76f643d4fb6b" }

[dev-addresses]
#このモジュールの開発の際、使用します。
address_to_be_filled_in = "0x101010101"

パッケージマニフェストのセクションの殆どは一目瞭然ですが、名前付きアドレスは理解するのが少し難しい場合があるので、もう少し詳しく調べる価値があります。

コンパイル時の名前付きアドレス

Moveには名前付きアドレスがあり、名前付きアドレスは Moveでは宣言出来ない事を思い出して下さい。これまでは名前付きアドレスとその値をコマンド ラインでコンパイラへ渡す必要が有りました。

Moveパッケージシステムでは、この作業は不要となり、パッケージ内で名前付きアドレスを宣言したり、スコープ内で他の名前付きアドレスをインスタンス化したり、Moveパッケージ システムマニフェストファイル内で他のパッケージから名前付きアドレスの名前を変更したりする事が出来ます。これらをそれぞれ見てみましょう。

宣言

example_pkg/sources/A.moveで以下のようなMoveモジュールがあるとします。

module named_addr::A {
public fun x(): address { @named_addr }
}

example_pkg/Move.toml内で、名前付きアドレスnamed_addrは2つの異なる方法で宣言できます。ひとつ目は:

[package]
name = "ExamplePkg"
# ...
[addresses]
named_addr = "_"

ExamplePkgパッケージ内の名前付きアドレスとしてnamed_addrを宣言し このアドレスには任意の有効なアドレス値を指定出来ます 。従って、インポートパッケージは、名前付きアドレスnamed_addrの値を任意のアドレスとして選択出来ます。直感的には、これは名前付きアドレスnamed_addrによってExamplePkgパッケージをパラメータ化していると考える事が出来、その後パッケージをインポートパッケージによってインスタンス化出来ます。

named_addrは以下のような宣言をする事も出来ます。

[package]
name = "ExamplePkg"
# ...
[addresses]
named_addr = "0xCAFE"

これは、名前付きアドレスnamed_addrが正確に0xCAFEであり、変更出来ないことを示しています。これは、他のインポートパッケージが、割り当てられた正確な値を心配する事無く、この名前付きアドレスを使用出来る様にするため便利です。これら2つの異なる宣言方法を使用すると、名前付きアドレスに関する情報がパッケージグラフ内を流れる方法が2つあります。

  • 前者(「未割り当ての名前付きアドレス」)では、名前付きアドレスの値が輸入サイトから申告サイトへ流れます。
  • 後者(「割り当てられた名前付きアドレス」)では、名前付きアドレスの値がパッケージグラフ内の申告サイトから利用サイトへ流れます。

パッケージグラフ全体へ名前付きアドレス情報を流すこれら2つの方法では、スコープと名前変更に関連のルールの理解が重要です。

名前付きアドレスのスコープ設定と名前変更

Pパッケージ内の名前付きアドレスNがスコープ内にあるのは、以下の場合です。

  1. 名前付きアドレスNを宣言します。または
  2. Pの推移的依存関係のひとつの中のパッケージが名前付きアドレスNを宣言し、パッケージグラフ内のPと宣言パッケージNの間にNの名前変更なしで依存関係パスが存在します。

更に、パッケージ内の全ての名前付きアドレスはエクスポートされます。この事と上記のスコープ規則により、各パッケージは、パッケージがインポートされた時スコープへ取り込まれる名前付きアドレスのセットが付属される、と見なす事が出来ます。例えばExamplePkgパッケージがインポートされた場合、そのインポートによって名前付きアドレスnamed_addrがスコープへ取り込まれます。このため、Pが2つのパッケージP1P2をインポートしその両方が名前付きアドレスNを宣言している場合Pで以下の問題が発生します。NPで参照されている場合、どちらの「N」を意味しますか?それはP1の物ですか?P2の物ですか?

名前付きアドレスがどのパッケージの物であるかの曖昧さを回避するため、パッケージ内の全ての依存関係によって導入されるスコープのセットが互いに重ならないようにし、名前付きアドレスをスコープに取り込むパッケージがインポートされた時、 名前付アドレスの名前を変更する 方法を提供します。

インポート時、名前付きアドレスの名前が変更可能となりました。上記例のPP1P2では以下のようにします。

[package]
name = "P"
...
[dependencies]
P1 = { local = "some_path_to_P1", addr_subst = { "P1N" = "N" } }
P2 = { local = "some_path_to_P2" }

このNの名前変更で、P2からNを参照し、P1NP1からNを参照します。

module N::A {
public fun x(): address { @P1N }
}

注意: 名前の変更はローカルではありませんPパッケージ内で名前付きアドレスNN2へ名前変更されると、P をインポートする全てのパッケージはNを参照せず、Pの外部からNが再導入されない限りN2のみを参照します。このため、このセクションの冒頭にあるスコープ規則の規則(2)では「Nの名前を変更せず、PNの宣言パッケージ間のパッケージグラフ内の依存関係パス」を指定しています。

インスタンス化

名前付きアドレスは、常に同じ値である限り、パッケージグラフ全体で複数回インスタンス化できます。同じ名前付きアドレス (名前の変更に関係なく) がパッケージグラフ全体で異なる値でインスタンス化されると、エラーが発生します。

Moveパッケージは、全ての名前付きアドレスが値へ解決される場合のみコンパイル出来ます。パッケージがインスタンス化されていない名前付きアドレスを公開する場合、問題が発生します。これが、[dev-addresses]セクションが解決するものです。このセクションでは、名前付きアドレスの値を設定できますが、名前付きアドレスを導入する事は出来ません。更に、ルートパッケージ内の[dev-addresses]のみがdevモードに含まれます。例えば、以下のマニフェストを持つルートパッケージは、named_addrがインスタンス化されていないため、 devモードの外部ではコンパイルされません。

[package]
name = "ExamplePkg"
...
[addresses]
named_addr = "_"

[dev-addresses]
named_addr = "0xC0FFEE"

使用法、成果物、データ構造

Moveパッケージシステムには、Move CLImove <flags> <command> <command_flags>の一部としてコマンドラインオプションが付属しています。特定のパスが指定されていない限り、全てのパッケージコマンドは現在の作業ディレクトリで実行されます。Move CLIのコマンドとフラグの完全なリストはmove --helpを実行すると表示されます。

使用法

パッケージは、Move CLIコマンドを通じて、またはcompile_package関数を使用したRustのライブラリコマンドとしてコンパイル出来ます。これは、CompiledPackageを作成し、コンパイルされたバイトコードとその他のコンパイル成果物 (ソースマップ、ドキュメント、ABI) をメモリ内へ保持します。このCompiledPackageOnDiskPackageへ変換出来、その逆も可能です。後者は、ファイルシステムへ次の形式でレイアウトされたCompiledPackageのデータです。

a_move_package
├── Move.toml
...
└── build
├── <dep_pkg_name>
│ ├── BuildInfo.yaml
│ ├── bytecode_modules
│ │ └── *.mv
│ ├── source_maps
│ │ └── *.mvsm
│ ├── bytecode_scripts
│ │ └── *.mv
│ ├── abis
│ │ ├── *.abi
│ │ └── <module_name>/*.abi
│ └── sources
│ └── *.move
...
└── <dep_pkg_name>
├── BuildInfo.yaml
...
└── sources

これらのデータ構造の詳細と、Moveパッケージシステムを Rustライブラリとして使用する方法はmove-packageクレートを御覧下さい。

依存関係にバイトコードを使用する

Moveバイトコードは、依存関係のMoveソースコードがローカルで利用できない場合、依存関係として使用出来ます。この機能を使用するには、ファイルを同じレベルのディレクトリに共存させ、対応するMove.tomlファイルでパスを指定する必要が有ります。

要件と制限

ローカルバイトコードを依存関係として使用するには、バイトコードファイルをローカルへダウンロードする必要があり、名前付きアドレスごとに実際のアドレスをMove.tomlまたは--named-addressesのいずれかで指定する必要があります。

注意: aptos move proveaptos move testコマンドはどちらも現在、依存関係としてバイトコードをサポートしていません。

推奨構造

この機能を使用した開発フローを説明するため例を使用します。パッケージAをコンパイルするとします。パッケージのレイアウトは:

./A
├── Move.toml
├── sources
AModule.move

A.moveは、モジュールBarFooに応じて以下のように定義されます。

module A::AModule {
use B::Bar;
use C::Foo;
public fun foo(): u64 {
Bar::foo() + Foo::bar()
}
}

BarFooのソースは利用出来ませんが、対応するバイトコードBar.mvFoo.mvはローカルで利用出来るとします。これらを依存関係として使用するには、以下の方法を使います。

BarFooに対しMove.tomlを指定します。注意: 名前付きアドレスは、バイトコード内の実際のアドレスで既にインスタンス化されています。

この例では、Cの実際のアドレスは既に0x3へ拘束されています。その結果以下で示すように[addresses]C0x3として指定する必要が有ります。

[package]
name = "Foo"
version = "0.0.0"
[addresses]
C = "0x3"

バイトコードファイルと対応するMove.tomlファイルを同じディレクトリに配置し、バイトコードをbuildサブディレクトリに配置します。注意: 空のsourcesディレクトリが必要です。

例えば、フォルダーB(パッケージBarの場合)とC(パッケージFooの場合)のレイアウトは以下と類似しています。

./B
├── Move.toml
├── sources
├── build
Bar.mv
./C
├── Move.toml
├── sources
├── build
├── Foo
├──bytecode_modules
Foo.mv

ターゲット(最初の)パッケージのMove.tomlへ、依存(2番目の)パッケージの場所で[dependencies]を指定します。例えば、3つのパッケージディレクトリがすべて同じレベルだと仮定すると、AMove.tomlは以下と類似します。

[package]
name = "A"
version = "0.0.0"
[addresses]
A = "0x2"
[dependencies]
Bar = { local = "../B" }
Foo = { local = "../C" }

注意: 同じパッケージのバイトコードとソースコードの両方が検索パス内に存在する場合、コンパイラは宣言が重複していると警告します。