| Distcc というソフトウェアをご存じでしょうか。このソフトウェアは、プログラムのコンパイルを複数の計算機に分散して並列で行ない、コンパイルをスピードアップさせるためのものです。特に手許の計算機が遅い場合や、リモートに高速な計算機がある場合に使うと効果的です。台数が多いと段々効力は下がりますが、分散コンパイルに使う計算機の数が少なければ、ほぼ足した分だけコンパイルが速くなります。この文書では Gentoo Linux を用いて distcc の設定から始め、異なるバージョンの gcc を切り替えて使う方法、そしてその応用として distcc を用いたクロスコンパイルの方法までを解説します。あなたも遊んでいるサーバを有効利用してみませんか? |
今回 distcc の環境を構築するために用いるものは Gentoo Linux です。Gentoo Linux では Portage というパッケージシステムでソフトウェアの管理をしますが、このシステム側で distcc をサポートしており、設定さえ済めばパッケージのコンパイルに簡単に distcc を使うことができます(Gentoo 本家の distcc ガイド)。設定といってもとても簡単なもので、インストールには以下のコマンドを実行します(# をつけているのは root ユーザで実行するという意味です)。
# emerge distcc
こうすると自動的に必要なソースを取得してコンパイルが始まり、インストールが完了します。distcc を動かすために必要なユーザも自動で追加されます。
インストールが完了したらさっそく distcc サーバを動かしてみましょう。distcc サーバは distcc クライアントからコンパイル用のソースを受け取り、サーバ側のコンパイラを起動してコンパイルして結果(オブジェクトファイル)をクライアントに返すもので、distcc を使うときはコンパイルは全て distcc を経由するので、分散コンパイルに参加させようとしている計算機は自分自身も含めて全ての計算機で動かしておく必要があります。Gentoo では distcc などのサービスの起動・停止には /etc/init.d の下にあるスクリプトを用います。
# /etc/init.d/distccd start
これで distccd (distcc サーバ)が立ち上がりました。次回の起動以降も自動で行ないたい場合は、rc-update というコマンドを用いて自動で起動するよう登録します。
# /sbin/rc-update add distccd default
このようにすると次回の起動以降もそのまま自動で distccd が立ち上がります。同様にして分散コンパイルに参加させたい計算機全てで distccd を立ち上げてください。余っている計算機が Gentoo でない場合は、パッケージからインストールしたり、ソースからコンパイルしたりしてください。(Vine の場合については次の節で触れます)
ここまで済めばあとは使うだけです。Portage のコンパイルに distcc を使うには、まず並列コンパイルのためのフラグを有効にし、/etc/make.conf の FEATURES という変数に distcc を追加します。そして、distcc 用の一時ディレクトリに DISTCC_DIR も指定します。
# Advanced Features
# =================
#
# MAKEOPTS provides extra options that may be passed to 'make' when a
# program is compiled. Presently the only use is for specifying
# the number of parallel makes (-j) to perform. The suggested number
# for parallel makes is CPUs+1.
MAKEOPTS="-j6" # ←分散コンパイルする計算機の数以上の値を入れてください
# あまり増やしても意味はありませんが、そこそこないと分散
# されません
# FEATURES are settings that affect the functionality of portage. Most of
# these settings are for developer use, but some are available to non-
# developers as well.
# ……中略……
FEATURES="sandbox buildpkg ccache distcc digest" # 設定例(distcc を加えます)
# ……さらに数行略……
# DISTCC_DIR sets the temporary space used by distcc.
DISTCC_DIR="${PORTAGE_TMPDIR}/.distcc" # 設定例(コメントを外します)
あとは /etc/distcc/hosts というファイルに分散コンパイルに参加させる計算機の名前もしくは IP アドレスを追加します。
localhost cipher simon
こうすることによって、emerge コマンドを実行してコンパイルするとき、自動的に3つの計算機にコンパイル命令が送られ、コンパイルされたものが返ってきます。distcc が動いているかどうかは distcc サーバが動いている計算機のログ (たとえば /var/log/messages) を見るか、または distcc クライアントとなって(emerge を動かしている)計算機で
# distcc-config --set-log /var/log/distcc.log
と実行すると /var/log/distcc.log にログが作られるので、これをチェックすることで確かめられます。
注意点としては Portage で distcc をサポートしていても個々の ebuild の src_compile セクションで emake と書かれていなければ distcc は使われない(emake と書かれていればそれは自動的に上で指定した ${MAKEOPTS} が付加されて make -j6 が実行されるので、distcc が動きます)ということです。
Portage を使ってのコンパイル以外でも、distcc を使うことができます。そのときは DISTCC_HOSTS という環境変数を指定し、
rico% tar xvzf emacs-21.3.tar.gz rico% cd emacs-21.3 rico% ./configure rico% DISTCC_HOSTS="localhost simon cipher" make -j6
といったように手動で make に -j オプションを渡す必要があります。
前の節ではサーバもクライアントも同じ環境(Gentoo で同じバージョンのものを使っている)を想定していましたが、実際並列で計算機を使いたいとなるといろいろな環境が混じることが考えられます。Gentoo 以外のディストリビューションを参加させたいがうまくいかない、ということもあるでしょうし、同じ Gentoo でも使っている gcc のバージョンが異なるとうまくいかないことがあります(c++ は頻繁にこの状態になりますが、cc でも場合によっては問題が起きます)。
とはいえ、複数のディストリビューションでコンパイルするのは、いずれも Linux が動いている前提なのであまり難しくはありません。クロスコンパイラを用意する必要がないので、分散コンパイルに参加する全部の計算機で gcc のバージョンを揃えるだけでよいからです。
ちなみに今回の実験に使ったのは Gentoo のクライアントに加えて Vine を distcc サーバとして2台追加したので、ここでは Vine を使って異なるバージョンの gcc の共存の方法を解説します。Gentoo は上記の方法でインストールしてあり、Vine は
# apt-get install distcc
でインストールして以下のような内容のファイルを /etc/init.d/distccd に置き、
# /etc/init.d/distccd # /sbin/chkconfig --add distccd
にて有効にしました。
#!/bin/sh
# chkconfig: 345 92 14
# description: distccd is a server for compilation under distributed network.
#
# Source function library.
. /etc/init.d/functions
DISTCCD=/usr/bin/distccd
PATH=/usr/local/bin:${PATH}
[ -f $DISTCCD ] || exit 0
# See how we were called.
case "$1" in
start)
# Start daemons.
echo -n "Starting distccd: "
/usr/bin/distccd --daemon
echo
touch /var/lock/subsys/distccd
;;
stop)
# Stop daemons.
echo -n "Shutting down distccd: "
killall distccd
echo
rm -f /var/lock/subsys/distccd
;;
*)
echo "Usage: distccd {start|stop}"
exit 1
esac
exit 0
この環境に gcc を入れるわけですが、現在 Vine 2.6 に標準で使われている gcc のバージョンは 2.95.3、Gentoo 1.4 で使われているものは安定版の x86 では 3.2.3、テスト版の ~x86 では 3.3.2 なので、いずれの場合にせよサーバ側で gcc を入れなければなりません。サーバもクライアントも Gentoo の場合でも、片方が安定版で片方がテスト版のときは、サーバ側の gcc をクライアント側に合わせないといけません。
簡単にインストールするのであれば普通にパッケージで入れればいいのですが、Vine のように gcc 3.3.2 が簡単にインストールできない状態(VineSeed には gcc3 のパッケージがありますが)もあり、また、Gentoo でも現時点ではクロスコンパイルのことを考えると同じバージョンの gcc を入れることができない(違うバージョンの gcc であれば同時に入れることができます)ので、最初からパッケージで入れることは諦めて手で /usr/local の下にインストールします。
rico% tar xvzf gcc-3.3.2.tar.gz rico% mkdir gcc-work rico% cd gcc-work rico% ../gcc-3.3.2/configure --enable-languages=c,c++ rico% make rico% su # make install
こうすると gcc/g++ のプログラムがインストールされるますが、元からシステムに入っている gcc とパスが違うだけで名前が被っています。distcc ではバージョンの異なった gcc やクロスコンパイラを呼び出すにはファイル名で区別するのが確実です。(実は名前を分けなくても gcc は1つのファイルで -V や -b というオプションで指定できるのですが、autoconf や libtool を使ったソフトでは失敗するので、ファイル名を別にするのが推奨です) そういうわけで、ファイルをリンクしておきましょう。
# cd /usr/local/bin # ln gcc gcc-3.3.2 (もうすでにリンクができているときはこの作業は不要ですし、3.3.2 だとできてます) # ln g++ g++-3.3.2 # ln c++ c++-3.3.2
また、distcc からすると手許の計算機もリモートの計算機も区別せず、同じ名前のプログラムがインストールされていることを期待するので、distcc を利用するクライアント側でもシンボリックリンクを張って名前を揃えるか、もしくは /usr/local の下に上と同じようにしてインストールしておきます(下はシンボリックリンクでごまかす一例です)。
# cd /usr/bin # ln -s ../i686-pc-linux-gnu/gcc-bin/3.3/gcc gcc-3.3.2 # ln -s ../i686-pc-linux-gnu/gcc-bin/3.3/g++ g++-3.3.2 # ln -s ../i686-pc-linux-gnu/gcc-bin/3.3/c++ c++-3.3.2
この準備が終わったら distcc を利用するクライアント側で CC および CXX にいまコンパイルしたプログラムのファイル名を設定します。Gentoo の場合は /etc/env.d/05gcc というファイルを編集します。
CC="gcc-3.3.2" CXX="g++-3.3.2"
これを保存して
rico% su - # vi /etc/env.d/05gcc # /usr/sbin/env-update # exit rico% source /etc/profile
とすると新しい環境変数が読み込まれ、前節で示した手順で残りの設定をすれば distcc が使えるようになります。
distcc でクロスコンパイルをするのもほとんど手順は同じです。ポイントは前の節と違ってクロスコンパイラを用意することくらいです(クロスコンパイラの構築についてはこの文書の範囲を越えるので扱いません)。クロスコンパイラも本体のコンパイラと名前を分けてインストールし、呼び出す側でクロスコンパイラの名前を指定すれば使用できます。手順は普通にクロスコンパイラを入れるときと同じく、binutils を入れて gcc を入れます。(libc はターゲットの glibc を入れれば済むので newlib は不要です)
ここでは Alpha PC の分散コンパイルに参加させて x86 のバイナリのコンパイルを手伝わせてみます。(普通はもっと低速でマイナーなアーキテクチャに比較的高速ですぐ手に入る x86 の計算機から仕事を手伝わせるのでしょうが……)
rico% tar xvzf binutils-2.13.tar.gz rico% mkdir binutils-work rico% cd binutils-work rico% ../binutils-2.13/configure --target=i686-linux rico% make rico% su # make install
次に glibc を展開しておきます。
# mkdir /usr/local/i686-linux (このディレクトリがなければ作成しますが、binutils の作成のときにできてます) # cd /usr/local/i686-linux # mkdir usr # mount -o bind . usr (Gentoo の GRP を展開するだけでごまかすためです) # tar xvjf /path/to/glibc-2.3.2-r1.tbz2 (ソースではなく Gentoo の GRP から取得したものを使います。なければ quickpkg コマンドで作成することもできます)
ただ一ヶ所直すところがあるので /usr/local/i686-linux/lib/libc.so を開き、 GROUP のところを修正します。
GROUP ( ./lib/libc.so.6 ./usr/lib/libc_nonshared.a )
以上です。
次に、カーネルのヘッダも必要なので展開します。
# cd /usr/local/i686-linux # tar xvjf /path/to/kernel-headers-2.4.19-r1.tbz2 (これも手に入るバージョンのものでかまいません)
そして最後に gcc をインストールします。
rico% tar xvzf gcc-3.3.2.tar.gz
rico% mkdir gcc-work
rico$ cd gcc-work
rico% ../gcc-3.3.2/configure --target=i686-linux --program-suffix=-3.3.2 \
--enable-languages=c,c++
rico% make
rico% su
# make install
以上で distcc サーバ側の設定は完了です。Alpha 機上で i686-linux-gcc-3.3.2、および i686-linux-g++-3.3.2 というファイルが作られていることを確認してください。そして、distcc クライアント側ではこのプログラムが起動したら distcc が立ち上がるように設定します。
# cd /usr/lib/distcc/bin # ln -s /usr/bin/distcc i686-linux-gcc-3.3.2 # ln -s /usr/bin/distcc i686-linux-g++-3.3.2 # ln -s /usr/bin/distcc i686-linux-c++-3.3.2
あとは前節で述べたように、シンボリックリンクを張って起動されるプログラムの名前を一致させることと、忘れず /etc/env.d/05gcc もアップデートすれば使えるようになります。
# cd /usr/bin # ln -s ../i686-pc-linux-gnu/gcc-bin/3.3/gcc i686-linux-gcc-3.3.2 # ln -s ../i686-pc-linux-gnu/gcc-bin/3.3/g++ i686-linux-g++-3.3.2 # ln -s ../i686-pc-linux-gnu/gcc-bin/3.3/c++ i686-linux-c++-3.3.2
CC="i686-linux-gcc-3.3.2" CXX="i686-linux-g++-3.3.2"
他のアーキテクチャについても --target の部分を i686-linux 以外にすれば対応できますが、クロスコンパイラが構築できない場合はもちろん distcc は使えません。クライアント側の gcc とバージョンを揃える必要があるため、うまく環境が整わないこともありますが、環境が構築できればかなり便利になることは間違いありません。みなさんも distcc を利用して計算機資源の有効活用をしてください ;-p
