CentOS6.xでGCC5.2をOS標準の4.4.7と共存できるようにソースビルドした

あくまでもやってみたらこうだったよという記録です。

#GCCのダウンロード
追記: 2016年4月現在GCC6.1がリリースされています。今後6.2以降が出てくると思いますが、5.2のところを適宜変更してください。最新バージョンの確認を行うようにしてください。

##tarball(.tar.○○ファイル)を利用
```
wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-5.2.0/gcc-5.2.0.tar.gz
```
※bz2でもOKです。
ダウンロードしたら、次のコマンドで解凍・移動します。

```bash
tar xf gcc-5.2.0.tar.gz
cd gcc-5.2.0
```

##Subversionを利用

Subversionがある場合はSubversionのリポジトリから取ってくる方法もあります。
基本的にバージョン管理システムのリポジトリから取ってくる場合はタグを使用します。最新安定版を使うなら以下のコマンドで`gcc`フォルダを作ってその中にソースコード一式がダウンロードされます。

```bash
svn co svn://gcc.gnu.org/svn/gcc/tags/`svn ls svn://gcc.gnu.org/svn/gcc/tags | grep -F gcc_ | tail -n 1` gcc
```

手動でやりたい場合は

```bash
svn ls svn://gcc.gnu.org/svn/gcc/tags | grep -F gcc_
```

でインストールしたいバージョンのタグ名を確認してから


```bash
svn co svn://gcc.gnu.org/svn/gcc/tags/(タグ名) gcc
```

でダウンロードします。その後、`gcc`ディレクトリに移動します。

##Gitを利用

**ある程度ディスク領域に余裕があり、**Gitを使いたい場合は

```bash
git clone git://gcc.gnu.org/git/gcc.git
git tag #このコマンドでインストールしたいバージョンのタグ名を確認
git checkout (インストールしたいバージョンのタグ名)
```

を実行します。その後、Subversion同様`gcc`ディレクトリに移動します。

#必要なパッケージのダウンロード

```
contrib/download_prerequisites 
```

GCCのルートディレクトリで上のコマンドを実行すると自動的にやってくれます。`gcc.gnu.org`に接続できない場合は、例えば

```bash
sed -e "s@ftp://gcc.gnu.org/pub@http://ftp.tsukuba.wide.ad.jp/software@" contrib/download_prerequisites > contrib/download_prerequisites2
contrib/download_prerequisits2
```

のように、サーバを変更して再実行してください。(この例では筑波大学のサーバを使用)

#ビルド用ディレクトリ作成・移動
公式では`configure`のあるディレクトリでコンパイルするのを非推奨としています。ここではそのディレクトリの親ディレクトリに別のディレクトリを作りました。

```bash
mkdir ../gccbuild
cd ../gccbuild
```

今思えば`configure`のあるディレクトリの下にディレクトリを作ってもよかったかもしれません。(CentOS/RHELの付属コンパイラはこの方法を取っているようです)

#configure
```
../gcc-5.2.0/configure --program-suffix=5 --disable-multilib --enable-languages=c,c++ --host=x86_64-redhat-linux --build=x86_64-redhat-linux --target=x86_64-redhat-linux 
```
元のGCC 4.4.7と共存させるためにsuffixをつけました。また、64bit専用にするつもりで`--disable-multilib`をつけました。bootstrapは有効にしてあります。

追記: Javaはいらないので`--disable-libgcj`もつけた方がいいみたいです。また、`/usr/local`以外にインストールする場合には`--prefix`以外にも`--with-local-prefix`も指定した方がいいらしいです。(ライブラリのインストール先)あとは「`--with-system-zlib --enable-checking=release`」などでしょうか？(更に追記: OS付属のものより新しいZlibが必要な場合は`--with-system-zlib`をつけないようにしてください)(`gcc -v`でOS付属のGCCのコンパイルオプションが確認できます)また、`--enable-languages`には**「s」をつけてください。**「s」なしでやってJava・Fortranのコンパイラも抱き合わせられてしまいましたので。これらを総括すると、

```bash
../gcc-5.2.0/configure --program-suffix=5 --disable-multilib --enable-languages=c,c++ --host=x86_64-redhat-linux --build=x86_64-redhat-linux --target=x86_64-redhat-linux --disable-libgcj --with-system-zlib --enable-checking=release #--prefix=DIR --with-local-prefix=DIR2 (#の後は場所を変える時のみ)
```

追記: `--enable-cheking`が`--enable--cheking`になっていたことをお詫びします。

#ビルド
```bash
make -j8 BOOT_CFLAGS='-march=core2 -O3'
```

当方デスクトップ版i5なので`-j8`にしました。`-j`の直後はコア数の2倍がいいらしいです。ビルドにはかなり時間がかかりました。

追記: GCCのビルドは3回(GCC4.4.7でビルドしたGCC5.2(Aとする)、AでビルドしたGCC5.2(Bとする)、BでビルドしたGCC5.2(Cとする))行われますが、Aのビルドには`STAGE_1_CFLAGS`で指定したオプションが適用されます。また、B・Cのビルドには`BOOT_CFLAGS`で指定したオプションが適用されます。`STAGE_1_CFLAGS`にはGCC4.4で使用できるものを指定します。(例えば`-Og`・`-march=corei7`などは不可)また、`BOOT_CFLAGS`にはGCC5.2で使用できるものを指定します。こちらには`-march=corei7`・`-march=core-avx2`などもOKです。例えば、最終的に生成されるGCCのバイナリサイズをなるべく小さくしてなおかつ現在使用しているコンピュータ以外のコンピュータにバイナリを配布しないというのであれば、

```bash
make -j8 STAGE1_CFLAGS='-march=core2 -O3' BOOT_CFLAGS='-march=native -Os'
```

でコンパイルを実行します。また、さらに、出力される大量のメッセージが見たくないというのであれば、

```bash:(Ba)shなど
make -k -j8 STAGE1_CFLAGS='-march=core2 -O3' BOOT_CFLAGS='-march=native -Os' > /dev/null 2>&1 && echo "GCCビルド成功" || echo "GCビルド失敗"
```

```tcsh:Cシェル・tcshなど
make -k -j8 STAGE1_CFLAGS='-march=core2 -O3' BOOT_CFLAGS='-march=native -Os' >& /dev/null && echo "GCCビルド成功" || echo "GCビルド失敗"
```

とすると結果だけ出力されてすっきりします。(更に末尾に&をつけるとバックグラウンドでコンパイルしながら他のことをすることも可能)

追記: stage1だけビルドするには`make ～ all-stage1`でできるようです。

#インストール
porgをインストールしているので次のコマンドでインストールしました。**(GCCは`make uninstall`に対応していません)**

```
sudo porg -lp gcc-5.2.0 'make install'
```

追記:このままだと`gccbuild`以下に記録されるファイルがあるので、以下のようにして除外した方がいいと思います。(ついでに`make install`にオプション`-j8`をつける)

```
sudo porg -lp gcc-5.2.0 -E "${PWD}:/tmp:/dev:/proc:/selinux:/sys:/mnt:/media" 'make install -j8'
```

`/usr/local`以外にインストールした場合はパスを通すのを忘れないようにしましょう。

```bash:Bash
export PATH=(prefixのディレクトリ)/bin:$PATH
```

```tcsh:tcsh
setenv PATH (prefixのディレクトリ)/bin:$PATH
```


#コンパイル
まずC言語をコンパイルしてみました。

```
tatsu@localhostF ~/gcctest> cat hello.c
#include <stdio.h>
#include <math.h>

