🐧

Gentoo Install Battle Clang edition

2024/04/14に公開

初めに

私は現在Gentoo LinuxのデフォルトのコンパイラをClangにして使っています。
今回は、ClangをブートストラップしてGentooをインストールする過程を書きたいと思います。
公式のインストールガイドにある情報や、Gentooに特有でない知識などの説明は省くことが多いです。ご了承ください。

インストールメディアの準備

ディスクの準備

パーティショニング

  • サイズを+2Gのように相対的に指定できる
  • wを実行し、確認に答えて初めてディスクに変更が書き込まれる
    ので、gdiskがおすすめです。
gdisk /dev/sda

例として、以下のようなレイアウトを使います。

デバイスパス マウントポイント ファイルシステム 概要
/dev/sda1 /boot vfat EFI システム パーティション(ESP)
/dev/sda2 なし swap スワップパーティション
/dev/sda3 / btrfs ルートパーティション

フォーマット

ESPはFAT32でフォーマットします。

mkfs.fat -F 32 /dev/sda1

スワップパーティションを初期化します。

mkswap /dev/sda2

BTRFSを使います。

mkfs.btrfs /dev/sda3

マウント

ルートパーティションをマウントします。zstdを使った透過的自動圧縮を有効化します。

mount -o compress=zstd /dev/sda3 /mnt/gentoo

ブートパーティションもマウントします。

mount --mkdir /dev/sda1 /mnt/gentoo/boot

スワップパーティションを有効化します。

swapon /dev/sda2

stageファイルのダウンロード

プロファイルは

  • amd64
  • OpenRC
  • nomultilib
    です。
    OpenRCを使うかsystemdを使うかは好みです。慣れていないならsystemdで良いと思います。
    Wineなどの32-bitのサポートが必要なソフトウェアを使いたい場合はmultilibを使ってください。

事前にカレントディレクトリを/mnt/gentooにしておきます。

cd /mnt/gentoo

wgetを使います。

wget https://url/to/mirror/releases/amd64/autobuilds/<release>/stage3-amd64-nomultilib-openrc-<release>.tar.xz

<release>は日付になっています。適宜置き換えてください。

stageファイルを展開します。

tar xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner

コンパイルオプションの設定

make.confを編集します。この時点ではvinanoが使えたはずですが、chroot先ではnanoしか入っていないです。

nano /mnt/gentoo/etc/portage/make.conf
/mnt/gentoo/etc/portage/make.conf
COMMON_FLAGS="-O2 -pipe -march=native"

MAKEOPTS="-j12 -l12"

PORTAGE_SCHEDULING_POLICY="idle"

EMERGE_DEFAULT_OPTS="--jobs=12 --load-average=12 --keep-going --quiet-build --with-bdeps=y --complete-graph=y"

12は自分の環境に合わせて置き換えてください。

PORTAGE_SCHEDULING_POLICY

このオプションをidleに設定すると、Portageプロセスの優先度が非常に低くなるので、
裏でPortageを走らせながら他の作業をするときに、画面のカクつきなどの違和感が減ります。

EMERGE_DEFAULT_OPTS

emergeコマンドにデフォルトで渡されるオプションです。

--keep-going

あるパッケージのビルドに失敗したときでも可能ならばビルドを継続します。
GCCだとあまり失敗することはないですがClangだとときどき失敗するのであった方が良いです。

--quiet-build

ビルドログを表示させないようにします。エラーが起きたら表示されます。

less /var/tmp/portage/category/package/temp/build.log

でログを読めます。

--with-bdeps=y

パッケージのビルドだけに必要なパッケージも保持します。

--complete-graph=y

依存関係について深く考慮させます。
依存関係の計算にかかる時間がかなり長くなります。

ミラーの設定

mirrorselectを使います。

mirrorselect -i -o >> /mnt/gentoo/etc/portage/make.conf

chroot

DNS情報をコピーします。

cp --dereference /etc/resolv.conf /mnt/gentoo/etc/

fstabを生成します。

genfstab -U /mnt/gentoo >> /mnt/gentoo/etc/fstab

chrootします。

arch-chroot /mnt/gentoo
source /etc/profile

Portageの設定

Gentoo ebuild repositoryの設定

repos.confディレクトリを作成します。

mkdir --parents --verbose /etc/portage/repos.conf

