diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index e171ab20d..3756ae5fc 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -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', @@ -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 @@ -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' @@ -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': diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py index cfd4952c5..8bc6c037b 100644 --- a/cibuildwheel/linux.py +++ b/cibuildwheel/linux.py @@ -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 @@ -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'), @@ -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: @@ -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']) + 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']), diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py index 87ce5d178..570d51378 100644 --- a/cibuildwheel/util.py +++ b/cibuildwheel/util.py @@ -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'