概要
ここではcondaビルド済みパッケージを作成する際に、躓いた点について備忘録的にまとめていく。
目次
-
rattler-buildとは - build環境とhost環境
-
run_exportsについて -
stdlib("c")の設定 - cmakeにおけるbuildスクリプトの書き方基本
- テスト時におけるソースコードの利用
- platform-specific
noarchpackage -
meson-python利用時のビルド注意点
rattler-buidとは
prefix.dev社が開発しているRustを使って開発されている次世代のcondaパッケージ作成ツールである。conda-buildと同様なyaml形式のレシピ(meta.yaml ではなく recipe.yaml)を作成し、それに基づいてpypiにおけるwheel形式のような、conda用のビルド済み配布物(.condaファイル)の作成を行う。
詳しくはこちらのブログから: https://prefix.dev/blog/the_new_rattler_build
build環境とhost環境
condaビルドを行う際、build環境とhost環境がある。build環境はソースコードをビルドする環境、host環境はビルド済み配布物をインストールする環境である。そのため、host環境はtarget_platformとも呼ばれ、cross-compiling (例えば、linux-64からlinux-aarch64 (intelからARM)のコンパイルを行うこと)の際にも使用される。
また、それぞれの環境に必要なパッケージが異なり、build、host環境で必要となるパッケージはそれぞれ
- buildtimeに必要なパッケージ (
cmake,gcc,meson,pkg-configなどのコンパイラー・ビルドツール) - コンパイラ時にリンクするパッケージ (
libboost,glib,hdf5,pythonなどのsharedライブラリやpythonライブラリ)
である。
Python-C++ライブラリにおけるrecipe.yamlのrequirementセクションの例として以下のように設定する。
requirements:
build:
- ${{ compiler("c") }}
- ${{ compiler("cxx") }}
- ${{ stdlib("c") }}
- git
- cmake
- ninja
- pkg-config
host:
- pip
- python
- numpy
- cython
- setuptools
- setuptools-scm
- scikit-build-core
- scikit-build
- libboost-devel
- hdf5
このとき、libboost-develパッケージをbuildセクションに移動させると、build時に以下のようなエラーが表示される。
file Multiple conflicting paths found for libc++.1.dylib:
$BUILD_PREFIX/lib//libc++.1.dylib
$PREFIX/lib/libc++.1.dylib
これはコンパイル時のリンクで-Wl,-rpath,$BUILD_PREFIX/lib $BUILD_PREFIX/lib/libboost_filesystem.dylibと-ldl $BUILD_PREFIX/lib/libboost_atomic.dylibが追加されるため、conflictionが起きていると思われる。
参考: https://prefix-dev.github.io/rattler-build/latest/compilers/#cross-compilation
run_exportsについて
ビルド時にリンクしたライブラリ(shared library等)はruntime環境でも必要になる。そのため、hostやbuildのrequirementで指定したパッケージをruntimeでのrequirementにも自動で追加させるためにrun_exportsが必要となる。
requirements:
run_exports: my-package
例えば、numpyをhost環境のrequirementとして指定した場合、runtime環境でのrequirementに明示的に指定する必要はない。レシピ作成者がリンク時に利用したものと同じバージョンのnumpyを自動的にruntime環境で指定してくれる。
参考: https://prefix-dev.github.io/rattler-build/latest/reference/recipe_file/#run-exports
stdlib("c")の設定
build環境で${{ compiler("c") }}を指定する場合、conda-forgeにおいてC言語のstandard libraryをstdlib("c")として明示的に指定する必要がある。
参照: https://conda-forge.org/news/2024/03/24/stdlib-migration/
conda-forgeにおいてcompilerやstdlibは、conda_build_config.yamlにあるデフォル値によって決まるが、ローカルなPC環境でratller-buildを用いる場合、variants.yamlを手動で作成する必要がある。LinuxとmacOSにおけるC言語のstdlibのみを設定する場合、以下のようになる。
c_stdlib:
- if: linux
then: sysroot
- if: osx
then: macosx_deployment_target
c_stdlib_version:
- if: linux
then: 2.17
- if: osx and x86_64
then: 10.13
- if: osx and arm64
then: 11.0
また、このファイルにpythonのバージョンを複数設定することで、github actionにおけるmatrixのような、複数ビルドも可能となる。(conda-forgeのconda_build_config.yamlのpythonセクションを参照)
cmakeにおけるbuildスクリプトの書き方基本
cmakeをbuild requirementに指定した場合、CMAKE_ARGSなど自動的に設定される。
また、cmakeにおけるPython_EXECUTABLEを、Host環境上のpythonに指定する必要がある。
以下にcmakeとninjaを用いたビルド・コンパイルスクリプト(build.sh)の例を示す。
#!/bin/bash
# CMake extra configuration:
extra_cmake_args=(
-G Ninja
-DCMAKE_INSTALL_LIBDIR=lib
-DBUILD_SHARED_LIBS=ON
# Python bindings
-DPython_EXECUTABLE="$PYTHON"
-DPython3_EXECUTABLE="$PYTHON"
)
cmake ${CMAKE_ARGS} "${extra_cmake_args[@]}" \
-DCMAKE_PREFIX_PATH=$PREFIX \
-DCMAKE_INSTALL_PREFIX=$PREFIX \
-B build -S $SRC_DIR
# Build and install
ninja
ninja install
ビルド時のデフォルト環境変数についてはこちらを参照
参考: https://conda-forge.org/docs/maintainer/knowledge_base/#using-cmake
テスト時におけるソースコードの利用
ビルド配布物を作成後、rattler-buildではテストが行われる。その際、ビルド環境は削除され、ソースコードへのパスである$SRC_DIRも利用できない。
テスト環境へ、ソースコードにあるテストスクリプトや設定ファイルをコピーするためにはrecipe.yamlのtestセクションを以下のように設定する。
tests:
- script:
- pytest
files:
source:
- python/tests/
- pyproject.toml
requirements:
run:
- pytest
ここではpytestを用いたテストスクリプトを想定している。script:におけるfile: sources:にテスト時必要となるファイルをソースフォルダからコピーできる。
参考: https://prefix-dev.github.io/rattler-build/latest/reference/recipe_file/#extra-test-files
Platform-specific noarch package
noarchパッケージとはpure-PythonパッケージのようなCPU architectureやOSに依存しない(コンパイラされたバイナリーファイルを含まない)パッケージである。しかし、例えば、activationスクリプトを含める場合や、runtime依存にOS依存がある場合、プラットフォーム(OS)依存のnoarchパッケージが必要となる。
このとき、conda-forgeでのビルドであればこれを達成できる。
方法
recipe.yamlのrun requirementにてターゲットOSを示すvirtual packages: __linux, __osx, __win, __unixを加える。(__unixはLinuxとMacOS両方を示す。)
build:
number: 0
noarch: python
requirements:
# ...
run:
- python >=${{ python_min }}
- numpy
- if: linux
then:
- __linux
- linux-only-dependency
- if: osx
then:
- __osx
- osx-only-dependency
- if: win
then:
- __win
- windows-only-dependency
conda-forgeにおいて、noarchだとlinux_64のCIランナーしかデフォルトで走らないが、複数のビルドplatformを走らせたい場合はconda-forge.yamlのnoarch_platformsに追加する。
noarch_platforms:
- linux_64
- osx_64
- win_64
meson-python利用時のビルド注意点
meson-pythonをbuild backendに持つパッケージをインストールする際のコマンドは
python -m pip install --no-deps --no-build-isolation .
であるが、cross build (cross-compile)をする際は注意が必要である。
conda-forgeではosx-arm64用のビルドをする際はosx-64からcross-compileを利用して、行われる。
上記のままだと、meson側がそれを検知できずにnative buildのまま処理されてしまいエラーになってしまう。
conda-forgeではmesonパッケージが読み出された場合、環境変数$MESON_ARGSが自動的に定義され、その中に--cross-file $BUILD_PREFIX/meson_cross_file.txtが記述されている。
このため、このオプションをうまくビルドオプションとして渡す必要がある。
matplolib-feedstockを参考にすると、pypaのbuild(python-build)を用いたbuildスクリプトを記述している:
#!/bin/bash
set -ex
export AR=$GCC_AR
# binary is called `pkg-config`, but package metadata is under `lib/pkgconfig`
export MESON_ARGS="${MESON_ARGS} --pkg-config-path=${PREFIX}/lib/pkgconfig"
# meson-python already sets up a -Dbuildtype=release argument to meson, so
# we need to strip --buildtype out of MESON_ARGS or fail due to redundancy
MESON_ARGS_REDUCED="$(echo $MESON_ARGS | sed 's/--buildtype release //g')"
# -wnx flags mean: --wheel --no-isolation --skip-dependency-check
$PYTHON -m build -w -n -x \
-Cbuilddir=builddir \
-Csetup-args=${MESON_ARGS_REDUCED// / -Csetup-args=} \
|| (cat builddir/meson-logs/meson-log.txt && exit 1)
$PYTHON -m pip install --find-links dist cherab-lhd
ここで、setup-argsに$MESON_ARGSを改良した変数$MESON_ARGS_REDUCEDを渡すことで、cross buildが可能となる。
meson-pythonを利用しているパッケージビルドは常に、build.shを用いたカスタムビルドを行うほうが良いだろう。
conda-forgeにおけるcross-compileに関する注意点はこちらを参照:
https://conda-forge.org/docs/maintainer/knowledge_base/#cross-compilation