Portageに付属しているファイルをコピーします。

cp /usr/share/portage/config/repos.conf /etc/portage/repos.conf/gentoo.conf

Repositoryの同期

Gentoo ebuild repositoryのスナップショットをインストールします。

emerge-webrsync

ニュースがあれば必ず読みましょう。

eselect news list
eselect news read

プロファイルの選択

現在のプロファイルを表示します。

eselect profile show

これが正しくなければ、

eselect profile list
eselect profile set 2

などとして適切なプロファイルを選んでください。

システムのアップグレード

emerge --ask --verbose --update --deep --newuse @world

短縮形:

emerge -avuDN @world

Clangのブートストラップ

おそらく一番時間がかかります。

USE変数の設定

まずUSE変数を設定します。

/etc/portage/make.conf
USE="-clang -llvm"

これを設定しなかったときはsys-libs/llvm-libunwindのインストールに失敗しました。

Clang toolchainのビルド(一回目)

GCCでClangをインストールします。

emerge --ask --verbose clang llvm compiler-rt llvm-libunwind lld

かなり時間がかかります。お茶でも飲んで休憩しましょう。

ClangでClangをインストールするための設定

Gentooではパッケージ毎にコンパイルオプションを設定できます。
それを使って先程インストールしたClangでClang自身をビルドするための設定をします。

まず/etc/portage/envディレクトリを作ります。

mkdir /etc/portage/env

そして以下のようなファイルを作成します。

/etc/portage/env/compiler-clang
COMMON_FLAGS="-O2 -pipe -march=native -flto=thin"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS} -stdlib=libstdc++"
LDFLAGS="-stdlib=libstdc++ -fuse-ld=lld -rtlib=compiler-rt -unwindlib=libunwind -Wl,-O2 -Wl,--as-needed"

CC="clang"
CXX="clang++"
LD="ld.lld"
AR="llvm-ar"
NM="llvm-nm"
RANLIB="llvm-ranlib"
STRIP="llvm-strip"
OBJCOPY="llvm-objcopy"
OBJDUMP="llvm-objdump"

-flto=thin

ThinLTOを有効化します。
ビルド時間は長くなります。

-stdlib=libstdc++

LLVM製のlibc++ではなくGNU製のlibstdc++を使います。
両者のABIに互換性がなく、システムが壊れる可能性があるためです。

-fuse-ld=lld

GNU製のbfdではなくLLVMのlldを使います。こちらの方が速いです。

-rtlib=compiler-rt

ランタイムライブラリにLLVMのcompiler-rtを使います。

-unwindlib=libunwind

LLVMのlibunwindを使います。

また、make.confを編集し、USE変数を変更します。

/etc/portage/make.conf
USE="clang llvm default-compiler-rt default-lld llvm-libunwind -default-libcxx"

次に、package.env/compiler-clangを作成し、上記の設定を必要なパッケージに適用します。

mkdir /etc/portage/package.env
/etc/portage/package.env/compiler-clang
sys-devel/llvm compiler-clang
sys-libs/libcxx compiler-clang
sys-libs/libcxxabi compiler-clang
sys-libs/compiler-rt compiler-clang
sys-libs/compiler-rt-sanitizers compiler-clang
sys-libs/llvm-libunwind compiler-clang
sys-devel/lld compiler-clang
sys-devel/clang compiler-clang

Clang toolchainのビルド(二回目)

emerge --ask --verbose clang llvm libcxx libcxxabi compiler-rt llvm-libunwind lld

設定が上手くいっていればこれらのパッケージはClangでコンパイルされるはずです。
これも時間がかかります。コーヒーでも飲んで休憩しましょう。

Clangをデフォルトのコンパイラとして使うための設定

make.confを編集します。と言っても、先程env/compiler-clangに書いた内容を持ってくるだけです。

/etc/portage/make.conf
COMMON_FLAGS="-O2 -pipe -march=native -flto=thin"
CXXFLAGS="${COMMON_FLAGS} -stdlib=libstdc++"
LDFLAGS="-stdlib=libstdc++ -fuse-ld=lld -rtlib=compiler-rt -unwindlib=libunwind -Wl,-O2 -Wl,--as-needed"

CC="clang"
CXX="clang++"
LD="ld.lld"
AR="llvm-ar"
NM="llvm-nm"
RANLIB="llvm-ranlib"
STRIP="llvm-strip"
OBJCOPY="llvm-objcopy"
OBJDUMP="llvm-objdump"

