From ebf56281edfcf7ae9f093305b1b0ea6ace116161 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 20 Jul 2020 15:35:51 +0100 Subject: [PATCH] Add pure wheel detection, and failing with an error in this case --- cibuildwheel/linux.py | 11 +++++++---- cibuildwheel/macos.py | 14 +++++++++----- cibuildwheel/util.py | 16 ++++++++++++++++ cibuildwheel/windows.py | 14 +++++++++----- test/test_pure_wheel.py | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 test/test_pure_wheel.py diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py index a3dcb036..a328c608 100644 --- a/cibuildwheel/linux.py +++ b/cibuildwheel/linux.py @@ -7,7 +7,7 @@ from pathlib import Path, PurePath from typing import List, NamedTuple, Union from .docker_container import DockerContainer -from .util import (BuildOptions, BuildSelector, +from .util import (BuildOptions, BuildSelector, NonPlatformWheelError, get_build_verbosity_extra_flags, prepare_command) @@ -174,11 +174,14 @@ def build(options: BuildOptions) -> None: docker.call(['rm', '-rf', repaired_wheel_dir]) docker.call(['mkdir', '-p', repaired_wheel_dir]) - if built_wheel.name.endswith('none-any.whl') or not options.repair_command: - docker.call(['mv', built_wheel, repaired_wheel_dir]) - else: + if built_wheel.name.endswith('none-any.whl'): + raise NonPlatformWheelError() + + if options.repair_command: repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir) docker.call(['sh', '-c', repair_command_prepared], env=env) + else: + docker.call(['mv', built_wheel, repaired_wheel_dir]) repaired_wheels = docker.glob(repaired_wheel_dir, '*.whl') diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index 48ec210b..fe292cba 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -9,7 +9,7 @@ from pathlib import Path from typing import Dict, List, NamedTuple, Optional, Sequence, Union from .environment import ParsedEnvironment -from .util import (BuildOptions, BuildSelector, download, +from .util import (BuildOptions, BuildSelector, NonPlatformWheelError, download, get_build_verbosity_extra_flags, get_pip_script, prepare_command) @@ -215,12 +215,16 @@ def build(options: BuildOptions) -> None: if repaired_wheel_dir.exists(): shutil.rmtree(repaired_wheel_dir) repaired_wheel_dir.mkdir(parents=True) - if built_wheel.name.endswith('none-any.whl') or not options.repair_command: - # pure Python wheel or empty repair command - shutil.move(str(built_wheel), repaired_wheel_dir) - else: + + if built_wheel.name.endswith('none-any.whl'): + raise NonPlatformWheelError() + + if options.repair_command: repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir) call(repair_command_prepared, env=env, shell=True) + else: + shutil.move(str(built_wheel), repaired_wheel_dir) + repaired_wheel = next(repaired_wheel_dir.glob('*.whl')) if options.test_command: diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py index 012afa1a..891f0f28 100644 --- a/cibuildwheel/util.py +++ b/cibuildwheel/util.py @@ -1,4 +1,5 @@ import os +import textwrap import urllib.request from fnmatch import fnmatch from pathlib import Path @@ -126,3 +127,18 @@ class BuildOptions(NamedTuple): resources_dir = Path(__file__).resolve().parent / 'resources' get_pip_script = resources_dir / 'get-pip.py' + + +class NonPlatformWheelError(Exception): + def __init__(self) -> None: + message = textwrap.dedent(''' + cibuildwheel: Build failed because a pure Python wheel was generated. + + If you intend to build a pure-Python wheel, you don't need cibuildwheel - use + `pip wheel -w DEST_DIR .` instead. + + If you expected a platform wheel, check your project configuration, or run + cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs. + ''') + + super().__init__(message) diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index c958b9b6..f793acf4 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -10,7 +10,7 @@ from zipfile import ZipFile import toml from .environment import ParsedEnvironment -from .util import (BuildOptions, BuildSelector, download, +from .util import (BuildOptions, BuildSelector, NonPlatformWheelError, download, get_build_verbosity_extra_flags, get_pip_script, prepare_command) @@ -236,12 +236,16 @@ def build(options: BuildOptions) -> None: if repaired_wheel_dir.exists(): shutil.rmtree(repaired_wheel_dir) repaired_wheel_dir.mkdir(parents=True) - if built_wheel.name.endswith('none-any.whl') or not options.repair_command: - # pure Python wheel or empty repair command - shutil.move(str(built_wheel), repaired_wheel_dir) - else: + + if built_wheel.name.endswith('none-any.whl'): + raise NonPlatformWheelError() + + if options.repair_command: repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir) shell(repair_command_prepared, env=env) + else: + shutil.move(str(built_wheel), repaired_wheel_dir) + repaired_wheel = next(repaired_wheel_dir.glob('*.whl')) if options.test_command: diff --git a/test/test_pure_wheel.py b/test/test_pure_wheel.py new file mode 100644 index 00000000..3373143a --- /dev/null +++ b/test/test_pure_wheel.py @@ -0,0 +1,36 @@ +import subprocess +import pytest +from test import test_projects +from . import utils + +pure_python_project = test_projects.TestProject() +pure_python_project.files['setup.py'] = ''' +from setuptools import Extension, setup + +setup( + name="spam", + py_modules=['spam'], + version="0.1.0", +) +''' + +pure_python_project.files['spam.py'] = ''' +def a_function(): + pass +''' + + +def test(tmp_path, capfd): + # this test checks that if a pure wheel is generated, the build should + # fail. + project_dir = tmp_path / 'project' + pure_python_project.generate(project_dir) + + with pytest.raises(subprocess.CalledProcessError): + actual_wheels = utils.cibuildwheel_run(project_dir) + print('produced wheels:', actual_wheels) + + captured = capfd.readouterr() + print('out', captured.out) + print('err', captured.err) + assert "Build failed because a pure Python wheel was generated" in captured.err