Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable building x86_64, arm64 and universal2 wheels on supported macOS Python versions #173

Merged
merged 8 commits into from
Nov 16, 2022
10 changes: 10 additions & 0 deletions config/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ updated. Example:
"sudo apt-get update && sudo apt-get install -y libxml2-dev libxslt-dev",
"pip install tox-factor"
]
additional-build-dependencies = [
"cffi",
"python-ldap",
]
test-commands = [
"tox -f ${{ matrix.config[1] }}",
]
Expand Down Expand Up @@ -586,6 +590,12 @@ additional-install
For the template ``c-code`` this option is currently used to replace how to
install the package itself and run tests and coverage.

additional-build-dependencies
Additional Python packages to install into the virtual environment before
building a package with C extensions. This is used for the ``c-code``
template to work around issues on macOS where setuptools attempts to retrieve
wheels and convert them to eggs multiple times.

test-commands
Replacement for the test command in ``tests.yml``.
This option has to be a list of strings.
Expand Down
130 changes: 108 additions & 22 deletions config/c-code/tests.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ jobs:
run: |
pip install -U pip
pip install -U setuptools wheel twine "cffi != 1.15.1"
{% if gha_additional_build_dependencies %}
{% for line in gha_additional_build_dependencies %}
pip install -U %(line)s
{% endfor %}
{% endif %}
- name: Install Build Dependencies (other Python versions)
if: >
!startsWith(matrix.python-version, 'pypy-2.7')
Expand All @@ -112,52 +117,130 @@ jobs:
run: |
pip install -U pip
pip install -U setuptools wheel twine cffi

{% if with_future_python %}
- name: Build %(package_name)s (%(future_python_version)s)
if: ${{ startsWith(matrix.python-version, '%(future_python_version)s') }}
run: |
# Next, build the wheel *in place*. This helps ccache, and also lets us cache the configure
# output (pip install uses a random temporary directory, making this difficult).
python setup.py build_ext -i
python setup.py bdist_wheel
# Also install it, so that we get dependencies in the (pip) cache.
pip install -U 'faulthandler; python_version == "2.7" and platform_python_implementation == "CPython"'
pip install --pre .[test]
{% if gha_additional_build_dependencies %}
{% for line in gha_additional_build_dependencies %}
pip install -U %(line)s
{% endfor %}
{% endif %}
{% for kind in ('Python 3.10 and 3.11 on MacOS', 'all other versions') %}