フォールバック環境の設定

多くのパッケージはClangでコンパイルできますが、失敗するものもあります。
そういった場合にGCCを使うことができるように設定します。
env/compiler-gccを作成します。

/etc/portage/env/compiler-gcc
COMMON_FLAGS="-O2 -pipe -march=native"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
LDFLAGS="-fuse-ld=bfd -Wl,-O2 -Wl,--as-needed"

CC="gcc"
CXX="g++"
LD="ld.bfd"
AR="gcc-ar"
NM="gcc-nm"
RANLIB="gcc-ranlib"
STRIP="strip"
OBJCOPY="objcopy"
OBJDUMP="objdump"

Clangで失敗したパッケージはpackage.env/compiler-gccに書いていけば良いです。
私の環境で失敗したパッケージを載せておきます。perlのライブラリが多いです。

/etc/portage/package.env/compiler-gcc
media-libs/alsa-lib compiler-gcc
dev-perl/Socket6 compiler-gcc
dev-perl/Net-SSLeay compiler-gcc
dev-perl/Clone compiler-gcc
perl-core/Compress-Raw-Zlib compiler-gcc
dev-perl/HTML-Parser compiler-gcc
dev-perl/XML-Parser compiler-gcc
app-i18n/libskk compiler-gcc
dev-perl/Sub-Name compiler-gcc

ここでシステムをアップグレードします。

emerge -avuDN @world

make.confの設定

USE

/etc/portage/make.conf
USE="clang llvm default-compiler-rt default-lld llvm-libunwind -default-libcxx X wayland opengl vulkan elogind dbus jpeg gtk pulseaudio pipewire -qt -gnome -kde -branding -bluetooth"

自分の環境に合わせて設定してください。

CPU_FLAGS

app-portage/cpuid2cpuflagsをインストールします。

emerge --ask --verbose --oneshot cpuid2cpuflags

出力をpackage.use/00cpu-flagsにリダイレクトします。

echo "*/* $(cpuid2cpuflags) > /etc/portage/package.use/00cpu-flags

VIDEO_CARDS

私はIntelのGPUを使っているので

/etc/portage/make.conf
VIDEO_CARDS="intel"

としました。

ACCEPT_LICENSE

基本的には

/etc/portage/make.conf
ACCEPT_LICENSE="-* @FREE"

として、自由でないライセンスのソフトウェアをインストールするときに
package.license以下に

/etc/portage/package.license/firmware
sys-kernel/linux-firmware @BINARY-REDISTRIBUTABLE
sys-firmware/intel-microcode intel-ucode

とするのが面倒ですが良いと思います。

GRUB_PLATFORMS

amd64でUEFIなら

/etc/portage/make.conf
GRUB_PLATFORMS="efi-64"

タイムゾーンの設定

日本に住んでいるなら

echo "Asia/Tokyo" > /etc/timezone
emerge --config sys-libs/timezone-data

です。

ロケールの設定

/etc/locale.genen_US.UTF-8ja_JP.UTF-8をアンコメントします。

/etc/locale.gen
en_US.UTF-8
ja_JP.UTF-8

ロケールを生成します。

locale-gen

この時点ではen_US.UTF-8を選択してください。

eselect locale list
eselect locale set 4

ここで環境をリロードします。

env-update && source /etc/profile

ファームウェアのインストール

emerge --ask --verbose linux-firmware

AMD製のCPUであればsys-kernel/linux-firmwareにマイクロコードが入っています。
IntelのCPUなら別途マイクロコードをインストールしてください。

emerge --ask --verbose intel-microcode

ライセンスのエラーが出たら、package.licenseを設定してください。

installkernelのインストール

2024年2月26日から、ソースからカーネルをインストールする場合は明示的にsys-kernel/installkernelをインストールする必要があります。

emerge --ask --verbose installkernel

カーネルのインストール

完全手動でやります。

カーネルのソースをインストールします。慣れていないならsys-kernel/gentoo-sourcesが良いと思います。

emerge --ask --verbose gentoo-sources

カーネルを選択します。

eselect kernel list
eselect kernel set 1

ソースがあるディレクトリに移動します。

cd /usr/src/linux

TUIで設定できます。

