diff --git a/tests/integration/internal_rpath/Makefile b/tests/integration/internal_rpath/Makefile index 9d67725c..d2043918 100644 --- a/tests/integration/internal_rpath/Makefile +++ b/tests/integration/internal_rpath/Makefile @@ -1,5 +1,9 @@ +.PHONY: all clean + all: lib-src/b/libb.so lib-src/a/liba.so +clean: + -rm -f lib-src/b/libb.so lib-src/a/liba.so lib-src/b/libb.so: lib-src/b/b.c gcc -fPIC -shared -o lib-src/b/libb.so lib-src/b/b.c diff --git a/tests/integration/multiple_top_level/Makefile b/tests/integration/multiple_top_level/Makefile index 28f10504..67fd7729 100644 --- a/tests/integration/multiple_top_level/Makefile +++ b/tests/integration/multiple_top_level/Makefile @@ -1,9 +1,13 @@ +.PHONY: all clean + all: lib-src/b/libb.so lib-src/a/liba.so +clean: + -rm -f lib-src/b/libb.so lib-src/a/liba.so lib-src/b/libb.so: lib-src/b/b.c gcc -fPIC -shared -o lib-src/b/libb.so lib-src/b/b.c lib-src/a/liba.so: lib-src/b/libb.so lib-src/a/a.c - gcc -fPIC -shared -o lib-src/a/liba.so -Ilib-src/b -Llib-src/b -lb lib-src/a/a.c + gcc -fPIC -shared -o lib-src/a/liba.so -Ilib-src/b lib-src/a/a.c -Llib-src/b -lb diff --git a/tests/integration/nonpy_rpath/extensions/testcrypt.cpp b/tests/integration/nonpy_rpath/extensions/testcrypt.cpp index 5a3c1e39..0818de54 100644 --- a/tests/integration/nonpy_rpath/extensions/testcrypt.cpp +++ b/tests/integration/nonpy_rpath/extensions/testcrypt.cpp @@ -2,5 +2,9 @@ #include std::string crypt_something() { - return std::string(crypt("will error out", NULL)); + char const* result = crypt("will error out", "\0"); + if (result == NULL) { + return std::string("*"); + } + return std::string(result); } diff --git a/tests/integration/test_manylinux.py b/tests/integration/test_manylinux.py index 56afaf35..0ac68a0d 100644 --- a/tests/integration/test_manylinux.py +++ b/tests/integration/test_manylinux.py @@ -45,17 +45,22 @@ POLICY_ALIASES = { "manylinux_2_17": ["manylinux2014"], } -DOCKER_CONTAINER_NAME = "auditwheel-test-manylinux" +DOCKER_CONTAINER_NAME = "auditwheel-test-anylinux" PYTHON_MAJ_MIN = [str(i) for i in sys.version_info[:2]] PYTHON_ABI_MAJ_MIN = "".join(PYTHON_MAJ_MIN) PYTHON_ABI_FLAGS = "m" if sys.version_info.minor < 8 else "" PYTHON_ABI = f"cp{PYTHON_ABI_MAJ_MIN}-cp{PYTHON_ABI_MAJ_MIN}{PYTHON_ABI_FLAGS}" -PYTHON_IMAGE_ID = f'python:{".".join(PYTHON_MAJ_MIN)}-slim' +MANYLINUX_PYTHON_IMAGE_ID = f'python:{".".join(PYTHON_MAJ_MIN)}-slim' +MUSLLINUX_IMAGES = { + "musllinux_1_1": f"quay.io/pypa/musllinux_1_1_{PLATFORM}:latest", +} +MUSLLINUX_PYTHON_IMAGE_ID = f'python:{".".join(PYTHON_MAJ_MIN)}-alpine' DEVTOOLSET = { "manylinux_2_5": "devtoolset-2", "manylinux_2_12": "devtoolset-8", "manylinux_2_17": "devtoolset-10", "manylinux_2_24": "devtoolset-not-present", + "musllinux_1_1": "devtoolset-not-present", } PATH_DIRS = [ f"/opt/python/{PYTHON_ABI}/bin", @@ -72,6 +77,11 @@ NUMPY_VERSION = "1.19.2" ORIGINAL_NUMPY_WHEEL = f"numpy-{NUMPY_VERSION}-{PYTHON_ABI}-linux_{PLATFORM}.whl" ORIGINAL_SIX_WHEEL = "six-1.11.0-py2.py3-none-any.whl" +SHOW_RE = re.compile( + r'[\s](?P\S+) is consistent with the following platform tag: "(?P\S+)"', + flags=re.DOTALL, +) +TAG_RE = re.compile(r"^manylinux_(?P[0-9]+)_(?P[0-9]+)_(?P\S+)$") def find_src_folder(): @@ -135,13 +145,6 @@ def docker_exec(container, cmd, expected_retcode=0): return output -@pytest.fixture() -def io_folder(tmp_path): - d = tmp_path / "io" - d.mkdir(exist_ok=True) - return str(d) - - @contextmanager def tmp_docker_image(base, commands, setup_env={}): """Make a temporary docker image for tests @@ -166,68 +169,13 @@ def tmp_docker_image(base, commands, setup_env={}): client.images.remove(image.id) -@pytest.fixture(scope="session") -def docker_python_img(): - """The Python base image with up-to-date pip""" - with tmp_docker_image(PYTHON_IMAGE_ID, ["pip install -U pip"]) as img_id: - yield img_id - - -@pytest.fixture(scope="session", params=MANYLINUX_IMAGES.keys()) -def any_manylinux_img(request): - """Each manylinux image, with auditwheel installed. - - Plus up-to-date pip, setuptools and pytest-cov - """ - policy = request.param - base = MANYLINUX_IMAGES[policy] - env = {"PATH": PATH[policy]} - with tmp_docker_image( - base, - [ - "pip install -U pip setuptools pytest-cov", - "pip install -U -e /auditwheel_src", - ], - env, - ) as img_id: - yield policy, img_id - - -@pytest.fixture() -def docker_python(docker_python_img, io_folder): - with docker_container_ctx(docker_python_img, io_folder) as container: - yield container - - -@pytest.fixture() -def any_manylinux_container(any_manylinux_img, io_folder): - policy, manylinux_img = any_manylinux_img - env = {"PATH": PATH[policy]} - for key in os.environ: - if key.startswith("COV_CORE_"): - env[key] = os.environ[key] - - with docker_container_ctx(manylinux_img, io_folder, env) as container: - platform_tag = ".".join( - [f"{p}_{PLATFORM}" for p in [policy] + POLICY_ALIASES.get(policy, [])] - ) - yield f"{policy}_{PLATFORM}", platform_tag, container - - -SHOW_RE = re.compile( - r'[\s](?P\S+) is consistent with the following platform tag: "(?P\S+)"', - flags=re.DOTALL, -) -TAG_RE = re.compile(r"^manylinux_(?P[0-9]+)_(?P[0-9]+)_(?P\S+)$") - - def assert_show_output(manylinux_ctr, wheel, expected_tag, strict): output = docker_exec(manylinux_ctr, f"auditwheel show /io/{wheel}") output = output.replace("\n", " ") match = SHOW_RE.match(output) assert match assert match["wheel"] == wheel - if strict: + if strict or "musllinux" in expected_tag: assert match["tag"] == expected_tag else: expected_match = TAG_RE.match(expected_tag) @@ -240,643 +188,742 @@ def assert_show_output(manylinux_ctr, wheel, expected_tag, strict): assert actual_glibc <= expected_glibc -def test_build_repair_numpy(any_manylinux_container, docker_python, io_folder): - # Integration test: repair numpy built from scratch - - # First build numpy from source as a naive linux wheel that is tied - # to system libraries (atlas, libgfortran...) - policy, tag, manylinux_ctr = any_manylinux_container - if policy.startswith("manylinux_2_24_"): - docker_exec(manylinux_ctr, "apt-get update") - docker_exec( - manylinux_ctr, "apt-get install -y --no-install-recommends libatlas-dev" +class Anylinux: + @pytest.fixture() + def io_folder(self, tmp_path): + d = tmp_path / "io" + d.mkdir(exist_ok=True) + return str(d) + + @pytest.fixture() + def docker_python(self, docker_python_img, io_folder): + with docker_container_ctx(docker_python_img, io_folder) as container: + yield container + + @pytest.fixture() + def any_manylinux_container(self, any_manylinux_img, io_folder): + policy, manylinux_img = any_manylinux_img + env = {"PATH": PATH[policy]} + for key in os.environ: + if key.startswith("COV_CORE_"): + env[key] = os.environ[key] + + with docker_container_ctx(manylinux_img, io_folder, env) as container: + platform_tag = ".".join( + [f"{p}_{PLATFORM}" for p in [policy] + POLICY_ALIASES.get(policy, [])] + ) + yield f"{policy}_{PLATFORM}", platform_tag, container + + def test_build_repair_numpy( + self, any_manylinux_container, docker_python, io_folder + ): + # Integration test: repair numpy built from scratch + + # First build numpy from source as a naive linux wheel that is tied + # to system libraries (atlas, libgfortran...) + policy, tag, manylinux_ctr = any_manylinux_container + if policy.startswith("musllinux_"): + docker_exec(manylinux_ctr, "apk add openblas-dev") + elif policy.startswith("manylinux_2_24_"): + docker_exec(manylinux_ctr, "apt-get update") + docker_exec( + manylinux_ctr, "apt-get install -y --no-install-recommends libatlas-dev" + ) + else: + docker_exec(manylinux_ctr, "yum install -y atlas atlas-devel") + + if op.exists(op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_NUMPY_WHEEL)): + # If numpy has already been built and put in cache, let's reuse this. + shutil.copy2( + op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_NUMPY_WHEEL), + op.join(io_folder, ORIGINAL_NUMPY_WHEEL), + ) + else: + # otherwise build the original linux_x86_64 numpy wheel from source + # and put the result in the cache folder to speed-up future build. + # This part of the build is independent of the auditwheel code-base + # so it's safe to put it in cache. + docker_exec( + manylinux_ctr, + f"pip wheel -w /io --no-binary=:all: numpy=={NUMPY_VERSION}", + ) + os.makedirs(op.join(WHEEL_CACHE_FOLDER, policy), exist_ok=True) + shutil.copy2( + op.join(io_folder, ORIGINAL_NUMPY_WHEEL), + op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_NUMPY_WHEEL), + ) + filenames = os.listdir(io_folder) + assert filenames == [ORIGINAL_NUMPY_WHEEL] + orig_wheel = filenames[0] + assert "manylinux" not in orig_wheel + + # Repair the wheel using the manylinux container + repair_command = ( + f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" ) - else: - docker_exec(manylinux_ctr, "yum install -y atlas atlas-devel") - - if op.exists(op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_NUMPY_WHEEL)): - # If numpy has already been built and put in cache, let's reuse this. - shutil.copy2( - op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_NUMPY_WHEEL), - op.join(io_folder, ORIGINAL_NUMPY_WHEEL), + docker_exec(manylinux_ctr, repair_command) + filenames = os.listdir(io_folder) + assert len(filenames) == 2 + repaired_wheel = f"numpy-{NUMPY_VERSION}-{PYTHON_ABI}-{tag}.whl" + assert repaired_wheel in filenames + assert_show_output(manylinux_ctr, repaired_wheel, policy, False) + + # Check that the repaired numpy wheel can be installed and executed + # on a modern linux image. + docker_exec(docker_python, "pip install /io/" + repaired_wheel) + output = docker_exec( + docker_python, + "python /auditwheel_src/tests/integration/quick_check_numpy.py", ) - else: - # otherwise build the original linux_x86_64 numpy wheel from source - # and put the result in the cache folder to speed-up future build. - # This part of the build is independent of the auditwheel code-base - # so it's safe to put it in cache. + assert output.strip() == "ok" + + # Check that numpy f2py works with a more recent version of gfortran + if policy.startswith("musllinux_"): + docker_exec(docker_python, "apk add musl-dev gfortran") + else: + docker_exec(docker_python, "apt-get update -yqq") + docker_exec(docker_python, "apt-get install -y gfortran") docker_exec( - manylinux_ctr, f"pip wheel -w /io --no-binary=:all: numpy=={NUMPY_VERSION}" - ) - os.makedirs(op.join(WHEEL_CACHE_FOLDER, policy), exist_ok=True) - shutil.copy2( - op.join(io_folder, ORIGINAL_NUMPY_WHEEL), - op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_NUMPY_WHEEL), + docker_python, + "python -m numpy.f2py -c /auditwheel_src/tests/integration/foo.f90 -m foo", ) - filenames = os.listdir(io_folder) - assert filenames == [ORIGINAL_NUMPY_WHEEL] - orig_wheel = filenames[0] - assert "manylinux" not in orig_wheel - - # Repair the wheel using the manylinux container - repair_command = ( - f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" - ) - docker_exec(manylinux_ctr, repair_command) - filenames = os.listdir(io_folder) - assert len(filenames) == 2 - repaired_wheel = f"numpy-{NUMPY_VERSION}-{PYTHON_ABI}-{tag}.whl" - assert repaired_wheel in filenames - assert_show_output(manylinux_ctr, repaired_wheel, policy, False) - - # Check that the repaired numpy wheel can be installed and executed - # on a modern linux image. - docker_exec(docker_python, "pip install /io/" + repaired_wheel) - output = docker_exec( - docker_python, "python /auditwheel_src/tests/integration/quick_check_numpy.py" - ) - assert output.strip() == "ok" - - # Check that numpy f2py works with a more recent version of gfortran - docker_exec(docker_python, "apt-get update -yqq") - docker_exec(docker_python, "apt-get install -y gfortran") - docker_exec( - docker_python, - "python -m numpy.f2py" " -c /auditwheel_src/tests/integration/foo.f90 -m foo", - ) - # Check that the 2 fortran runtimes are well isolated and can be loaded - # at once in the same Python program: - docker_exec(docker_python, ["python", "-c", "'import numpy; import foo'"]) + # Check that the 2 fortran runtimes are well isolated and can be loaded + # at once in the same Python program: + docker_exec(docker_python, ["python", "-c", "'import numpy; import foo'"]) + def test_build_wheel_with_binary_executable( + self, any_manylinux_container, docker_python, io_folder + ): + # Test building a wheel that contains a binary executable (e.g., a program) -def test_build_wheel_with_binary_executable( - any_manylinux_container, docker_python, io_folder -): - # Test building a wheel that contains a binary executable (e.g., a program) + policy, tag, manylinux_ctr = any_manylinux_container + if policy.startswith("musllinux_"): + docker_exec(manylinux_ctr, "apk add gsl-dev") + elif policy.startswith("manylinux_2_24_"): + docker_exec(manylinux_ctr, "apt-get update") + docker_exec( + manylinux_ctr, "apt-get install -y --no-install-recommends libgsl-dev" + ) + else: + docker_exec(manylinux_ctr, "yum install -y gsl-devel") - policy, tag, manylinux_ctr = any_manylinux_container - if policy.startswith("manylinux_2_24_"): - docker_exec(manylinux_ctr, "apt-get update") docker_exec( - manylinux_ctr, "apt-get install -y --no-install-recommends libgsl-dev" + manylinux_ctr, + [ + "bash", + "-c", + "cd /auditwheel_src/tests/integration/testpackage && " + "python -m pip wheel --no-deps -w /io .", + ], ) - else: - docker_exec(manylinux_ctr, "yum install -y gsl-devel") - - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - "cd /auditwheel_src/tests/integration/testpackage && " - "python -m pip wheel --no-deps -w /io .", - ], - ) - filenames = os.listdir(io_folder) - assert filenames == ["testpackage-0.0.1-py3-none-any.whl"] - orig_wheel = filenames[0] - assert "manylinux" not in orig_wheel + filenames = os.listdir(io_folder) + assert filenames == ["testpackage-0.0.1-py3-none-any.whl"] + orig_wheel = filenames[0] + assert "manylinux" not in orig_wheel - # Repair the wheel using the appropriate manylinux container - repair_command = ( - f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" - ) - docker_exec(manylinux_ctr, repair_command) - filenames = os.listdir(io_folder) - assert len(filenames) == 2 - repaired_wheel = f"testpackage-0.0.1-py3-none-{tag}.whl" - assert repaired_wheel in filenames - assert_show_output(manylinux_ctr, repaired_wheel, policy, False) - - docker_exec(docker_python, "pip install /io/" + repaired_wheel) - output = docker_exec( - docker_python, - ["python", "-c", "from testpackage import runit; print(runit(1.5))"], - ) - assert output.strip() == "2.25" - - -@pytest.mark.parametrize("with_dependency", ["0", "1"]) -def test_build_wheel_with_image_dependencies( - with_dependency, any_manylinux_container, docker_python, io_folder -): - # try to repair the wheel targeting different policies - # - # with_dependency == 0 - # The python module has no dependencies that should be grafted-in and - # uses versioned symbols not available on policies pre-dating the policy - # matching the image being tested. - # with_dependency == 1 - # The python module itself does not use versioned symbols but has a - # dependency that should be grafted-in that uses versioned symbols not - # available on policies pre-dating the policy matching the image being - # tested. - - policy, tag, manylinux_ctr = any_manylinux_container - - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - "cd /auditwheel_src/tests/integration/testdependencies && " - f"WITH_DEPENDENCY={with_dependency} python setup.py -v build_ext -f " - "bdist_wheel -d /io", - ], - ) + # Repair the wheel using the appropriate manylinux container + repair_command = ( + f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" + ) + docker_exec(manylinux_ctr, repair_command) + filenames = os.listdir(io_folder) + assert len(filenames) == 2 + repaired_wheel = f"testpackage-0.0.1-py3-none-{tag}.whl" + assert repaired_wheel in filenames + assert_show_output(manylinux_ctr, repaired_wheel, policy, False) - filenames = os.listdir(io_folder) - orig_wheel = filenames[0] - assert "manylinux" not in orig_wheel + docker_exec(docker_python, "pip install /io/" + repaired_wheel) + output = docker_exec( + docker_python, + ["python", "-c", "from testpackage import runit; print(runit(1.5))"], + ) + assert output.strip() == "2.25" - repair_command = ( - "LD_LIBRARY_PATH=" - "/auditwheel_src/tests/integration/testdependencies:$LD_LIBRARY_PATH " - "auditwheel -v repair --plat {policy} -w /io /io/{orig_wheel}" - ) + def test_build_repair_pure_wheel(self, any_manylinux_container, io_folder): + policy, tag, manylinux_ctr = any_manylinux_container - policy_priority = get_priority_by_name(policy) - older_policies = [ - f"{p}_{PLATFORM}" - for p in MANYLINUX_IMAGES.keys() - if policy_priority < get_priority_by_name(f"{p}_{PLATFORM}") - ] - for target_policy in older_policies: - # we shall fail to repair the wheel when targeting an older policy than - # the one matching the image - with pytest.raises(CalledProcessError): - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - repair_command.format(policy=target_policy, orig_wheel=orig_wheel), - ], + if op.exists(op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_SIX_WHEEL)): + # If six has already been built and put in cache, let's reuse this. + shutil.copy2( + op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_SIX_WHEEL), + op.join(io_folder, ORIGINAL_SIX_WHEEL), + ) + logger.info(f"Copied six wheel from {WHEEL_CACHE_FOLDER} to {io_folder}") + else: + docker_exec(manylinux_ctr, "pip wheel -w /io --no-binary=:all: six==1.11.0") + os.makedirs(op.join(WHEEL_CACHE_FOLDER, policy), exist_ok=True) + shutil.copy2( + op.join(io_folder, ORIGINAL_SIX_WHEEL), + op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_SIX_WHEEL), ) - # check all works properly when targeting the policy matching the image - docker_exec( - manylinux_ctr, - ["bash", "-c", repair_command.format(policy=policy, orig_wheel=orig_wheel)], - ) - filenames = os.listdir(io_folder) - assert len(filenames) == 2 - repaired_wheel = f"testdependencies-0.0.1-{PYTHON_ABI}-{tag}.whl" - assert repaired_wheel in filenames - assert_show_output(manylinux_ctr, repaired_wheel, policy, True) - - # check the original wheel with a dependency was not compliant - # and check the one without a dependency was already compliant - if with_dependency == "1": - expected = f"linux_{PLATFORM}" - else: - expected = policy - assert_show_output(manylinux_ctr, orig_wheel, expected, True) + filenames = os.listdir(io_folder) + assert filenames == [ORIGINAL_SIX_WHEEL] + orig_wheel = filenames[0] + assert "manylinux" not in orig_wheel - docker_exec(docker_python, "pip install /io/" + repaired_wheel) - docker_exec( - docker_python, - [ - "python", - "-c", - "from sys import exit; from testdependencies import run; exit(run())", - ], - ) + # Repair the wheel using the manylinux container + repair_command = f"auditwheel repair --plat {policy} -w /io /io/{orig_wheel}" + output = docker_exec(manylinux_ctr, repair_command, expected_retcode=1) + assert "This does not look like a platform wheel" in output + + output = docker_exec( + manylinux_ctr, f"auditwheel show /io/{orig_wheel}", expected_retcode=1 + ) + assert "This does not look like a platform wheel" in output + @pytest.mark.parametrize("dtag", ["rpath", "runpath"]) + def test_build_wheel_depending_on_library_with_rpath( + self, any_manylinux_container, docker_python, io_folder, dtag + ): + # Test building a wheel that contains an extension depending on a library + # with RPATH or RUNPATH set. + # Following checks are performed: + # - check if RUNPATH is replaced by RPATH + # - check if RPATH location is correct, i.e. it is inside .libs directory + # where all gathered libraries are put -def test_build_repair_pure_wheel(any_manylinux_container, io_folder): - policy, tag, manylinux_ctr = any_manylinux_container + policy, tag, manylinux_ctr = any_manylinux_container - if op.exists(op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_SIX_WHEEL)): - # If six has already been built and put in cache, let's reuse this. - shutil.copy2( - op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_SIX_WHEEL), - op.join(io_folder, ORIGINAL_SIX_WHEEL), + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + ( + "cd /auditwheel_src/tests/integration/testrpath " + "&& rm -rf build " + f"&& DTAG={dtag} python setup.py bdist_wheel -d /io" + ), + ], ) - logger.info(f"Copied six wheel from {WHEEL_CACHE_FOLDER} to {io_folder}") - else: - docker_exec(manylinux_ctr, "pip wheel -w /io --no-binary=:all: six==1.11.0") - os.makedirs(op.join(WHEEL_CACHE_FOLDER, policy), exist_ok=True) - shutil.copy2( - op.join(io_folder, ORIGINAL_SIX_WHEEL), - op.join(WHEEL_CACHE_FOLDER, policy, ORIGINAL_SIX_WHEEL), + with open( + op.join(op.dirname(__file__), "testrpath", "a", "liba.so"), "rb" + ) as f: + elf = ELFFile(f) + dynamic = elf.get_section_by_name(".dynamic") + tags = {t.entry.d_tag for t in dynamic.iter_tags()} + assert f"DT_{dtag.upper()}" in tags + filenames = os.listdir(io_folder) + assert filenames == [f"testrpath-0.0.1-{PYTHON_ABI}-linux_{PLATFORM}.whl"] + orig_wheel = filenames[0] + assert "manylinux" not in orig_wheel + + # Repair the wheel using the appropriate manylinux container + repair_command = ( + f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" ) + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + "LD_LIBRARY_PATH=" + "/auditwheel_src/tests/integration/testrpath/a:$LD_LIBRARY_PATH " + + repair_command, + ], + ) + filenames = os.listdir(io_folder) + assert len(filenames) == 2 + repaired_wheel = f"testrpath-0.0.1-{PYTHON_ABI}-{tag}.whl" + assert repaired_wheel in filenames + assert_show_output(manylinux_ctr, repaired_wheel, policy, False) - filenames = os.listdir(io_folder) - assert filenames == [ORIGINAL_SIX_WHEEL] - orig_wheel = filenames[0] - assert "manylinux" not in orig_wheel - - # Repair the wheel using the manylinux container - repair_command = f"auditwheel repair --plat {policy} -w /io /io/{orig_wheel}" - output = docker_exec(manylinux_ctr, repair_command, expected_retcode=1) - assert "This does not look like a platform wheel" in output - - output = docker_exec( - manylinux_ctr, f"auditwheel show /io/{orig_wheel}", expected_retcode=1 - ) - assert "This does not look like a platform wheel" in output - - -@pytest.mark.parametrize("dtag", ["rpath", "runpath"]) -def test_build_wheel_depending_on_library_with_rpath( - any_manylinux_container, docker_python, io_folder, dtag -): - # Test building a wheel that contains an extension depending on a library - # with RPATH or RUNPATH set. - # Following checks are performed: - # - check if RUNPATH is replaced by RPATH - # - check if RPATH location is correct, i.e. it is inside .libs directory - # where all gathered libraries are put - - policy, tag, manylinux_ctr = any_manylinux_container - - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - ( - "cd /auditwheel_src/tests/integration/testrpath " - "&& rm -rf build " - f"&& DTAG={dtag} python setup.py bdist_wheel -d /io" - ), - ], - ) - with open(op.join(op.dirname(__file__), "testrpath", "a", "liba.so"), "rb") as f: - elf = ELFFile(f) - dynamic = elf.get_section_by_name(".dynamic") - tags = {t.entry.d_tag for t in dynamic.iter_tags()} - assert f"DT_{dtag.upper()}" in tags - filenames = os.listdir(io_folder) - assert filenames == [f"testrpath-0.0.1-{PYTHON_ABI}-linux_{PLATFORM}.whl"] - orig_wheel = filenames[0] - assert "manylinux" not in orig_wheel - - # Repair the wheel using the appropriate manylinux container - repair_command = ( - f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" - ) - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - "LD_LIBRARY_PATH=" - "/auditwheel_src/tests/integration/testrpath/a:$LD_LIBRARY_PATH " - + repair_command, - ], - ) - filenames = os.listdir(io_folder) - assert len(filenames) == 2 - repaired_wheel = f"testrpath-0.0.1-{PYTHON_ABI}-{tag}.whl" - assert repaired_wheel in filenames - assert_show_output(manylinux_ctr, repaired_wheel, policy, False) - - docker_exec(docker_python, "pip install /io/" + repaired_wheel) - output = docker_exec( - docker_python, - ["python", "-c", "from testrpath import testrpath; print(testrpath.func())"], - ) - assert output.strip() == "11" - with zipfile.ZipFile(os.path.join(io_folder, repaired_wheel)) as w: - for name in w.namelist(): - if "testrpath/.libs/lib" in name: - with w.open(name) as f: - elf = ELFFile(io.BytesIO(f.read())) - dynamic = elf.get_section_by_name(".dynamic") - assert ( - len( - [ + docker_exec(docker_python, "pip install /io/" + repaired_wheel) + output = docker_exec( + docker_python, + [ + "python", + "-c", + "from testrpath import testrpath; print(testrpath.func())", + ], + ) + assert output.strip() == "11" + with zipfile.ZipFile(os.path.join(io_folder, repaired_wheel)) as w: + for name in w.namelist(): + if "testrpath/.libs/lib" in name: + with w.open(name) as f: + elf = ELFFile(io.BytesIO(f.read())) + dynamic = elf.get_section_by_name(".dynamic") + assert ( + len( + [ + t + for t in dynamic.iter_tags() + if t.entry.d_tag == "DT_RUNPATH" + ] + ) + == 0 + ) + if ".libs/liba" in name: + rpath_tags = [ t for t in dynamic.iter_tags() - if t.entry.d_tag == "DT_RUNPATH" + if t.entry.d_tag == "DT_RPATH" ] - ) - == 0 - ) - if ".libs/liba" in name: - rpath_tags = [ - t - for t in dynamic.iter_tags() - if t.entry.d_tag == "DT_RPATH" - ] - assert len(rpath_tags) == 1 - assert rpath_tags[0].rpath == "$ORIGIN/." - - -def test_build_repair_multiple_top_level_modules_wheel( - any_manylinux_container, docker_python, io_folder -): - - policy, tag, manylinux_ctr = any_manylinux_container - - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - "cd /auditwheel_src/tests/integration/multiple_top_level " - "&& make " - "&& pip wheel . -w /io", - ], - ) + assert len(rpath_tags) == 1 + assert rpath_tags[0].rpath == "$ORIGIN/." - filenames = os.listdir(io_folder) - assert filenames == [f"multiple_top_level-1.0-{PYTHON_ABI}-linux_{PLATFORM}.whl"] - orig_wheel = filenames[0] - assert "manylinux" not in orig_wheel + def test_build_repair_multiple_top_level_modules_wheel( + self, any_manylinux_container, docker_python, io_folder + ): - # Repair the wheel using the appropriate manylinux container - repair_command = ( - f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" - ) - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - ( - "LD_LIBRARY_PATH=" - "/auditwheel_src/tests/integration/multiple_top_level/lib-src/a:" - "/auditwheel_src/tests/integration/multiple_top_level/lib-src/b:" - "$LD_LIBRARY_PATH " - ) - + repair_command, - ], - ) - filenames = os.listdir(io_folder) - assert len(filenames) == 2 - repaired_wheel = f"multiple_top_level-1.0-{PYTHON_ABI}-{tag}.whl" - assert repaired_wheel in filenames - assert_show_output(manylinux_ctr, repaired_wheel, policy, False) - - docker_exec(docker_python, "pip install /io/" + repaired_wheel) - for mod, func, expected in [ - ("example_a", "example_a", "11"), - ("example_b", "example_b", "110"), - ]: - output = docker_exec( - docker_python, + policy, tag, manylinux_ctr = any_manylinux_container + + docker_exec( + manylinux_ctr, [ - "python", + "bash", "-c", - f"from {mod} import {func}; print({func}())", + "cd /auditwheel_src/tests/integration/multiple_top_level " + "&& make clean all " + "&& pip wheel . -w /io", ], - ).strip() - assert output.strip() == expected - with zipfile.ZipFile(os.path.join(io_folder, repaired_wheel)) as w: - for lib_name in ["liba", "libb"]: - assert any( - re.match(rf"multiple_top_level.libs/{lib_name}.*\.so", name) - for name in w.namelist() - ) + ) + filenames = os.listdir(io_folder) + assert filenames == [ + f"multiple_top_level-1.0-{PYTHON_ABI}-linux_{PLATFORM}.whl" + ] + orig_wheel = filenames[0] + assert "manylinux" not in orig_wheel -def test_build_repair_wheel_with_internal_rpath( - any_manylinux_container, docker_python, io_folder -): - policy, tag, manylinux_ctr = any_manylinux_container - - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - "cd /auditwheel_src/tests/integration/internal_rpath " - "&& make " - "&& mv lib-src/a/liba.so internal_rpath" - "&& pip wheel . -w /io", - ], - ) + # Repair the wheel using the appropriate manylinux container + repair_command = ( + f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" + ) + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + ( + "LD_LIBRARY_PATH=" + "/auditwheel_src/tests/integration/multiple_top_level/lib-src/a:" + "/auditwheel_src/tests/integration/multiple_top_level/lib-src/b:" + "$LD_LIBRARY_PATH " + ) + + repair_command, + ], + ) + filenames = os.listdir(io_folder) + assert len(filenames) == 2 + repaired_wheel = f"multiple_top_level-1.0-{PYTHON_ABI}-{tag}.whl" + assert repaired_wheel in filenames + assert_show_output(manylinux_ctr, repaired_wheel, policy, False) + + docker_exec(docker_python, "pip install /io/" + repaired_wheel) + for mod, func, expected in [ + ("example_a", "example_a", "11"), + ("example_b", "example_b", "110"), + ]: + output = docker_exec( + docker_python, + [ + "python", + "-c", + f"from {mod} import {func}; print({func}())", + ], + ).strip() + assert output.strip() == expected + with zipfile.ZipFile(os.path.join(io_folder, repaired_wheel)) as w: + for lib_name in ["liba", "libb"]: + assert any( + re.match(rf"multiple_top_level.libs/{lib_name}.*\.so", name) + for name in w.namelist() + ) + + def test_build_repair_wheel_with_internal_rpath( + self, any_manylinux_container, docker_python, io_folder + ): + policy, tag, manylinux_ctr = any_manylinux_container + + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + "cd /auditwheel_src/tests/integration/internal_rpath " + "&& make clean all " + "&& mv lib-src/a/liba.so internal_rpath " + "&& pip wheel . -w /io", + ], + ) - filenames = os.listdir(io_folder) - assert filenames == [f"internal_rpath-1.0-{PYTHON_ABI}-linux_{PLATFORM}.whl"] - orig_wheel = filenames[0] - assert "manylinux" not in orig_wheel + filenames = os.listdir(io_folder) + assert filenames == [f"internal_rpath-1.0-{PYTHON_ABI}-linux_{PLATFORM}.whl"] + orig_wheel = filenames[0] + assert "manylinux" not in orig_wheel - # Repair the wheel using the appropriate manylinux container - repair_command = ( - f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" - ) - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - ( - "LD_LIBRARY_PATH=" - "/auditwheel_src/tests/integration/internal_rpath/lib-src/b:" - "$LD_LIBRARY_PATH " - ) - + repair_command, - ], - ) - filenames = os.listdir(io_folder) - assert len(filenames) == 2 - repaired_wheel = f"internal_rpath-1.0-{PYTHON_ABI}-{tag}.whl" - assert repaired_wheel in filenames - assert_show_output(manylinux_ctr, repaired_wheel, policy, False) - - docker_exec(docker_python, "pip install /io/" + repaired_wheel) - for mod, func, expected in [ - ("example_a", "example_a", "11"), - ("example_b", "example_b", "10"), - ]: + # Repair the wheel using the appropriate manylinux container + repair_command = ( + f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" + ) + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + ( + "LD_LIBRARY_PATH=" + "/auditwheel_src/tests/integration/internal_rpath/lib-src/b:" + "$LD_LIBRARY_PATH " + ) + + repair_command, + ], + ) + filenames = os.listdir(io_folder) + assert len(filenames) == 2 + repaired_wheel = f"internal_rpath-1.0-{PYTHON_ABI}-{tag}.whl" + assert repaired_wheel in filenames + assert_show_output(manylinux_ctr, repaired_wheel, policy, False) + + docker_exec(docker_python, "pip install /io/" + repaired_wheel) + for mod, func, expected in [ + ("example_a", "example_a", "11"), + ("example_b", "example_b", "10"), + ]: + output = docker_exec( + docker_python, + [ + "python", + "-c", + f"from internal_rpath.{mod} import {func}; print({func}())", + ], + ).strip() + assert output.strip() == expected + with zipfile.ZipFile(os.path.join(io_folder, repaired_wheel)) as w: + for lib_name in ["libb"]: + assert any( + re.match(rf"internal_rpath.libs/{lib_name}.*\.so", name) + for name in w.namelist() + ) + + def test_strip_wheel(self, any_manylinux_container, docker_python, io_folder): + policy, tag, manylinux_ctr = any_manylinux_container + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + "cd /auditwheel_src/tests/integration/sample_extension " + "&& python -m pip wheel --no-deps -w /io .", + ], + ) + + orig_wheel, *_ = os.listdir(io_folder) + assert orig_wheel.startswith("sample_extension-0.1.0") + + # Repair the wheel using the appropriate manylinux container + repair_command = ( + f"auditwheel repair --plat {policy} --strip -w /io /io/{orig_wheel}" + ) + docker_exec(manylinux_ctr, repair_command) + + repaired_wheel, *_ = glob.glob(f"{io_folder}/*{policy}*.whl") + repaired_wheel = os.path.basename(repaired_wheel) + + docker_exec(docker_python, "pip install /io/" + repaired_wheel) output = docker_exec( docker_python, [ "python", "-c", - f"from internal_rpath.{mod} import {func}; print({func}())", + "from sample_extension import test_func; print(test_func(1))", ], - ).strip() - assert output.strip() == expected - with zipfile.ZipFile(os.path.join(io_folder, repaired_wheel)) as w: - for lib_name in ["libb"]: - assert any( - re.match(rf"internal_rpath.libs/{lib_name}.*\.so", name) - for name in w.namelist() - ) - + ) + assert output.strip() == "2" -def test_strip_wheel(any_manylinux_container, docker_python, io_folder): - policy, tag, manylinux_ctr = any_manylinux_container - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - "cd /auditwheel_src/tests/integration/sample_extension " - "&& python -m pip wheel --no-deps -w /io .", - ], - ) + def test_nonpy_rpath(self, any_manylinux_container, docker_python, io_folder): + # Tests https://github.com/pypa/auditwheel/issues/136 + policy, tag, manylinux_ctr = any_manylinux_container + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + "cd /auditwheel_src/tests/integration/nonpy_rpath " + "&& python -m pip wheel --no-deps -w /io .", + ], + ) - orig_wheel, *_ = os.listdir(io_folder) - assert orig_wheel.startswith("sample_extension-0.1.0") + orig_wheel, *_ = os.listdir(io_folder) + assert orig_wheel.startswith("nonpy_rpath-0.1.0") + assert "manylinux" not in orig_wheel - # Repair the wheel using the appropriate manylinux container - repair_command = ( - f"auditwheel repair --plat {policy} --strip -w /io /io/{orig_wheel}" - ) - docker_exec(manylinux_ctr, repair_command) + # Repair the wheel using the appropriate manylinux container + repair_command = ( + f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" + ) + docker_exec(manylinux_ctr, ["bash", "-c", repair_command]) + filenames = os.listdir(io_folder) + assert len(filenames) == 2 + repaired_wheel = f"nonpy_rpath-0.1.0-{PYTHON_ABI}-{tag}.whl" + assert repaired_wheel in filenames + assert_show_output(manylinux_ctr, repaired_wheel, policy, False) + + # Test the resulting wheel outside the manylinux container + docker_exec(docker_python, "pip install /io/" + repaired_wheel) + docker_exec( + docker_python, + [ + "python", + "-c", + "import nonpy_rpath\n" + "assert nonpy_rpath.crypt_something().startswith('*')", + ], + ) - repaired_wheel, *_ = glob.glob(f"{io_folder}/*{policy}*.whl") - repaired_wheel = os.path.basename(repaired_wheel) - docker_exec(docker_python, "pip install /io/" + repaired_wheel) - output = docker_exec( - docker_python, - ["python", "-c", "from sample_extension import test_func; print(test_func(1))"], - ) - assert output.strip() == "2" +class TestManylinux(Anylinux): + @pytest.fixture(scope="session") + def docker_python_img(self): + """The glibc Python base image with up-to-date pip""" + with tmp_docker_image( + MANYLINUX_PYTHON_IMAGE_ID, ["pip install -U pip"] + ) as img_id: + yield img_id + + @pytest.fixture(scope="session", params=MANYLINUX_IMAGES.keys()) + def any_manylinux_img(self, request): + """Each manylinux image, with auditwheel installed. + + Plus up-to-date pip, setuptools and pytest-cov + """ + policy = request.param + base = MANYLINUX_IMAGES[policy] + env = {"PATH": PATH[policy]} + with tmp_docker_image( + base, + [ + "pip install -U pip setuptools pytest-cov", + "pip install -U -e /auditwheel_src", + ], + env, + ) as img_id: + yield policy, img_id + + @pytest.mark.parametrize("with_dependency", ["0", "1"]) + def test_build_wheel_with_image_dependencies( + self, with_dependency, any_manylinux_container, docker_python, io_folder + ): + # try to repair the wheel targeting different policies + # + # with_dependency == 0 + # The python module has no dependencies that should be grafted-in and + # uses versioned symbols not available on policies pre-dating the policy + # matching the image being tested. + # with_dependency == 1 + # The python module itself does not use versioned symbols but has a + # dependency that should be grafted-in that uses versioned symbols not + # available on policies pre-dating the policy matching the image being + # tested. + + policy, tag, manylinux_ctr = any_manylinux_container + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + "cd /auditwheel_src/tests/integration/testdependencies && " + f"WITH_DEPENDENCY={with_dependency} python setup.py -v build_ext -f " + "bdist_wheel -d /io", + ], + ) -@pytest.mark.parametrize( - "target_policy", - [f"{p}_{PLATFORM}" for p in list(MANYLINUX_IMAGES.keys())] - + [f"{p}_{PLATFORM}" for aliases in POLICY_ALIASES.values() for p in aliases], -) -@pytest.mark.parametrize("only_plat", [True, False]) -def test_build_wheel_compat( - target_policy, only_plat, any_manylinux_container, docker_python, io_folder -): - # test building wheels with compatibility with older spec - # check aliases for older spec - - policy, tag, manylinux_ctr = any_manylinux_container - - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - "cd /auditwheel_src/tests/integration/testsimple " - "&& python -m pip wheel --no-deps -w /io .", - ], - ) + filenames = os.listdir(io_folder) + orig_wheel = filenames[0] + assert "manylinux" not in orig_wheel - filenames = os.listdir(io_folder) - orig_wheel = filenames[0] - assert "manylinux" not in orig_wheel + repair_command = ( + "LD_LIBRARY_PATH=" + "/auditwheel_src/tests/integration/testdependencies:$LD_LIBRARY_PATH " + "auditwheel -v repair --plat {policy} -w /io /io/{orig_wheel}" + ) - if PLATFORM in {"x86_64", "i686"}: - expect = f"manylinux_2_5_{PLATFORM}" - expect_tag = f"manylinux_2_5_{PLATFORM}.manylinux1_{PLATFORM}" - else: - expect = f"manylinux_2_17_{PLATFORM}" - expect_tag = f"manylinux_2_17_{PLATFORM}.manylinux2014_{PLATFORM}" - - target_tag = target_policy - for pep600_policy, aliases in POLICY_ALIASES.items(): - policy_ = f"{pep600_policy}_{PLATFORM}" - aliases_ = [f"{p}_{PLATFORM}" for p in aliases] - if target_policy == policy_ or target_policy in aliases_: - target_tag = f'{policy_}.{".".join(aliases_)}' - - only_plat_arg = "--only-plat" if only_plat else "" - # we shall ba able to repair the wheel for all targets - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - f"auditwheel -v repair --plat {target_policy} {only_plat_arg} -w /io" - f" /io/{orig_wheel}", - ], - ) - filenames = os.listdir(io_folder) - assert len(filenames) == 2 - if only_plat or target_tag == expect_tag: - repaired_tag = target_tag - else: - repaired_tag = f"{expect_tag}.{target_tag}" - repaired_wheel = f"testsimple-0.0.1-{PYTHON_ABI}-{repaired_tag}.whl" - assert repaired_wheel in filenames - assert_show_output(manylinux_ctr, repaired_wheel, expect, True) + policy_priority = get_priority_by_name(policy) + older_policies = [ + f"{p}_{PLATFORM}" + for p in MANYLINUX_IMAGES.keys() + if policy_priority < get_priority_by_name(f"{p}_{PLATFORM}") + ] + for target_policy in older_policies: + # we shall fail to repair the wheel when targeting an older policy than + # the one matching the image + with pytest.raises(CalledProcessError): + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + repair_command.format( + policy=target_policy, orig_wheel=orig_wheel + ), + ], + ) + + # check all works properly when targeting the policy matching the image + docker_exec( + manylinux_ctr, + ["bash", "-c", repair_command.format(policy=policy, orig_wheel=orig_wheel)], + ) + filenames = os.listdir(io_folder) + assert len(filenames) == 2 + repaired_wheel = f"testdependencies-0.0.1-{PYTHON_ABI}-{tag}.whl" + assert repaired_wheel in filenames + assert_show_output(manylinux_ctr, repaired_wheel, policy, True) + + # check the original wheel with a dependency was not compliant + # and check the one without a dependency was already compliant + if with_dependency == "1": + expected = f"linux_{PLATFORM}" + else: + expected = policy + assert_show_output(manylinux_ctr, orig_wheel, expected, True) + + docker_exec(docker_python, "pip install /io/" + repaired_wheel) + docker_exec( + docker_python, + [ + "python", + "-c", + "from sys import exit; from testdependencies import run; exit(run())", + ], + ) - docker_exec(docker_python, f"pip install /io/{repaired_wheel}") - docker_exec( + @pytest.mark.parametrize( + "target_policy", + [f"{p}_{PLATFORM}" for p in list(MANYLINUX_IMAGES.keys())] + + [f"{p}_{PLATFORM}" for aliases in POLICY_ALIASES.values() for p in aliases], + ) + @pytest.mark.parametrize("only_plat", [True, False]) + def test_build_wheel_compat( + self, + target_policy, + only_plat, + any_manylinux_container, docker_python, - [ - "python", - "-c", - "from sys import exit; from testsimple import run; exit(run())", - ], - ) + io_folder, + ): + # test building wheels with compatibility with older spec + # check aliases for older spec + policy, tag, manylinux_ctr = any_manylinux_container -def test_nonpy_rpath(any_manylinux_container, docker_python, io_folder): - # Tests https://github.com/pypa/auditwheel/issues/136 - policy, tag, manylinux_ctr = any_manylinux_container - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - "cd /auditwheel_src/tests/integration/nonpy_rpath " - "&& python -m pip wheel --no-deps -w /io .", - ], - ) - - orig_wheel, *_ = os.listdir(io_folder) - assert orig_wheel.startswith("nonpy_rpath-0.1.0") - assert "manylinux" not in orig_wheel + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + "cd /auditwheel_src/tests/integration/testsimple " + "&& python -m pip wheel --no-deps -w /io .", + ], + ) - # Repair the wheel using the appropriate manylinux container - repair_command = ( - f"auditwheel repair --plat {policy} --only-plat -w /io /io/{orig_wheel}" - ) - docker_exec(manylinux_ctr, repair_command) - filenames = os.listdir(io_folder) - assert len(filenames) == 2 - repaired_wheel = f"nonpy_rpath-0.1.0-{PYTHON_ABI}-{tag}.whl" - assert repaired_wheel in filenames - assert_show_output(manylinux_ctr, repaired_wheel, policy, False) - - # Test the resulting wheel outside the manylinux container - docker_exec(docker_python, "pip install /io/" + repaired_wheel) - docker_exec( - docker_python, - [ - "python", - "-c", - "import nonpy_rpath; assert nonpy_rpath.crypt_something().startswith('*')", - ], - ) + filenames = os.listdir(io_folder) + orig_wheel = filenames[0] + assert "manylinux" not in orig_wheel + + if PLATFORM in {"x86_64", "i686"}: + expect = f"manylinux_2_5_{PLATFORM}" + expect_tag = f"manylinux_2_5_{PLATFORM}.manylinux1_{PLATFORM}" + else: + expect = f"manylinux_2_17_{PLATFORM}" + expect_tag = f"manylinux_2_17_{PLATFORM}.manylinux2014_{PLATFORM}" + + target_tag = target_policy + for pep600_policy, aliases in POLICY_ALIASES.items(): + policy_ = f"{pep600_policy}_{PLATFORM}" + aliases_ = [f"{p}_{PLATFORM}" for p in aliases] + if target_policy == policy_ or target_policy in aliases_: + target_tag = f'{policy_}.{".".join(aliases_)}' + + only_plat_arg = "--only-plat" if only_plat else "" + # we shall ba able to repair the wheel for all targets + docker_exec( + manylinux_ctr, + [ + "bash", + "-c", + f"auditwheel -v repair --plat {target_policy} {only_plat_arg} -w /io" + f" /io/{orig_wheel}", + ], + ) + filenames = os.listdir(io_folder) + assert len(filenames) == 2 + if only_plat or target_tag == expect_tag: + repaired_tag = target_tag + else: + repaired_tag = f"{expect_tag}.{target_tag}" + repaired_wheel = f"testsimple-0.0.1-{PYTHON_ABI}-{repaired_tag}.whl" + assert repaired_wheel in filenames + assert_show_output(manylinux_ctr, repaired_wheel, expect, True) + + docker_exec(docker_python, f"pip install /io/{repaired_wheel}") + docker_exec( + docker_python, + [ + "python", + "-c", + "from sys import exit; from testsimple import run; exit(run())", + ], + ) + def test_zlib_blacklist(self, any_manylinux_container, docker_python, io_folder): + policy, tag, manylinux_ctr = any_manylinux_container + if policy.startswith("manylinux_2_17_"): + pytest.skip(f"{policy} image has no blacklist symbols in libz.so.1") -def test_zlib_blacklist(any_manylinux_container, docker_python, io_folder): - policy, tag, manylinux_ctr = any_manylinux_container - if policy.startswith("manylinux_2_17_"): - pytest.skip(f"{policy} image has no blacklist symbols in libz.so.1") - if policy.startswith("manylinux_2_24_"): - docker_exec(manylinux_ctr, "apt-get update") docker_exec( - manylinux_ctr, "apt-get install -y --no-install-recommends libz-dev" + manylinux_ctr, + [ + "bash", + "-c", + "cd /auditwheel_src/tests/integration/testzlib " + "&& python -m pip wheel --no-deps -w /io .", + ], ) - else: - docker_exec(manylinux_ctr, "yum install -y zlib-devel") - docker_exec( - manylinux_ctr, - [ - "bash", - "-c", - "cd /auditwheel_src/tests/integration/testzlib " - "&& python -m pip wheel --no-deps -w /io .", - ], - ) - orig_wheel, *_ = os.listdir(io_folder) - assert orig_wheel.startswith("testzlib-0.0.1") - - # Repair the wheel using the appropriate manylinux container - repair_command = f"auditwheel repair --plat {policy} -w /io /io/{orig_wheel}" - with pytest.raises(CalledProcessError): - docker_exec(manylinux_ctr, repair_command) + orig_wheel, *_ = os.listdir(io_folder) + assert orig_wheel.startswith("testzlib-0.0.1") - # Check auditwheel show warns about the black listed symbols - output = docker_exec(manylinux_ctr, f"auditwheel -v show /io/{orig_wheel}") - assert "black-listed symbol dependencies" in output.replace("\n", " ") + # Repair the wheel using the appropriate manylinux container + repair_command = f"auditwheel repair --plat {policy} -w /io /io/{orig_wheel}" + with pytest.raises(CalledProcessError): + docker_exec(manylinux_ctr, repair_command) + + # Check auditwheel show warns about the black listed symbols + output = docker_exec(manylinux_ctr, f"auditwheel -v show /io/{orig_wheel}") + assert "black-listed symbol dependencies" in output.replace("\n", " ") + + +class TestMusllinux(Anylinux): + @pytest.fixture(scope="session") + def docker_python_img(self): + """The alpine Python base image with up-to-date pip""" + with tmp_docker_image( + MUSLLINUX_PYTHON_IMAGE_ID, ["pip install -U pip"] + ) as img_id: + yield img_id + + @pytest.fixture(scope="session", params=MUSLLINUX_IMAGES.keys()) + def any_manylinux_img(self, request): + """Each musllinux image, with auditwheel installed. + + Plus up-to-date pip, setuptools and pytest-cov + """ + policy = request.param + base = MUSLLINUX_IMAGES[policy] + env = {"PATH": PATH[policy]} + with tmp_docker_image( + base, + [ + "pip install -U pip setuptools pytest-cov", + "pip install -U -e /auditwheel_src", + ], + env, + ) as img_id: + yield policy, img_id diff --git a/tests/integration/testrpath/setup.py b/tests/integration/testrpath/setup.py index aab8d8b4..45e7a16a 100644 --- a/tests/integration/testrpath/setup.py +++ b/tests/integration/testrpath/setup.py @@ -8,7 +8,7 @@ cmd = ( "gcc -fPIC -shared -o a/liba.so " "-Wl,{dtags_flag} -Wl,-rpath=$ORIGIN/../b " - "-Ib -Lb -lb a/a.c" + "-Ib a/a.c -Lb -lb" ).format( dtags_flag=( "--enable-new-dtags"