パッケージ
パッケージを使用すると、Moveプログラマーはより簡単にコードを再利用し、プロジェクト間で共有できます。Moveパッケージシステムを使用すると、プログラマーは以下を簡単に実行出来ます。
- Moveコードを含むパッケージを定義します。
- 名前付きアドレスでパッケージをパラメータ化します。
- 他のMoveコードでパッケージをインポートして使用し、名前付きアドレスをインスタンス化します。
- パッケージを構築し、パッケージから関連するコンパイル成果物を生成します。
- コンパイルされたMove成果物の周囲の共通のインターフェースで操作します。
パッケージレイアウトとマニフェスト構文
MoveパッケージソースディレクトリにはMove.toml
、パッケージマニフェストファイルと一連のサブディレクトリが含まれています。
a_move_package
├── Move.toml
├── sources (必須)
├ ├── module.move
├ └── *.move
├── examples (オプション、テスト及び開発モード)
├── scripts (オプション、ソースへ入れる事も出来ます)
├── doc_templates (オプション)
└── tests (オプション、テストモード)
required
とマークされたディレクトリは、ディレクトリがMoveパッケージとみなされ、コンパイルされるために存在する 必要 が有ります。オプションのディレクトリが存在出来、その場合はコンパイルプロセスへ含められます。パッケージがビルドされるモード(test
かdev
)に応じてtests
とexamples
ディレクトリも含まれます。
sources
ディレクトリは、Moveモジュールと Moveスクリプト(Moveスクリプトとスクリプト関数を含むモジュールの両方)の両方を含む事が出来ます。examples
ディレクトリは、開発やチュートリアルの目的でのみ使用される追加のコードを収容する事が出来ます。これらのコードは、test
やdev
モード外でコンパイルされた時は含まれません。
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
がスコープ内にあるのは、以下の場合です。
- 名前付きアドレス
N
を宣言します。または P
の推移的依存関係のひとつの中のパッケージが名前付きアドレスN
を宣言し、パッケージグラフ内のP
と宣言パッケージN
の間にN
の名前変更なしで依存関係パスが存在します。
更に、パッケージ内の全ての名前付きアドレスはエクスポートされます。この事と上記のスコープ規則により、各パッケージは、パッケージがインポートされた時スコープへ取り込まれる名前付きアドレスのセットが付属される、と見なす事が出来ます。例えばExamplePkg
パッケージがインポートされた場合、そのインポートによって名前付きアドレスnamed_addr
がスコープへ取り込まれます。このため、P
が2つのパッケージP1
、P2
をインポートしその両方が名前付きアドレスN
を宣言している場合P
で以下の問題が発生します。N
がP
で参照されている場合、どちらの「N
」を意味しますか?それはP1
の物ですか?P2
の物ですか?
名前付きアドレスがどのパッケージの物であるかの曖昧さを回避するため、パッケージ内の全ての依存関係によって導入されるスコープのセットが互いに重ならないようにし、名前付きアドレスをスコープに取り込むパッケージがインポートされた時、 名前付アドレスの名前を変更する 方法を提供します。
インポート時、名前付きアドレスの名前が変更可能となりました。上記例のP
、P1
、P2
では以下のようにします。
[package]
name = "P"
...
[dependencies]
P1 = { local = "some_path_to_P1", addr_subst = { "P1N" = "N" } }
P2 = { local = "some_path_to_P2" }
このN
の名前変更で、P2
からN
を参照し、P1N
はP1
からN
を参照します。
module N::A {
public fun x(): address { @P1N }
}
注意: 名前の変更はローカルではありません 。P
パッケージ内で名前付きアドレスN
がN2
へ名前変更されると、P
をインポートする全てのパッケージはN
を参照せず、P
の外部からN
が再導入されない限りN2
のみを参照します。このため、このセクションの冒頭にあるスコープ規則の規則(2)では「N
の名前を変更せず、P
とN
の宣言パッケージ間のパッケージグラフ内の依存関係パス」を指定しています。
インスタンス化
名前付きアドレスは、常に同じ値である限り、パッケージグラフ全体で複数回インスタンス化できます。同 じ名前付きアドレス (名前の変更に関係なく) がパッケージグラフ全体で異なる値でインスタンス化されると、エラーが発生します。
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) をメモリ内へ保持します。このCompiledPackage
はOnDiskPackage
へ変換出来、その逆も可能です。後者は、ファイルシステムへ次の形式でレイアウトされた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 prove
とaptos move test
コマンドはどちらも現在、依存関係としてバイトコードをサポートしていません。
推奨構造
この機能を使用した開発フローを説明するため例を使用します。パッケージA
をコンパイルするとします。パッケージのレイアウトは:
./A
├── Move.toml
├── sources
├ AModule.move
A.move
は、モジュールBar
とFoo
に応じて以下のように定義されます。
module A::AModule {
use B::Bar;
use C::Foo;
public fun foo(): u64 {
Bar::foo() + Foo::bar()
}
}
Bar
とFoo
のソースは利用出来ませんが、対応するバイトコードBar.mv
とFoo.mv
はローカルで利用出来るとします。これらを依存関係として使用するには、以下の方法を使います。
Bar
とFoo
に対しMove.toml
を指定します。注意: 名前付きアドレスは、バイトコード内の実際のアドレスで既にインスタンス化されています。
この例では、C
の実際のアドレスは既に0x3
へ拘束されています。その結果以下で示すように[addresses]
は C
を0x3
として指定する必要が有ります。
[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つのパッケージディレクトリがすべて同じレベルだと仮定す ると、A
のMove.toml
は以下と類似します。
[package]
name = "A"
version = "0.0.0"
[addresses]
A = "0x2"
[dependencies]
Bar = { local = "../B" }
Foo = { local = "../C" }
注意: 同じパッケージのバイトコードとソースコードの両方が検索パス内に存在する場合、コンパイラは宣言が重複していると警告します。