日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

Cargo で gcc のフラグを任意に渡す方法→cargo rustc からのみ可能(2016-05-08修正)

要は WindowsGUI サンプルコードをお試しで動かすと コンソールも一緒に立ち上がってたのが嫌だったので調べると見つかった。

stackoverflow.com

で、このフラグを Cargo から渡す方法はないか調べた。

確認環境

  • Win7 64bit
  • Rust Stable 1.8.0 GNU ABI

2016-05-08修正

申し訳ない、最初に書いた記事は自分の誤読で案について話しているだけで Cargoの設定ファイルで gcc へ任意のオプションを渡す方法は現時点では存在しておりませんでした。

ただ、cargo rustc からであれば渡せます。

cargo rustc --release -- -C link_args=-Wl,--subsystem,windows

これは簡単なツール作って Windows エクスプローラから exe 起動して コマンドプロンプトが出なくなったのを確認済。

修正前の記事で紹介した issue#544の件 のはまさしくこの cargo rustc での機能を実装したよと言う事のみでした。すみません。


↓で見つけた。 github.com

やり方は簡単で、対象のプロジェクトの Cargo.toml に以下の一文を追加する。

[rustc]
flags="-C link_args=\"-Wl,--subsystem,windows\""

MSVC ABI だとこの現象が発生しないのかは未確認。

ちなみにissue#544はcargo rustcタスクでコマンドラインからオプションとして渡す方法がメインの話なのだが、 こちらでやると 前の記事の方法で設定した 追加ライブラリサーチパスが有効にならない。あくまでcargo runorcargo buildした時だけって事なのかな。

Cargo でグローバルにライブラリサーチパスを追加する方法

Cargo で C ネイティブライブラリをリンクさせる為に 配置されているパスを Cargo に明示的に指示したい場合どうするか。


確認環境*1

  • Win7 64bit
  • Rust Stable 1.8.0 GNU ABI

プロジェクト単位での設定方法

探すと Qiita でこんな記事が見つかった。

qiita.com

グローバルに設定する方法

今回自分がやりたいのはこちら。

先に紹介した方法でもいいんだけど特に自分のような Windows ユーザだと MSVC/MinGW/Cygwin とかで配置する場所の慣習が変わってくるのと ほぼマルチユーザで使う事はないのでライブラリをインストールしたら プロジェクト単位すら面倒でグローバルに設定したくなる。

で Cargo のドキュメント探してたらあった。

Cargo は設定ファイルを記述する事が可能らしく *2cargo runcargo build 実行時はまずその設定を読み込んでくれるらしい。

自分は %HOME% を設定してるので、%HOME%.cargo\config ファイルを配置。 そしてライブラリサーチパスの設定を以下のように書いた。 *3

[target.native]
rustc-link-search = ["C:/path/to/lib"]

["C:/path/to/lib"] は自分がリンクさせたいライブラリが存在するパスを指定する事。

参考

一度このドキュメントはどこかでちゃんと読み込んでおきたいかな。 Tomlファイル自体の形式は分かりやすいんだけど何をどう設定できるのかがあんまり把握できてないし。


*1:しつこいようだけど Rust は情報追っかけてると 6week リリースするとか更新早そうなのと今はともかくこれまでは破壊的変更が何度もされてきたみたいなのでRust 関連記事は今後も極力確認した環境をまず書いていこうと思う

*2:英語だけどパスの一覧がリストにあるので場所は分かると思う

*3:例にあるようにパス区切りは / でも行ける。Windows区切りだと \\ と書く必要があり少し見づらくなるのでRust の場合は / で行った方が良さそう。

Rust で try! マクロが上手く動かなかった

Rust 絶賛学習中なんだけども、try! を 使用したサンプルが動かなかったのでメモとして残す。 原因はちゃんと追ってないが、状況と回避方法だけ。 (と言っても try! マクロ使わないようにするってだけだけど)

2016-05-08 追記

