ビルド済みバイナリのrelocationには複数の解法がある
ビルド済みpackageを別マシンへ配るとき、単にarchiveを展開すれば動くとは限らない。compiled binaryやscriptには、build時のinstall prefix、library search path、interpreter pathなどが埋め込まれることがある。これを別のprefixへ移す relocation は、binary package managerの設計を左右する基本問題である。
ビルド済みパッケージ配布を一括りにしない で挙げたHomebrew、Spack、Conda、Nixは、この問題にかなり異なる方法で対処する。
Homebrew: relocatableかを判定し、無理ならBottleを使わない
Homebrew Bottleにはcellarという情報があり、BottleがbuildされたHOMEBREW_CELLARへの参照を持つかどうかを扱う。公式ドキュメントは、多くのcompiled softwareがbuild locationへの参照を持つため、単純にはどこへでも移せないと説明している。
Bottleが:anyまたは:any_skip_relocationなら別Cellarでも使える。一方、BottleのCellarが現在のHOMEBREW_CELLARと合わず、relocatableでもない場合、HomebrewはそのBottleを使わない。
ここでは「artifactを無理にどこでも動かす」より、relocation可能性をpackage metadataに持たせ、適合しなければprebuilt pathを諦める戦略になっている。
Spack: install prefixを書き換える
Spack Build Cacheは、installed specとdependenciesのinstall prefixをtarball化して配る。別マシンではinstall rootが異なる可能性があるため、Spackはbinaryやscriptに埋め込まれたpathをinstallation時にrelocateする。
ただし任意長のpathを書き換えられるわけではない。元のbinary中に確保されている領域より長いprefixへ移そうとすると失敗する場合があり、Spackはbuild時にinstall rootへpaddingを入れておくことを推奨している。relocation不能ならsource buildが必要になる。
これは binaryを書き換えて新しいprefixへ適合させる戦略である。
Conda: prefix replacementをpackage formatの一部にする
Conda packageでは、build prefixを含むfileをinfo/has_prefix等で記録し、install時にtarget prefixへ置換する。text fileだけでなくbinary fileも対象になり得る。
binary replacementでは長さを保つ必要があるため、Conda buildは長いplaceholder prefixを使い、install prefixがその範囲に収まるようにする。またLinux/macOSではobject file側を相対pathへ変換する処理も行う。
Spackと似ているが、Condaでは relocation metadataとplaceholder設計をpackage formatそのものへ組み込む点が特徴的である。
Nix: むしろstore pathをidentityの一部にする
Nixは別方向を取る。通常、artifactは/nix/store/<hash>-nameという固有pathに置かれ、dependencyへの参照もそのstore pathを前提にする。つまり「任意prefixへinstallして後から書き換える」より、content/build identityに結び付いた予測可能なstore namespaceへ配置することでrelocation問題を避ける方向である。
その代わり、Nix store同士でobjectをcopyする際にはstore directoryが同じことが基本条件になる。NixのHTTP Binary Cache Storeのstore設定にも、store pathをcopyできるのは同じstore設定同士という制約が記載されている。
relocation問題はABI問題とは別
pathを書き換えられたとしても、そのbinaryが別hostで動くとは限らない。glibc version、CPU instruction set、CUDA driver、OS APIなどの互換性は linux/amd64だけではビルド済みバイナリの互換性を表せない の問題である。
したがってprebuilt artifactのportabilityは少なくとも二段階に分けるべきである。
- location portability: 別prefixへ置けるか
- runtime compatibility: そのhost ABIで実行できるか
Homebrew、Spack、Conda、Nixの違いを見ると、relocationは単なる実装上の後処理ではなく、package identityとfilesystem layoutをどう設計するかという根本問題だと分かる。