int main(void) {
  puts("Hello, world!");
  double inmu = M_E * (pow((M_E + M_PI) * (M_E + M_E + M_PI),M_E) + 1.0 / ((pow(M_PI,M_E) - M_E) * (M_E + pow(M_E,M_PI) - pow(M_PI, M_E))));
  printf("Inmu number: %f\n",inmu);
}
tatsu@localhostF ~/gcctest> gcc5 hello.c -o hello
tatsu@localhostF ~/gcctest> ./hello
Hello, world!
Inmu number: 114514.191981
```

次にC++をコンパイルしてみました。

```
tatsu@localhostF ~/gcctest> cat cpptest.cpp 
#include <iostream>

using namespace std;

int main() {
  cout << "(｡╹ω╹｡)ㄘんㄘんㄟ⁰ㄋㄟ⁰ㄋㄜㄝㄋ" << endl;
  int inmu = 0b11011111101010010;
  cout << "2進数11011111101010010を10進数に直すと " << inmu << endl;
  return 0;
}
tatsu@localhostF ~/gcctest> g++5 cpptest.cpp  -o cpptest
tatsu@localhostF ~/gcctest> ./cpptest
(｡╹ω╹｡)ㄘんㄘんㄟ⁰ㄋㄟ⁰ㄋㄜㄝㄋ
2進数11011111101010010を10進数に直すと 114514
```

ちゃんと2進リテラルも正しくコンパイルされています。

#リンクしているライブラリを確かめる

念のためちゃんとライブラリとリンクしているか確かめてみました。

```
tatsu@localhostF ~/gcctest> ldd hello
	linux-vdso.so.1 =>  (0x00007ffd5d99d000)
	libc.so.6 => /lib64/libc.so.6 (0x00000039f5200000)
	/lib64/ld-linux-x86-64.so.2 (0x00000039f4e00000)
```

OS付属の古いライブラリとリンクしています。Cライブラリは今回のビルドで最新版を、ということはないようです。(OSの基礎をなすものなので差し替えたらマズいという話も)C++の方も確認してみました。

```
tatsu@localhostF ~/gcctest> ldd cpptest
	linux-vdso.so.1 =>  (0x00007ffd971cb000)
	libstdc++.so.6 => /usr/local/lib64/libstdc++.so.6 (0x00007f0121577000)
	libm.so.6 => /lib64/libm.so.6 (0x00000039f6200000)
	libgcc_s.so.1 => /usr/local/lib64/libgcc_s.so.1 (0x00007f0121360000)
	libc.so.6 => /lib64/libc.so.6 (0x00000039f5200000)
	/lib64/ld-linux-x86-64.so.2 (0x00000039f4e00000)
```

こちらはlibstdc++・libgcc(C++のランタイムライブラリ)が自分でビルドしたものにリンクされました。

#ライブラリのパスを通す
環境変数`LD_LIBRARY_PATH`にライブラリをインストールしたディレクトリを追加します。

```bash:Bash
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:(/usr/localかwith-local-prefixのディレクトリ)/lib64
```

```tcsh:tcsh
setenv LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:(/usr/localかwith-local-prefixのディレクトリ)/lib64
```

**補足: 誤記があったので修正しました。**


`(/usr/localかwith-local-prefixのディレクトリ)/lib`も一緒に追加(コロンで繋げる)したほうがいいかもしれません。
この変数が定義されていない場合を考慮して、

```bash:Bash
if [ -n "${LD_LIBRARY_PATH+x}" ]; then
  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib64 #/usr/localの部分は適宜変更します
else
  export LD_LIBRARY_PATH=/usr/local/lib64
fi
```

```tcsh:tcsh
if ( $?LD_LIBRARY_PATH ) then
  setenv LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:/usr/local/lib64
else
  setenv LD_LIBRARY_PATH /usr/local/lib64
fi
```

とすることをおすすめします。

##更に追記: root権限がある場合
https://www.vultr.com/docs/how-to-install-gcc-on-centos-6 によると、`/etc/ld.so.conf.d/`の中のファイルにライブラリのディレクトリを書くという手もあるみたいです。

```bash:Bash
sudo -i
echo "/usr/local/lib64" >> /etc/ld.so.conf.d/usrlocallib64.conf 
#「usrlocallib64」の名前は任意です。(拡張子は固定した方がいいと思います)
ldconfig
exit
```

#ヘッダファイルのパスを通す
環境変数`CPATH`にライブラリをインストールしたディレクトリを追加します。

```bash:Bash
export CPATH=$CPATH:(/usr/localかwith-local-prefixのディレクトリ)/include
```

```tcsh:tcsh
setenv CPATH ${CPATH}:(/usr/localかwith-local-prefixのディレクトリ)/include
```
これも

```bash:Bash
if [ -n "${CPATH+x}" ]; then
    export CPATH=/usr/local/include:$CPATH
else
    export CPATH=/usr/local/include
fi
```

```tcsh:tcsh
if ( $?CPATH ) then
    setenv CPATH /usr/local/include:$CPATH
else
    setenv CPATH /usr/local/include
endif
```

のように書くことをおすすめします。


#まとめ
一応GCC5.2はCentOS6で動くということがわかりました。<del>ただし、リンカ(ld)が古いライブラリとリンクしてしまうことがあります。</del><del>GCC4.4.7で利用できない関数は使用を控えた方がいいのかもしれませんが、そこはよく分かっていません。</del>`std::atomic`・`std::mt19937_64`はGCC4.4.7で対応しいないみたいですが、GCC5.2だと動くようです。(Clangのビルド・大学の課題で確認)とりあえず、ちゃんと動くようです。めでたしめでたし。