異なるアーキテクチャ間でクロスコンパイル環境をつくる

作成した仮想マシン上でクロスコンパイル環境を作ります。 今回はこんな環境を想定しています。

常時起動のサーバ
ネットワークは 192.168.0.120 。CPU は Pen4i686 。 distcc で投げる側。
作った[http
//d.hatena.ne.jp/korokorokoron/20100317#1268848095:title=仮想マシン]:ネットワークは 192.168.0.130 。CPU は Core-i7 。 x86_64 。 distcc で受ける側。

投げる側と受ける側とでアーキテクチャが違う (i686 / x86_64) ことに注意。

distccの導入

投げる側と受ける側双方に distcc をインストールします。 distcc は分散コンパイルのためのパッケージです。

@192.168.0.120 $ sudo emerge -v distcc
@192.168.0.130 $ sudo emerge -v distcc

crossdevの導入

crossdev は異なるアーキテクチャ間でクロスコンパイルを行う際に必要となるツールをビルドするためのパッケージです。今回は受ける側の x86_64 な環境で i686コンパイルする環境を作る必要があるため、受ける側にいれます。

受ける側で、 crossdev をインストールします。
@192.168.0.130 $ sudo emerge -v crossdev
crossdev の使い方を見てみます。

実際に作成…する前に、とりあえずヘルプを見てみます。

@192.168.0.130 $ sudo crossdev --help
Usage: crossdev [options] --target TARGET

Options:
    --b, --binutils ver   Specify version of binutils to use
    --g, --gcc ver        Specify version of gcc to use
    --k, --kernel ver     Specify version of kernel headers to use
    --l, --libc ver       Specify version of libc to use
    -S, --stable          Use latest stable versions as default
    -C, --clean target    Uninstall specified target
    -P, --portage opts    Options to pass to emerge (see emerge(1))
    --with[out]-headers   Build C library headers before C compiler?
Stage Options:
    -s0, --stage0         Build just binutils
    -s1, --stage1         Also build a C compiler (no libc/C++)
    -s2, --stage2         Also build kernel headers
    -s3, --stage3         Also build the C library (no C++)
    -s4, --stage4         Also build a C++ compiler [default]
Extra Fun (must be run after above stages):
    --ex-only             Skip the stage steps above
    --ex-gcc              Build extra gcc targets (gcj/ada/etc...)
    --ex-gdb              Build a cross gdb
    --ex-insight          Build a cross insight
Target (-t) takes a tuple ARCH-VENDOR-OS-LIBC; see 'crossdev -t help'

とりあえず、 -t ARCH で間違いないようですが、一応 -t help もみておきます。

@192.168.0.130 $ sudo crossdev -t help
Supported Architectures:
   - alpha                                     - arm / armeb
   - hppa (parisc)                             - ia64
   - i386 / i486 / i586 / i686 (x86)           - m68k
   - mips / mipsel / mips64 / mips64el
   - powerpc (ppc) / powerpc64 (ppc64)
   - sparc / sparc64                           - s390 / s390x
   - sh / sh[1-5] / sh64                       - x86_64 (amd64)
Supported C Libraries:
   - glibc (gnu)
   - klibc       [prob wont work]
   - newlib      [bare metal/no operating system]
   - uclibc      [not all arches are ported]
Special Targets:
   - avr      http://www.nongnu.org/avr-libc/
   - bfin     http://blackfin.uclinux.org/
   - h8300    http://h8300-hms.sourceforge.net/
   - mingw32  http://www.mingw.org/
   - msp430   http://mspgcc.sourceforge.net/
   - nios2    http://www.altera.com/products/ip/processors/nios2/ni2-index.html
   - xc16x    http://www.infineon.com/
   - ee / iop / dvp (ps2) [Playstation 2 targets]
   - ppu / spu (cell) [Cell/Playstation 3 targets]
Softfloat toolchains:
   Include 'softfloat' in the 'vendor' field
   e.g. armeb-softfloat-linux-uclibc  powerpc-booya_softfloat-linux-gnu

今回は crossdev -t i686 でいいようです。

実際に crossdev でツールを作ってみます (失敗編)

実際に作ってみます。

@192.168.0.130 $ sudo crossdev -t i686

i686 用の binutils/gcc/linux-headers/glibcコンパイルし始めます。ちょっと待っていれば終わります。


…とココで問題が。 crossdev で入れたツールのバージョンが何かやたらと新しいです。

@192.168.0.130 $ emerge -pv gcc
∗  cross-i686-pc-linux/gcc
      Latest version available: 4.4.3
      Latest version installed: 4.4.3

∗  sys-devel/gcc
      Latest version available: 4.3.4
      Latest version installed: 4.3.4

下に出ている sys-devel/gcc は入れたばかりの仮想マシンで実際に動いているバージョン。 amd64 の安定版です。対し、上が crossdev で作ったバージョン。ずいぶんと新しい。当然ながら、投げる側のマシンにもこんな新しいバージョンは入っていません。クロスコンパイル時にコンパイラ等のバージョンが異なるのは不具合の原因になるので揃えたい。てことでやり直し。

実際に crossdev でツールを作る前に (リトライ編)

投げる側のバージョンを調べます。

@192.168.0.120 $ emerge -p binutils linux-headers gcc glibc

These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild   R   ] sys-devel/binutils-2.18-r3
[ebuild   R   ] sys-kernel/linux-headers-2.6.30-r1
[ebuild   R   ] sys-devel/gcc-4.3.4
[ebuild   R   ] sys-libs/glibc-2.10.1-r1
パッケージ バージョン
binutils 2.18-r3
linux-headers 2.6.30-r1
gcc 4.3.4
glibc 2.10.1-r1
crossdev で作るツールのバージョンを指定する (リトライ編)

