ソースから自前ビルドしたソフトウエアの効率的な管理方法

ぼくは長年こういう方法で管理してますよ、というお話です。Linuxです。

ディレクトリレイアウト概観

たとえば、asoとbmdという名前のソフトウエアをインストールしている状態はこんな感じ:

/usr/local/ ┬ app/ ┬ aso  → aso-1.3
            │      ├ aso-1.2/ ┬ bin/ ┬ armored
            │      │          │      └ scrum
            │      │          ├ sbin/ ─ syd
            │      │          └ share/ ─ man/
            │      ├ aso-1.3/ ┬ bin/ ┬ armored
            │      │          │      └ scrum
            │      │          ├ sbin/ ─ syd
            │      │          └ share/ ─ man/
            │      ├ bmd  → bmd-2.0
            │      └ bmd-2.0/ ┬ bin/ ─ tri
            │                  ├ include/ ─ angle.h
            │                  └ lib/libsnk.so.2.0.0
            ├ bin/ ┬ armored → ../app/aso/bin/armored
            │      ├ scrum   → ../app/aso/bin/scrum
            │      └ tri     → ../app/bmd/bin/tri
            ├ include/ ─ bmd → ../app/bmd/include
            └ sbin/ ─ syd    → ../app/aso/sbin/syd
  • /usr/local/app/NAME-VERSION の下に一式インストールする
  • /usr/local/app/NAME は /usr/local/app/NAME-VERSION への symlink
  • /usr/local/app/NAME/bin/* については、/usr/local/bin/ の下に symlink を作る
  • /usr/local/app/NAME/sbin/* についても同様、/usr/local/sbin/ の下に symlink を作る
  • /usr/local/app/NAME/include については、
    • /usr/local/app/NAME/include/*.h のタイプの場合、/usr/local/include/NAME に /usr/local/app/NAME/include への symlink を作る
    • /usr/local/app/NAME/include/NAME/*.h のタイプの場合、/usr/local/include/NAME に /usr/local/app/NAME/include/NAME への symlink を作る
  • /usr/local/app/NAME/lib については、echo "/usr/local/app/NAME/lib" > /etc/ld.so.conf.d/NAME.conf する (/usr/local/lib/にはsymlinkを張らない)
  • /usr/local/app/NAME/share/man については、vi /etc/manpath.configしてMANDATORY_MANPATH /usr/local/app/NAME/share/man を追加する (/usr/local/share/man/にはsymlinkを張らない)

新規にインストールする場合の具体手順

次に具体的な手順。

"neo"という名のソフトウエアを新規にインストールする場合の手順。

tar zxf neo-1.0.tar.gz
cd neo-1.0
./configure --prefix=/usr/local/app/neo-1.0 --sysconfdir=/usr/local/etc/neo
make
make install

ln -snf neo-1.0 /usr/local/app/neo

cd /usr/local/bin/    && ln -sf  ../app/neo/bin/*  .     # bin/*  があれば
cd /usr/local/sbin/   && ln -sf  ../app/neo/sbin/* .     # sbin/* があれば
ln -snf ../app/neo/include /usr/local/include/neo        # include があれば
echo "/usr/local/app/neo/lib" > /etc/ld.so.conf.d/neo.conf # lib があれば
ldconfig
vi /etc/manpath.config                                     # share/man があれば
  "MANDATORY_MANPATH  /usr/local/app/NAME/share/man" を追加

後半の symlink 張りとかがめんどくさいですが、一度きりの作業(後述)なので気にしない。

異なるバージョンのをインストールする場合の具体手順

つづいてニューバージョンの neo-1.1 をインストールする場合。このへんからがミソです。

tar zxf neo-1.1.tar.gz
cd neo-1.1
./configure --prefix=/usr/local/app/neo-1.1 --sysconfdir=/usr/local/etc/neo
make
make install

ln -snf neo-1.1 /usr/local/app/neo

基本的に、/usr/local/app/neo の symlink を張り替えるだけで OK。なぜなら、新規のときの bin, sbin, include, lib, man の symlink やパス指定は、全て /usr/local/app/neo-VERISON ではなく /usr/local/app/neo というパスを使っているため。

もし、neo-1.1 でなにか不具合があった場合には、前の neo-1.0 を指す symlink に張り直すだけで切り戻しが完了するのでラクチン。

このような2段階のsymlinkは、Debian系の/etc/alternatives/などでも見られる手法ですね。

$ readlink /usr/bin/vim
/etc/alternatives/vim
$ readlink /etc/alternatives/vim # ここのsymlinkがつけかわる
/usr/bin/vim.gnome
$ readlink /usr/bin/vim.gnome
# (実体)

あと、他のマシンにも neo-1.1 をインストールする場合、構成が一緒なら、rsync -avR /usr/local/app/neo /usr/local/app/neo-1.1 OTHERHOST:/ で完了するのでラクチン。


つまり、初回インストール時は symlink 張りなどちょいとめんどい(でも一度きり)ですが、バージョンアップ時(こっちは何度かあるはず)には手順が非常に少なくて済む管理手法というわけです。

おまけ

./configure のヘルパースクリプト

configureのオプション指定も、こんなヘルパースクリプトを用意して簡単にしています。

#!/bin/sh
# /usr/local/src/conf/generic

. /usr/local/src/conf/common
decide_version "$@"

[ -z "$SYS_HOME" ] && SYS_HOME=/usr/local
CONFIGURE_OPT="--sysconfdir=${SYS_HOME}/etc/${APP}"

[ "$1" = "-0" ] && CONFIGURE_OPT=

myconfigure \
  --prefix=/usr/local/app/${APP}-${VER} \
  $CONFIGURE_OPT \
  ;
# -*- mode: sh; -*-
# /usr/local/src/conf/common

#_CFLAGS='-march=pentium3 -O3 -pipe -fomit-frame-pointer'
_CFLAGS='-O2 -pipe -fomit-frame-pointer'

myconfigure() {
  {
    echo env CFLAGS="${_CFLAGS}" ./configure "$@";
    echo
    env CFLAGS="${_CFLAGS}" ./configure "$@";
  } 2>&1|tee _configure.log
}

debugconfigure() {
  {
    echo env CFLAGS="-O0 -g" ./configure "$@";
    echo
    env CFLAGS="-O0 -g" ./configure "$@";
  } 2>&1|tee _configure.log
}

decide_version() {
  VER=${PWD##*-}
  APP=${PWD##*/}; APP=${APP%%-*}
  case $VER in
    [0-9]*) ;;
    *) VER=;;
  esac
  case $1 in
    [0-9]*)
      VER=$1
      shift
      ;;
  esac

  if [ -z "$VER" ]; then
    echo -n "input version string> "
    read VER
  fi
  echo "APP=$APP"
  echo "VER=$VER"
  read -t 8 -p 'hit any key' dummy
}
$ cd neo-1.2
$ /usr/local/src/conf/generic
--prefix=/usr/local/app/nao-1.2  # よきにはからってくれる
--sysconfdir=/usr/local/etc/neo
hit any key
...(snip)...
$ make

