Develop with pleasure!

福岡でCloudとかBlockchainとか。

Guixを使った決定性ビルド

先日、Bitcoin CoreをGitian Buildを使ってビルドする記事を書いた↓

techmedia-think.hatenablog.com

けど、最近以下のPRがBitcoin Coreにマージされた結果、Linux / Windows / macOSのバイナリのビルドがGNU Guixでできるようになった。

github.com

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

最後に、.zshrcbashの場合は.bashrc)に以下を追加。

source $HOME/.guix-profile/etc/profile
export GUIX_LOCPATH="$HOME/.guix-profile/lib/locale"

単純にテストしたい場合は、便利なdocker環境が用意されているので、それ使うのもあり。

macOS用のSDKを配置

Gitianと同様XcodeからSDKを抽出する必要がある。抽出方法は、↑のGuitianの記事参照。SDKbitcoin/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

このスクリプトが実行するのは、

  1. bitcoin/dependsの必要なソースコードのダウンロード
  2. guix time-machineコマンドを使ってコンテナ上でバイナリをビルド(初回はかなり時間がかかる)

デフォルトで以下の環境向けのバイナリが生成される:

↑を全部ビルドしたくない場合は、環境変数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