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

Support qemu in GitHub Actions #482

Merged
merged 28 commits into from
Jan 1, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
76fe472
Support docker qemu emulation in any linux env
asfaltboy Dec 12, 2020
90a14b9
fixup! Support docker qemu emulation in any linux env
asfaltboy Dec 12, 2020
ec99c01
Allow architecture selection
asfaltboy Dec 16, 2020
0d29bf8
refactor: --archs, CIBW_ARCHS, "auto"
henryiii Dec 18, 2020
26b7b72
fix: drop printout
henryiii Dec 18, 2020
0c9af03
Add platform-specific CIBW_ARCHS options.
joerick Dec 20, 2020
f73b608
Add unit test for the option parsing
joerick Dec 21, 2020
7c86223
Add platform-specific arch option unit test
joerick Dec 21, 2020
5aa85e8
Add integration test for emulation
joerick Dec 21, 2020
0ca294e
Add separate runner for emulation tests
joerick Dec 21, 2020
91800e4
Update .github/workflows/test.yml
joerick Dec 21, 2020
27f1a4e
Use 'latest' for CI
joerick Dec 21, 2020
8a1a0a4
Add asserts on macOS/Windows so that they can't be anything but default
joerick Dec 21, 2020
b34bdee
Fix native archs expansion for windows
joerick Dec 21, 2020
eb03a11
Rename Architecture to match value amd64 -> AMD64
joerick Dec 23, 2020
3696f15
Add example github config
joerick Dec 23, 2020
68dc286
Add docs for ARCHS option
joerick Dec 23, 2020
3983fb2
Fix broken link in docs
joerick Dec 23, 2020
460eaa7
Add mini-guide to the FAQ section
joerick Dec 23, 2020
f6ce1c9
Update example config
joerick Dec 23, 2020
6b00931
Allow the testing of any of the example configs in /examples
joerick Dec 23, 2020
0893731
Simplify arch matching code
joerick Dec 28, 2020
b5f5437
Suggestion from review
joerick Dec 31, 2020
5ee663f
Add test for failure when setting CIBW_ARCHS on macos/windows
joerick Dec 31, 2020
f434072
option ordering
joerick Dec 31, 2020
d65da2e
docs edits from review
joerick Dec 31, 2020
5b5078e
Copy edit of --archs help text to make 'auto' clearer
joerick Dec 31, 2020
aa87128
Make the StrEnums proper Enums
joerick Dec 31, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, windows-latest, macos-latest]
os: [ubuntu-18.04]
# os: [ubuntu-18.04, windows-latest, macos-latest]
joerick marked this conversation as resolved.
Show resolved Hide resolved
python_version: ['3.7']
timeout-minutes: 180
steps:
Expand All @@ -47,6 +48,13 @@ jobs:
id: pr-labels
uses: joerick/pr-labels-action@v1.0.6

- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
with:
platforms: all
if: runner.os == 'Linux'
Copy link
Contributor Author

@asfaltboy asfaltboy Dec 13, 2020

Choose a reason for hiding this comment

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

The user can select which (additional linux) platforms they want to support with the above action.

In other CIs, they can simply execute docker run --privileged --rm tonistiigi/binfmt --install all (or any other platform selection), as per binfmt docs

This section, and more, can be added to the README for explanation.

Copy link
Contributor

Choose a reason for hiding this comment

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

We aren't running a test that uses this yet, though. Should we add a build with ARM or PowerPC? Possibly trying to not add too much, since it's probably 5-10x slower?