make menuconfig

完全に環境によるのでconfigについては省略します。
https://wiki.gentoo.org/wiki/Handbook:AMD64/Full/Installation/ja#.E5.88.A5.E3.81.AE.E6.96.B9.E6.B3.95:_.E3.83.9E.E3.83.8B.E3.83.A5.E3.82.A2.E3.83.AB.E8.A8.AD.E5.AE.9A を見てください。

configが終わったら、コンパイルします。ここでもClangを使えます。

make LLVM=1 LLVM_IAS=1 KFLAGS="-O2 -pipe -march" -j12 && make modules_install

LLVM=1は

CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \
  OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump READELF=llvm-readelf \
  HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld

と同じです。

-j12についてはMAKEOPTSと同じです。

カーネルのconfigに書いていなければここで

  • LTOなし
  • FullLTO
  • ThinLTO
    のどれかを選択します。
    ThinLTOにします。

終わったらインストールします。

make install

initramfsのビルド

sys-kernel/dracutをインストールします。

emerge --ask -verbose dracut

initramfsをビルドします。

dracut --kver=6.6.21-gentoo

--kverは適宜置き換えてください。

ネットワークの設定

ホストネームを設定します。

echo your_hostname > /etc/hostname

ここではnet-misc/dhcpcdをインストールします。自分の環境に合わせて設定してください。

emerge --ask --verbose dhcpcd

起動時に自動的にスタートするようにします。

rc-update add dhcpcd default

hostsファイルを設定します。

/etc/hosts
127.0.0.1    your_hostname.homenetwork your_hostname localhost

システムの設定

パスワードを設定します。

passwd

OpenRCの設定をします。
私は以下をアンコメントしています。

/etc/rc.conf
rc_parallel="YES"
rc_logger="YES"
rc_send_sighup="YES"
rc_timeout_stopsec="10"
rc_send_sigkill="YES"

rc_parallel="YES"

可能ならサービスを並列起動します。

rc_logger="YES"

起動時のログを有効化します。デフォルトでは/var/log/rc.logに出力されます。

rc_send_sighup="YES"

stopsigとsigcontの直後にcgroupのプロセスにsighupを送ります。

rc_timeout_stopsec="10"

sigcontとsighupを送った後に、cgroupのプロセスにsigkillを送るまでの遅延時間です。単位は秒です。

rc_send_sigkill="YES"

cgroupのプロセスにsigkillを送ります。

キーボードの設定をします。US配列なら変えなくても動くと思います。
JIS配列なら

/etc/conf.d/keymaps
keymap="jp106"

とします。

/bin/shはデフォルトではbashへのシンボリックリンクになっています。ここではdashに変更します。

echo "app-alternatives/sh -bash dash" > /etc/portage/package.use/sh
emerge --ask --verbose --oneshot app-alternatives/sh

ロガーのインストール

とりあえず動かしたいならapp-admin/sysklogdが良いと思います。

emerge --ask --verbose sysklogd

起動時に自動でスタートさせます。

rc-update add sysklogd default

cronデーモンのインストール

色々ありますがsys-process/cronieを使います。

emerge --ask --verbose cronie
rc-update add cronie default

時刻同期の設定

net-misc/chronyをインストールします。

emerge --ask --verbose chrony

/etc/chrony/chrony.confに設定を書きます。

/etc/chrony/chrony.conf
server ntp1.jst.mfeed.ad.jp
rc-update add chronyd default

ファイルシステムツールのインストール

使っているファイルシステムのものをインストールします。
sys-fs/e2fsprogs(ext4)は既にインストールされているはずです。
ここではsys-fs/btrfs-progs(btrfs)とsys-fs/dosfstools(vfat)をインストールします。

emerge --ask --verbose btrfs-progs
emerge --ask --verbose dosfstools

ブートローダのインストール

GRUBを使います。

emerge --ask --verbose grub
grub-install --target=x86_64-efi --efi-directory=/boot --removal

基本的にはこれで問題ないはずです。

grub-mkconfig -o /boot/grub/grub.cfg

再起動

上手くいったことを願って再起動します。

まずchroot環境を出ます。

exit

パーティションをアンマウントします。

umount -R /mnt/gentoo

再起動します。

reboot

長くなったのでインストール後の環境構築は別の記事で書きたいと思います。

Discussion