Windows + MinGW + MSYS2でRust実装のPythonライブラリーを作る
はじめに
RustでPythonライブラリーを作成する手段としてPyO3があり、過去の記事「PyO3 + PoetryでPythonからRust実装を使う」でやり方を解説したが、暗黙的にLinuxやmacOSを対象としていた。
ライブラリーによってはWindows向けのWheel配布が必要なこともあるため、この記事ではGitHub Actionsを使ってWindows + MinGW + MSYS2環境でPyO3とRustを使ったPythonライブラリーのビルド方法について述べる。
この記事で利用するコードは下記のGitHubリポジトリーでまとめて公開している。
GitHub Actionsの設定
さっそくGitHub Actionsの設定を解説する。ただし次のようなmatrixが定義されているものとする。
| 名前 | 意味 |
|---|---|
msystem |
MSYS2のシステムでMINGW64など[1]
|
arch |
MSYS2でインストールする依存のアーキテクチャーでx86_64やaarch64など |
path |
MinGWがインストールされているファイルパスでmingw64など |
rust_target |
Rustのコンパイルターゲットでx86_64-pc-windows-gnuやx86_64-pc-windows-msvc
|
なお最終的なGitHub Actionsの設定は下記にある。
1. MSYS2をインストール
msys2/setup-msys2を使うことで簡単にGitHub ActionsのWindowsにMSYS2をインストールできる。今回は最終的にPythonライブラリーを作るのが目的なため、Python関連のライブラリーをインストールしておく必要がある。ただしRustはこのあとの手順で公式のRustupからインストールするため、ここでインストールしなくてもよい。
- name: Install msys2 and dependencies
uses: msys2/setup-msys2@v2
with:
update: true
path-type: inherit
msystem: ${{ matrix.msystem }}
install: >-
mingw-w64-${{ matrix.arch }}-toolchain
mingw-w64-${{ matrix.arch }}-python
mingw-w64-${{ matrix.arch }}-cython
mingw-w64-${{ matrix.arch }}-python-pip
mingw-w64-${{ matrix.arch }}-python-poetry
mingw-w64-${{ matrix.arch }}-python-maturin
注意として、path-type: inheritとしてPATH環境変数のアップデートがグローバルに適用されるようにしておく。またcythonやtoolchainをインストールしておかないとPyO3まわりでエラーになってしまうので、これらも必要となる。
また今回はPoetryを使うのでpython-poetryもインストールしておく。
2. MSYS2をデフォルトシェルに設定
GitHub Actionsは実行をWindows(windows-latest)にした場合、pwsh(PowerShell Core)がデフォルトで利用される。今回はMSYS2を使っていくため下記のような設定でmsys2をデフォルトにする。
jobs:
test-mingw:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
3. RustupをMSYS2環境にインストール
RustをGithub Actionsにインストールする場合、たとえばdtolnay/rust-toolchainといった便利なツールがある。しかしこれらのツールではシェルとしてbashを指定している[2]ため、MSYS2環境を徹頭徹尾使うという方針に反してしまう。よって次のように手動でwin.rustup.rsからインストーラーのEXEファイルをダウンロードし、これでインストールする[3]。
- name: Install Rustup using win.rustup.rs
run: |
curl -o rustup-init.exe -sSL https://win.rustup.rs/
./rustup-init.exe -y --default-host=${{ matrix.rust_target }} --profile=minimal
rm rustup-init.exe
このときrustup-init.exeがPATHの設定をするので、最初で行ったMSYS2の設定path-type: inheritが必要となる。あとはLinuxなどと同じく次のような手順でRustをインストールすればよい。
- name: Install Rust
run: |
rustup install stable --profile minimal
rustup default stable
rustup target add ${{ matrix.rust_target }}
4. poetry installを実行
今回はpoetryを使うのでpoetry installを行うが、SETUPTOOLS_USE_DISTUTILS=stdlibという環境変数を設定してから行う必要がある。
- name: Install Poetry
run: |
export SETUPTOOLS_USE_DISTUTILS=stdlib
poetry install
この環境変数を設定せずにpoetry installすると下記のようにmsgpack._cmsgpackのインストールに失敗してしまう。
copying msgpack/__init__.py -> build/lib.mingw_x86_64-cpython-311/msgpack
running build_ext
building 'msgpack._cmsgpack' extension
error: --plat-name must be one of ('win32', 'win-amd64', 'win-arm32', 'win-arm64')
5. libpythonのシンボリックリンクを設定
場合によっては(C言語のネイティブコードがあるとか?🤔)libpythonのシンボリックリンクの設定をしておかないと下記のようなコンパイルエラーになってしまう。
= note: C:/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lpython3.11: No such file or directory
collect2.exe: error: ld returned 1 exit status
よって、次のようなlibpythonのシンボリックリンク設定をしておく必要がある[4]。
- name: Create libpython symlink
run: ln -s /${{ matrix.path }}/lib/libpython3.11.dll.a /${{ matrix.path }}/lib/libpython311.dll.a
6. PyO3ライブラリーのコンパイル + Wheel作成
あとは過去記事PyO3 + PoetryでPythonからRust実装を使うなどの通りpoetry run maturin buildなどでWheel作成などを行うことができる。
まとめ
環境変数SETUPTOOLS_USE_DISTUTILSを設定したりlibpythonのシンボリックリンクを作成したりと、普段はあまりWindowsを使わない筆者からすると謎な手順が多い。もしかしたらもっとストレートフォワードな手順があるのかもしれないが、とりあえず現状これで上手くいったので記事にしておく。
-
たとえばここで
bashを指定している👉 https://github.com/dtolnay/rust-toolchain/blob/21dc36fb71dd22e3317045c0c31a3f4249868b17/action.yml#L74 ↩︎ -
この
rustup-init.exeを使う方法はRustupのWindows向けCIを参考にした。 ↩︎ -
この設定は
setuptools_rustのWindows向けのCIを参考にした。 ↩︎
Discussion