relenv 0.22.2__tar.gz → 0.22.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {relenv-0.22.2/relenv.egg-info → relenv-0.22.3}/PKG-INFO +1 -1
  2. relenv-0.22.3/relenv/_resources/xz/crc32_table.c +22 -0
  3. relenv-0.22.3/relenv/_resources/xz/crc64_table.c +33 -0
  4. relenv-0.22.3/relenv/_resources/xz/readme.md +13 -0
  5. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/windows.py +8 -0
  6. {relenv-0.22.2 → relenv-0.22.3}/relenv/common.py +1 -1
  7. {relenv-0.22.2 → relenv-0.22.3}/relenv/python-versions.json +43 -0
  8. {relenv-0.22.2 → relenv-0.22.3}/relenv/relocate.py +85 -3
  9. {relenv-0.22.2 → relenv-0.22.3}/relenv/runtime.py +28 -19
  10. {relenv-0.22.2 → relenv-0.22.3/relenv.egg-info}/PKG-INFO +1 -1
  11. {relenv-0.22.2 → relenv-0.22.3}/relenv.egg-info/SOURCES.txt +3 -0
  12. {relenv-0.22.2 → relenv-0.22.3}/tests/test_build.py +2 -6
  13. relenv-0.22.3/tests/test_relocate_tools.py +152 -0
  14. {relenv-0.22.2 → relenv-0.22.3}/tests/test_runtime.py +72 -0
  15. {relenv-0.22.2 → relenv-0.22.3}/tests/test_verify_build.py +3 -0
  16. relenv-0.22.2/relenv/_resources/xz/readme.md +0 -4
  17. {relenv-0.22.2 → relenv-0.22.3}/LICENSE.md +0 -0
  18. {relenv-0.22.2 → relenv-0.22.3}/MANIFEST.in +0 -0
  19. {relenv-0.22.2 → relenv-0.22.3}/NOTICE +0 -0
  20. {relenv-0.22.2 → relenv-0.22.3}/README.md +0 -0
  21. {relenv-0.22.2 → relenv-0.22.3}/pyproject.toml +0 -0
  22. {relenv-0.22.2 → relenv-0.22.3}/relenv/__init__.py +0 -0
  23. {relenv-0.22.2 → relenv-0.22.3}/relenv/__main__.py +0 -0
  24. {relenv-0.22.2 → relenv-0.22.3}/relenv/_resources/xz/config.h +0 -0
  25. {relenv-0.22.2 → relenv-0.22.3}/relenv/_scripts/install_vc_build.ps1 +0 -0
  26. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/__init__.py +0 -0
  27. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/common/__init__.py +0 -0
  28. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/common/_sysconfigdata_template.py +0 -0
  29. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/common/builder.py +0 -0
  30. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/common/builders.py +0 -0
  31. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/common/download.py +0 -0
  32. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/common/install.py +0 -0
  33. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/common/ui.py +0 -0
  34. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/darwin.py +0 -0
  35. {relenv-0.22.2 → relenv-0.22.3}/relenv/build/linux.py +0 -0
  36. {relenv-0.22.2 → relenv-0.22.3}/relenv/buildenv.py +0 -0
  37. {relenv-0.22.2 → relenv-0.22.3}/relenv/check.py +0 -0
  38. {relenv-0.22.2 → relenv-0.22.3}/relenv/create.py +0 -0
  39. {relenv-0.22.2 → relenv-0.22.3}/relenv/fetch.py +0 -0
  40. {relenv-0.22.2 → relenv-0.22.3}/relenv/manifest.py +0 -0
  41. {relenv-0.22.2 → relenv-0.22.3}/relenv/pyversions.py +0 -0
  42. {relenv-0.22.2 → relenv-0.22.3}/relenv/toolchain.py +0 -0
  43. {relenv-0.22.2 → relenv-0.22.3}/relenv.egg-info/dependency_links.txt +0 -0
  44. {relenv-0.22.2 → relenv-0.22.3}/relenv.egg-info/entry_points.txt +0 -0
  45. {relenv-0.22.2 → relenv-0.22.3}/relenv.egg-info/requires.txt +0 -0
  46. {relenv-0.22.2 → relenv-0.22.3}/relenv.egg-info/top_level.txt +0 -0
  47. {relenv-0.22.2 → relenv-0.22.3}/setup.cfg +0 -0
  48. {relenv-0.22.2 → relenv-0.22.3}/setup.py +0 -0
  49. {relenv-0.22.2 → relenv-0.22.3}/tests/__init__.py +0 -0
  50. {relenv-0.22.2 → relenv-0.22.3}/tests/_pytest_typing.py +0 -0
  51. {relenv-0.22.2 → relenv-0.22.3}/tests/conftest.py +0 -0
  52. {relenv-0.22.2 → relenv-0.22.3}/tests/test_common.py +0 -0
  53. {relenv-0.22.2 → relenv-0.22.3}/tests/test_create.py +0 -0
  54. {relenv-0.22.2 → relenv-0.22.3}/tests/test_downloads.py +0 -0
  55. {relenv-0.22.2 → relenv-0.22.3}/tests/test_fips_photon.py +0 -0
  56. {relenv-0.22.2 → relenv-0.22.3}/tests/test_module_imports.py +0 -0
  57. {relenv-0.22.2 → relenv-0.22.3}/tests/test_pyversions_runtime.py +0 -0
  58. {relenv-0.22.2 → relenv-0.22.3}/tests/test_relocate.py +0 -0
  59. {relenv-0.22.2 → relenv-0.22.3}/tests/test_relocate_module.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: relenv
