跳到主要內容

conda-forge 上的 macOS ARM 版本建置

·8 分鐘閱讀
Isuru Fernando
conda-forge/core 成員

一個新的平台 osx-arm64 已新增至 conda-forge 的建置矩陣中。osx-arm64 套件是為了在即將推出的 macOS arm64 處理器(以 Apple Silicon 名義銷售)上執行而建置的。此平台的安裝程式可以在這裡找到。

這將會安裝一個包含 python 和 conda 的 conda 環境。已安裝的 conda 將能夠安裝如 numpy, scipy 等套件。目前約有 100 個套件(在 10000 個套件中)是為此平台預先建置的。

所有這些套件都是在 conda-forge 目前的 macOS x86_64 基礎架構上建置的。為了做到這一點,我們對基礎架構進行了許多變更,包括 conda、conda-build、conda-smithy、constructor、conda-forge-ci-setup,以促進交叉編譯,這是編譯將在 host 平台(在本例中為 osx-arm64)上執行的套件的過程,而編譯是在 build 平台(在本例中為 osx-64linux-64)上完成的。

osx-arm64 是第一個完全使用 conda-build 的交叉編譯功能進行啟動的 conda 平台。先前,在新增一個新平台時,conda-build 是在新平台上使用現有的 python 和 pip 環境建置的。透過交叉編譯,當編譯器和系統根目錄在不同的平台上設定好時,現有的 conda-build 安裝(在本例中為 osx-64linux-64)就能立即開始建置套件。

用於 osx-arm64 的交叉編譯版本建置

為了交叉編譯 osx-arm64 的套件,我們需要編譯器。因此,我們首先建置了 clang=11.0.0.rc1,它支援以 osx-arm64 為目標。我們也建置了 compiler-rt=11.0.0.rc1 作為同時支援 osx-64osx-arm64 的通用版本建置。

連結器、封存器、otoolinstall_name_tool 是使用 Thomas Pöchtrager 的 cctools-port 專案 建置的。

我們遇到的其中一個問題是 macOS 11.0 Big Sur Beta 7 要求所有可執行檔和共享程式庫都必須進行臨時簽署,也就是在沒有驗證簽名的情況下進行簽署。在 cctools-port 開發人員的建議下,我們在 cctools-port 中加入了支援,可以使用 ldid 來簽署這些可執行檔,ldid 可以在 Linux 和 macOS 上使用進行簽署。

使用這些工具,我們建置的第一個交叉編譯套件是 libcxx,以促進 C++ 版本的建置。對於 osx-arm64 系統根目錄,我們使用了 Azure pipelines 和 Travis-CI 上已安裝的 MacOSX11.0.sdk。由於授權問題,我們無法發布此系統根目錄,但即使在 Linux 上也可以從 Apple 開發人員網站下載。

透過 clang,我們有了 C/C++ 編譯器,但缺少 Fortran 編譯器。我們使用了 用於 darwin-arm64 的 GCC 分支。首先,建置了一個交叉編譯器 (build == host != target)。使用該編譯器,我們建置了一個 cross-native 編譯器 (build != host == target),這讓我們可以使用如 libgfortran.dylib 等共享程式庫。

我們也在 conda 中的 rust 套件中加入了對交叉編譯 rust 程式的支援,並且在 Linux 上安裝 rust_osx-64 將為您提供一個可以為 osx-64 建置套件的編譯器。

由於我們之前沒有做過交叉編譯,許多套件需要更新。大多數都是微不足道的變更,我們稍後會將其自動化。這些變更包括取得更新的 config.sub 以識別新的 autotools 平台 arm64-apple-darwin20.0.0、使用環境變數 CMAKE_ARGS 將選項新增至 CMake 以正確設定工具鏈,以及更新配方以使用 cmake ${CMAKE_ARGS} ..。建置時執行測試也被停用,方法是使用環境變數 CONDA_BUILD_CROSS_COMPILATION 來保護如 make checkmake testctest 等命令。

交叉編譯 python 擴充功能非常棘手,因為 distutils 並沒有真正設定好來做到這一點。感謝 crossenv 專案,這在非官方的情況下獲得了支援,但有一些怪癖。透過 crossenv,我們可以在建置機器(在本例中為 osx-64linux-64)上執行一個 python,使其表現得像是在 osx-arm64 上一樣。crossenv monkey-patch 了幾個函數,例如 os.uname,並設定了如 _PYTHON_SYSCONFIG_DATA 等值,以使在 osx-64linux-64 上執行的 python 行為像 osx-arm64。一個問題是,monkey-patch sys.platform 無法運作,因此,如果 python 套件在其 setup.py 中使用 sys.platform 來區分作業系統,當您從 linux-64 進行交叉編譯時,這將導致意想不到的後果。因此,當為 osx-arm64 進行交叉編譯時,我們必須使用 osx-64 作為我們的 build 系統。請注意,使用 sysconfig.get_platform() 的套件將會取得正確的平台。

為了為 conda 建立安裝程式,我們需要一個獨立的 conda 可執行檔來啟動 conda 環境。對於其他平台,我們依賴 conda-standalone,它是一個使用 pyinstaller 建立的獨立 conda 可執行檔。由於 pyinstaller 不支援交叉編譯,我們決定使用 micromamba 作為啟動程式,並在 micromamba 中新增了功能,使其可以作為啟動程式運作。

如何將 osx-arm64 版本建置新增至 feedstock