{% for kind in ('macOS x86_64, Python 3.8+',
'macOS arm64, Python 3.8+',
'macOS universal2, Python 3.8+',
'all other versions') %}
- name: Build %(package_name)s (%(kind)s)
if: >
{% if kind == 'Python 3.10 and 3.11 on MacOS' %}
{% if kind == 'macOS x86_64, Python 3.8+' %}
startsWith(runner.os, 'Mac')
&& (startsWith(matrix.python-version, '3.10')
|| startsWith(matrix.python-version, '3.11'))
&& !(startsWith(matrix.python-version, 'pypy')
|| matrix.python-version == '2.7'
|| matrix.python-version == '3.5'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks okay as this list will shrink when we stop supporting Python < 3.6. (Maybe we could even think about dropping Python 3.6 support.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make sure it's a list that doesn't need growing as new Python versions are releases. I'm still unhappy that there's apparently no way to safely express something like matrix.python-version < '3.8', something that understands versioning.

|| matrix.python-version == '3.6'
|| matrix.python-version == '3.7')
env:
_PYTHON_HOST_PLATFORM: macosx-11-x86_64
MACOSX_DEPLOYMENT_TARGET: 10.9
_PYTHON_HOST_PLATFORM: macosx-10.9-x86_64
ARCHFLAGS: -arch x86_64
{% elif kind == 'macOS arm64, Python 3.8+' %}
startsWith(runner.os, 'Mac')
&& !(startsWith(matrix.python-version, 'pypy')
|| matrix.python-version == '2.7'
|| matrix.python-version == '3.5'
|| matrix.python-version == '3.6'
|| matrix.python-version == '3.7')
env:
MACOSX_DEPLOYMENT_TARGET: 11.0
_PYTHON_HOST_PLATFORM: macosx-11.0-arm64
ARCHFLAGS: -arch arm64
{% elif kind == 'macOS universal2, Python 3.8+' %}
startsWith(runner.os, 'Mac')
&& !(startsWith(matrix.python-version, 'pypy')
|| matrix.python-version == '2.7'
|| matrix.python-version == '3.5'
|| matrix.python-version == '3.6'
|| matrix.python-version == '3.7')
env:
MACOSX_DEPLOYMENT_TARGET: 10.9
_PYTHON_HOST_PLATFORM: macosx-10.9-universal2
ARCHFLAGS: -arch arm64 -arch x86_64
{% else %}
!startsWith(runner.os, 'Mac')
|| !(startsWith(matrix.python-version, '3.10')
|| startsWith(matrix.python-version, '3.11'))
|| startsWith(matrix.python-version, 'pypy')
|| matrix.python-version == '2.7'
|| matrix.python-version == '3.5'
|| matrix.python-version == '3.6'
|| matrix.python-version == '3.7'
{% endif %}
run: |
# Next, build the wheel *in place*. This helps ccache, and also lets us cache the configure
# output (pip install uses a random temporary directory, making this difficult).
python setup.py build_ext -i
python setup.py bdist_wheel
# Also install it, so that we get dependencies in the (pip) cache.
{% endfor %}

{% if with_future_python %}
- name: Install %(package_name)s and dependencies (%(future_python_version)s)
if: matrix.python-version == '%(future_python_version)s'
run: |
# Install to collect dependencies into the (pip) cache.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the comment could contain a hint about the --pre flag.
We added it to build AccessControl against Python 3.11beta-X because a dev version of RestrictedPython was needed as its released version was not even installable on Python 3.11.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, comment added.

# Use "--pre" here because dependencies with support for this future
# Python release may only be available as pre-releases
pip install --pre .[test]
{% endif %}
- name: Install %(package_name)s and dependencies
{% if with_future_python %}
if: matrix.python-version != '%(future_python_version)s'
{% endif %}
run: |
# Install to collect dependencies into the (pip) cache.
pip install -U 'faulthandler; python_version == "2.7" and platform_python_implementation == "CPython"'
pip install .[test]

{% endfor %}
- name: Check %(package_name)s build
run: |
ls -l dist
twine check dist/*
- name: Upload %(package_name)s wheel
{% for kind in ('macOS x86_64',
'macOS arm64',
'macOS universal2',
'all other platforms') %}
- name: Upload %(package_name)s wheel (%(kind)s)
if: >
{% if kind == 'macOS x86_64' %}
startsWith(runner.os, 'Mac')
{% elif kind == 'macOS arm64' or kind == 'macOS universal2' %}
startsWith(runner.os, 'Mac')
&& !(startsWith(matrix.python-version, 'pypy')
|| matrix.python-version == '2.7'
|| matrix.python-version == '3.5'
|| matrix.python-version == '3.6'
|| matrix.python-version == '3.7')
{% else %}
!startsWith(runner.os, 'Mac')
{% endif %}
uses: actions/upload-artifact@v3
with:
{% if kind == 'macOS arm64' %}
# The arm64 wheel is uploaded with a different name just so it can be
# manually downloaded when desired. The wheel itself *cannot* be tested
# on the GHA runner, which uses x86_64 architecture.
name: %(package_name)s-${{ runner.os }}-${{ matrix.python-version }}-arm64.whl
{% elif kind == 'macOS universal2' %}
# The universal2 wheel is uploaded with a different name just so it
# can be manually downloaded when desired.
name: %(package_name)s-${{ runner.os }}-${{ matrix.python-version }}-universal2.whl
{% else %}
name: %(package_name)s-${{ runner.os }}-${{ matrix.python-version }}.whl
{% endif %}
{% if kind == 'macOS x86_64' %}
path: dist/*x86_64.whl
{% elif kind == 'macOS arm64' %}
path: dist/*arm64.whl
{% elif kind == 'macOS universal2' %}
path: dist/*universal2.whl
{% else %}
path: dist/*whl
{% endif %}
{% endfor %}
- name: Publish package to PyPI (mac)
# We cannot 'uses: pypa/[email protected]' because
# that's apparently a container action, and those don't run on
Expand Down Expand Up @@ -201,6 +284,8 @@ jobs:
# when we ask it to load tests from that directory. This
# might also save some build time?
unzip -n dist/%(package_name)s-*whl -d src
# Use "--pre" here because dependencies with support for this future
# Python release may only be available as pre-releases
pip install --pre -U -e .[test]
{% endif %}
- name: Install %(package_name)s
Expand All @@ -211,6 +296,7 @@ jobs:
pip install -U wheel setuptools
pip install -U coverage
pip install -U 'faulthandler; python_version == "2.7" and platform_python_implementation == "CPython"'
pip install -U 'cffi; platform_python_implementation == "CPython"'
# Unzip into src/ so that testrunner can find the .so files
# when we ask it to load tests from that directory. This
# might also save some build time?
Expand Down
3 changes: 3 additions & 0 deletions config/config-package.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,13 +380,16 @@ def copy_with_meta(
'steps-before-checkout', [])
gha_additional_install = meta_cfg['github-actions'].get(
'additional-install', [])
gha_additional_build_dependencies = meta_cfg['github-actions'].get(
'additional-build-dependencies', [])
gha_test_commands = meta_cfg['github-actions'].get(
'test-commands', [])
copy_with_meta(
'tests.yml.j2', workflows / 'tests.yml', config_type,
gha_additional_config=gha_additional_config,
gha_additional_exclude=gha_additional_exclude,
gha_additional_install=gha_additional_install,
gha_additional_build_dependencies=gha_additional_build_dependencies,
gha_test_commands=gha_test_commands,
package_name=path.name,
services=gha_services,
Expand Down