linux/amd64だけではビルド済みバイナリの互換性を表せない
ビルド済みartifactの互換性をlinux/amd64のようなOSとCPU architectureだけで表すと、実際のruntime compatibilityを取りこぼすことがある。
ビルド済みバイナリのrelocationには複数の解法がある で扱った「別prefixへ置けるか」と、この問題は別である。pathを完全にrelocateできても、host側のABI条件を満たさなければbinaryは動かない。
Linuxでは典型的にglibc versionが問題になる。ほかにもCPU instruction set、kernel feature、GPU driver/CUDA compatibility、C++ runtime、OpenSSL等のlibrary ABIが影響し得る。macOSでもOS versionやarchitecture、Rosetta利用可否などが配布matrixに入る。
Condaはこの問題をsolverの入力としてかなり明示的に表している。__glibc、__cuda、__osx、__linux、archspec等をvirtual packageとして検出し、通常のpackage dependencyと同じようにsolverが参照できる。例えばあるpackageが特定のglibc以上を必要とする場合、host側で検出された__glibc versionを使って解決可否を判断できる。
これは「platform」を単なるdownload URL選択用のtupleではなく、binary compatibility constraintの集合として扱う設計である。
一方、aquaのsupported_envsは主としてGOOS/GOARCH単位で対応platformを表す。Rosetta 2やWindows ARM emulationを表す補助もあるが、glibcやCUDAのようなruntime ABIを一般的なsolver constraintとして解決するモデルではない。これはaquaがaquaはupstream releaseを正規化するパッケージマネージャーと捉えられるとして、upstream release assetを選択することを中心にしているためでもある。
Homebrew BottleでもOS/architectureだけでなく、default prefixやformula側のpour_bottle?条件によってBottle利用可否を変えられる。Spackではcompiler、variants、dependency versionsを含むconcretized specがbinary cache selectionに強く関わるため、より細かなbuild configurationがartifact identityへ入る。
ここからprebuilt package managerには二つの方向性が見える。
- coarse platform selection:
linux/amd64のような少数のrelease assetから選ぶ。単純でupstream releaseと相性がよい。 - compatibility solving: ABIやruntime featureまでmetadataとして持ち、solverが利用可能artifactを決める。表現力は高いがmetadata・solver・build matrixが複雑になる。
後者を細かくしすぎると、OS × architecture × libc × compiler × CPU feature × runtime × optional featureの組み合わせが増え、prebuilt artifactの数も増える。これは prebuilt binaryはbuild matrixが増えるほどcache効率が落ちる の問題になる。
したがって「Linux向けbinaryを配布している」という事実だけではportabilityを判断できない。最低限、どのABI baselineでbuildされたのか、それをpackage managerがmetadataとして認識しているのか、それともupstream側の暗黙の互換性規約に依存しているのかを見る必要がある。