以下所有變更都將由機器人完成,而機器人將傳送 PR 的套件是由 conda-forge-pinning 及其依賴項中的套件列表決定的。如果您想新增支援,請傳送 PR 將 feedstock 名稱新增到上面的列表中。在該 PR 合併後,您可以在 conda-forge 狀態頁面 監控狀態,如果特定的 PR 停滯不前,您可以傳送 PR 到 feedstock 以修復它。

以下說明適用於您想要手動新增支援的情況。

將以下內容新增至 conda-forge.yml(在 Linux 或 OSX 上),

build_platform:
osx_arm64: osx_64
test: native_and_emulated

您可以使用以下方式重新渲染:

conda smithy rerender

對於 python 套件,根據需要將以下一或多個項目新增至 recipe/meta.yaml,請注意,如果 numpycython 和/或 pybind11 也用於 host: 中,則您必須僅新增它們,

requirements:
build:
- python # [build_platform != target_platform]
- cross-python_{{ target_platform }} # [build_platform != target_platform]
- cython # [build_platform != target_platform]
- numpy # [build_platform != target_platform]
- pybind11 # [build_platform != target_platform]

對於 autotools 套件,將以下內容新增至 recipe/meta.yaml

requirements:
build:
- gnuconfig # [unix]

以及新增至 recipe/build.sh

# Get an updated config.sub and config.guess
cp $BUILD_PREFIX/share/gnuconfig/config.* .

對於 cmake 套件,將以下內容新增至 recipe/build.sh

cmake ${CMAKE_ARGS} ..

對於 meson 套件,將以下內容新增至 recipe/build.sh

meson ${MESON_ARGS} builddir/
注意

當交叉編譯時,Conda 會自動建立一個 交叉建置定義檔案,並將必要的引數新增至 ${MESON_ARGS} 以將 meson 指向該檔案。${MESON_ARGS} 僅在交叉編譯時定義,而不是在正常建置時定義。

對於 rust 套件,將以下內容新增至 recipe/meta.yaml

requirements:
build:
- {{ compiler('rust') }}

如果在 recipe/build.sh 中有類似 make check 這樣在交叉編譯時無法執行的行,請執行以下操作,

if [[ "$CONDA_BUILD_CROSS_COMPILATION" != "1" ]]; then
make check
fi

在這些變更之後,可能需要再次重新渲染。

一些有用的 jinja 變數,

  1. build_platform - BUILD_PREFIX 的 conda 子目錄。例如:linux-64
  2. target_platform - PREFIX 的 conda 子目錄。例如:osx-arm64

一些有用的環境變數,

  1. build_platform
  2. target_platform
  3. CONDA_BUILD_CROSS_COMPILATION - 如果是交叉編譯,則為 1
  4. CMAKE_ARGS - 傳遞給 cmake 的引數
  5. CC_FOR_BUILD - 建置平台的 C 編譯器
  6. CXX_FOR_BUILD - 建置平台的 C++ 編譯器
  7. HOST - 傳遞給 autoconf 的 host 三元組。例如:arm64-apple-darwin20.0.0
  8. BUILD - 傳遞給 autoconf 的 build 三元組。例如:x86_64-conda-linux-gnu

conda-forge.yml 中一些有用的設定選項

  1. build_platform - 將 build 子目錄對應到 host 子目錄的字典。例如

    build_platform:
    osx_arm64: osx_64
    linux_ppc64le: linux_64
    linux_aarch64: linux_64
  2. test_on_native_only - 一個布林值,用於關閉交叉編譯時的測試。如果測試不需要模擬(例如:檢查檔案是否存在),則 test_on_native_only: false 將在交叉編譯時也執行測試。

在本機建置

為了在本機建置,請在 $HOME/conda_build_config.yaml 中新增以下內容。

SDKROOT:
- /path/to/MacOSX11.0.sdk

之後,在 feedstock 根目錄的 .ci_support 資料夾中尋找您想要執行的設定。例如:.ci_support/osx_arm64_.yaml。然後執行,

conda build recipe -m .ci_support/osx_arm64_.yaml -c conda-forge -c conda-forge/label/rust_dev

這應該會開始一個新的 osx-arm64 版本建置。

測試套件

為了測試預定在未來 Apple Silicon 硬體上執行的套件,Apple 提供了一台名為 Developer Transition Kit (DTK) 的機器。Jonathan Helmus 和 Eli Rykoff 協助在 DTK 上測試這些套件。感謝 Eli Rykoff,我們現在每天執行這些套件的測試作為 cron job,這導致在我們的交叉編譯基礎架構中發現了幾個錯誤,以及在我們的配方中也發現了錯誤。

為了測試交叉編譯的配方,請將建置的 conda 套件傳輸到 host 並執行,

conda build --test /path/to/package -c conda-forge

如果沒有許多人的幫助,這項工作是不可能完成的,包括編譯器基礎架構的上游維護者(包括 conda、conda-build、cctools、tapi、cctools-port、ldid、llvm、clang、compiler-rt、openmp、libcxx、crossenv、rust、gcc-darwin-arm64)、conda-forge/help-osx-arm64 團隊(包括 Matt Becker、Eli Rykoff 和 Uwe Korn,他們傳送了 PR 來修復配方)、conda-forge/bot 團隊,以及所有審查和修復 PR 的 100 個 feedstock 的 conda-forge 維護者。

Isuru Fernando