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

Build wheels using QEMU emulation #469

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 13 additions & 3 deletions cibuildwheel/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ def main() -> None:
considered the 'project' and is copied into the Docker container on
Linux. Default: the working directory.
''')
parser.add_argument('--use-binfmt',
default=False,
action='store_true',
help='''
Use QEMU Emulation to run the Docker images of other architectures.
''')

parser.add_argument('--print-build-identifiers',
action='store_true',
Expand Down Expand Up @@ -168,8 +174,11 @@ def main() -> None:
print('cibuildwheel: Could not find setup.py, setup.cfg or pyproject.toml at root of package', file=sys.stderr)
exit(2)

# qemu is only available on linux platforms
use_binfmt = args.use_binfmt and platform == 'linux'

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

manylinux_images: Optional[Dict[str, str]] = None
Expand Down Expand Up @@ -216,6 +225,7 @@ def main() -> None:
environment=environment,
dependency_constraints=dependency_constraints,
manylinux_images=manylinux_images,
use_binfmt=use_binfmt
)

# Python is buffering by default when running on the CI platforms, giving problems interleaving subprocess call output with unflushed calls to 'print'
Expand Down Expand Up @@ -284,10 +294,10 @@ 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, use_binfmt: bool) -> 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, use_binfmt)
elif platform == 'windows':
python_configurations = cibuildwheel.windows.get_python_configurations(build_selector)
elif platform == 'macos':
Expand Down
22 changes: 19 additions & 3 deletions cibuildwheel/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ def matches_platform(identifier: str) -> bool:
return False


def matches_qemu_capable(identifier: str) -> bool:
arch = identifier.split('_')[-1]
qemu_platform = Path(f"/proc/sys/fs/binfmt_misc/qemu-{arch}")

if not qemu_platform.exists():
return False

contents = open(qemu_platform, "r").readlines()
return contents[0].strip() == "enabled"


class PythonConfiguration(NamedTuple):
version: str
identifier: str
Expand All @@ -43,7 +54,7 @@ def path(self):
return PurePath(self.path_str)


def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
def get_python_configurations(build_selector: BuildSelector, use_binfmt: bool = False) -> 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 @@ -79,7 +90,8 @@ def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfi
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)]
return [c for c in python_configurations if (matches_platform(c.identifier)
or (use_binfmt and matches_qemu_capable(c.identifier))) and build_selector(c.identifier)]


def build(options: BuildOptions) -> None:
Expand All @@ -92,8 +104,12 @@ def build(options: BuildOptions) -> None:
file=sys.stderr)
exit(2)

if options.use_binfmt:
log.step('Installing binfmt...')
subprocess.check_output(['docker', 'run', '--rm', '--privileged', 'docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64'])
Copy link

@scarletcafe scarletcafe Dec 12, 2020

Choose a reason for hiding this comment

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

Judging by the about section and archival of the docker/binfmt repo, maybe this should be replaced with linuxkit/binfmt as per its suggestion?

EDIT: Whoops, just realized this was already discussed in the thread.

Copy link
Author

Choose a reason for hiding this comment

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

Yeah absolutely. I will push up a patch.


assert options.manylinux_images is not None
python_configurations = get_python_configurations(options.build_selector)
python_configurations = get_python_configurations(options.build_selector, options.use_binfmt)
platforms = [
('cp', 'manylinux_x86_64', options.manylinux_images['x86_64']),
('cp', 'manylinux_i686', options.manylinux_images['i686']),
Expand Down
1 change: 1 addition & 0 deletions cibuildwheel/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class BuildOptions(NamedTuple):
test_requires: List[str]
test_extras: str
build_verbosity: int
use_binfmt: bool


resources_dir = Path(__file__).resolve().parent / 'resources'
Expand Down