/home/user/localにVimをインストール

もう11月とな。道理で寒いわけだ。

会社のサーバを弄るとき、インフラチームでない私にはroot権限がないので、最初は何かと不便を強いられていました。が、1ヶ月もすると慣れてくるもので、むしろroot権限なんて必要ないよなぁ、と思うように。慣れって怖い。いや、rootを自由自在に触れる環境のほうが怖いか。

root権限がないので、「あれがないこれがない」に対応するには自分でホームディレクトリにインストールするしかないです。ソースを落として、configureして、makeして、make installして、と。configure --helpを読みながらやらざるを得ないので、正直言って最初は面倒です。途中でハマったりしてググったとしても、パッケージ管理システムを使ってインストールする記事ばかりですし。

閑話休題。そんなこんなで、新しい開発サーバに入るときなどは一からVimを入れたりする必要があるので、その他諸々(lynxとctags)をインストールするためのスクリプトを(会社の先輩が作ったものをパクって)書いてみました。Gitから最新のVimを取ってくるので、今現在だとVim 8.0がインストールされることになります。

#!/bin/bash
set -Ceu

install_dir="$HOME/local"
work_dir="${HOME}/work"

make_install_dir() {
    if [ ! -d "$install_dir" ]; then
        mkdir -p "$install_dir"
    fi
}
make_work_dir() {
    if [ ! -d "$work_dir" ]; then
        mkdir -p "$work_dir"
    fi
}

set_env() {
    # luajitの実行パスがわからないとvimのconfigureでこけるため
    export PATH="$install_dir/bin:$PATH"
}

install_lua() {
    cd "${work_dir}/"
    echo 'install lua'
    if [ ! -d "${work_dir}/luajit-2.0" ]; then
        git clone http://luajit.org/git/luajit-2.0.git
    else
        cd "${work_dir}/luajit-2.0"
        git checkout master
        git fetch --prune
        git pull --prune
    fi
    cd "${work_dir}/luajit-2.0"
    make PREFIX=$install_dir && make install PREFIX=$install_dir && make clean
}

install_ncurses() {
    cd "${work_dir}/"
    echo 'install ncurses'
    if [ ! -f "${work_dir}/ncurses-6.0.tar.gz" ]; then
        wget http://ftp.yz.yamagata-u.ac.jp/pub/GNU/ncurses/ncurses-6.0.tar.gz
    fi
    tar -zxvf ncurses-6.0.tar.gz
    cd "${work_dir}/ncurses-6.0"
    ./configure --prefix=$install_dir --without-cxx-bindings --with-shared
    make && make install && make clean
}

install_vim() {
    cd "${work_dir}/"
    echo 'install vim'
    if [ ! -d "${work_dir}/vim" ]; then
        git clone https://github.com/vim/vim.git
    else
        cd "${work_dir}/vim"
        git checkout master
        git fetch --prune
        git pull --prune
    fi
    if [ -f ${work_dir}/vim/src/auto/config.cache ]; then
        rm "${work_dir}/vim/src/auto/config.cache"
    fi
    cd "${work_dir}/vim/src"
    export LDFLAGS="-L$install_dir/lib"
    ./configure --with-local-dir=$install_dir \
        --with-features=huge \
        --enable-luainterp=dynamic \
        --with-luajit \
        --with-lua-prefix=$install_dir \
        --with-tlib=ncurses \
        --prefix=$install_dir
    make && make install && make clean
}

install_lynx() {
    cd "${work_dir}/"
    echo 'install lynx'
    if [ ! -f ${work_dir}/lynx2.8.8rel.2.tar.gz ]; then
        wget http://invisible-mirror.net/archives/lynx/tarballs/lynx2.8.8rel.2.tar.gz
    fi
    tar -zxvf lynx2.8.8rel.2.tar.gz
    cd "${work_dir}/lynx2-8-8"
    ./configure --prefix=$install_dir --with-curses-dir=$install_dir
    make && make install && make clean
}

install_ctags() {
    cd "${work_dir}/"
    echo 'install ctags'
    if [ ! -d ${work_dir}/ctags ]; then
        git clone https://github.com/vim-jp/ctags.git
    else
        cd "${work_dir}/ctags"
        git checkout master
        git fetch --prune
        git pull --prune
    fi
    cd "${work_dir}/ctags"
    autoheader
    autoconf
    ./configure --prefix=$install_dir
    make && make install && make clean
}

