SOLID未分類 SOLID for Raspberry Pi 4 (連載7)

SOLID for Raspberry Pi 4 (連載7)

(2022/12/7)

パッケージマネージャ Cargo

今までは、用意された環境でビルドをおこなってきたので、気にしなくても良かったのですが、Rustを知るならば避けて通れない、

パッケージマネージャ:Cargo

について、いよいよ学んでいこうと思います。

 

1. Cargoとは

まず、パッケージ、とは何を指すのか。

Cargoにおけるパッケージとは、
「パッケージマネージャで管理できるRustコードの単位」
と理解することができます。

では、管理、とは。

おおざっぱに言うと、
「そのRustコードをビルド・リンクする際に必要なパッケージ(だいたいライブラリ)を把握し、ダウンロードし、ビルド&リンクを実行すること」
となります。
これがCargoの役目です。

具体的にどういう事をしているのか、見ていきましょう。

 

1.1 Cargoを使わないビルド--- 公式ドキュメント から抜粋

まずCargo使わない場合どうなるか見てみます。

以下のURLに説明が掲載されています。
https://doc.rust-jp.rs/book-ja/ch01-02-hello-world.html

手順を抜粋すると、
①ソースコード等格納のためのフォルダを作成
②main.rsを、好きなエディタで書く
③コンパイル:rustc main.rs
④実行:./main.exe

gccでコンパイルする時と、あまり変わりありませんね。
ライブラリを使わない、ごくごく小さなプログラムだと、これでいい、という事ですね。

では、開発が大規模になり、多種多様なライブラリを使いたくなった場合はどうでしょうか。

例えばgccの場合、あらかじめ必要なライブラリを準備し、ビルド手順を明示的に表すためmakefileを作ることになります。
Rustの場合はどうなるのでしょうか。

そう、Cargoを使うわけです。

 

1.2 Cargoを使う---公式ドキュメント から抜粋

以下のURLに説明が掲載されていますので、見ていきます。
https://doc.rust-jp.rs/book-ja/ch01-03-hello-cargo.html

ポイント1:新規アプリケーションパッケージ作成
cargo new hello_cargo をすることによって、
・フォルダが作成される。
・フォルダ内にmain.rsだけでなく、Cargoパッケージの情報を記述するCargoマニフェストファイルであるCargo.toml(※)も作成される。
・さらに、新しいGitリポジトリも作成される。

 

ポイント2:ビルド
cargo build をすることによって、
・中間・最終生成物格納フォルダ (デバッグビルドの場合 target/debug) や、その他必要なフォルダが作成され、実行ファイルが格納される。
・プロジェクトの全依存関係を記録するCargo.lock(※) も作成される。
・cargo run をすると、ビルドだけでなく実行まで一気に行える
このひとまとまりを、パッケージと呼ぶ。
参考:https://doc.rust-lang.org/cargo/guide/project-layout.html

 

ポイント3:コードチェック
cargo checkで、バイナリ生成せずともコードをチェックし、エラー有無を確認できる。

 

ポイント4:リリースビルド
cargo build --release をすると、releaseフォルダに、最適化された実行ファイルを生成できる。

 

すなわち、Cargoの役目とは、
「ビルド&リンクフェーズにおけるパッケージ情報を管理する」
ということがわかりました。

では、
・Cargoパッケージの情報を記述するCargoマニフェストファイルであるCargo.toml(※)
・プロジェクトの全依存関係を記録するCargo.lock(※)
について、見ていきましょう。

2 依存関係

「依存関係」とは、そのソースコードで必要とする、パッケージを明記したものです。
パッケージのことを「クレート」と呼びます。

[※]
厳密に言えばCargoの用語では「クレート」はCargoパッケージのひとつの要素であってパッケージそのものではありません。
依存関係の定義においてはCargoパッケージに含まれるライブラリクレートだけに注目するので、ややカジュアルな呼び方としてCargoパッケージをクレートを呼ぶことがあります。

実際に見てみましょう。

今まで作ったプロジェクトを開き、ソリューションエクスプローラーを見てみると、Cargo.tomlとCargo.lockがありました。

 

まず、Cargo.tomlを見ていきましょう。

 

2.1  Cargo.tomlを見てみる

Cargo.tomlを見ると、こうなっています。


[package]
name = "rust_blinky_lib"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tock-registers = "0.7.0"
itron = { version = "= 0.1.9", features = ["unstable", "nightly", "solid_fmp3"] }
bcm2711_pac = { git = "https://github.com/KyotoMicrocomputer/solid-rapi4-examples.git" } 

[lib]
crate-type = ["staticlib"]

 

「依存関係」が明言されているのは、[dependencies]の部分です。

