先日、Bitcoin CoreをGitian Buildを使ってビルドする記事を書いた↓
techmedia-think.hatenablog.com
けど、最近以下のPRがBitcoin Coreにマージされた結果、Linux / Windows / macOSのバイナリのビルドがGNU Guixでできるようになった。
GitianとGuix
Gitianは元々Bitcoin Coreで再現性のあるビルドをできるよう開発されたツール。ただこのツール、VMを使ってバイナリをビルドするんだけど、Ubuntuに結構依存している。Bitcoinのような管理者のいないコードベースのエコシステムにおいては、監査可能性や透明なバイナリを提供するのが大切で、環境依存はなるべく少ない方が望ましい。
そのため既存のGitianベースのビルドの仕組みを完全に置き換えるのに、GNU Guixベースのビルドを導入しようという流れみたい。Guixはクロスプラットホームのパッケージ管理機能を提供するオープンソースのツールで、再現性のあるビルドをする機能もその機能の一部。
Guixを使ったBitcoin Coreのビルド
今回はGuixを使ってBitcoin CoreのLinux / Windows / macOSバイナリを生成してみよう(環境はUbuntu 20.04 LTS)。ガイドはこちら。
Guixのインストール
インストールガイドを参考にインストールする。今回はダウンロード、インストール、初期設定を自動で行うスクリプトを使ってインストールする。
準備として、Guix開発者のPGP公開鍵をインポートする。
$ wget 'https://sv.gnu.org/people/viewgpg.php?user_id=15145' -qO - | sudo -i gpg --import -
続いて、スクリプトを使ってインストール。
$ cd /tmp $ wget https://git.savannah.gnu.org/cgit/guix.git/plain/etc/guix-install.sh .. $ chmod +x guix-install.sh $ sudo ./guix-install.sh ..
インストールが終わったら、日本語環境なのでglibc-locales
をインストールしとく。
$ guix install glibc-locales
最後に、.zshrc
(bashの場合は.bashrc
)に以下を追加。
source $HOME/.guix-profile/etc/profile export GUIX_LOCPATH="$HOME/.guix-profile/lib/locale"
単純にテストしたい場合は、便利なdocker環境が用意されているので、それ使うのもあり。
macOS用のSDKを配置
Gitianと同様XcodeからSDKを抽出する必要がある。抽出方法は、↑のGuitianの記事参照。SDKをbitcoin/depends/SDKs
以下に展開する。
$ mkdir <cloneしたbitcoinのパス>/depends/SDKs $ cp Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz <cloneしたbitcoinのパス>/depends/SDKs $ cd <cloneしたbitcoinのパス>/depends/SDKs $ tar -xvf Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz
ビルド
Bitcoin CoreにはGuixを使ってビルドするためのスクリプトが用意されているので、基本的にそれを叩くだけ↓
$ ./contrib/guix/guix-build.sh
このスクリプトが実行するのは、
bitcoin/depends
の必要なソースコードのダウンロードguix time-machine
コマンドを使ってコンテナ上でバイナリをビルド(初回はかなり時間がかかる)
デフォルトで以下の環境向けのバイナリが生成される:
- x86_64-linux-gnu
- arm-linux-gnueabihf
- aarch64-linux-gnu
- riscv64-linux-gnu
- x86_64-w64-mingw32
- x86_64-apple-darwin18
↑を全部ビルドしたくない場合は、環境変数HOSTS
にビルドしたいものをスペース区切りでセットすればいい。
現状は現状のローカルリポジトリの最新コミットに対してビルドが実行されるっぽい。ビルドを実行すると、x86_64-linux-gnu
であればdistsrc-6a726cb534ed-x86_64-linux-gnu
といったディレクトリが作られ、ソースがコピーされる。6a726cb534ed
はコミットのハッシュ値の一部で、同じコミットのビルドを実行しようとすると、これらのディレクトリがあるとエラーで実行されない。特定のタグのビルドが作りたければ、そのタグをチェックアウトして実行するんだろう。
実際に実行されるguix time-machine
コマンドは↓
time-machine environment --manifest="${PWD}/contrib/guix/manifest.scm" \ --container \ --pure \ --no-cwd \ --share="$PWD"=/bitcoin \ --share="$DISTSRC_BASE"=/distsrc-base \ --share="$OUTDIR"=/outdir \ --expose="$(git rev-parse --git-common-dir)" \ ${SOURCES_PATH:+--share="$SOURCES_PATH"} \ --max-jobs="$MAX_JOBS" \ ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \ ${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \ -- env HOST="$host" \ MAX_JOBS="$MAX_JOBS" \ SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \ ${V:+V=1} \ ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \ DISTSRC="$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")" \ OUTDIR=/outdir \ bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh"
ビルドは分離されたコンテナで実行される。使われるコンテナには、各環境毎のディレクトリ内のマニュフェストファイル(例:distsrc-6a726cb534ed-x86_64-linux-gnu/contrib/guix/manifest.scm
)で定義されたパッケージがセットアップされる。
Guixでコンテナがセットアプされると、そのコンテナ上で↑の最後に記載されているコマンドbash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh"
が実行される。
ビルドが終わる、ホストOS側のoutput
ディレクトリ以下に各環境毎のバイナリが生成されている。
$ ls output bitcoin-6a726cb534ed-aarch64-linux-gnu-debug.tar.gz bitcoin-6a726cb534ed-riscv64-linux-gnu.tar.gz bitcoin-6a726cb534ed-aarch64-linux-gnu.tar.gz bitcoin-6a726cb534ed-win-unsigned.tar.gz bitcoin-6a726cb534ed-arm-linux-gnueabihf-debug.tar.gz bitcoin-6a726cb534ed-win64-debug.zip bitcoin-6a726cb534ed-arm-linux-gnueabihf.tar.gz bitcoin-6a726cb534ed-win64-setup-unsigned.exe bitcoin-6a726cb534ed-osx-unsigned.dmg bitcoin-6a726cb534ed-win64.zip bitcoin-6a726cb534ed-osx-unsigned.tar.gz bitcoin-6a726cb534ed-x86_64-linux-gnu-debug.tar.gz bitcoin-6a726cb534ed-osx64.tar.gz bitcoin-6a726cb534ed-x86_64-linux-gnu.tar.gz bitcoin-6a726cb534ed-riscv64-linux-gnu-debug.tar.gz src