main() {
    make_install_dir
    make_work_dir
    set_env
    install_lua
    install_ncurses
    install_vim
    install_lynx
    install_ctags
}

main

あと、環境変数はなるべく変更したくないので、例えばインストールしたVimを使う場合は、

echo 'alias vim="LD_LIBRARY_PATH=$HOME/local/lib $HOME/local/bin/vim"' >> .zshrc

などエイリアスさせています。まあPATHくらいならログインシェルの設定に追加しておけばいいけども。

これで安心して、今年のクリスマスはVim 8.0と一緒に過ごせますね(ぉ

github.com

追記

zshをコンパイルするときに怒られたので、ncursesを-fPICオプションでコンパイルするように「--with-shared」をconfigureに追加。

また、Ubuntu 16.04でncursesをコンパイルできない場合は以下を参考に。

tydk27.hatenadiary.com

Rust入門 - 変数宣言と関数定義

Rustは強い静的型付け言語なので、関数定義もそれを意識しておこなう必要があります。

関数定義の前に、まずは変数宣言から見てみましょう。Rustは型推論してくれるので、以下のように型を付けなくてもエラーにはなりません。が、やっぱり明示的に型を指定したいところ。

というわけで厳密に書くとなると、以下のようになるわけですが最初はなかなか慣れないと思います。

let foo: i32 = 100;

let 変数名: 型 = 値; という意味。数値型の場合は符号付きか否か、何ビットかでそれぞれ型名が異なってきます。ここでのi32は、符号付き32ビット、のこと。

C系など、静的型付け言語を普段から書いている人ならなんてことはないですが、Web系言語では意識することがあまりなかったりするのでちょっと取っ付きにくいかもです。

関数定義も似たような感じになります。与えられた数値に100を加えて返す関数を書いてみます。

fn add_hundred(foo: i32) -> i32 {
    foo + 100
}

見た目が少しアレですが、こういうもんだ、ということにしておきます。

引数がある場合、変数宣言のように型を省略するとコンパイルエラーになってしまうので注意です。なるほど、確かに引数は型推論ではなくて明示的に決めたいよね。でないと、関数内で意図しないバグが大量発生することがあるので。

他でもよく見られるように値を返すときはreturnが使えますが、Rust的には非推奨だそうです。なぜか、についてまとめてみます。

Rust推奨の書き方は、上の例のようにアロー演算子みたいに書くようです。何を渡して何が返ってくるかが一目瞭然ですね、これ好き。

さて、中身を見て気付くかと思われますが、セミコロンがないですね。セミコロンを付けなくてもいい、ではなく、セミコロンを付けたらダメ、なのです。試しにセミコロンを付けてコンパイルするとエラーになります。

error: not all control paths return a value
fn add_hundred(x: i32) -> i32 {
    x + 100;
}

help: consider removing this semicolon:
    x + 100;
           ^

ここからわかるのは、Rustが文ではなく式ベース言語であるということと、セミコロンの意味が他言語とちょっと異なるということです。なるほどわからん。

式と文についておさらい。式は値を返すもの、文はそうでないもの、ですよね。Cの勉強をしていると、式の末尾にセミコロンを付けたものが文である、なんて説明をよく見かけますが…まぁいいや。

上の例の関数は返り値をi32の型と定義しています。しかしこれにセミコロンを付けてしまうと、i32ではなく空を返そうとするわけです。Rustはそこを察知し、なんか違う!とコンパイルエラーを出してくれるみたい。

ここでreturnの話に戻ると、returnはもちろん文になるのでセミコロンを付ければ正しく値を返せますが、式ベースのRustとしてはちょっと違くね?ということで非推奨みたいです。わかったような、わからないような。

兎にも角にも、Rustが非推奨しているなら使わないほうがいいです。今は非推奨でも将来的には仕様自体が消え去ることだってあるのだし。もちろん逆もあるけども。

ということで、以下のようになりました。

use std::io;

fn main() {
    // 関数へのポインタは普通に代入演算子でいけます
    let f = add_hundred;
    // ちなみに省略せずに書くと
    // let f: fn(i32) -> i32 = add_hundred;

    let x = f(100);
    println!("the value is: {}", x);
}

fn add_hundred(foo: i32) -> i32 {
    foo + 100
}

Rust入門 - 開発環境構築

我らが敬愛するMozillaによって開発中のRust、そのバージョン1.10.0がリリースされました。RustのRustによるRustな言語になったわけです(ぇ Rustの言語仕様だとかミッションポリシー的なことはググればいろいろ出てくるのでここでは割愛します。が、簡単に言うと安全なC++(異論は認める)という感じです。システムレベルの低層プログラ厶をゴリゴリ書けます。

開発環境構築からHello Worldまでの手順をまとめてみました。

インストールは超簡単。公式通りにやります。

% curl https://sh.rustup.rs -sSf | sh
% rustc --version
% export PATH="$HOME/.cargo/bin:$PATH"

さて、これでコーディングしてコンパイルして実行、までできるようになりました。が、当然のようにVimで開発するわけでして、シンタックスハイライトと補完くらいはできたらいいよね、ということでプラギンを入れていきましょう。今回はrust-lang/rust.vimracer-rust/vim-racerの2つを入れます。

ただ、単純にプラギンを入れればおkではなく、今回使わせてもらうプラギンにはRustのライブラリが別途必要になります。となるとパッケージマネージャーなどが欲しくなりますよね。ご安心ください。Rustインストール時にCargoというパッケージマネージャーがついてくるので、これを使えば問題ないです。cargoコマンドで普通にインストールしていきます。インストール後にパスを通しておくのを忘れずに。

% cargo install racer
% rustup component add rust-src
% export RUST_SRC_PATH="$HOME/.multirust/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src"
% cargo install rustfmt

RUST_SRC_PATHはvim-racerで使います。環境によってパスは異なるので注意。

ライブラリのインストールが終われば、あとはいつものように.vimrcでバンドルして、コンフィグをちょろっと書くだけ。以下は参考に。

let g:rustfmt_autosave = 1
let g:rustfmt_command = $HOME . '/.cargo/bin/rustfmt'

set hidden
let g:racer_cmd = $HOME . '/.cargo/bin/racer'

これで準備はおしまい。一先ずHello Worldしましょう。Rustの拡張子はrsなので、ブロジェクトディレクトリ下にsrcディレクトリを作り、main.rsなどとして作ってみることにします。ファイル名はhello_world.rsとかでも大丈夫ですが、アンダーバー記法推奨です。

% mkdir -p project/rust/hello_world/src
% cd project/rust/hello_world
% vim src/main.rs
fn main() {
    println!("Hello, world!");
}

見ての通りですが、どことなくCっぽい。うんうん、システムプログラミングはこうだよね(ぇ

main.rsをrustcコマンドでコンパイルすれば実行ファイルが出力されます、が、これは一旦忘れて別の方法でコンパイルします。そこで使うのが、パッケージマネージャーでもありビルドツールでもある先程のCargoになります。上記ライブラリのインストールに結構時間がかかったと思いますが、それはライブラリをコンパイルしていたからだったんですね。

Cargoでコンパイルするときは、コンフィグファイルを用意します。ファイル名はCargo.tomlです、Cは大文字。どことなくiniファイルに似ている。このコンフィグファイルには依存ライブラリ等も記述できます。なので、GitHubからクローンしたプロジェクトなどにローカルインストールしていないライブラリがあったとしても、build時にそれらも一緒にコンパイルしてくれるようになります。npm install的な、あんな感じです。そこらの話はまた別の機会に。

% vim Cargo.toml
[package]

name = "hello_world"
version = "0.0.1"
authors = [ "hoge piyo <hoge@piyo.com>" ]

コンフィグファイルができたら、buildを叩く。

% cargo build

buildが終わったら./target/debug下に出力されています。ちなみにreleaseオプションをつけることで最適化してくれます。あとは通常通り、実行ファイルを叩けば標準出力にHello Worldされます。が、ここではcargoを使って実行してみようと思います。

% cargo run

runはbuildと実行を一発でやってくれる便利コマンドで、rebuildが必要なときはbuildして実行、必要ないときは純粋に実行だけしてくれるのです。runでrebuildもしてくれるからといって、何でもかんでもrunで走らせるのはアレなので、build→runの手順を順守する癖を付けといたほうがいいかもです(別にRustだけに限った話ではないのだけども)

ちなみにcargoでtomlファイル含めてディレクトリもいろいろサンプルを自動生成してくれます。

cargo new hello_world --bin

とりあえずここまで。ハマるポイントは特にないですね。

2017/03/20 追記

バージョン1.16用に修正