3
- Version: 0.22.2
3
+ Version: 0.22.3
4
4
  Project-URL: Source Code, https://github.com/saltstack/relative-environment-for-python
5
5
  Project-URL: Documentation, https://relenv.readthedocs.io/en/latest/
6
6
  Project-URL: Changelog, https://relenv.readthedocs.io/en/latest/changelog.html
@@ -0,0 +1,22 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ /// \file crc32_table.c
4
+ /// \brief Precalculated CRC32 table with correct endianness
5
+ //
6
+ // Author: Lasse Collin
7
+ //
8
+ // This file has been put into the public domain.
9
+ // You can do whatever you want with this file.
10
+ //
11
+ ///////////////////////////////////////////////////////////////////////////////
12
+
13
+ #include "common.h"
14
+
15
+ // Having the declaration here silences clang -Wmissing-variable-declarations.
16
+ extern const uint32_t lzma_crc32_table[8][256];
17
+
18
+ #ifdef WORDS_BIGENDIAN
19
+ # include "crc32_table_be.h"
20
+ #else
21
+ # include "crc32_table_le.h"
22
+ #endif
@@ -0,0 +1,33 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ /// \file crc64_table.c
4
+ /// \brief Precalculated CRC64 table with correct endianness
5
+ //
6
+ // Author: Lasse Collin
7
+ //
8
+ // This file has been put into the public domain.
9
+ // You can do whatever you want with this file.
10
+ //
11
+ ///////////////////////////////////////////////////////////////////////////////
12
+
13
+ #include "common.h"
14
+
15
+
16
+ // FIXME: Compared to crc64_fast.c this has to check for __x86_64__ too
17
+ // so that in 32-bit builds crc64_x86.S won't break due to a missing table.
18
+ #if (defined(__x86_64__) && defined(__SSSE3__) \
19
+ && defined(__SSE4_1__) && defined(__PCLMUL__)) \
20
+ || (defined(__e2k__) && __iset__ >= 6)
21
+ // No table needed. Use a typedef to avoid an empty translation unit.
22
+ typedef void lzma_crc64_dummy;
23
+
24
+ #else
25
+ // Having the declaration here silences clang -Wmissing-variable-declarations.
26
+ extern const uint64_t lzma_crc64_table[4][256];
27
+
28
+ # if defined(WORDS_BIGENDIAN)
29
+ # include "crc64_table_be.h"
30
+ # else
31
+ # include "crc64_table_le.h"
32
+ # endif
33
+ #endif
@@ -0,0 +1,13 @@
1
+ The config.h file was removed from XZ-Utils starting with version 5.5.0.
2
+ XZ-Utils switched to CMake and removed Visual Studio project files and
3
+ pre-generated headers/sources.
4
+
5
+ We include the following files from XZ 5.4.7 to maintain compatibility with
6
+ Python's MSBuild-based build system on Windows (PCbuild/liblzma.vcxproj):
7
+
8
+ - config.h (src/common/config.h)
9
+ - crc32_table.c (src/liblzma/check/crc32_table.c)
10
+ - crc64_table.c (src/liblzma/check/crc64_table.c)
11
+
12
+ These files are copied into the extracted XZ source directory during the
13
+ Windows build process if they are missing.
@@ -197,6 +197,14 @@ def update_xz(dirs: Dirs, env: EnvMapping) -> None:
197
197
  config_file_source = dirs.root / "_resources" / "xz" / "config.h"
198
198
  if not config_file.exists():
