🪟

Windows + MinGW + MSYS2でRust実装のPythonライブラリーを作る

2024/06/30に公開

はじめに

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_64aarch64など
path MinGWがインストールされているファイルパスでmingw64など
rust_target Rustのコンパイルターゲットでx86_64-pc-windows-gnux86_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環境変数のアップデートがグローバルに適用されるようにしておく。またcythontoolchainをインストールしておかないと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.exePATHの設定をするので、最初で行った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を使わない筆者からすると謎な手順が多い。もしかしたらもっとストレートフォワードな手順があるのかもしれないが、とりあえず現状これで上手くいったので記事にしておく。

脚注
  1. https://www.msys2.org/docs/environments/ ↩︎

  2. たとえばここでbashを指定している👉 https://github.com/dtolnay/rust-toolchain/blob/21dc36fb71dd22e3317045c0c31a3f4249868b17/action.yml#L74 ↩︎

  3. このrustup-init.exeを使う方法はRustupのWindows向けCIを参考にした。 ↩︎

  4. この設定はsetuptools_rustのWindows向けのCIを参考にした。 ↩︎

Discussion