原因分かった。未読だったエラーハンドリング を読んでtry! の API docでのサンプル見てやっと理解した。

Result を返す関数の中で記述を簡略化する為のものなのね。 Rust の main は戻り値が無い(()を返す) 所に try!マクロ内の処理が失敗すると return Err が返るようになってるからコンパイルエラーになると。

けどそれならそれでサンプルは何がしかの Result を返す関数内で使うサンプルにすべきじゃないのかなあ。 まだまだ勉強が足りんかな。

と言うわけで単にtry!を使わないようにすると言うのはプロトタイピングレベルじゃなければやらないようにすべきなので 冒頭のは完全に消しはしないけど打ち消し線つけさせておいてもらいます。

環境

> rustc --version
rustc 1.8.0 (db2939409 2016-04-11)

状況

ファイル IO をやってみたくて API doc のサンプルコンパイルするとコンパイルエラー発生。

fn main() {
    use std::io::prelude::*;
    use std::fs::File;
    
    let mut buffer = try!(File::create("foo.txt"));
    
    try!(buffer.write(b"some bytes"));
}
<std macros>:5:8: 6:42 error: mismatched types:
 expected `()`,
    found `core::result::Result<_, _>`
(expected (),
    found enum `core::result::Result`) [E0308]
<std macros>:5 return $ crate:: result:: Result:: Err (
<std macros>:6 $ crate:: convert:: From:: from ( err ) ) } } )
main.rs:37:22: 37:51 note: in this expansion of try! (defined in <std macros>)
<std macros>:5:8: 6:42 help: run `rustc --explain E0308` to see a detailed explanation
<std macros>:5:8: 6:42 error: mismatched types:
 expected `()`,
    found `core::result::Result<_, _>`
(expected (),
    found enum `core::result::Result`) [E0308]
<std macros>:5 return $ crate:: result:: Result:: Err (
<std macros>:6 $ crate:: convert:: From:: from ( err ) ) } } )
main.rs:39:5: 39:39 note: in this expansion of try! (defined in <std macros>)
<std macros>:5:8: 6:42 help: run `rustc --explain E0308` to see a detailed explanation
error: aborting due to 2 previous errors

エラーメッセージ見るとマクロ展開時に期待する型じゃないとか言われた。 けど API doc じゃ File::createは Result 返すし try! マクロ も Result を返すもので使うみ たいな事書いてあるのに、エラーメッセージはどうもそうなってない。

とりあえず try! を使用しないようにすればファイル書き込みはいけた。

fn main() {
    use std::io::prelude::*;
    use std::fs::File;

    let mut buffer = File::create("foo.txt").expect("cannot create file.");

    buffer.write(b"some bytes");
}

マクロのコードも追っかけてみたいけどまだ Rust でのマクロの作り方は全く学習してないので保留。

Rust 開発環境構築 - Eclipse で構築してみた

LifeTime や Trait、関数型プログラミング的な事も可能らしいシステムプログラミング言語辺りの ワードが気になって Rust を始めてみようと思いたった。

で、まずは環境構築からと言う事で探してみた。


開発環境選定

タイトルにも入れてるが今回は Eclipseプラグインで提供されている RustDT を選ぶことにした。 デバッグ機能が頭一つ抜けていると思ったため。

定義ジャンプ・コード補完・色付け辺りは大体どの環境も備えてて、gdbコマンドラインでガシガ シ使える人ならどれを選んでもそんなに機能差は無いと思う。

以下各環境を少し調べた雑感。

Emacs

  • 個人的には手を切りたいエディターというのはさておき
  • 例によって色々モードはあり設定も今はさほど面倒でもない
  • GDB とのデバッグ連携もかなり GUI っぽくなってるんだが 見た目がもはや他の IDE よりも煩雑になってる感じなのでスルー

IntelliJ

  • こちらもプラグインがあるのだが軽く情報を探った感じデバッガ連携が見当たらなかったので見送り
  • サイトを見ただけで試してはいない

