SOLID未分類 SOLID for Raspberry Pi 4 (連載8)
(2022/12/13)
前回、Rustの特徴の一つである「依存関係」について、使う側として見てきました。
今回は、その、依存される側となるクレートを作ってみたいと思います。
まず、パッケージ、とは何を指すのか。
Cargoにおけるパッケージとは、
「パッケージマネージャで管理できるRustコードの単位」
と理解することができます。
では、管理、とは。
おおざっぱに言うと、
「そのRustコードをビルド・リンクする際に必要なパッケージ(だいたいライブラリ)を把握し、ダウンロードし、ビルド&リンクを実行すること」
となります。
これがCargoの役目です。
具体的にどういう事をしているのか、見ていきましょう。
ビルド時に、対象のライブラリを含める手続きに関しては、前回の記事で見たように、
・Cargo.tomlの[dependencies]に、使いたいクレートを明言する。
となります。
自分のプログラム内でその関数をコールする手続きについて、「RustでLチカ (連載4)」で使用したプログラムを見てみましょう。
https://github.com/KyotoMicrocomputer/solid-rapi4-examples/blob/main/rust-blinky-pac-rtos/rustapp/src/lib.rs
mod green_led の中で、
use bcm2711_pac::gpio;
と書いています。
そして、自分のプログラム(green_ledモジュールのスコープ内)で、
fn gpio_regs() -> &'static gpio::Registers {
// Safety: SOLID for RaPi4B provides an identity mapping in this area, and we don't alter
// the mapping
unsafe { &*(gpio::BASE.to_arm_pa().unwrap() as usize as *const gpio::Registers) }
}
と、使っています。
せっかくなので、先ほどの例のgreen_ledモジュールを、クレート化して、メインのプログラムから使ってみましょう。
メインのプログラムが、とてもすっきりするはずです。
まず、コマンドプロンプトを開き、適当なフォルダでcargo new green_led --libを実行します。
green_ledフォルダが生成され、その中に新しいCargoパッケージが生成されています。
新しいCargoパッケージの中にあるlib.rsに、green_ledモジュールの内容をコピーします。
そして、Cargo.tomlには、green_ledモジュールが必要とする依存関係の定義をします。
順番に見ていきましょう。
モジュール名は、Cargo.tomlで後から定義するため、ここにモジュール宣言文不要です。
その他をコピーします。
できがったlib.rsは以下のようになります。
use bcm2711_pac::gpio;
use tock_registers::interfaces::{ReadWriteable, Writeable};
const GPIO_NUM: usize = 42;
fn gpio_regs() -> &'static gpio::Registers {
// Safety: SOLID for RaPi4B provides an identity mapping in this area, and we don't alter
// the mapping
unsafe { &*(gpio::BASE.to_arm_pa().unwrap() as usize as *const gpio::Registers) }
}
pub fn init() {
// Configure the GPIO pin for output
gpio_regs().gpfsel[GPIO_NUM / gpio::GPFSEL::PINS_PER_REGISTER].modify(
gpio::GPFSEL::pin(GPIO_NUM % gpio::GPFSEL::PINS_PER_REGISTER).val(gpio::GPFSEL::OUTPUT),
);
}
pub fn update(new_state: bool) {
if new_state {
gpio_regs().gpset[GPIO_NUM / gpio::GPSET::PINS_PER_REGISTER]
.write(gpio::GPSET::set(GPIO_NUM % gpio::GPSET::PINS_PER_REGISTER));
} else {
gpio_regs().gpclr[GPIO_NUM / gpio::GPCLR::PINS_PER_REGISTER].write(gpio::GPCLR::clear(
GPIO_NUM % gpio::GPCLR::PINS_PER_REGISTER,
));
}
}
Cargo.tomlには、依存関係を明言します。
use bcm2711_pac::gpio;
use tock_registers::interfaces::{ReadWriteable, Writeable};
を使いたいと宣言しているので、対象のクレートが依存関係として明言される必要があります。
ついでに、パッケージ名も変えておきましょう。green_led_operationとしてみましょう。
できあがったCargo.tomlは以下のようになります。
[package]
name = "green_led_operation"
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"
bcm2711_pac = { git = "https://github.com/KyotoMicrocomputer/solid-rapi4-examples.git" }
ここでフォルダは以下のようになっています。
srcフォルダ内に先程編集したlib.rsがあります。
これでgreen_led_operationクレートができました。
ではここで、SOLID-IDEを使って、新規プロジェクトを作成してみましょう。
RustでLチカ (連載4)の記事を参照していただくと、作成方法を記載しています。
簡単には、
①C++で新規ワークスペースを作成
②RustのLibraryプロジェクトをワークスペースに追加する
③メインプロジェクトのリンカ設定で、 追加のライブラリファイル にライブラリ名を指定
④main.cppを空にする
⑤lib.rsにプログラムを書く
こちらのlib.rsには、以下のようなプログラムを書いてみました。
use itron::{task::delay, time::duration};
use green_led_operation::{init, update};
#[no_mangle]
pub extern "C" fn slo_main()
{
println!("Starting LED blinker");
// Configure the LED port
init();
loop {
// Turn on the LED
update(true);
delay(duration!(ms: 200)).unwrap();
// Turn off the LED
update(false);
delay(duration!(ms: 200)).unwrap();
}
}
そして、依存関係を明言するために、Cargo.tomlを編集します。
今回は以下のように書きました。
作成したgreen_led_operationクレートが存在するフォルダを明記しています。
[package]
name = "rust_lib1"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itron = { version = "= 0.1.9", features = ["unstable", "nightly", "solid_fmp3"] }
green_led_operation.path = "../../green_led"
[lib]
crate-type = ["staticlib"]
※green_led_operation.pathは、WindowsPCのフルパス指定でも大丈夫です。例えば以下のような。
green_led_operation.path = "C:\\Users\\user\\green_led"
ビルドし、実行してみます。
せっかくなので、ブレークポイントを設定してみます。
実行してみます。
ステップインしてみましょう。
先程作成したgreen_led_operationクレートのファイル内に入って行きました。
という事で、無事、クレートを作ることができました。
ライブラリに含まれる関数の使い方における、C言語とRustでの違いについて、有識者の方に助言をいただきました。
C言語ではライブラリの作成と使用は次の手順に分解できます (ライブラリのビルドはアプリケーションのビルドパイプライン内で行うものと仮定する)。
1. ライブラリ関数のプロトタイプを foo.h で宣言する。(void func(void);)
2. ライブラリ関数を foo.c で実装する。(void func() { ... })
3. ユーザーは foo.c をコンパイルしてリンクするステップをアプリケーションのビルドパイプラインに追加する。
4. app.c から foo.h を #include する。
5. app.c に func を呼び出すコードを追加する。(func();)
6. make
これらは次のようにRustと対応付けることができます。
1. (2に相当) ライブラリ関数を foo/src/lib.rs で実装する。(fn func() { ... })
2. (3, 4に相当) foo を app/Cargo.toml の [dependencies] に追加する。
3. (5に相当) app/src/main.rs に func を呼び出すコードを追加する。(foo::func();)
4. (6に相当) cargo build
ただし、プログラマ観点ではC言語において1に相当する手順はありません。
Rustではプロトタイプ宣言に相当するものが不要である等の違いはありますが、必要なライブラリをどこかで指定したら静的リンクされ、自分のプログラムのソースコード中で使えるようになるという基本的な流れは似ています。
今回はここまで。
次回は「Rustの文法」を見てみようと思います。