tock-registers:
以下のクレートをダウンロードし使用する、ことを明言。
https://crates.io/crates/tock-registers/0.7.0
itron:
以下のクレートをダウンロードし使用する、ことを明言。
https://crates.io/crates/itron
SOLID extensionを使うことも明言。
bcm2711_pac:
Gitリポジトリsolid-rapi4-examples.gitをダウンロードし、その中にあるbcm2711_pac という名前のCargoパッケージに含まれるライブラリクレートを使用することを指定。
(ここではgitからダウンロードする前提での記述になっていますが、ファイルシステムにあることを前提とした記述も可能)

 

したがって、Cargo buildの際に、これらのクレートをダウンロードして展開し、ビルド対象ファイルとして含める、ということになります。

さらに、「lib」のところ。
crate-type = ["staticlib"] と記述されています。

デフォルトは crate-type = ["lib"] です。

crate-type = ["staticlib"] :
C++など他の言語のプログラムで使用できるスタティックライブラリを生成するための crate-type です。

crate-type = ["lib"] :
下流クレートで再利用できるよう、Rust固有の中間コード等を含む独自形式のライブラリファイルを生成するための crate-type です。

ここでは下流クレートで再利用したいのではなく、ビルド出力をSOLIDツールチェーンのリンカに渡したいので staticlib を指定しています。

 

ところで、https://crates.io/というURLが良く出てきます。
これ、Rust用クレートのレジストリになっていて、膨大な量のクレートが配布されています。探せばいろいろと便利なものがありそうですね。
(なんとなくですが、C#のNuGetと同じような感じがします。。。)

 

2.2  Cargo.lockを見てみる

Cargo.lockを見ると、こういう風な記述がたくさん書かれています。


[[package]]
name = "itron"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab72a2ac7834689b3478b66077c32269d712c650ce138c2fd093a340e3fcc68b"
dependencies = [
"tt-call",
]

[[package]]
name = "rust_blinky_lib"
version = "0.1.0"
dependencies = [
"bcm2711_pac",
"itron",
"tock-registers",
]

[[package]]
name = "tt-call"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e66dcbec4290c69dd03c57e76c2469ea5c7ce109c6dd4351c13055cf71ea055"

※これで全部ではありません。一部抜粋です。

このファイルは、ビルドが最初に成功した時の依存パッケージとそのバージョンがすべてが記載されています。
Cargoにより生成されるファイルであり、私たちユーザが編集するものではありません。

 

ここでCargo.tomlを思い出してみましょう。
Cargo.tomlは、依存関係をユーザが明記していました。
使いたいパッケージを書き、ビルド時にそれを含めて欲しい、という意図でした。

あれ?なら、Cargo.tomlが依存関係知ってるってこと?
それじゃ、Cargo.lockの存在は?
??

なんだか疑問だらけになってしまい、筆者も混乱してしまって、理解するのに時間を要してしまいました。

ヒントは、依存しているパッケージの先のお話。

Cargo.tomlには、「ユーザが自分の作成するプログラムが」必要とする依存パッケージを明記します。
さらに、その、必要とされたパッケージにも依存関係があり、Cargo.tomlを持っているはず。
さらに、その必要とされたパッケージが必要とするパッケージにも依存関係が。。。(以下省略)

深い!

そこでCargo.lockの登場です。
Cargo.lockは
「ビルドが最初に成功した時の依存パッケージすべてが記載されている」

先程のCargo.lockを見てみると、

・rust_blinky_libパッケージ(自分)の依存関係は"bcm2711_pac","itron","tock-registers"。
・そのitronパッケージのバージョンは0.1.9で依存関係は"tt-call"。
・そのtt-callパッケージのバージョンは1.0.8で依存関係なし。

等々。。。がすべて記載されています。

Cargo.tomlをたどらなくていいんですね、
必要としたパッケージが必要としたパッケージの依存関係、もCargo.lockは知っている。
そのまた先も。

しかも、依存関係とした時のバージョンも記載されています。
例えば先程のCargo.lockでは、itronパッケージが使用しているtt-callのバージョンが1.0.8、とわかります。

バージョンが違う場合、だいたいにおいて下位互換であり、気にしなくていいのかもしれないのですが、まれに(自分の欲している)互換性が途切れたりしますよね。
Cargo.lockは、そういう事がないよう、バージョン固定してくれる、便利なシステムだったんですね。

 

2.3  クレートは自由に使ってもいいの?

ところで。一体、これらのライセンスってどうなっているのでしょう?

Rust API Guidelinesによれば、できるだけMIT or Apache 2.0か、そうでなければ寛大なライセンスにするべき、と記載されています。

Rust API Guidelines:
https://rust-lang.github.io/api-guidelines/necessities.html#crate-and-its-dependencies-have-a-permissive-license-c-permissive

製品に適用するような場合は、各クレートのライセンス条項を確認するようにしないといけないような気がします。。。

今回は以上です。
次回は「クレートを作ってみよう」の予定です。