Modernize wheel building job config (#1783)

It is now possible to build Mac wheels on native machines in Github
Actions, so ARM64 Mac wheels are now built and tested on M1 machines.

Also, the artifact up-/download was migrated to v4, which made it
necessary to upload wheels to unique artifact names, and then later
stitch them together again in a subsequent job.

The cross-platform Mac build injection in setup.py was removed,
since it is no longer necessary.

I relanded a monkey-patching of Bazel build files, this time for
MODULE.bazel. This is because `rules_python` does not allow running
as the root user, which is the case in cibuildwheel+Linux (happens
in a Docker container). Since I did not see a quick way of switching
to rootless containers, and did not want to hardcode the config change
(it can apparently cause cache misses and build failures), I inject the
"ignore_root_user_error" flag into the MODULE.bazel file when running
in cibuildwheel on Linux.
This commit is contained in:
Nicholas Junge 2024-04-15 18:44:09 +02:00 committed by GitHub
parent 185c55d793
commit bc946b919c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 83 additions and 43 deletions

View file

@ -3,11 +3,10 @@ if ! bazel version; then
if [ "$arch" == "aarch64" ]; then if [ "$arch" == "aarch64" ]; then
arch="arm64" arch="arm64"
fi fi
echo "Installing wget and downloading $arch Bazel binary from GitHub releases." echo "Downloading $arch Bazel binary from GitHub releases."
yum install -y wget curl -L -o $HOME/bin/bazel --create-dirs "https://github.com/bazelbuild/bazel/releases/download/7.1.1/bazel-7.1.1-linux-$arch"
wget "https://github.com/bazelbuild/bazel/releases/download/6.4.0/bazel-6.4.0-linux-$arch" -O /usr/local/bin/bazel chmod +x $HOME/bin/bazel
chmod +x /usr/local/bin/bazel
else else
# bazel is installed for the correct architecture # Bazel is installed for the correct architecture
exit 0 exit 0
fi fi

View file

@ -15,16 +15,16 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install Python 3.11 - name: Install Python 3.12
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: 3.11 python-version: 3.12
- run: python -m pip install build - run: python -m pip install build
- name: Build sdist - name: Build sdist
run: python -m build --sdist run: python -m build --sdist
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: dist name: dist-sdist
path: dist/*.tar.gz path: dist/*.tar.gz
build_wheels: build_wheels:
@ -32,7 +32,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-13, macos-14, windows-latest]
steps: steps:
- name: Check out Google Benchmark - name: Check out Google Benchmark
@ -47,32 +47,44 @@ jobs:
platforms: all platforms: all
- name: Build wheels on ${{ matrix.os }} using cibuildwheel - name: Build wheels on ${{ matrix.os }} using cibuildwheel
uses: pypa/cibuildwheel@v2.16.2 uses: pypa/cibuildwheel@v2.17
env: env:
CIBW_BUILD: 'cp38-* cp39-* cp310-* cp311-* cp312-*' CIBW_BUILD: "cp38-* cp39-* cp310-* cp311-* cp312-*"
CIBW_SKIP: "*-musllinux_*" CIBW_SKIP: "*-musllinux_*"
CIBW_TEST_SKIP: "*-macosx_arm64" CIBW_TEST_SKIP: "cp38-macosx_*:arm64"
CIBW_ARCHS_LINUX: x86_64 aarch64 CIBW_ARCHS_LINUX: auto64 aarch64
CIBW_ARCHS_MACOS: x86_64 arm64 CIBW_ARCHS_WINDOWS: auto64
CIBW_ARCHS_WINDOWS: AMD64
CIBW_BEFORE_ALL_LINUX: bash .github/install_bazel.sh CIBW_BEFORE_ALL_LINUX: bash .github/install_bazel.sh
# Grab the rootless Bazel installation inside the container.
CIBW_ENVIRONMENT_LINUX: PATH=$PATH:$HOME/bin
CIBW_TEST_COMMAND: python {project}/bindings/python/google_benchmark/example.py CIBW_TEST_COMMAND: python {project}/bindings/python/google_benchmark/example.py
- name: Upload Google Benchmark ${{ matrix.os }} wheels - name: Upload Google Benchmark ${{ matrix.os }} wheels
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.os }}
path: wheelhouse/*.whl
merge_wheels:
name: Merge all built wheels into one artifact
runs-on: ubuntu-latest
needs: build_wheels
steps:
- name: Merge wheels
uses: actions/upload-artifact/merge@v4
with: with:
name: dist name: dist
path: wheelhouse/*.whl pattern: dist-*
delete-merged: true
pypi_upload: pypi_upload:
name: Publish google-benchmark wheels to PyPI name: Publish google-benchmark wheels to PyPI
needs: [build_sdist, build_wheels] needs: [merge_wheels]
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
id-token: write id-token: write
steps: steps:
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
name: dist path: dist
path: dist - uses: pypa/gh-action-pypi-publish@v1
- uses: pypa/gh-action-pypi-publish@v1.8.11

View file

@ -27,10 +27,6 @@ python.toolchain(
is_default = True, is_default = True,
python_version = "3.12", python_version = "3.12",
) )
use_repo(
python,
python = "python_versions",
)
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip", dev_dependency = True) pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip", dev_dependency = True)
pip.parse( pip.parse(

View file

@ -1,14 +1,17 @@
import contextlib
import os import os
import platform import platform
import re
import shutil import shutil
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any, Generator
import setuptools import setuptools
from setuptools.command import build_ext from setuptools.command import build_ext
IS_WINDOWS = platform.system() == "Windows" IS_WINDOWS = platform.system() == "Windows"
IS_MAC = platform.system() == "Darwin" IS_MAC = platform.system() == "Darwin"
IS_LINUX = platform.system() == "Linux"
# hardcoded SABI-related options. Requires that each Python interpreter # hardcoded SABI-related options. Requires that each Python interpreter
# (hermetic or not) participating is of the same major-minor version. # (hermetic or not) participating is of the same major-minor version.
@ -17,6 +20,46 @@ py_limited_api = version_tuple >= (3, 12)
options = {"bdist_wheel": {"py_limited_api": "cp312"}} if py_limited_api else {} options = {"bdist_wheel": {"py_limited_api": "cp312"}} if py_limited_api else {}
def is_cibuildwheel() -> bool:
return os.getenv("CIBUILDWHEEL") is not None
@contextlib.contextmanager
def _maybe_patch_toolchains() -> Generator[None, None, None]:
"""
Patch rules_python toolchains to ignore root user error
when run in a Docker container on Linux in cibuildwheel.
"""
def fmt_toolchain_args(matchobj):
suffix = "ignore_root_user_error = True"
callargs = matchobj.group(1)
# toolchain def is broken over multiple lines
if callargs.endswith("\n"):
callargs = callargs + " " + suffix + ",\n"
# toolchain def is on one line.
else:
callargs = callargs + ", " + suffix
return "python.toolchain(" + callargs + ")"
CIBW_LINUX = is_cibuildwheel() and IS_LINUX
try:
if CIBW_LINUX:
module_bazel = Path("MODULE.bazel")
content: str = module_bazel.read_text()
module_bazel.write_text(
re.sub(
r"python.toolchain\(([\w\"\s,.=]*)\)",
fmt_toolchain_args,
content,
)
)
yield
finally:
if CIBW_LINUX:
module_bazel.write_text(content)
class BazelExtension(setuptools.Extension): class BazelExtension(setuptools.Extension):
"""A C/C++ extension that is defined as a Bazel BUILD target.""" """A C/C++ extension that is defined as a Bazel BUILD target."""
@ -73,21 +116,11 @@ class BuildBazelExtension(build_ext.build_ext):
for library_dir in self.library_dirs: for library_dir in self.library_dirs:
bazel_argv.append("--linkopt=/LIBPATH:" + library_dir) bazel_argv.append("--linkopt=/LIBPATH:" + library_dir)
elif IS_MAC: elif IS_MAC:
if platform.machine() == "x86_64": # C++17 needs macOS 10.14 at minimum
# C++17 needs macOS 10.14 at minimum bazel_argv.append("--macos_minimum_os=10.14")
bazel_argv.append("--macos_minimum_os=10.14")
# cross-compilation for Mac ARM64 on GitHub Mac x86 runners. with _maybe_patch_toolchains():
# ARCHFLAGS is set by cibuildwheel before macOS wheel builds. self.spawn(bazel_argv)
archflags = os.getenv("ARCHFLAGS", "")
if "arm64" in archflags:
bazel_argv.append("--cpu=darwin_arm64")
bazel_argv.append("--macos_cpus=arm64")
elif platform.machine() == "arm64":
bazel_argv.append("--macos_minimum_os=11.0")
self.spawn(bazel_argv)
if IS_WINDOWS: if IS_WINDOWS:
suffix = ".pyd" suffix = ".pyd"