diff --git a/auditwheel/wheel_abi.py b/auditwheel/wheel_abi.py index 8b9eafee..2e27faef 100644 --- a/auditwheel/wheel_abi.py +++ b/auditwheel/wheel_abi.py @@ -40,43 +40,56 @@ def get_wheel_elfdata(wheel_fn: str): uses_PyFPE_jbuf = False with InGenericPkgCtx(wheel_fn) as ctx: + shared_libraries_in_purelib = [] + for fn, elf in elf_file_filter(ctx.iter_files()): - is_py_ext, py_ver = elf_is_python_extension(fn, elf) # Check for invalid binary wheel format: no shared library should # be found in purelib so_path_split = fn.split(os.sep) + + # If this is in purelib, add it to the list of shared libraries in purelib if 'purelib' in so_path_split: - raise RuntimeError( - ('Invalid binary wheel, found shared library "%s" in ' - 'purelib folder.\n' - 'The wheel has to be platlib compliant in order to be ' - 'repaired by auditwheel.') % - so_path_split[-1]) - - log.debug('processing: %s', fn) - elftree = lddtree(fn) - - for key, value in elf_find_versioned_symbols(elf): - log.debug('key %s, value %s', key, value) - versioned_symbols[key].add(value) - - # If the ELF is a Python extension, we definitely need to include - # its external dependencies. - if is_py_ext: - full_elftree[fn] = elftree - uses_PyFPE_jbuf |= elf_references_PyFPE_jbuf(elf) - if py_ver == 2: - uses_ucs2_symbols |= any( - True for _ in elf_find_ucs2_symbols(elf)) - full_external_refs[fn] = lddtree_external_references(elftree, - ctx.path) - else: - # If the ELF is not a Python extension, it might be included in - # the wheel already because auditwheel repair vendored it, so - # we will check whether we should include its internal - # references later. - nonpy_elftree[fn] = elftree + shared_libraries_in_purelib.append(so_path_split[-1]) + + # If at least one shared library exists in purelib, this is going to + # fail and there's no need to do further checks + if not shared_libraries_in_purelib: + log.debug('processing: %s', fn) + elftree = lddtree(fn) + + for key, value in elf_find_versioned_symbols(elf): + log.debug('key %s, value %s', key, value) + versioned_symbols[key].add(value) + + is_py_ext, py_ver = elf_is_python_extension(fn, elf) + + # If the ELF is a Python extention, we definitely need to include + # its external dependencies. + if is_py_ext: + full_elftree[fn] = elftree + uses_PyFPE_jbuf |= elf_references_PyFPE_jbuf(elf) + if py_ver == 2: + uses_ucs2_symbols |= any( + True for _ in elf_find_ucs2_symbols(elf)) + full_external_refs[fn] = lddtree_external_references(elftree, + ctx.path) + else: + # If the ELF is not a Python extension, it might be included in + # the wheel already because auditwheel repair vendored it, so + # we will check whether we should include its internal + # references later. + nonpy_elftree[fn] = elftree + + # If at least one shared library exists in purelib, raise an error + if shared_libraries_in_purelib: + raise RuntimeError( + ( + 'Invalid binary wheel, found the following shared library/libraries in purelib folder:\n' + '\t%s\n' + 'The wheel has to be platlib compliant in order to be repaired by auditwheel.' + ) % '\n\t'.join(shared_libraries_in_purelib) + ) # Get a list of all external libraries needed by ELFs in the wheel. needed_libs = { diff --git a/test-requirements.txt b/test-requirements.txt index 4c60c312..658b14c1 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,3 +4,4 @@ jsonschema numpy pypatchelf flake8 +pretend diff --git a/tests/unit/test_wheel_abi.py b/tests/unit/test_wheel_abi.py new file mode 100644 index 00000000..9d1d7a49 --- /dev/null +++ b/tests/unit/test_wheel_abi.py @@ -0,0 +1,44 @@ +import os + +import pytest +import pretend +from auditwheel import wheel_abi + + +class TestGetWheelElfdata: + @pytest.mark.parametrize( + "filenames, message", + [ + ( + # A single invalid file + [os.sep.join(["purelib", "foo"])], + "Invalid binary wheel, found the following shared library/libraries in purelib folder:\n\tfoo\nThe wheel has to be platlib compliant in order to be repaired by auditwheel.", + ), + ( + # Multiple invalid files + [os.sep.join(["purelib", "foo"]), os.sep.join(["purelib", "bar"])], + "Invalid binary wheel, found the following shared library/libraries in purelib folder:\n\tfoo\n\tbar\nThe wheel has to be platlib compliant in order to be repaired by auditwheel.", + ), + ], + ) + def test_finds_shared_library_in_purelib(self, filenames, message, monkeypatch): + entered_context = pretend.stub(iter_files=lambda: filenames) + context = pretend.stub( + __enter__=lambda: entered_context, __exit__=lambda *a: None + ) + InGenericPkgCtx = pretend.stub(__call__=lambda a: context) + + monkeypatch.setattr(wheel_abi, "InGenericPkgCtx", InGenericPkgCtx) + monkeypatch.setattr( + wheel_abi, "elf_is_python_extension", lambda fn, elf: (fn, elf) + ) + monkeypatch.setattr( + wheel_abi, + "elf_file_filter", + lambda fns: [(fn, pretend.stub()) for fn in fns], + ) + + with pytest.raises(RuntimeError) as exec_info: + wheel_abi.get_wheel_elfdata("/fakepath") + + assert exec_info.value.args == (message,)