Arch Linux on WSL2 で systemd を動かす
2022/02/01 追記
genie をやめ、 Distrod へ移行しました。
Distrod は以下が良いです
- 既存ディストロへのインストールが簡単 (シェルスクリプト実行するだけ)
- 新規ディストロのインストールが簡単 (コマンド実行するだけで systemd 対応のディストリを WSL へ登録できる)
- genie 等のコマンドを経由しないWSLコマンド実行 (VSCode の Remote 拡張のシェルセッションなど) も systemd 管理下プロセスとして起動される
- (genieと比較して) .NET ランタイムや libc への依存がなく、ディストロのパッケージ更新で壊れる可能性が低い (Distrod の依存ライブラリが同梱されており、システム側ライブラリに依存しない)
昨日のエントリは systemd はいらないなどと言っていましたが、 podman コンテナ上で systemd を動かすためにはホスト側でも systemd が動いていないといけないようだったので、観念して systemd をセットアップしました。
tl;dr
- GitHub - arkane-systems/genie: A quick way into a systemd "bottle" for WSL
- genie をインストール
- How To Run A Script or Command At Logoff in Windows 7 & 8 - NEXTOFWINDOWS.COM
- Windows ログオフ時に systemd を終了させるようグループポリシーを設定
- WindowsTerminal で genie 経由で systemd セッションを立ち上げるよう変更
genie のインストール
上記リポジトリに含まれている PKGBUILD は古いバージョン用のものだったので、自分で新たに PKGBUILD を作成しました。
2021/1/18 追記: genie 1.31 へアップデートしました。 2021/1/18 追記2: GitHub へ移動しました。
Arch パッケージガイドライン - ArchWiki によると /usr/libexec
は避けろとのことだったので、 /usr/lib/genie
にファイルを配置しても動くよう、ソースコードを編集するような PKGBUILD になっています。
手元環境で試した限りでは問題なく動作しています。
depends
と makedepends
が AUR にあり makepkg -s
が使えないため、以下のようにインストール。また、 community リポジトリの dotnet はバージョンが古いため、 AUR の dotnet-sdk-bin
をインストールします。
$ paru -S --needed --asdeps daemonize inetutils dotnet-sdk-bin $ makepkg -si
グループポリシーの設定
以下のスクリプトを適当なパス (今回は %userprofile%\scripts\logoff.bat
) に配置します。
wsl -d Arch -- genie -u wsl -t Arch
wsl -t Arch
を実行しているのは、 genie -u
実行後、WSL を再起動することなく再度 genie -i
などを実行すると何かおかしなことになるためです。
(ログオフ時には WSL も終了されると思うので明示的に行う必要はないと思いますが、念のため。)
以下記事に従い設定します。
以下方法で動作確認しました。
- 上記を設定
genie -i
を実行し systemd を立ち上げる- Windows をログアウト & 再ログイン
genie -s
でシェル立ち上げ後、journalctl
でログを確認するsystemd-shutdown
等のログが出ていればOK (たぶん)
Windows Terminal の設定
自分は Windows Terminal を利用しているため、設定変更し端末を開いた時点で systemd 管理下にログインするよう設定しました。
{ "guid": "{a5a97cb8-8961-5535-816d-772efe0c6a3f}", "hidden": false, "name": "Arch", "source": "Windows.Terminal.Wsl", "startingDirectory": "//wsl$/Arch/home/nksm", "icon": "ms-appdata:///Local/archlinux.png", "commandline": "wsl -d Arch -- genie -s" },
重要なのは commandline
です。
以上で systemd の設定が完了します。 また、無事 podman コンテナ内で systemd を立ち上げることもできるようになりました。
追記
ログイン時にも wsl -d Arch -- genie -i
を実行する方が良いかもしれません。
初回のWindows Terminal 起動時に wsl -d Arch -- genie -s
を実行すると、 systemd の起動が間に合わないからか failed to connect bus
といったエラーが出ることがあるためです。
2021/1/18 追記: 上記 issue は genie 1.31 で修正されました。
追記2
systemd インストール後、全パッケージを再インストールした方が良いかもしれません。 非 systemd 環境ではパッケージインストール時に dbus 関連でエラーが出るパッケージがあり、正しく設定されていないかもしれないためです。
$ pacman -Qqn | sudo pacman -S -
追記3
PKGBUILD に間違いがあった (10-genie-envvar.sh のインストール先がおかしかった) ため、修正しました。
Arch Linux on WSL2 で podman でコンテナを動かす
自分用メモ。
tl;dr
- GitHub - yuk7/ArchWSL: ArchLinux based WSL Distribution. Supports multiple install.
- Arch Linux on WSL2 を入れる。
scoop
使うと楽。
- Arch Linux on WSL2 を入れる。
- Podman - ArchWiki
/etc/sub{u,g}id
の設定が必要。設定する値は<user>:100000:65536
などとすると分かりやすい (コンテナのuid=1000
がホストのuid=101000
になり対応関係が一目瞭然)- 上記設定前に
podman
叩いちゃった場合はpodman system migrate
やpodman system reset
を実行する必要があるみたい
- How to run Podman on Windows with WSL2 | Enable Sysadmin
- Red Hat の記事。最後の方の
/etc/containers
の設定例だけ見れば良い。
- Red Hat の記事。最後の方の
pacman -S shadow
- ArchWSL インストール直後だと
/usr/bin/new{u,g}idmap
に capability (or setuid bit) が設定されていないため、これらのバイナリが含まれるshadow
パッケージを再インストールすることで設定させる 以下になっていればOK。capabilityがセットされていれば setuid bit の設定は不要。
$ getcap /usr/bin/new{u,g}idmap /usr/bin/newgidmap cap_setgid=ep /usr/bin/newuidmap cap_setuid=ep $ ls -l /usr/bin/new{u,g}idmap -rwxr-xr-x 1 root root 41K 9月 7 22:42 /usr/bin/newgidmap -rwxr-xr-x 1 root root 37K 9月 7 22:42 /usr/bin/newuidmap
- ArchWSL インストール直後だと
当時のツイート
wsl2+archlinux+podmanでコンテナ超簡単に起動できて良かった。daemon less なの最高
— 飲酒フィットアドベンチャー (@gifnksm) 2020年12月31日
hello-worldで満足してたら他のイメージはrootlessでまともに動かないことに気づいたけど、subuid/subgid設定 + chmod u+s /usr/sbin/new{u,g}idmap で順調に
— 飲酒フィットアドベンチャー (@gifnksm) 2020年12月31日
動くようになった
shadowインストールしなおしたら workaround 不要になった。archwsl の tar から展開したときに capability が吹っ飛んだっぽい?
— 飲酒フィットアドベンチャー (@gifnksm) 2020年12月31日
飲酒正月気分で殴り書きしてるから今読むと日本語がおかしい。
その他
podmanデフォルトrootlessだしもうdockerじゃなくてpodmanでいいじゃんって気分になっている
— 飲酒フィットアドベンチャー (@gifnksm) 2021年1月1日
dockerの定型起動オプションをdocker-composeの設定に雑にまとめておくのが良かったけどpodman-composeはこの使い方に適してなさそう (単独サービスの立ち上げに対応してない) ので、適当な代替物を探している (シェルスクリプト書けば良いというのはそう)
— 飲酒フィットアドベンチャー (@gifnksm) 2021年1月1日
- podman in podman も余裕やろと思ってやってみたら overlayfs on overlayfs (?) はできないみたいなエラーが出て挫折してしまった。
ググった感じ
/var/lib/containers
あたりを volume にすれば良さそうっぽいが試してない。 - podman をググると docker と違って英語記事ばかり出てくるのは良いことなのかどうなのか。
Red Hat のサブスクリプションがないと読めない記事ばっか出てくるのは辛い。Red Hat Developerに登録 (無料) すれば読めるとのこと。Shion Tanaka さん教えて頂きありがとうございます!Red Hat Developerに登録するとログインが必要なKCSも参照できます。もちろん登録は無料です。また開発用途のサブスクリプションも払い出されるのでRHELも使えます。
— Shion Tanaka (@tnk4on) 2021年1月3日
詳細は下記の記事を参照してご活用ください!https://t.co/04wXPR3Le3 - WSL2 で systemd を動かすかどうかは悩ましいところ。
以下を使えば簡単に systemd を使えるようになるらしいが、今のところ導入していない。
github.com
導入していない理由は以下。
- ホストマシンのシャットダウン時に systemd に対するシャットダウン操作が必要になるなど、WSL2の気軽さが損なわれてしまいそうな気がする。
- systemd の提供する仕組みと Windows の提供する init の仕組みがコンフリクトしたりしないかが心配。ネットワーク周りとか。
- 今のところ systemd なしでも困っていない。動かしたいサービスがあったら podman コンテナで代用できそう。 まあそのうち気が変わるかもしれない。
Rust の Index bounds check の性能影響を調べてみた
Rust の配列 (Vec<T>
や [T]
など) では、インデックスアクセス時に境界チェック (index bounds check) が常に行われます。
常に行われる、というのはビルドの種類 (リリースビルド・デバッグビルド) に依存しないという意味です。
とにかく安全性を重視する Rust らしい仕様であると言えます。
他のプログラミング言語に目を向けて見ると、常にチェックが行われるシステムプログラミング言語というのも珍しい気がします。 すべてがプログラム作成者の責任になる C では当然何のチェックも行われませんし、 安全なシステムプログラミング言語という意味で Rust と立ち位置が近い D では、デバッグビルドの場合のみ境界チェックが行われます (リリースビルドでは行われない)。
配列の境界チェックは実行時の処理になるため、普通に考えると Rust の方がチェック処理の分だけ実行性能が落ちてしまうことになります。境界チェックが行われない配列アクセス手段が用意されている とはいえ、配列アクセスが遅いのであれば Rust で書かれる様々なプログラムの実行性能も遅くなってしまわないか心配になってしまいます。
というわけで、Rust の配列アクセス性能を測定してみました。
tl;dr
Index bounds check のコストが支配的になるケースは少なそう。
注意
以下で出てくるベンチマーク結果は、仮想マシン上の Linux で測定したものなので実マシン上とは異なる結果になっている可能性があります。 また、筆者はアセンブリが読めず、CPUの気持ちも分からないため、コンパイル結果に関する見解は多くの誤りが含まれている可能性があります。 もし変な記述があれば、コメント等頂けると大変助かります。
テストに利用したソースコード一式は、以下Gistからどうぞ。
https://gist.github.com/gifnksm/9a792feff8567067b2d272c86258c2d4
利用したコンパイラは、nightly (2016/5/30) です。
$ rustc --version rustc 1.11.0-nightly (a967611d8 2016-05-30)
テスト1: 配列先頭からシーケンシャルアクセスする
まずは、シンプルなケースから。いくつかの関数について性能を比較します。
1. インデックスアクセスその1 (D index)
#[inline(never)] pub fn direct_index(arr: &[u64]) -> u64 { let mut sum = 0; for i in 0..arr.len() { sum += arr[i]; } sum }
libcoreのソース によると、arr[i]
へのアクセスで arr.len()
との比較により index bounds check が行われます (assert!
はリリースモードでも消えずに残ります)
arr.len()
との比較は Range::next()
でも行われている ため、コンパイラの最適化により arr[i]
の index bounds check は除去されるかもしれません
2. インデックスアクセスその2 (D index_len)
#[inline(never)] pub fn direct_index_len(arr: &[u64], len: usize) -> u64 { let mut sum = 0; for i in 0..len { sum += arr[i]; } sum }
i
の範囲を引数で渡すように変更したものです。arr[i]
の index bounds check が除去されずに残ることが期待値です。
3. イテレータでのアクセス (D iter)
#[inline(never)] pub fn direct_iter(arr: &[u64]) -> u64 { let mut sum = 0; for &n in arr { sum += n; } sum }
std::slice::Iter::next の実装によると、イテレータを使った場合はインデックスアクセスの場合と異なり、Cと同等の性能になることが期待されます。
4. C プログラム (D c)
uint64_t direct_c(const uint64_t *arr, size_t len) { uint64_t sum = 0; for (size_t i = 0; i < len; i++) { sum += arr[i]; } return sum; }
テスト1の測定結果
$ cargo bench test tests::D_c ... bench: 26,372 ns/iter (+/- 362) test tests::D_index ... bench: 26,389 ns/iter (+/- 373) test tests::D_index_len ... bench: 59,863 ns/iter (+/- 602) test tests::D_iter ... bench: 26,386 ns/iter (+/- 465)
1, 3, 4 (index, iter, c) はほぼ性能は同じです。objdump -d
の結果を見ると 1, 4 (index, c) はほぼ同じアセンブリが出力されていましたが、 3 (iter) は少し長めのアセンブリでした。
1, 2 の結果を比較すると、1の場合は想定通り、 arr[i]
の index bounds check は除去されているように思えます。
テスト1のまとめ
配列をシーケンシャルに先頭からアクセスしていく場合、 index bounds check のコストは無視して良いと思います。
先頭からアクセスする場合、多くのケースではイテレータを使うことが多いでしょう。また、スライスを渡す場合でも、スライスと len をペアで渡すよりも、&[..len] のように slice を slicing して渡すような書き方をする方が、引き渡す引数の数を減らすことができ、インタフェースがシンプルになるなどのメリットがあるためです。 slicing した場合、1と同じような性能になることが予想されます。
テスト2: 外部から指定された順序でインデックスアクセスする
配列先頭からシーケンシャルアクセスするのではなく、ランダムな順番など関数外から渡される順番に基づいて間接的 (?) にアクセスするケースについて測定します。
1. index bounds check ありの間接アクセス (I iter, R iter)
#[inline(never)] fn indirect_iter(arr: &[u64], idx: &[usize]) -> u64 { let mut sum = 0; for &i in idx { sum += arr[i]; } sum }
2. index bounds check なしの間接アクセス (I iter_nc, R iter_nc)
#[inline(never)] unsafe fn indirect_iter_uc(arr: &[u64], idx: &[usize]) -> u64 { let mut sum = 0; for &i in idx { sum += *arr.get_unchecked(i); } sum }
1 の index bounds check を行わない版です。
3. C プログラム (I c, R c)
uint64_t indirect_c(const uint64_t *arr, const size_t *idx, size_t idx_len) { uint64_t sum = 0; for (size_t i = 0; i < idx_len; i++) { sum += arr[idx[i]]; } return sum; }
テスト1の測定結果
$ cargo bench test tests::I_c ... bench: 60,888 ns/iter (+/- 1,113) test tests::I_iter ... bench: 69,746 ns/iter (+/- 1,124) test tests::I_iter_uc ... bench: 69,355 ns/iter (+/- 633) test tests::R_c ... bench: 162,968 ns/iter (+/- 14,711) test tests::R_iter ... bench: 169,162 ns/iter (+/- 1,470) test tests::R_iter_uc ... bench: 163,944 ns/iter (+/- 1,850)
I_XXX
はシーケンシャルにアクセスする場合、R_XXX
はランダムアクセスする場合です。
どちらのケースでも c, iter_uc, iter の順で高速です。
_iter
と _iter_uc
で実行速度に差はあるのですが、Cと差が生じているのは別の理由によるのかもしれません。
テスト2のまとめ
この例のように配列のインデックスを間接アクセスするケースは、配列の要素をランダムアクセスすることが多いと思いますが、その場合、 index bounds check ではない別の原因で大きく性能が落ちてしまう (キャッシュ関連?) ようです。 ですので、index bounds check だけをことさら取り上げる必要もないのかなぁ、と思います。
まとめ
考察が雑。
Xperia Z5の画面が割れてタッチに反応しなくなった時にやったこと
機種変更して1ヶ月くらいのXperia Z5の画面を割ってしまう事故を起こしてしまった時にやったことを記録がてらメモ。 同じような事故を起こしてしまった人の助けとなれば。。。
前提
電源を切る
携帯電話を目覚まし時計代わりに使っている方は多いと思いますが、タッチ操作が効かなくなってしまった場合、鳴り始めたアラームをとめることが出来ません。 特にスヌーズ機能を有効にしている場合、アラームはいつまでも鳴り続けるようです。 (朝、家を出るときにアラームをとめられず、諦めて家に放置した携帯電話が、夜帰宅して鳴り続けているのを見たときの絶望感ときたら。。。)
アラームをとめるために電源を切ろうとしても、電源ボタンを長押しするだけでは電源オフ、再起動等の選択肢が表示されるだけで、タッチ操作が要求されてしまいます、が、以下のいずれかの方法で、タッチ操作なしで電源を切ることが出来ます。
- 電源ボタンと、ボリューム増ボタンを同時に長押し
- microSDカード差し込み口横の緊急停止ボタンを押す http://csqa.kddi.com/posts/view/qid/k15102833051
電源+ボリューム増ボタン長押しの方が、カバーを開ける必要がなく楽です。
マウスを接続
タッチ操作が効かなくなった携帯の操作方法として、マウスを接続する方法が有名ですが、 Xperia Z5 の場合、電源オン状態でマウスを接続するだけではマウスを使う事が出来ません (通常時はUSBポートに通電されておらず、機器接続のためにいくつか操作する必要があるそうです)。
しかし、マウスを接続したまま端末を再起動すると、なぜか機器が認識され、ロック画面にマウスカーソルが表示されます。 タッチ操作が効かなくなった場合の救済措置でしょうか。
USB type A 接続のマウスはそのままではつなぐことは出来ないため、 USB type A メス <-> マイクロ"B" オス の変換コネクタが必要です。 マイクロUSBにもAとBのタイプがあるので、お間違えなく。 わたしは、間違えて無駄な変換コネクタを買ってしまいました。。。
機種破損時の交換サービスを利用する
わたしは au ユーザで、au 安心携帯サポートというサービスに入っていたため、以下から申し込みをすることで、実費負担2000円でリフレッシュ品の機種と交換することができました。
日付が変わる頃に申し込みをしたのですが、その二日後には交換用端末が届きました。申し込みとほぼ同時に発送してくれるようです。 壊れてしまった機種の交換もポストに投函するだけと非常に簡単でした。 もしこのサービスに加入されているのなら使わない手はないと思います。
なんかauの宣伝みたいですね。
データを移行する
ここまでの作業で新しい端末も手に入りますので、後は壊れてしまった端末からデータを移すだけです。 Xperia Transfer Mobile というアプリを使うと、Xperia間のアプリケーションのデータやホーム画面のアイコン配置までそっくりそのままコピーしてくれました。移行には結構時間がかかったのですが、環境がほとんどそのまま移動されるので便利です (一部アプリは移行できていなかったようですが)。
まとめ
携帯の破損でいろいろと苦労しましたが、最終的には問題解決して、元と同じ環境に戻すことができました。 戻せるとは言え、携帯を壊してしまうと面倒な作業が発生しますし、こんな作業はやらないで済むに越したことはないですね。。。
EmscriptenでRustをJavaScriptへコンパイルする
以前のエントリではうまくいかなかった、RustのJavaScriptへのコンパイルですが、
先日フォーラムに投稿されていたやり方をまねるだけで簡単にできるようになっていました。
Rust本体にEmscripten対応が取り込まれたのですが、動かすためには専用のLLVMが必要などいくつか制限があります。 制限があるとはいえ、以前よりも簡単にEmscriptenでのコンパイルが試せるようになったのは朗報ですね!
追記
リンクしか内容がない記事というのもアレなので、Emscripten を使ったコンパイル方法を簡単に書いておきます。
Emscripten の環境変数を設定する
$ source <path to emsdk>/emsdk_portable/emsdk_env.sh
rustc
を--target
オプションつきで呼び出す$ <path to rust root>/x86_64-unknown-linux-gnu/stage2/bin/rustc --target asmjs-unknown-emscripten ./hello.rs -O
以上。簡単ですね。
以下のプログラムをコンパイルしてみたところ、1MB程度の巨大なJavaScriptのコードが生成されました。ただの Hello world にしてはすごい量ですが、Emscriptenのランタイム (?) のソースコードの量が多いようです。
fn main() { println!("Hello emscripten world!"); }
-O
や、 -C lto
等の最適化オプションをつけることもできます。
最適化あり・なしの結果をGistに貼り付けてみました。
main関数はここから始まるようです。 なにがなんやら、、、といった感じですね。。。。
LTO有効だからといって、劇的にサイズが減るわけでもないようです。
EmscriptenでRustをJSへコンパイルする (コンパイルできてない)
2016/3/8追記
うまくいきました。エントリ書きました。
RustでEmscriptenを使う方法を調べたのでメモ。2015年12月28日時点でなんとか動作させるための手順です。今後オフィシャルにEmscriptenサポートが入るなどすると、もっと簡単なやり方になるかと思います。
Emscripten SDK をインストール
Emscriptenのサイト から Emscripten SDK をダウンロードしてきて、インストールします。
$ wget https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz $ tar xvf emsdk-portable.tar.gz $ cd emsdk_portable $ ./emsdk update $ ./emsdk install latest $ ./emsdk activate latest
Emscripten 対応の rust をコンパイル
brson/rust の emscripten
ブランチをクローン。
$ git clone git@github.com:brson/rust.git -b emscripten
なお、12/28時点でのコミットのハッシュは c42f32e
です。
当該ブランチの src/llvm
submodule は rust-lang
のものが読み込まれていますが、ハッシュ値は emscripten の LLVM fork (emscripten-fastcomp) のものとなっていますので、以下の変更を加えます
(.gitmodules
のコミット漏れ?)
diff --git a/.gitmodules b/.gitmodules index 83be070..5f92864 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "src/llvm"] path = src/llvm - url = https://github.com/rust-lang/llvm.git + url = https://github.com/kripken/emscripten-fastcomp.git branch = master [submodule "src/compiler-rt"] path = src/compiler-rt
ここまで準備できたらいよいよ configure
& make
です。以下の作業が必要なようです。
- Emscripten SDK の環境変数の有効化
- 最適化の無効化 (releaseモードだとうまく動作しないらしい)
$ source <path to SDK>/emsdk_env.sh $ ./configure --target=asmjs-unknown-emscripten $ make -j4
なお、手元の OSX 環境では configure
に失敗するため、EmscriptenのIssueコメントを参考にし、 CC=gcc CXX=g++
の環境変数を指定して configure
したところ、うまく動作しました (デフォルトコンパイラが clang の環境でEmscripten 側の clang が使われてしまうとだめなのかもしれません)。
configure: error: C preprocessor "/lib/cpp" fails sanity check
hello world! のコンパイル
謎のエラーが出て通らず。。。
$ LD_LIBRARY_PATH=../rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/ ../rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/bin/rustc ./main.rs --target asmjs-unknown-emscripten error: linking with `emcc` failed: exit code: 1 note: "emcc" "-L" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib" "main.0.o" "-o" "main.js" "-Wl,--gc-sections" "-nodefaultlibs" "-L" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib" "-Wl,-Bstatic" "-Wl,-Bdynamic" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib/libstd-ca1c970e.rlib" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib/libcollections-ca1c970e.rlib" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib/librustc_unicode-ca1c970e.rlib" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib/librand-ca1c970e.rlib" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib/liballoc-ca1c970e.rlib" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib/liballoc_system-ca1c970e.rlib" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib/liblibc-ca1c970e.rlib" "/home/nksm/tmp/rust-emscripten-c42f32e/x86_64-unknown-linux-gnu/stage2/lib/rustlib/asmjs-unknown-emscripten/lib/libcore-ca1c970e.rlib" "-l" "c" note: error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) error: Unknown attribute kind (45) Traceback (most recent call last): File "/home/nksm/dev/emsdk_portable/emscripten/master/emcc", line 1309, in <module> final = shared.Building.llvm_opt(final, link_opts, DEFAULT_FINAL) File "/home/nksm/dev/emsdk_portable/emscripten/master/tools/shared.py", line 1471, in llvm_opt assert os.path.exists(target), Failed to run llvm optimizations: + output AssertionError: Failed to run llvm optimizations: error: aborting due to previous error
うーん、なんでしょうこれは。
kcovを使ってRustプログラムのカバレッジを測定する
本記事は Rust Advent Calendar 2015 13日目の記事です。
前置き
Rust 1.5リリースおめでとうございます!!!
cargo install
の登場により、Rust製ツール群のインストールが飛躍的に楽になりました。最近のアップデートに追従できていなかったという方も、是非是非ダウンロードしてみてください。
背景
Rustには、 言語組み込みのユニットテスト機能 や、cargo test
のインテグレーションテストサポートなど、テストを実行させる方法は充実しているのですが、gcov相当のカバレッジ採取機能については、 2015年12月時点では残念ながらサポートされていません (RFCリポジトリにIssueはあります)。しかし、Rust にはプログラムのDWARF情報 (デバッグ情報) を生成する機能があるため、 kcov というプログラムを利用することでカバレッジを採取することが可能です。
本記事では、 kcov を利用して Rust プログラムのカバレッジを測定する方法について説明します。
kcov のインストール
各ディストリビューションでバイナリが配付されていない場合、ソースコードからビルドすることになります。 以下に載っているコマンドラインを参考にしてみてください。
- Ubuntuの場合
- CentOSの場合
Arch Linux の場合: AUR に kcov-git パッケージが存在するため、以下コマンドでインストール可能です (yaourt を利用している場合)。
$ yaourt -S kcov-git
kcov を使う
cargo test --no-run
でデバッグシンボルのついたテスト用実行ファイルを作成し、kcov 経由で実行します。
$ cargo test --no-run $ kcov target/cov target/debug/$TEST_EXECUTABLE
上記実行すると、 target/cov
ディレクトリ配下にカバレッジ測定結果を格納したHTMLファイルが生成されます。
デフォルトでは、 ~/.cargo
配下のファイル (Cargo.toml
で依存関係として指定したライブラリ) についてもカバレッジ測定対象となってしまうため、邪魔であれば以下のように --exclude-pattern
オプションを指定することで、カバレッジ測定対象を絞ることが出来ます。
$ kcov --exclude-pattern=/.cargo target/cov target/debug/$TEST_EXECUTABLE # multirust を使っている場合、以下 $ kcov --exclude-pattern=/.multirust target/cov target/debug/$TEST_EXECUTABLE
また、kcov
を複数回実行した場合、カバレッジ情報が蓄積されていきます。引数などの条件を変えて何度もコマンドを実行し、それらの結果を合算することなどが可能です。
$ kcov --exclude-pattern=/.cargo target/cov target/debug/$TEST_EXECUTABLE $ kcov --exclude-pattern=/.cargo target/cov target/debug/$TEST_EXECUTABLE --ignored
Coveralls との連携
Coveralls とは、 GitHub (BitBucketも?) で開発しているソースコードのテストカバレッジを表示してくれるサービスです。 Travis CI と連携して利用します。
kcov には測定したカバレッジを Coveralls へと送信してくれる機能があり、.travis.yml
上で kcov を以下のように呼び出すよう設定するだけで Coveralls を利用できます。
$ kcov --coveralls-id=${TRAVIS_JOB_ID} target/cov target/debug/$TEST_EXECUTABLE
ただし、Travis CI にはデフォルトで kcov はインストールされていないため、 .travis.yml
中で kcov をインストールしてやる必要があります。
language: rust sudo: false addons: apt: packages: - libcurl4-openssl-dev - libelf-dev - libdw-dev before_script: - | if [ "${TRAVIS_OS_NAME}" = "linux" ]; then export KCOV="${TRAVIS_BUILD_DIR}/kcov/build/src/kcov --exclude-pattern=/.cargo --coveralls-id=${TRAVIS_JOB_ID} ${TRAVIS_BUILD_DIR}/coverage" wget https://github.com/SimonKagstrom/kcov/archive/master.zip unzip master.zip mv kcov-master kcov mkdir kcov/build cd kcov/build cmake .. make cd ${TRAVIS_BUILD_DIR} fi script: - ${KCOV} target/debug/$TEST_EXECUTABLE
すこしめんどくさいですね。
travis-cargo を使う
すべての Rust プロジェクトの .travis.yml
に kcov のインストール手順を書くのは面倒だというあなたのために、 travis-cargo という便利ツールが公開されています。
rustdoc
の生成物を github.io
にアップロードするなど、カバレッジ生成採取の便利な機能も搭載されています。
一部、travis-cargo
では実現できないこともあるのですが (cargo test
で生成されるバイナリ以外のカバレッジ採取する場合など)、多くの場合、 travis-cargo
を使うのが便利だと思います。
最後に
筆者が公開している以下リポジトリでは、ビルド時にカバレッジを採取し、バッジとして README に表示しています。.travis.yml
の書き方の参考にどうぞ。
- gifnksm/srither: Rustで書かれたスリザーリンク (パズル) のソルバー。
travis-cargo
は使っていません。 - gifnksm/oauth-client-rs
- gifnksm/generic-matrix-rs
- gifnksm/union-find-rs
- gifnksm/twitter-api-rs
- gifnksm/topological-sort-rs
- gifnksm/ProjectEulerRust
- gifnksm/polynomial-rs