199
199
  shutil.copy(str(config_file_source), str(config_file))
200
+
201
+ # Also copy crc32_table.c and crc64_table.c which are missing in newer XZ tarballs
202
+ check_dir = target_dir / "src" / "liblzma" / "check"
203
+ for filename in ["crc32_table.c", "crc64_table.c"]:
204
+ target_file = check_dir / filename
205
+ source_file = dirs.root / "_resources" / "xz" / filename
206
+ if not target_file.exists():
207
+ shutil.copy(str(source_file), str(target_file))
200
208
  # Update externals.spdx.json with the correct version, url, and hash
201
209
  # This became a thing in 3.12
202
210
  if env["RELENV_PY_MAJOR_VERSION"] in ["3.12", "3.13", "3.14"]:
@@ -34,7 +34,7 @@ from typing import (
34
34
  )
35
35
 
36
36
  # relenv package version
37
- __version__ = "0.22.2"
37
+ __version__ = "0.22.3"
38
38
 
39
39
  log = logging.getLogger(__name__)
40
40
 
@@ -203,6 +203,14 @@
203
203
  "linux",
204
204
  "darwin"
205
205
  ]
206
+ },
207
+ "3.6.1": {
208
+ "url": "https://github.com/openssl/openssl/releases/download/openssl-{version}/openssl-{version}.tar.gz",
209
+ "sha256": "b1bfedcd5b289ff22aee87c9d600f515767ebf45f77168cb6d64f231f518a82e",
210
+ "platforms": [
211
+ "linux",
212
+ "darwin"
213
+ ]
206
214
  }
207
215
  },
208
216
  "sqlite": {
@@ -225,6 +233,16 @@
225
233
  "darwin",
226
234
  "win32"
227
235
  ]
236
+ },
237
+ "3.51.2.0": {
238
+ "url": "https://sqlite.org/2026/sqlite-autoconf-{version}.tar.gz",
239
+ "sha256": "fbd89f866b1403bb66a143065440089dd76100f2238314d92274a082d4f2b7bb",
240
+ "sqliteversion": "3510200",
241
+ "platforms": [
242
+ "linux",
243
+ "darwin",
244
+ "win32"
245
+ ]
228
246
  }
229
247
  },
230
248
  "xz": {
@@ -235,6 +253,15 @@
235
253
  "linux",
236
254
  "darwin"
237
255
  ]
256
+ },
257
+ "5.8.2": {
258
+ "url": "http://tukaani.org/xz/xz-{version}.tar.gz",
259
+ "sha256": "ce09c50a5962786b83e5da389c90dd2c15ecd0980a258dd01f70f9e7ce58a8f1",
260
+ "platforms": [
261
+ "linux",
262
+ "darwin",
263
+ "win32"
264
+ ]
238
265
  }
239
266
  },
240
267
  "libffi": {
@@ -262,6 +289,13 @@
262
289
  "platforms": [
263
290
  "linux"
264
291
  ]
292
+ },
293
+ "6.6": {
294
+ "url": "https://mirrors.ocf.berkeley.edu/gnu/ncurses/ncurses-{version}.tar.gz",
295
+ "sha256": "355b4cbbed880b0381a04c46617b7656e362585d52e9cf84a67e2009b749ff11",
296
+ "platforms": [
297
+ "linux"
298
+ ]
265
299
  }
266
300
  },
267
301
  "readline": {
@@ -344,6 +378,15 @@
344
378
  "darwin",
345
379
  "win32"
346
380
  ]
381
+ },
382
+ "2.7.4": {
383
+ "url": "https://github.com/libexpat/libexpat/releases/download/R_2_7_4/expat-{version}.tar.xz",
384
+ "sha256": "9e9cabb457c1e09de91db2706d8365645792638eb3be1f94dbb2149301086ac0",
385
+ "platforms": [
386
+ "linux",
387
+ "darwin",
388
+ "win32"
389
+ ]
347
390
  }
348
391
  }
349
392
  }
@@ -11,6 +11,7 @@ import os as _os
11
11
  import pathlib
12
12
  import shutil as _shutil
13
13
  import subprocess as _subprocess
14
+ import sys as _sys
14
15
  from typing import Optional
15
16
 
16
17
  log = logging.getLogger(__name__)
@@ -18,6 +19,7 @@ log = logging.getLogger(__name__)
18
19
  os = _os
19
20
  shutil = _shutil
20
21
  subprocess = _subprocess
22
+ sys = _sys
21
23
 
