Ubuntu上でGCCをビルドする

Ubuntuをはじめとして、まぁGCCはどのLinuxディストリビューションに入っているんだけど、クラスター計算機等のユーザー/ファイルの共有環境では、サーバーとクライアントでビルド環境をそろえたいだろう ということで、特定のバージョンのGCCを`/opt_share`下に準備する(このフォルダがNFSで共有されている)ための方法をまとめた。
NFSでマウントした`/opt_share`下の内容は全ユーザーから見えるように設置したいので、基本的には`sudo`コマンドですべての作業は行った。

公式: https://gcc.gnu.org/

参考にしたページ: [GNU GCCのビルド（ソースから） #Linux - Qiita](https://qiita.com/silverskyvicto/items/9cd51bc9aa035eb301bb)

# 準備
ファイルをミラーサーバーからダウンロードする。
[GCC mirror sites - GNU Project](https://gcc.gnu.org/mirrors.html)
今回はUbuntu 22.04で使われているのと同じGCC 11.4を使う。

```bash:コマンド(つくばのミラーを使う場合)
wget https://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-11.4.0/gcc-11.4.0.tar.gz
```
でダウンロード(Proxy必要な場合は`-e HTTP_PROXY=proxy-hoge.co.jp:8080`を忘れずに)。
```bash:コマンド
sudo tar -zxvf gcc-11.4.0.tar.gz
```
で展開。

# ビルド
## GCC 11.4のビルド
GCC 11.4では`${GCC_ROOT}`で`./configure & make & make install`ではビルドできず`./contrib/download_prerequisites`の実行が必要。

この作業の中で`wget`でのダウンロードが入っているので、proxy等が必要な場合は考慮に入れる必要がある。一般ユーザーは`.bashrc`等ですでにproxy通していることもあるけれど、`sudo`の方にそれを設定していないことがあるので、以下のようなコマンドで環境変数`http_proxy`と`https_proxy`にproxyサーバーを設定する(port番号は8080だとした):
```bash:コマンド
 sudo http_proxy=http://proxy-hoge.co.jp:8080 https_proxy=https://proxy-hoge.co.jp:8080 ./contrib/download_prerequisites
```

`sudo mkdir build`で`cd build`に移動したのち(こうやってビルドするのが推奨されていることを最近知った)に
```bash:コマンド
 sudo  ../configure --enable-languages=c,c++,fortran --prefix=/opt_share/compiler/gcc/11.4.0 --disable-bootstrap --disable-multilib
```
`--disable-bootstrap`と`--disable-multilib`のオプションにについてはよくわかってないけれど、参考にしたページにあったので適当につけておいた。`fortran`は必須(みんな大好きFortran)。

```bash
sudo make -j8
sudo make check #ここでエラーで止まる場合は`apt install autogen`が必要かも
sudo make install
```
でインストール完了。

<details><summary>make checkでFAILあったけど気にしないことにする</summary>

```console:標準出力・エラー出力(抜粋)
============================================================================
Testsuite summary for package-unused version-unused
============================================================================
# TOTAL: 36
# PASS:  34
# SKIP:  0
# XFAIL: 0
# FAIL:  2
# XPASS: 0
# ERROR: 0
```

```console:./libbacktrace/test-suite.log
   package-unused version-unused: ./test-suite.log
=====================================================

# TOTAL: 36
# PASS:  34
# SKIP:  0
# XFAIL: 0
# FAIL:  2
# XPASS: 0
# ERROR: 0

.. contents:: :depth: 2

FAIL: b2test_buildid
====================

test1: [0]: missing file name or function name
test2: [0]: missing file name or function name
test3: [0]: missing file name or function name
test4: [0]: missing file name or function name
FAIL: backtrace_full noinline
FAIL: backtrace_full inline
FAIL: backtrace_simple noinline
FAIL: backtrace_simple inline
PASS: backtrace_syminfo variable
FAIL b2test_buildid (exit status: 1)

FAIL: b3test_dwz_buildid
========================

test1: [0]: missing file name or function name
test2: [0]: missing file name or function name
test3: [0]: missing file name or function name
test4: [0]: missing file name or function name
FAIL: backtrace_full noinline
FAIL: backtrace_full inline
FAIL: backtrace_simple noinline
FAIL: backtrace_simple inline
PASS: backtrace_syminfo variable
FAIL b2test_buildid (exit status: 1)

FAIL: b3test_dwz_buildid
========================

test1: [0]: missing file name or function name
test2: [0]: missing file name or function name
test3: [0]: missing file name or function name
test4: [0]: missing file name or function name
FAIL: backtrace_full noinline
FAIL: backtrace_full inline
FAIL: backtrace_simple noinline
FAIL: backtrace_simple inline
PASS: backtrace_syminfo variable
FAIL b3test_dwz_buildid (exit status: 1)
```

</details>

# 事後の設定とか

## Environmental moduleの記述
当然Environmental moduleで利用するコンパイラ等は管理したい。
```:/opt_share/modulefiles/compiler/gcc/11.4.0
#%Module -*- tcl -*-
##
## gcc modulefile
##
proc ModulesHelp { } {
        puts stderr "\tAdds GCC-11.4.0 replacing the OS GCC. \n"
}

module-whatis   "adds GCC-11.4.0 replacing the OS GCC."

set          version          "11.4.0"
set          root             /opt_share/compiler/gcc/11.4.0

conflict     compiler/gcc-11.4.0
conflict     compiler/gcc/9.4.0

prepend-path PATH             $root/bin
prepend-path PATH             $root/sbin
prepend-path MANPATH          $root/share/man
prepend-path LD_LAIBRARY_PATH $root/lib

setenv       COMPNAME         gcc
setenv       COMPVER          $version
```
上記くらい書いておけば、ライブラリ用のPATHとかもきちんと通っているはず。
一応OSデフォルトで入っているGCC 11.4、それと別で入れたGCC 9.4と排他的になるように、`conflict     compiler/gcc-11.4.0`, `conflict     compiler/gcc/9.4.0`のの文を入れている。

## OpenMPIも入れよう
sudoは普通Environmental moduleの設定を読み込んでいないのと、実行するたびに環境がリセットされるので、その点で結構面倒。
この問題点を解決するために「Environmental moduleのセットアップ」「GCC 11.4のmodule読み込み」「configure」を一回のsudoで行うために、これらを単一の命令としてbashの引数に渡すことにする。
一気に実行してうまくいく気がしないので、まずは「Environmental moduleのセットアップ」「GCC 11.4のmodule読み込み」のテスト
```bash:module読み込みのテスト
sudo bash -c "source /etc/profile.d/modules.sh; module load compiler/gcc/11.4.0; module list; gcc -v"
```
の実行で、確かに所望のGCC 11.4の環境が準備されていることがわかる。以下が出力:
```console
Currently Loaded Modulefiles:
 1) compiler/gcc/11.4.0
組み込み spec を使用しています。
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/opt_share/compiler/gcc/11.4.0/libexec/gcc/x86_64-pc-linux-gnu/11.4.0/lto-wrapper
ターゲット: x86_64-pc-linux-gnu
configure 設定: ../configure --enable-languages=c,c++,fortran --prefix=/opt_share/compiler/gcc/11.4.0 --disable-bootstrap --disable-multilib
スレッドモデル: posix
サポートされている LTO 圧縮アルゴリズム: zlib
gcc バージョン 11.4.0 (GCC)
```

というわけで、晴れて`configure`までもっていく。
適当なOpenMPIのtarballをとってきて、展開。そのルートフォルダにbuildフォルダを作って、そこに入って以下のコマンドで`configure`
```bash:コマンド
sudo bash -c "source /etc/profile.d/modules.sh; module load compiler/gcc/11.4.0; ../configure --prefix=/opt_share/openmpi/5.0.3/gcc-11.4.0 --with-tm=/opt/openpbs/23.06.06"
```
```console:標準出力(抜粋)
Open MPI configuration:
-----------------------
Version: 5.0.3
MPI Standard Version: 3.1
Build MPI C bindings: yes
Build MPI Fortran bindings: mpif.h, use mpi, use mpi_f08
Build MPI Java bindings (experimental): no
Build Open SHMEM support: false (no spml)
Debug build: no
Platform file: (none)

Miscellaneous
-----------------------
Atomics: GCC built-in style atomics
Fault Tolerance support: mpi
HTML docs and man pages: installing packaged docs
hwloc: external
libevent: external
Open UCC: no
pmix: internal
PRRTE: internal
Threading Package: pthreads

Transports
-----------------------
Cisco usNIC: no
Cray uGNI (Gemini/Aries): no
Intel Omnipath (PSM2): no (not found)
Open UCX: no
OpenFabrics OFI Libfabric: no (not found)
Portals4: no (not found)
Shared memory/copy in+copy out: yes
Shared memory/Linux CMA: yes
Shared memory/Linux KNEM: no
Shared memory/XPMEM: no
TCP: yes

Accelerators
-----------------------
CUDA support: no
ROCm support: no

OMPIO File Systems
-----------------------
DDN Infinite Memory Engine: no
Generic Unix FS: yes
IBM Spectrum Scale/GPFS: no (not found)
Lustre: no (not found)
PVFS2/OrangeFS: no
```
まぁ大丈夫じゃない？
`make`するときにはやっぱりコンパイラ等環境の情報使うので上の技を再度使う:
```bash:コマンド
sudo bash -c "source /etc/profile.d/modules.sh; module load compiler/gcc/11.4.0; sudo make -j8
"
```
以下は毎回sudo使えばよいだけ:
```bash:コマンド
sudo make check
sudo make install
```
で終了。

確認のため
```bash:コマンド
module load compiler/gcc/11.4.0 
/opt_share/openmpi/5.0.3/gcc-11.4.0/bin/mpif90 -v
```
を実行すると
```console
組み込み spec を使用しています。
COLLECT_GCC=/opt_share/compiler/gcc/11.4.0/bin/gfortran
COLLECT_LTO_WRAPPER=/opt_share/compiler/gcc/11.4.0/libexec/gcc/x86_64-pc-linux-gnu/11.4.0/lto-wrapper
ターゲット: x86_64-pc-linux-gnu
configure 設定: ../configure --enable-languages=c,c++,fortran --prefix=/opt_share/compiler/gcc/11.4.0 --disable-bootstrap --disable-multilib
スレッドモデル: posix
サポートされている LTO 圧縮アルゴリズム: zlib
gcc バージョン 11.4.0 (GCC)
```
と確かに11.4.0がリンクされているのが確認できる。

この環境を読み込むためのenvironmental module設定ファイルは以下の通り:
```:/opt_share/modulefiles/openmpi/5.0.3/gcc-11.4.0
#%Module -*- tcl -*-
##
## dot modulefile
##
proc ModulesHelp { } {
  global moduleversion

  puts stderr "\tAdds openmpi to your environment variables,"
}

module-whatis "adds openmpi to your environment variables"

set              moduleversion     5.0.3
set              root              /opt_share/openmpi/5.0.3/gcc-11.4.0

conflict         openmpi/5.0.3/gcc-9.4.0

module load compiler/gcc/11.4.0

prepend-path     PATH              $root/bin
prepend-path     MANPATH           $root/share/man
setenv           MPI_HOME          $root
setenv           MPI_RUN           $root/bin/mpirun

prepend-path     LD_RUN_PATH       $root/lib
prepend-path     LD_LIBRARY_PATH   $root/lib
prepend-path     LIBRARY_PATH      $root/lib
```
`conflict         openmpi/5.0.3/gcc-9.4.0`で類似のGCC 9.4.0用に作ったopenmpiと排他的に呼ばれるように指定している。

### トラブルシューティング: モジュールで指定されたコンパイラがリンクされていない
確認した範囲ではmoduleでGCC 11.4をロードしていないと、`/opt_share/openmpi/5.0.3/gcc-11.4.0/bin/mpif90 -v`を実行すると
```console
Using built-in specs.
COLLECT_GCC=/usr/bin/gfortran
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.4.0-1ubuntu1~20.04.2' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-9QDOt0/gcc-9-9.4.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)
```
が得られてOSデフォルトのGCC 9.4.0が呼び出されているのがわかる。つまり、このビルド方法だとコンパイラのリンクはstaticなものではなくてdynamicなものになっていて、環境に応じて変わっている部分があることを示している。
なので、**<font color="red">必ず`module load compiler/gcc/11.4.0`を事前に呼び出す必要がある。</font>**

### sudo使わない場合。
```bash:コマンド
module load compiler/gcc/11.4.0
../configure --prefix=/opt_share/openmpi/5.0.3/gcc-11.4.0 --with-tm=/opt/openpbs/23.06.06
```
で通るはず。