固有のオプションをいろいろ指定したいときは、conf/generic を conf/nginx とかにコピーしてもろもろ指定してます。

#!/bin/sh
# /usr/local/src/conf/nginx

. /usr/local/src/conf/common
decide_version "$@"

[ -z "$SYS_HOME" ] && SYS_HOME=/usr/local

myconfigure \
  --prefix=/usr/local/app/nginx-${VER} \
  --conf-path=$SYS_HOME/etc/nginx/nginx.conf \
  --pid-path=/var/run/nginx/nginx.pid \
  --lock-path=/var/run/nginx/nginx.lock \
  --error-log-path=/var/log/nginx/default.err \
  --http-log-path=/var/log/nginx/default.acc \
  --http-client-body-temp-path=/var/tmp/nginx/req \
  --http-proxy-temp-path=/var/tmp/nginx/proxy \
  --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi \
  --without-mail_pop3_module \
  --without-mail_imap_module \
  --without-mail_smtp_module \
  --with-md5-asm \
  --with-sha1-asm \
  --with-http_stub_status_module \
  --with-http_realip_module \
  ;
debとかrpmパッケージにしないの?

わざわざパッケージをこさえる利点が自分には見いだせないので、debやrpmパッケージにはしてません。

なんでソースを自前ビルドするの? ディストリのバイナリパッケージ使わないの?

バイナリパッケージはバリバリ使っていて、むしろ、ソースから自前ビルドはごく少数です。

自分が自前ビルドする判断基準はこうです:

  • 先っちょを追っかけたい
    • ディストリ付属のは往々にしてちょっと古かったりするので
  • configureのオプションを把握しておきたい、好きなように指定したい
    • ApacheとかNginxとかですね。不要なものは積極的に--without-*の刑。

具体的にはあまりなくて、HTTPサーバ (Apache, Nginx, Lighttpd)、PHP、memcached、Tokyo Cabinetとライブラリ系のものがいくつかぐらいです。