読者です 読者をやめる 読者になる 読者になる

Ubuntu 16.04ではncurses 6.0のコンパイルに失敗する

サーバ

Ubuntu 16.04はgccのバージョンがデフォルトで5.4なので、そのままncursesをコンパイルしようとすると以下のように怒られます。

In file included from ./curses.priv.h:325:0,
                 from ../ncurses/lib_gen.c:19:
_30507.c:843:15: error: expected ‘)’ before ‘int’
../include/curses.h:1631:56: note: in definition of macro ‘mouse_trafo’
 #define mouse_trafo(y,x,to_screen) wmouse_trafo(stdscr,y,x,to_screen)
                                                        ^
Makefile:962: ターゲット '../objects/lib_gen.o' のレシピで失敗しました
make[1]: *** [../objects/lib_gen.o] エラー 1

解決策はいろいろありますが、ncurses-6.0/ncurses/base/MKlib_gen.shを直接修正するのが美しいと思います。ネットに転がっているものとほぼ同じですが、ncurses-6.0-gcc5.patchというパッチを作っておきましたのでご自由にどうぞ。

github.com

cd ncurses/base/
patch < ncurses-6.0-gcc5.patch

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

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

関数定義の前に、まずは変数宣言から見てみましょう。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
}