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

Improve diagnostics when auditwheel repair fails #807

Merged
merged 4 commits into from
Aug 28, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
24 changes: 20 additions & 4 deletions cibuildwheel/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,18 +306,27 @@ def build(options: BuildOptions) -> None:
log.step_end_with_error(
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
)
troubleshoot(options.package_dir, error)
troubleshoot(options, error)
sys.exit(1)


def troubleshoot(package_dir: Path, error: Exception) -> None:
def _matches_prepared_command(error_cmd: List[str], command_template: str) -> bool:
if len(error_cmd) < 3 or error_cmd[0:2] != ["sh", "-c"]:
return False
command_prefix = command_template.split("{", maxsplit=1)[0].strip()
return error_cmd[2].startswith(command_prefix)


def troubleshoot(options: BuildOptions, error: Exception) -> None:

if isinstance(error, subprocess.CalledProcessError) and (
error.cmd[0:4] == ["python", "-m", "pip", "wheel"]
or error.cmd[0:3] == ["python", "-m", "build"]
or _matches_prepared_command(error.cmd, options.repair_command)
):
# the wheel build step failed
print("Checking for common errors...")
so_files = list(package_dir.glob("**/*.so"))
so_files = list(options.package_dir.glob("**/*.so"))

if so_files:
print(
Expand All @@ -326,10 +335,17 @@ def troubleshoot(package_dir: Path, error: Exception) -> None:
NOTE: Shared object (.so) files found in this project.

These files might be built against the wrong OS, causing problems with
auditwheel.
auditwheel. If possible, run cibuildwheel in a clean checkout.

If you're using Cython and have previously done an in-place build,
remove those build files (*.so and *.c) before starting cibuildwheel.

setuptools uses the build/ folder to store its build cache. It
may be necessary to remove those build files (*.so and *.o) before
starting cibuildwheel.

Files that belong to a virtual environment are probably not an issue
unless you used a custom command telling cibuildwheel to activate it.
"""
),
file=sys.stderr,
Expand Down
52 changes: 40 additions & 12 deletions test/test_troubleshooting.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,58 @@
import pytest

from . import utils
from .test_projects import TestProject
from .test_projects import TestProject, new_c_project

so_file_project = TestProject()
SO_FILE_WARNING = "NOTE: Shared object (.so) files found in this project."

so_file_project.files["libnothing.so"] = ""

so_file_project.files[
"setup.py"
] = """
raise Exception('this build will fail')
"""
@pytest.mark.parametrize("project_contains_so_files", [False, True])
def test_failed_build_with_so_files(tmp_path, capfd, build_frontend_env, project_contains_so_files):
project = TestProject()
project.files["setup.py"] = "raise Exception('this build will fail')\n"
if project_contains_so_files:
project.files["libnothing.so"] = ""


def test_failed_project_with_so_files(tmp_path, capfd, build_frontend_env):
if utils.platform != "linux":
pytest.skip("this test is only relevant to the linux build")

project_dir = tmp_path / "project"
so_file_project.generate(project_dir)
project.generate(project_dir)

with pytest.raises(subprocess.CalledProcessError):
utils.cibuildwheel_run(project_dir, add_env=build_frontend_env)

captured = capfd.readouterr()
print("out", captured.out)
print("err", captured.err)
assert "NOTE: Shared object (.so) files found in this project." in captured.err

if project_contains_so_files:
assert SO_FILE_WARNING in captured.err
else:
assert SO_FILE_WARNING not in captured.err


@pytest.mark.parametrize("project_contains_so_files", [False, True])
def test_failed_repair_with_so_files(tmp_path, capfd, project_contains_so_files):
if utils.platform != "linux":
pytest.skip("this test is only relevant to the linux build")

project = new_c_project()

if project_contains_so_files:
project.files["libnothing.so"] = ""

project_dir = tmp_path / "project"
project.generate(project_dir)

with pytest.raises(subprocess.CalledProcessError):
utils.cibuildwheel_run(project_dir, add_env={"CIBW_REPAIR_COMMAND": "false"})

captured = capfd.readouterr()
print("out", captured.out)
print("err", captured.err)

if project_contains_so_files:
assert SO_FILE_WARNING in captured.err
else:
assert SO_FILE_WARNING not in captured.err