| 
 | 1 | +From 0934b8fc91e3f08369d98fb9dcc24a1a5521a132 Mon Sep 17 00:00:00 2001  | 
 | 2 | +From: Dimitri John Ledkov < [email protected]>  | 
 | 3 | +Date: Wed, 4 Jun 2025 22:30:07 +0100  | 
 | 4 | +Subject: [PATCH 1/4] test_analyze_wheel_abi: refactor test with explicit env  | 
 | 5 | + | 
 | 6 | +Be explicit in the test-case matrix when to set env variable. This  | 
 | 7 | +enables to check if environment variable is actually correctly reset.  | 
 | 8 | + | 
 | 9 | +This commit passes unit tests.  | 
 | 10 | +---  | 
 | 11 | + tests/integration/test_bundled_wheels.py | 25 ++++++++++++++----------  | 
 | 12 | + 1 file changed, 15 insertions(+), 10 deletions(-)  | 
 | 13 | + | 
 | 14 | +diff --git a/tests/integration/test_bundled_wheels.py b/tests/integration/test_bundled_wheels.py  | 
 | 15 | +index 046b8fc0..f7bc6296 100644  | 
 | 16 | +--- a/tests/integration/test_bundled_wheels.py  | 
 | 17 | ++++ b/tests/integration/test_bundled_wheels.py  | 
 | 18 | +@@ -1,13 +1,11 @@  | 
 | 19 | + from __future__ import annotations  | 
 | 20 | +   | 
 | 21 | + import importlib  | 
 | 22 | +-import os  | 
 | 23 | + import platform  | 
 | 24 | + import sys  | 
 | 25 | + import zipfile  | 
 | 26 | + from argparse import Namespace  | 
 | 27 | + from datetime import datetime, timezone  | 
 | 28 | +-from os.path import isabs  | 
 | 29 | + from pathlib import Path  | 
 | 30 | + from unittest.mock import Mock  | 
 | 31 | +   | 
 | 32 | +@@ -23,69 +21,76 @@  | 
 | 33 | +   | 
 | 34 | +   | 
 | 35 | + @pytest.mark.parametrize(  | 
 | 36 | +-    ("file", "external_libs", "exclude"),  | 
 | 37 | ++    ("file", "external_libs", "exclude", "env"),  | 
 | 38 | +     [  | 
 | 39 | +         (  | 
 | 40 | +             "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 41 | +             {"libffi.so.5", "libpython2.7.so.1.0"},  | 
 | 42 | +             frozenset(),  | 
 | 43 | ++            None,  | 
 | 44 | +         ),  | 
 | 45 | +         (  | 
 | 46 | +             "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 47 | +             set(),  | 
 | 48 | +             frozenset(["libffi.so.5", "libpython2.7.so.1.0"]),  | 
 | 49 | ++            None,  | 
 | 50 | +         ),  | 
 | 51 | +         (  | 
 | 52 | +             "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 53 | +             {"libffi.so.5", "libpython2.7.so.1.0"},  | 
 | 54 | +             frozenset(["libffi.so.noexist", "libnoexist.so.*"]),  | 
 | 55 | ++            None,  | 
 | 56 | +         ),  | 
 | 57 | +         (  | 
 | 58 | +             "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 59 | +             {"libpython2.7.so.1.0"},  | 
 | 60 | +             frozenset(["libffi.so.[4,5]"]),  | 
 | 61 | ++            None,  | 
 | 62 | +         ),  | 
 | 63 | +         (  | 
 | 64 | +             "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 65 | +             {"libffi.so.5", "libpython2.7.so.1.0"},  | 
 | 66 | +             frozenset(["libffi.so.[6,7]"]),  | 
 | 67 | ++            None,  | 
 | 68 | +         ),  | 
 | 69 | +         (  | 
 | 70 | +             "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 71 | +             {"libpython2.7.so.1.0"},  | 
 | 72 | +             frozenset([f"{HERE}/*"]),  | 
 | 73 | ++            "LD_LIBRARY_PATH",  | 
 | 74 | +         ),  | 
 | 75 | +         (  | 
 | 76 | +             "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 77 | +             {"libpython2.7.so.1.0"},  | 
 | 78 | +             frozenset(["libffi.so.*"]),  | 
 | 79 | ++            None,  | 
 | 80 | +         ),  | 
 | 81 | +-        ("cffi-1.5.0-cp27-none-linux_x86_64.whl", set(), frozenset(["*"])),  | 
 | 82 | ++        ("cffi-1.5.0-cp27-none-linux_x86_64.whl", set(), frozenset(["*"]), None),  | 
 | 83 | +         (  | 
 | 84 | +             "python_snappy-0.5.2-pp260-pypy_41-linux_x86_64.whl",  | 
 | 85 | +             {"libsnappy.so.1"},  | 
 | 86 | +             frozenset(),  | 
 | 87 | ++            None,  | 
 | 88 | +         ),  | 
 | 89 | +     ],  | 
 | 90 | + )  | 
 | 91 | +-def test_analyze_wheel_abi(file, external_libs, exclude):  | 
 | 92 | ++def test_analyze_wheel_abi(file, external_libs, exclude, env):  | 
 | 93 | +     # If exclude libs contain path, LD_LIBRARY_PATH need to be modified to find the libs  | 
 | 94 | +     # `lddtree.load_ld_paths` needs to be reloaded for it's `lru_cache`-ed.  | 
 | 95 | +-    modify_ld_library_path = any(isabs(e) for e in exclude)  | 
 | 96 | +   | 
 | 97 | +     with pytest.MonkeyPatch.context() as cp:  | 
 | 98 | +-        if modify_ld_library_path:  | 
 | 99 | +-            cp.setenv("LD_LIBRARY_PATH", f"{HERE}")  | 
 | 100 | ++        if env:  | 
 | 101 | ++            cp.setenv(env, f"{HERE}")  | 
 | 102 | +             importlib.reload(lddtree)  | 
 | 103 | +   | 
 | 104 | +         winfo = analyze_wheel_abi(  | 
 | 105 | +             Libc.GLIBC, Architecture.x86_64, HERE / file, exclude, False, True  | 
 | 106 | +         )  | 
 | 107 | +         assert set(winfo.external_refs["manylinux_2_5_x86_64"].libs) == external_libs, (  | 
 | 108 | +-            f"{HERE}, {exclude}, {os.environ}"  | 
 | 109 | ++            f"{HERE}, {exclude}, {env}"  | 
 | 110 | +         )  | 
 | 111 | +   | 
 | 112 | +-    if modify_ld_library_path:  | 
 | 113 | ++    if env:  | 
 | 114 | +         importlib.reload(lddtree)  | 
 | 115 | +   | 
 | 116 | +   | 
 | 117 | + | 
 | 118 | +From 9f06c60d451a1b92bda33eaca25f882634b9d5e7 Mon Sep 17 00:00:00 2001  | 
 | 119 | +From: Dimitri John Ledkov < [email protected]>  | 
 | 120 | +Date: Wed, 4 Jun 2025 22:39:22 +0100  | 
 | 121 | +Subject: [PATCH 2/4] test_analyze_wheel_abi: add failing test case  | 
 | 122 | + | 
 | 123 | +Add a failing test case confiring that environment is not being  | 
 | 124 | +correctly reset.  | 
 | 125 | +---  | 
 | 126 | + tests/integration/test_bundled_wheels.py | 6 ++++++  | 
 | 127 | + 1 file changed, 6 insertions(+)  | 
 | 128 | + | 
 | 129 | +diff --git a/tests/integration/test_bundled_wheels.py b/tests/integration/test_bundled_wheels.py  | 
 | 130 | +index f7bc6296..0c1d2227 100644  | 
 | 131 | +--- a/tests/integration/test_bundled_wheels.py  | 
 | 132 | ++++ b/tests/integration/test_bundled_wheels.py  | 
 | 133 | +@@ -59,6 +59,12 @@  | 
 | 134 | +             frozenset([f"{HERE}/*"]),  | 
 | 135 | +             "LD_LIBRARY_PATH",  | 
 | 136 | +         ),  | 
 | 137 | ++        (  | 
 | 138 | ++            "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 139 | ++            {"libffi.so.5", "libpython2.7.so.1.0"},  | 
 | 140 | ++            frozenset([f"{HERE}/*"]),  | 
 | 141 | ++            None,  | 
 | 142 | ++        ),  | 
 | 143 | +         (  | 
 | 144 | +             "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 145 | +             {"libpython2.7.so.1.0"},  | 
 | 146 | + | 
 | 147 | +From e2063e3893b01268c5821b47a663334e0ecc8c4e Mon Sep 17 00:00:00 2001  | 
 | 148 | +From: Dimitri John Ledkov < [email protected]>  | 
 | 149 | +Date: Wed, 4 Jun 2025 22:46:52 +0100  | 
 | 150 | +Subject: [PATCH 3/4] test_analyze_wheel_abi: reload and reimport wheel_abi  | 
 | 151 | + | 
 | 152 | +It seems that in addition to reloading lddtree, reload of  | 
 | 153 | +auditwheel.wheel_abi is needed to full flush and reset  | 
 | 154 | +analyze_wheel_abi() lru_caches.  | 
 | 155 | +---  | 
 | 156 | + tests/integration/test_bundled_wheels.py | 8 +++++---  | 
 | 157 | + 1 file changed, 5 insertions(+), 3 deletions(-)  | 
 | 158 | + | 
 | 159 | +diff --git a/tests/integration/test_bundled_wheels.py b/tests/integration/test_bundled_wheels.py  | 
 | 160 | +index 0c1d2227..7fa01700 100644  | 
 | 161 | +--- a/tests/integration/test_bundled_wheels.py  | 
 | 162 | ++++ b/tests/integration/test_bundled_wheels.py  | 
 | 163 | +@@ -11,6 +11,7 @@  | 
 | 164 | +   | 
 | 165 | + import pytest  | 
 | 166 | +   | 
 | 167 | ++import auditwheel.wheel_abi  | 
 | 168 | + from auditwheel import lddtree, main_repair  | 
 | 169 | + from auditwheel.architecture import Architecture  | 
 | 170 | + from auditwheel.libc import Libc  | 
 | 171 | +@@ -87,7 +88,8 @@ def test_analyze_wheel_abi(file, external_libs, exclude, env):  | 
 | 172 | +     with pytest.MonkeyPatch.context() as cp:  | 
 | 173 | +         if env:  | 
 | 174 | +             cp.setenv(env, f"{HERE}")  | 
 | 175 | +-            importlib.reload(lddtree)  | 
 | 176 | ++        importlib.reload(lddtree)  | 
 | 177 | ++        importlib.reload(auditwheel.wheel_abi)  | 
 | 178 | +   | 
 | 179 | +         winfo = analyze_wheel_abi(  | 
 | 180 | +             Libc.GLIBC, Architecture.x86_64, HERE / file, exclude, False, True  | 
 | 181 | +@@ -96,8 +98,8 @@ def test_analyze_wheel_abi(file, external_libs, exclude, env):  | 
 | 182 | +             f"{HERE}, {exclude}, {env}"  | 
 | 183 | +         )  | 
 | 184 | +   | 
 | 185 | +-    if env:  | 
 | 186 | +-        importlib.reload(lddtree)  | 
 | 187 | ++    importlib.reload(lddtree)  | 
 | 188 | ++    importlib.reload(auditwheel.wheel_abi)  | 
 | 189 | +   | 
 | 190 | +   | 
 | 191 | + def test_analyze_wheel_abi_pyfpe():  | 
 | 192 | + | 
 | 193 | +From 496e92ef4e00cbb34e1b18394f0d993eb233b764 Mon Sep 17 00:00:00 2001  | 
 | 194 | +From: Dimitri John Ledkov < [email protected]>  | 
 | 195 | +Date: Wed, 16 Apr 2025 12:36:33 +0100  | 
 | 196 | +Subject: [PATCH 4/4] lddtree: support alternative LD_LIBRARY_PATH called  | 
 | 197 | + AUDITWHEEL_LD_LIBRARY_PATH  | 
 | 198 | + | 
 | 199 | +Sometimes wheels are different only just by shared libraries that are  | 
 | 200 | +vendored in. Sometimes the wheel being repaired and the libraries that  | 
 | 201 | +need to be vendored in are accessible on the host that was not used  | 
 | 202 | +for building the wheel. In such cases, it could be the case that  | 
 | 203 | +shared libraries used by python that is executing the auditwheel  | 
 | 204 | +script, are incompatible (too old or too new) than those that need to  | 
 | 205 | +be vendored.  | 
 | 206 | + | 
 | 207 | +In such cases, LD_LIBRARY_PATH is not convenient to use, as the python  | 
 | 208 | +interpreter for the auditwheel script is crashing.  | 
 | 209 | + | 
 | 210 | +Add an AUDITWHEEL_LD_LIBRARY_PATH whichis used by lddtree, but  | 
 | 211 | +otherwise does not affect the python interpreter executing auditwheel  | 
 | 212 | +script.  | 
 | 213 | + | 
 | 214 | +This helps vendoring in older libraries from an alternative path, into  | 
 | 215 | +a wheel built against an older python, whilst otherwise running  | 
 | 216 | +auditwheel repair on a modern system with a much newer python. This is  | 
 | 217 | +particularly useful when stripping a wheel of vendored libraries, and  | 
 | 218 | +repair it again to update the shared library dependencies coming from  | 
 | 219 | +an unpacked chroot.  | 
 | 220 | + | 
 | 221 | +Separately it also helps creating alternative wheels with shared  | 
 | 222 | +libraries rebuilt with higher march settings. Given that python  | 
 | 223 | +upstream doesn't support loading optimised libraries as has been  | 
 | 224 | +implemented and supported in Intel ClearLinux Python which allows one  | 
 | 225 | +to have alternative march setting shared library deps.  | 
 | 226 | +---  | 
 | 227 | + src/auditwheel/lddtree.py                | 13 +++++++++++--  | 
 | 228 | + tests/integration/test_bundled_wheels.py |  6 ++++++  | 
 | 229 | + 2 files changed, 17 insertions(+), 2 deletions(-)  | 
 | 230 | + | 
 | 231 | +diff --git a/src/auditwheel/lddtree.py b/src/auditwheel/lddtree.py  | 
 | 232 | +index 44785791..661446a4 100644  | 
 | 233 | +--- a/src/auditwheel/lddtree.py  | 
 | 234 | ++++ b/src/auditwheel/lddtree.py  | 
 | 235 | +@@ -321,8 +321,17 @@ def load_ld_paths(  | 
 | 236 | +     """  | 
 | 237 | +     ldpaths: dict[str, list[str]] = {"conf": [], "env": [], "interp": []}  | 
 | 238 | +   | 
 | 239 | +-    # Load up $LD_LIBRARY_PATH.  | 
 | 240 | +-    env_ldpath = os.environ.get("LD_LIBRARY_PATH")  | 
 | 241 | ++    # Load up $AUDITWHEEL_LD_LIBRARY_PATH and $LD_LIBRARY_PATH  | 
 | 242 | ++    env_ldpath = ":".join(  | 
 | 243 | ++        filter(  | 
 | 244 | ++            None,  | 
 | 245 | ++            (  | 
 | 246 | ++                os.environ.get("AUDITWHEEL_LD_LIBRARY_PATH"),  | 
 | 247 | ++                os.environ.get("LD_LIBRARY_PATH"),  | 
 | 248 | ++            ),  | 
 | 249 | ++        )  | 
 | 250 | ++    )  | 
 | 251 | ++  | 
 | 252 | +     if env_ldpath is not None:  | 
 | 253 | +         if root != "/":  | 
 | 254 | +             log.warning("ignoring LD_LIBRARY_PATH due to ROOT usage")  | 
 | 255 | +diff --git a/tests/integration/test_bundled_wheels.py b/tests/integration/test_bundled_wheels.py  | 
 | 256 | +index 7fa01700..97f12487 100644  | 
 | 257 | +--- a/tests/integration/test_bundled_wheels.py  | 
 | 258 | ++++ b/tests/integration/test_bundled_wheels.py  | 
 | 259 | +@@ -60,6 +60,12 @@  | 
 | 260 | +             frozenset([f"{HERE}/*"]),  | 
 | 261 | +             "LD_LIBRARY_PATH",  | 
 | 262 | +         ),  | 
 | 263 | ++        (  | 
 | 264 | ++            "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 265 | ++            {"libpython2.7.so.1.0"},  | 
 | 266 | ++            frozenset([f"{HERE}/*"]),  | 
 | 267 | ++            "AUDITWHEEL_LD_LIBRARY_PATH",  | 
 | 268 | ++        ),  | 
 | 269 | +         (  | 
 | 270 | +             "cffi-1.5.0-cp27-none-linux_x86_64.whl",  | 
 | 271 | +             {"libffi.so.5", "libpython2.7.so.1.0"},  | 
0 commit comments