そのまま crossdev -t するとバージョンがあわなかったので、先程調べた投げる側のそれに合わせてあげます。バージョンを指定する方法は先程みたヘルプに書いてあったもの (--b とか --binutils とか) を使います。

@192.168.0.130 $ sudo crossdev -t i686 --b 2.18-r3 --k 2.6.30-r1 --g 4.3.4 --l 2.10.1-r1


ちなみに安定版の最新パッケージで揃えるというのなら、 -S オプションもいいと思います。最新の安定版がはいります。異なるアーキテクチャ間で stable 版のバージョンが一緒だとは限らないのですけれども、だいたいの場合は一緒です。

@192.168.0.130 $ sudo crossdev -S -t i686


USE フラグも揃えたいならこうかな…やってないけど。

@192.168.0.130 $ sudo USE="xxxx" crossdev -S -t i686


終わったら、念のためバージョンを確認します。

受ける側の distcc の設定を行う

設定ファイルを書き換えて、投げる側 (192.168.0.120) からのリクエストを受け付けるようにします。念のため、自分のアドレスも書いておきましたが、予備的に書いてあるだけで本来は不要です。

@192.168.0.130 $ sudo nano -w /etc/conf.d/distccd

DISTCCD_OPTS="${DISTCCD_OPTS} --allow 192.168.0.120 192.168.0.130"


設定が終わったら distccd サービスを起動し、デフォルトのランレベルに加えておきます。

@192.168.0.130 $ sudo /etc/init.d/distccd start
@192.168.0.130 $ sudo rc-update add distccd default

投げる側の distcc の設定を行う

投げる側は、どこに投げるかという設定を行います。

@192.168.0.120 $ sudo distcc-config --set-hosts "192.168.0.130"


emerge でも distcc が有効になるよう、 /etc/make.conf を書き換えます。同じ変数名を使った場合はあとから書かれた方で上書きされるので、下のほうに書くようにします。

また、投げる側では CFLAGS 変数に『 march=native 』は指定しません。指定した場合はうまくいかないはずです。ちなみにうちのサーバでは単純にこんな感じになっていますが、昔のおんぼろ PC で並列処理させていたころからの名残です。互換性は抜群ですが最適化も何もあったもんじゃありませんw

@192.168.0.120 $ sudo nano -w /etc/make.conf

CFLAGS="-march=i686 -O2 -pipe"        ←march=native とかにはしない

FEATURES="distcc"                     ←必須
DISTCC_HOSTS="192.168.0.130"          ←投げる先
MAKEOPTS="-j9"                        ←例によって適当


最後に、ココにあるとおりにラッパースクリプトを作成してリンクを張ります。こんな感じになっています。

@192.168.0.120 $ ls -la /usr/lib/distcc/bin/
合計 12
drwxr-xr-x 2 root root 4096 2010-03-18 09:21 .
drwxr-xr-x 3 root root 4096 2009-05-28 09:54 ..
lrwxrwxrwx 1 root root   25 2010-03-18 09:21 c++ -> i686-pc-linux-gnu-wrapper
lrwxrwxrwx 1 root root   25 2010-03-18 09:21 cc -> i686-pc-linux-gnu-wrapper
lrwxrwxrwx 1 root root   25 2010-03-18 09:21 g++ -> i686-pc-linux-gnu-wrapper
lrwxrwxrwx 1 root root   25 2010-03-18 09:21 gcc -> i686-pc-linux-gnu-wrapper
lrwxrwxrwx 1 root root   15 2009-05-28 09:54 i686-pc-linux-gnu-c++ -> /usr/bin/distcc
lrwxrwxrwx 1 root root   15 2009-05-28 09:54 i686-pc-linux-gnu-g++ -> /usr/bin/distcc
lrwxrwxrwx 1 root root   15 2009-05-28 09:54 i686-pc-linux-gnu-gcc -> /usr/bin/distcc
−rwxr-xr-x 1 root root   69 2010-03-18 09:20 i686-pc-linux-gnu-wrapper
28:53+09:00">(追記):実はこれだけでは不十分でした。 cc が呼ばれると上記ラッパー経由で i686-pc-linux-gnu-cc を呼びに行きますが、ご覧の通りそんなものはありませんので落ちます。 cc が呼ばれたら gcc を呼んでやればいいので、環境変数 CC に gcc を呼びに行くようにセットしてやります。投げる側の /etc/make.conf に CC="i686-pc-linux-gnu-gcc" の一文を追加します。
@192.168.0.120 $ sudo nano -w /etc/make.conf

CC="i686-pc-linux-gnu-gcc"          ←この一文を追加する

実際に投げてみる

投げる側のマシンで emerge してやるだけでです。

@192.168.0.120 $ sudo emerge -v1 bind-tools
とか
@192.168.0.120 $ sudo FEATURES="${FEATURES} distcc" emerge -v1 bind-tools
とか
@192.168.0.120 $ sudo FEATURES="${FEATURES} distcc" DISTCC_HOSTS="192.168.0.130" MAKEOPTS="-j9" emerge -v1 bind-tools
などなど


受ける側では ps aux | grep distcc とか top とかで確認することができます。 distccmon-text でもいいらしい?

22018 ?        SN     0:00 /usr/x86_64-pc-linux-gnu/i686-pc-linux-gnu/gcc-bin/4.3.4/i686-pc-linux-gnu-gcc -Wall
 -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common
 -Werror-implicit-function-declaration -Wno-format-security -fno-delete-null-pointer-checks
 -Os -m32 -msoft-float -mregparm=3 -freg-struct-return -mpreferred-stack-boundary=2
 -march=i686 -mtune=generic -Wa,-mtune=generic32 -ffreestanding -pipe -Wno-sign-compare
 -fno-asynchronous-unwind-tables -mno-sse -mno-mmx -mno-sse2 -mno-3dnow
 -fno-stack-protector -fomit-frame-pointer -Wdeclaration-after-statement -Wno-pointer-sign
 -fno-strict-overflow -c -o /tmp/distccd_d4d00739.o /tmp/distccd_d53a0739.i

192.168.0.120 から 192.168.0.130 に投げてみたところです。こんな感じで 192.168.0.130 側では i686 用のコンパイラが動いてます。 CFLAGS は投げたマシン側のが使われているはず。たぶん。


投げる側のマシンで distccd サービスが立ち上がっている必要は必ずしもないはずですが、もし自分自身にも投げて高速化を狙うのであれば、当然必要になってきます。ただし、あまりにも性能差があったりすると、遅いマシンに投げた場合は丸投げするよりも遅くなるので注意です。

MAKEOPTS の数値によっても結構かわるので、最適な設定を探ってみるといいです。

ちなみに

うちの環境だとマシン2つでコンパイルするよりも全てを Core-i7 に丸投げした方が速かったです。仮想環境につくったマシンとはいえ世代差が大きすぎました。ただし、 ./configure とかその辺は高速化できないしネットワークに送るロスもあるので、思ったよりは微妙な感じです。