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

SOLID for Raspberry Pi 4 (連載8)

(2022/12/13)

クレートを作ってみよう

前回、Rustの特徴の一つである「依存関係」について、使う側として見てきました。

今回は、その、依存される側となるクレートを作ってみたいと思います。

1. クレートの使い方おさらい

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

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) }
}

と、使っています。

 

2.クレートを作ってみる

 

せっかくなので、先ほどの例のgreen_ledモジュールを、クレート化して、メインのプログラムから使ってみましょう。
メインのプログラムが、とてもすっきりするはずです。

 

2.1  新しいCargoパッケージ作成

まず、コマンドプロンプトを開き、適当なフォルダでcargo new green_led --libを実行します。

green_ledフォルダが生成され、その中に新しいCargoパッケージが生成されています。

新しいCargoパッケージの中にあるlib.rsに、green_ledモジュールの内容をコピーします。
そして、Cargo.tomlには、green_ledモジュールが必要とする依存関係の定義をします。

順番に見ていきましょう。

 

2.2  green_ledモジュールの内容をlib.rsにコピー

モジュール名は、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,
      ));
   }
}

 

2.3  Cargo.toml編集

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クレートができました。

2.4 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"

 

2.5 ビルド&実行

ビルドし、実行してみます。
せっかくなので、ブレークポイントを設定してみます。

実行してみます。

 

ステップインしてみましょう。

先程作成したgreen_led_operationクレートのファイル内に入って行きました。

という事で、無事、クレートを作ることができました。

 

 

3.C言語との対比

ライブラリに含まれる関数の使い方における、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の文法」を見てみようと思います。