VisualStudio

  • Visual Rust というプラグインがある
  • が、補完が何故か予約語しか効かないので見送った
  • デバッガ連携可能かどうかは未確認

Eclipse

  • RustDT というプラグインがあり、gdb との連携も可能らしい
  • 他の環境が備えている機能は大体持っている
  • 今回はこれを試した

他にもいくつか見かけたが未確認。

事前環境

  • MinGW-w64
    • RustDT で gdb を使うのに必要
    • 自分は Git for Windows SDK 1.0.3 経由で入れた
      • 時間はややかかるが必要なの一式ほぼ勝手に入れてくれるので楽
  • Rust 1.8.0 GNU ABI
    • Windows だと MSVC ABI もあるのだが、GNU ABI でないと RustDT が連携している GDBデバッグ情報を認識してくれない為
  • racer
    • コード補完を受け持つ外部ツール
    • 一番楽なのは Rust 入れた後 cargo install racer とコマンド打つだけ
  • Rust のソースを DL、環境変数 RUST_SRC_PATH にソースを展開したパスを指定してある事
    • racer で必要

結構事前準備が必要なんだけどどれもさほど面倒でもないのと記事を書いた時点では 既に導入してしまっていたので割愛。

Eclipse, RustDT インストール

とは言っても、既に ブログ記事 があったので基本はそこに従った。

ただ、事前環境でも書いたが、Rust は GNU ABI の方を入れる事。

デバッグ動作確認

こんなコードを書いた。*1

fn main() {
    let a = 10;
    let b = 20;
    let c = "こんにちわ";
    
    println!("{}, {} 年後の世界!", c, add(a, b));
}

fn add(x: i32, y: i32) -> i32 {
    x + y
}

んで2行目にブレークポイント設定してデバッグ実行。ブレークポイント設定は行表示している辺りを ダブルクリックすれば設定される。

んでブレークした所にも止まるし、ステップ実行も可能なんだけど変数を見る時 数値はいいんだが文字列は C/C++ 同様パッと見た感じさっぱり分からん。

ここ に書かれていた事を頼りに gdb の Rust pretty-printer を導入してみたが 文字列は対応してないっぽい。

文字列の簡単なデバッグ方法が今後の課題だけどとりあえずは しばらく RustDT を使いつつ勉強してみよう。

*1:何も調べず書いたけどはてなブログって既に Rust のシンタックスハイライトも対応してたのね

Dungeon Crawl Stone Soup 0.17.1(英語版)をビルドしてみた

ちょっと思う所があって PC フリゲのローグライクの一つである Dungeon Crawl Stone Soupをソースからビルドして上手く行ったので その時の環境をメモしておく。

  • OS は Win7 Pro 32bit
  • ソースは 公式に置かれてる Full source with dependencies のリンクから DL
    • 試したバージョンは 0.17.1。
  • ビルド方法は MinGW。手順は INSTALL.txt に書かれている事そのまま実行するだけ。

ソースビルド上手く行ったよって記事は賞味期限めちゃくちゃ短い気がするけどまあ。 英語だけどソースDLすれば本当にちゃんと手順が書かれてるので細かい事はここでは書きません。 繰り返しだけどソースビルド関係は特に腐るの早そうだし。

cider での TDD っぽいワークフロー

ワークフロー

  1. REPL 起動(C-c M-j) ※ 1 回のみ
  2. 何か関数書く
  3. REPL 上でテスト
  4. ok なら保存・リロード(C-x C-s, C-c C-n, C-c C-k)
  5. REPL 上で実行したコードと結果を = とした is フォームにコピペし、 適当な名前を付けて保存・リロード(C-x C-s, C-c C-n, C-c C-k)
  6. C-c ,でテスト

2-6 を繰り返す。

TDD と言うよりは REPL で試した結果を残すようにすると言った方が正しいかも。

改善したい部分や気になる所