- name: Sample build
if: contains(steps.pr-labels.outputs.labels, ' ci-sample-build ')
run: |
Expand Down
26 changes: 23 additions & 3 deletions cibuildwheel/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
parse_environment,
)
from cibuildwheel.util import (
Architecture,
BuildOptions,
BuildSelector,
DependencyConstraints,
Expand Down Expand Up @@ -67,6 +68,22 @@ def main() -> None:
run in Windows, and it will build and test for all versions of
Python. Default: auto.
''')

parser.add_argument(
'-a',
'--architectures',
choices=[arch.value for arch in Architecture],
default=[],
help='''
Comma-separated list of CPU architectures to build for.
If unspecified, builds the architectures natively supported
on this machine. Set this option to build an architecture
via emulation, for example, using binfmt_misc and qemu.
''',
action="extend",
nargs="+",
type=Architecture,
)
joerick marked this conversation as resolved.
Show resolved Hide resolved
parser.add_argument('--output-dir',
default=os.environ.get('CIBW_OUTPUT_DIR', 'wheelhouse'),
help='Destination folder for the wheels.')
Expand Down Expand Up @@ -169,7 +186,7 @@ def main() -> None:
exit(2)

if args.print_build_identifiers:
print_build_identifiers(platform, build_selector)
print_build_identifiers(platform, build_selector, args.architectures)
exit(0)

manylinux_images: Optional[Dict[str, str]] = None
Expand Down Expand Up @@ -202,6 +219,7 @@ def main() -> None:
manylinux_images[build_platform] = image

build_options = BuildOptions(
architectures=args.architectures,
package_dir=package_dir,
output_dir=output_dir,
test_command=test_command,
Expand Down Expand Up @@ -284,10 +302,12 @@ def print_preamble(platform: str, build_options: BuildOptions) -> None:
print('\nHere we go!\n')


def print_build_identifiers(platform: str, build_selector: BuildSelector) -> None:
def print_build_identifiers(
platform: str, build_selector: BuildSelector, architectures: List[Architecture]
) -> None:
python_configurations: List[Any] = []
if platform == 'linux':
python_configurations = cibuildwheel.linux.get_python_configurations(build_selector)
python_configurations = cibuildwheel.linux.get_python_configurations(build_selector, architectures)
elif platform == 'windows':
python_configurations = cibuildwheel.windows.get_python_configurations(build_selector)
elif platform == 'macos':
Expand Down
55 changes: 29 additions & 26 deletions cibuildwheel/linux.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import platform
import re
import subprocess
import sys
import textwrap
Expand All @@ -8,29 +9,19 @@

from .docker_container import DockerContainer
from .logger import log
from .util import (BuildOptions, BuildSelector, NonPlatformWheelError,
get_build_verbosity_extra_flags, prepare_command)


def matches_platform(identifier: str) -> bool:
pm = platform.machine()
if pm == "x86_64":
# x86_64 machines can run i686 docker containers
if identifier.endswith('x86_64') or identifier.endswith('i686'):
return True
elif pm == "i686":
if identifier.endswith('i686'):
return True
elif pm == "aarch64":
if identifier.endswith('aarch64'):
return True
elif pm == "ppc64le":
if identifier.endswith('ppc64le'):
return True
elif pm == "s390x":
if identifier.endswith('s390x'):
return True
return False
from .util import (
Architecture, BuildOptions, BuildSelector, NonPlatformWheelError,
get_build_verbosity_extra_flags, prepare_command,
)


re_pattern = re.compile(r'[cp]p\d{2}-manylinux_(\w*)')


def matches_platform(identifier: str, supported_platforms: List[Architecture]) -> bool:
matched_architecture = re_pattern.search(identifier)
id_architecture = matched_architecture.group(1) if matched_architecture else ''
return id_architecture in supported_platforms
joerick marked this conversation as resolved.
Show resolved Hide resolved


class PythonConfiguration(NamedTuple):
Expand All @@ -43,7 +34,9 @@ def path(self):
return PurePath(self.path_str)


def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
def get_python_configurations(
build_selector: BuildSelector, architectures: List[Architecture]
) -> List[PythonConfiguration]:
python_configurations = [
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path_str='/opt/python/cp27-cp27m'),
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path_str='/opt/python/cp27-cp27mu'),
Expand Down Expand Up @@ -78,8 +71,18 @@ def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfi
PythonConfiguration(version='3.8', identifier='cp38-manylinux_s390x', path_str='/opt/python/cp38-cp38'),
PythonConfiguration(version='3.9', identifier='cp39-manylinux_s390x', path_str='/opt/python/cp39-cp39'),
]

# skip builds as required
return [c for c in python_configurations if matches_platform(c.identifier) and build_selector(c.identifier)]
target_archs = architectures or [Architecture(platform.machine())]
# x86_64 machines can run i686 docker containers
if Architecture.i686 not in target_archs and Architecture.x86_64 in target_archs:
target_archs.append(Architecture.i686)
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this only be appended if this is not specified manually?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, the Architecture.i686 not in target_archs part of the condition checks that it wasn't specified manually. I found this way is more explicit and allows to specify both, or i686 (without x86), or x86 (which includes both) on their own.

Copy link
Contributor

Choose a reason for hiding this comment

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

If I do cibuildwheel . then I get the native archs (x86_64 and i686) but if I specify manually cibuildwheel --archs "x86_64" ., I should only get the x86_64 build. So adding the i686 build should only happen if the user doesn't specify archs in their options.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think "native", which is both the default and allowed in the list would be better. native would expand to i686 and x86_64. Then you can do native + something else, or you can specify just one of these and not get the other.

print("matching platforms to architectures: ", target_archs)
return [
c for c in python_configurations
if matches_platform(c.identifier, target_archs)
and build_selector(c.identifier)
]


def build(options: BuildOptions) -> None:
Expand All @@ -93,7 +96,7 @@ def build(options: BuildOptions) -> None:
exit(2)

assert options.manylinux_images is not None
python_configurations = get_python_configurations(options.build_selector)
python_configurations = get_python_configurations(options.build_selector, options.architectures)
joerick marked this conversation as resolved.
Show resolved Hide resolved
platforms = [
('cp', 'manylinux_x86_64', options.manylinux_images['x86_64']),
('cp', 'manylinux_i686', options.manylinux_images['i686']),
Expand Down
9 changes: 9 additions & 0 deletions cibuildwheel/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,16 @@ def __repr__(self):
return f'{self.__class__.__name__}{self.base_file_path!r})'


class Architecture(str, Enum):
YannickJadoul marked this conversation as resolved.
Show resolved Hide resolved
x86_64 = 'x86_64'
i686 = 'i686'
aarch64 = 'aarch64'
ppc64le = 'ppc64le'
s390x = 's390x'


class BuildOptions(NamedTuple):
architectures: List[Architecture]
joerick marked this conversation as resolved.
Show resolved Hide resolved
package_dir: Path
output_dir: Path
build_selector: BuildSelector
Expand Down