22
24
  __all__ = [
23
25
  "is_macho",
@@ -70,6 +72,83 @@ LC_ID_DYLIB = "LC_ID_DYLIB"
70
72
  LC_LOAD_DYLIB = "LC_LOAD_DYLIB"
71
73
  LC_RPATH = "LC_RPATH"
72
74
 
75
+ # Cache for readelf binary path
76
+ _READELF_BINARY: Optional[str] = None
77
+
78
+ # Cache for patchelf binary path
79
+ _PATCHELF_BINARY: Optional[str] = None
80
+
81
+
82
+ def _get_readelf_binary() -> str:
83
+ """
84
+ Get the path to readelf binary, preferring toolchain version.
85
+
86
+ Returns the cached value if already computed. On Linux, prefers the
87
+ toolchain's readelf over the system version. Falls back to "readelf"
88
+ from PATH if toolchain is unavailable.
89
+
90
+ :return: Path to readelf binary
91
+ :rtype: str
92
+ """
93
+ global _READELF_BINARY
94
+ if _READELF_BINARY is not None:
95
+ return _READELF_BINARY
96
+
97
+ # Only Linux has the toolchain with readelf
98
+ if sys.platform == "linux":
99
+ try:
100
+ from relenv.common import get_toolchain, get_triplet
101
+
102
+ toolchain = get_toolchain()
103
+ if toolchain:
104
+ triplet = get_triplet()
105
+ toolchain_readelf = toolchain / "bin" / f"{triplet}-readelf"
106
+ if toolchain_readelf.exists():
107
+ _READELF_BINARY = str(toolchain_readelf)
108
+ return _READELF_BINARY
109
+ except Exception:
110
+ # Fall through to system readelf
111
+ pass
112
+
113
+ # Fall back to system readelf
114
+ _READELF_BINARY = "readelf"
115
+ return _READELF_BINARY
116
+
117
+
118
+ def _get_patchelf_binary() -> str:
119
+ """
120
+ Get the path to patchelf binary, preferring toolchain version.
121
+
122
+ Returns the cached value if already computed. On Linux, prefers the
123
+ toolchain's patchelf over the system version. Falls back to "patchelf"
124
+ from PATH if toolchain is unavailable.
125
+
126
+ :return: Path to patchelf binary
127
+ :rtype: str
128
+ """
129
+ global _PATCHELF_BINARY
130
+ if _PATCHELF_BINARY is not None:
131
+ return _PATCHELF_BINARY
132
+
133
+ # Only Linux has the toolchain with patchelf
134
+ if sys.platform == "linux":
135
+ try:
136
+ from relenv.common import get_toolchain
137
+
138
+ toolchain = get_toolchain()
139
+ if toolchain:
140
+ toolchain_patchelf = toolchain / "bin" / "patchelf"
141
+ if toolchain_patchelf.exists():
142
+ _PATCHELF_BINARY = str(toolchain_patchelf)
143
+ return _PATCHELF_BINARY
144
+ except Exception:
145
+ # Fall through to system patchelf
146
+ pass
147
+
148
+ # Fall back to system patchelf
149
+ _PATCHELF_BINARY = "patchelf"
150
+ return _PATCHELF_BINARY
151
+
73
152
 
74
153
  def is_macho(path: str | os.PathLike[str]) -> bool:
75
154
  """
@@ -192,8 +271,9 @@ def parse_rpath(path: str | os.PathLike[str]) -> list[str]:
192
271
  :return: The RPATH's found.
193
272
  :rtype: list
194
273
  """
274
+ readelf = _get_readelf_binary()
195
275
  proc = subprocess.run(
196
- ["readelf", "-d", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE
276
+ [readelf, "-d", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE
197
277
  )
198
278
  return parse_readelf_d(proc.stdout.decode())
199
279
 
@@ -280,8 +360,9 @@ def remove_rpath(path: str | os.PathLike[str]) -> bool:
280
360
  return True
281
361
 
282
362
  log.info("Remove RPATH from %s (was: %s)", path, old_rpath)
363
+ patchelf = _get_patchelf_binary()
283
364
  proc = subprocess.run(
284
- ["patchelf", "--remove-rpath", path],
365
+ [patchelf, "--remove-rpath", path],
285
366
  stderr=subprocess.PIPE,
286
367
  stdout=subprocess.PIPE,
287
368
  )
@@ -319,8 +400,9 @@ def patch_rpath(
319
400
  if new_rpath not in old_rpath:
320
401
  patched_rpath = ":".join([new_rpath] + old_rpath)
321
402
  log.info("Set RPATH=%s %s", patched_rpath, path)
403
+ patchelf = _get_patchelf_binary()
322
404
  proc = subprocess.run(
323
- ["patchelf", "--force-rpath", "--set-rpath", patched_rpath, path],
405
+ [patchelf, "--force-rpath", "--set-rpath", patched_rpath, path],
324
406
  stderr=subprocess.PIPE,
325
407
  stdout=subprocess.PIPE,
326
408
  )
@@ -384,25 +384,30 @@ def install_wheel_wrapper(func: Callable[..., Any]) -> Callable[..., Any]:
384
384
  direct_url,
385
385
  requested,
386
386
  )
387
- plat = pathlib.Path(scheme.platlib)
388
- rootdir = relenv_root()
389
- with open(plat / info_dir / "RECORD") as fp:
390
- for line in fp.readlines():
391
- file = plat / line.split(",", 1)[0]
392
- if not file.exists():
393
- debug(f"Relenv - File not found {file}")
394
- continue
395
- if relocate().is_elf(file):
396
- debug(f"Relenv - Found elf {file}")
397
- relocate().handle_elf(plat / file, rootdir / "lib", True, rootdir)
398
- elif relocate().is_macho(file):
399
- otool_bin = shutil.which("otool")
400
- if otool_bin:
401
- relocate().handle_macho(str(plat / file), str(rootdir), True)
402
- else:
403
- debug(
404
- "The otool command is not available, please run `xcode-select --install`"
387
+ if "RELENV_BUILDENV" in os.environ:
388
+ plat = pathlib.Path(scheme.platlib)
389
+ rootdir = relenv_root()
390
+ with open(plat / info_dir / "RECORD") as fp:
391
+ for line in fp.readlines():
392
+ file = plat / line.split(",", 1)[0]
393
+ if not file.exists():
394
+ debug(f"Relenv - File not found {file}")
395
+ continue
396
+ if relocate().is_elf(file):
397
+ debug(f"Relenv - Found elf {file}")
398
+ relocate().handle_elf(
399
+ plat / file, rootdir / "lib", True, rootdir
405
400
  )
401
+ elif relocate().is_macho(file):
402
+ otool_bin = shutil.which("otool")
403
+ if otool_bin:
404
+ relocate().handle_macho(
405
+ str(plat / file), str(rootdir), True
406
+ )
407
+ else:
408
+ debug(
409
+ "The otool command is not available, please run `xcode-select --install`"
410
+ )
406
411
 
407
412
  return wrapper
408
413
 
@@ -1022,7 +1027,11 @@ def install_cargo_config() -> None:
1022
1027
  cargo_home = dirs.data / "cargo"
1023
1028
  triplet = common().get_triplet()
1024
1029
 
1025
- toolchain = common().get_toolchain()
1030
+ try:
1031
+ toolchain = common().get_toolchain()
1032
+ except PermissionError:
1033
+ pass
1034
+
1026
1035
  if not toolchain:
1027
1036
  debug("Unable to set CARGO_HOME ppbt package not installed")
1028
1037
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: relenv
3
- Version: 0.22.2
3
+ Version: 0.22.3
4
4
  Project-URL: Source Code, https://github.com/saltstack/relative-environment-for-python
5
5
  Project-URL: Documentation, https://relenv.readthedocs.io/en/latest/
6
6
  Project-URL: Changelog, https://relenv.readthedocs.io/en/latest/changelog.html
@@ -25,6 +25,8 @@ relenv.egg-info/entry_points.txt
25
25
  relenv.egg-info/requires.txt
26
26
  relenv.egg-info/top_level.txt
27
27
  relenv/_resources/xz/config.h
28
+ relenv/_resources/xz/crc32_table.c
29
+ relenv/_resources/xz/crc64_table.c
28
30
  relenv/_resources/xz/readme.md
29
31
  relenv/_scripts/install_vc_build.ps1
30
32
  relenv/build/__init__.py
@@ -50,5 +52,6 @@ tests/test_module_imports.py
50
52
  tests/test_pyversions_runtime.py
51
53
  tests/test_relocate.py
52
54
  tests/test_relocate_module.py
55
+ tests/test_relocate_tools.py
53
56
  tests/test_runtime.py
54
57
  tests/test_verify_build.py
@@ -154,8 +154,8 @@ def test_get_dependency_version_sqlite_all_platforms() -> None:
154
154
  def test_get_dependency_version_xz_all_platforms() -> None:
155
155
  """Test getting XZ version for various platforms."""
156
156
  # XZ 5.5.0+ removed MSBuild support, so Windows uses a fallback version
157
- # and XZ is not in JSON for win32
158
- for platform in ["linux", "darwin"]:
157
+ # BUT we now have XZ 5.8.2 in python-versions.json for win32 too
158
+ for platform in ["linux", "darwin", "win32"]:
159
159
  result = get_dependency_version("xz", platform)
160
160
  assert result is not None, f"XZ should be available for {platform}"
161
161
  assert isinstance(result, dict)
@@ -166,10 +166,6 @@ def test_get_dependency_version_xz_all_platforms() -> None:
166
166
  assert "xz" in result["url"].lower()
167
167
  assert isinstance(result["sha256"], str)
168
168
 
169
- # Windows should return None (uses hardcoded fallback in windows.py)
170
- result = get_dependency_version("xz", "win32")
171
- assert result is None, "XZ should not be in JSON for win32 (uses fallback)"
172
-
173
169
 
174
170
  def test_get_dependency_version_nonexistent() -> None:
175
171
  """Test that nonexistent dependency returns None."""
@@ -0,0 +1,152 @@
1
+ # Copyright 2022-2026 Broadcom.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ import pathlib
5
+ from typing import Iterator
6
+ from unittest.mock import patch
7
+
8
+ import pytest
9
+
10
+ from relenv import relocate
11
+
12
+
13
+ @pytest.fixture(autouse=True) # type: ignore[misc]
14
+ def reset_globals() -> Iterator[None]:
15
+ """Reset global caches in relocate module before and after each test."""
16
+ relocate._READELF_BINARY = None
17
+ relocate._PATCHELF_BINARY = None
18
+ yield
19
+ relocate._READELF_BINARY = None
20
+ relocate._PATCHELF_BINARY = None
21
+
22
+
23
+ def test_get_readelf_binary_toolchain_exists(tmp_path: pathlib.Path) -> None:
24
+ """Test that toolchain readelf is used when available."""
25
+ toolchain_root = tmp_path / "toolchain"
26
+ toolchain_root.mkdir()
27
+ triplet = "x86_64-linux-gnu"
28
+
29
+ # Create the fake toolchain binary
30
+ bin_dir = toolchain_root / "bin"
31
+ bin_dir.mkdir(parents=True)
32
+ toolchain_readelf = bin_dir / f"{triplet}-readelf"
33
+ toolchain_readelf.touch()
34
+
35
+ with patch("relenv.relocate.sys.platform", "linux"):
36
+ # We need to mock relenv.common.get_toolchain and get_triplet
37
+ # Since they are imported inside the function, we can patch the module if it's already imported
38
+ # or use patch.dict(sys.modules)
39
+
40
+ # Ensure relenv.common is imported so we can patch it
41
+ import relenv.common # noqa: F401
42
+
43
+ with patch("relenv.common.get_toolchain", return_value=toolchain_root):
44
+ with patch("relenv.common.get_triplet", return_value=triplet):
45
+ readelf = relocate._get_readelf_binary()
46
+
47
+ assert readelf == str(toolchain_readelf)
48
+ assert relocate._READELF_BINARY == str(toolchain_readelf)
49
+
50
+
51
+ def test_get_readelf_binary_toolchain_missing(tmp_path: pathlib.Path) -> None:
52
+ """Test that system readelf is used when toolchain binary is missing."""
53
+ toolchain_root = tmp_path / "toolchain"
54
+ toolchain_root.mkdir()
55
+ triplet = "x86_64-linux-gnu"
56
+
57
+ # Do NOT create the binary
58
+
59
+ with patch("relenv.relocate.sys.platform", "linux"):
60
+ # Ensure relenv.common is imported so we can patch it
61
+ import relenv.common # noqa: F401
62
+
63
+ with patch("relenv.common.get_toolchain", return_value=toolchain_root):
64
+ with patch("relenv.common.get_triplet", return_value=triplet):
65
+ readelf = relocate._get_readelf_binary()
66
+
67
+ assert readelf == "readelf"
68
+ assert relocate._READELF_BINARY == "readelf"
69
+
70
+
71
+ def test_get_readelf_binary_no_toolchain() -> None:
72
+ """Test that system readelf is used when get_toolchain returns None."""
73
+ with patch("relenv.relocate.sys.platform", "linux"):
74
+ # Ensure relenv.common is imported so we can patch it
75
+ import relenv.common # noqa: F401
76
+
77
+ with patch("relenv.common.get_toolchain", return_value=None):
78
+ readelf = relocate._get_readelf_binary()
79
+
80
+ assert readelf == "readelf"
81
+ assert relocate._READELF_BINARY == "readelf"
82
+
83
+
84
+ def test_get_readelf_binary_not_linux() -> None:
85
+ """Test that system readelf is used on non-Linux platforms."""
86
+ with patch("relenv.relocate.sys.platform", "darwin"):
87
+ readelf = relocate._get_readelf_binary()
88
+
89
+ assert readelf == "readelf"
90
+ assert relocate._READELF_BINARY == "readelf"
91
+
92
+
93
+ def test_get_patchelf_binary_toolchain_exists(tmp_path: pathlib.Path) -> None:
94
+ """Test that toolchain patchelf is used when available."""
95
+ toolchain_root = tmp_path / "toolchain"
96
+ toolchain_root.mkdir()
97
+
98
+ # Create the fake toolchain binary
99
+ bin_dir = toolchain_root / "bin"
100
+ bin_dir.mkdir(parents=True)
101
+ toolchain_patchelf = bin_dir / "patchelf"
102
+ toolchain_patchelf.touch()
103
+
104
+ with patch("relenv.relocate.sys.platform", "linux"):
105
+ # Ensure relenv.common is imported so we can patch it
106
+ import relenv.common # noqa: F401
107
+
108
+ with patch("relenv.common.get_toolchain", return_value=toolchain_root):
109
+ patchelf = relocate._get_patchelf_binary()
110
+
111
+ assert patchelf == str(toolchain_patchelf)
112
+ assert relocate._PATCHELF_BINARY == str(toolchain_patchelf)
113
+
114
+
115
+ def test_get_patchelf_binary_toolchain_missing(tmp_path: pathlib.Path) -> None:
116
+ """Test that system patchelf is used when toolchain binary is missing."""
117
+ toolchain_root = tmp_path / "toolchain"
118
+ toolchain_root.mkdir()
119
+
120
+ # Do NOT create the binary
121
+
122
+ with patch("relenv.relocate.sys.platform", "linux"):
123
+ # Ensure relenv.common is imported so we can patch it
124
+ import relenv.common # noqa: F401
125
+
126
+ with patch("relenv.common.get_toolchain", return_value=toolchain_root):
127
+ patchelf = relocate._get_patchelf_binary()
128
+
129
+ assert patchelf == "patchelf"
130
+ assert relocate._PATCHELF_BINARY == "patchelf"
131
+
132
+
133
+ def test_get_patchelf_binary_no_toolchain() -> None:
134
+ """Test that system patchelf is used when get_toolchain returns None."""
135
+ with patch("relenv.relocate.sys.platform", "linux"):
136
+ # Ensure relenv.common is imported so we can patch it
137
+ import relenv.common # noqa: F401
138
+
139
+ with patch("relenv.common.get_toolchain", return_value=None):
140
+ patchelf = relocate._get_patchelf_binary()
141
+
142
+ assert patchelf == "patchelf"
143
+ assert relocate._PATCHELF_BINARY == "patchelf"
144
+
145
+
146
+ def test_get_patchelf_binary_not_linux() -> None:
147
+ """Test that system patchelf is used on non-Linux platforms."""
148
+ with patch("relenv.relocate.sys.platform", "darwin"):
149
+ patchelf = relocate._get_patchelf_binary()
150
+
151
+ assert patchelf == "patchelf"
152
+ assert relocate._PATCHELF_BINARY == "patchelf"
@@ -374,6 +374,7 @@ def test_finalize_options_wrapper_appends_include(
374
374
  def test_install_wheel_wrapper_processes_record(
375
375
  tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
376
376
  ) -> None:
377
+ monkeypatch.setenv("RELENV_BUILDENV", "1")
377
378
  plat_dir = tmp_path / "plat"
378
379
  info_dir = plat_dir / "demo.dist-info"
379
380
  info_dir.mkdir(parents=True)
@@ -440,6 +441,7 @@ def test_install_wheel_wrapper_processes_record(
440
441
  def test_install_wheel_wrapper_missing_file(
441
442
  tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
442
443
  ) -> None:
444
+ monkeypatch.setenv("RELENV_BUILDENV", "1")
443
445
  plat_dir = tmp_path / "plat"
444
446
  info_dir = plat_dir / "demo.dist-info"
445
447
  info_dir.mkdir(parents=True)
@@ -477,6 +479,7 @@ def test_install_wheel_wrapper_missing_file(
477
479
  def test_install_wheel_wrapper_macho_with_otool(
478
480
  tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
479
481
  ) -> None:
482
+ monkeypatch.setenv("RELENV_BUILDENV", "1")
480
483
  plat_dir = tmp_path / "plat"
481
484
  info_dir = plat_dir / "demo.dist-info"
482
485
  info_dir.mkdir(parents=True)
@@ -520,6 +523,7 @@ def test_install_wheel_wrapper_macho_with_otool(
520
523
  def test_install_wheel_wrapper_macho_without_otool(
521
524
  tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
522
525
  ) -> None:
526
+ monkeypatch.setenv("RELENV_BUILDENV", "1")
523
527
  plat_dir = tmp_path / "plat"
524
528
  info_dir = plat_dir / "demo.dist-info"
525
529
  info_dir.mkdir(parents=True)
@@ -565,6 +569,74 @@ def test_install_wheel_wrapper_macho_without_otool(
565
569
  assert any("otool command is not available" in msg for msg in messages)
566
570
 
567
571
 
572
+ def test_install_wheel_wrapper_skips_without_buildenv(
573
+ tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
574
+ ) -> None:
575
+ monkeypatch.delenv("RELENV_BUILDENV", raising=False)
576
+ plat_dir = tmp_path / "plat"
577
+ info_dir = plat_dir / "demo.dist-info"
578
+ info_dir.mkdir(parents=True)
579
+ record = info_dir / "RECORD"
580
+ record.write_text("libdemo.so,,\n")
581
+ binary = plat_dir / "libdemo.so"
582
+ binary.touch()
583
+
584
+ # If relocation runs, this mock will raise an error or capture calls
585
+ handled: list[tuple[pathlib.Path, pathlib.Path]] = []
586
+ monkeypatch.setattr(
587
+ relenv.runtime,
588
+ "relocate",
589
+ lambda: SimpleNamespace(
590
+ is_elf=lambda path: path.name.endswith(".so"),
591
+ is_macho=lambda path: False,
592
+ handle_elf=lambda file, lib_dir, fix, root: handled.append((file, lib_dir)),
593
+ handle_macho=lambda *args, **kwargs: None,
594
+ ),
595
+ )
596
+
597
+ wheel_utils = ModuleType("pip._internal.utils.wheel")
598
+ wheel_utils.parse_wheel = lambda _zf, _name: ("demo.dist-info", {})
599
+ monkeypatch.setitem(sys.modules, wheel_utils.__name__, wheel_utils)
600
+
601
+ class DummyZip:
602
+ def __init__(self, path: pathlib.Path) -> None:
603
+ self.path = path
604
+
605
+ def __enter__(self) -> DummyZip:
606
+ return self
607
+
608
+ def __exit__(self, *exc: object) -> bool:
609
+ return False
610
+
611
+ monkeypatch.setattr("zipfile.ZipFile", DummyZip)
612
+
613
+ install_module = ModuleType("pip._internal.operations.install.wheel")
614
+
615
+ def original_install(*_args: object, **_kwargs: object) -> str:
616
+ return "original"
617
+
618
+ install_module.install_wheel = original_install # type: ignore[attr-defined]
619
+ monkeypatch.setitem(sys.modules, install_module.__name__, install_module)
620
+
621
+ wrapped_module = relenv.runtime.wrap_pip_install_wheel(install_module.__name__)
622
+
623
+ scheme = SimpleNamespace(
624
+ platlib=str(plat_dir),
625
+ )
626
+ wrapped_module.install_wheel(
627
+ "demo",
628
+ tmp_path / "wheel.whl",
629
+ scheme,
630
+ "desc",
631
+ None,
632
+ None,
633
+ None,
634
+ None,
635
+ )
636
+
637
+ assert not handled
638
+
639
+
568
640
  def test_install_legacy_wrapper_prefix(
569
641
  monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
570
642
  ) -> None:
@@ -519,6 +519,9 @@ def test_pip_install_pyzmq(
519
519
  env["RELENV_BUILDENV"] = "yes"
520
520
  env["USE_STATIC_REQUIREMENTS"] = "1"
521
521
 
522
+ if pyzmq_version == "26.2.0":
523
+ env["CMAKE_ARGS"] = "-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
524
+
522
525
  if sys.platform == "linux":
523
526
  fake_bsd_root = tmp_path / "fake_libbsd"
524
527
  (fake_bsd_root / "bsd").mkdir(parents=True, exist_ok=True)
@@ -1,4 +0,0 @@
1
- The config.h file was removed from XZ-Utils tarting with version 5.5.0.
2
- XZ-Utils seems to build just fine with the config.h file from 5.4.7, so we're
3
- including it here. This will be copied into the src/windows directory in the
4
- extracted source for XZ-Utils.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes