relenv 0.22.1__py3-none-any.whl → 0.22.3__py3-none-any.whl
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.
- relenv/__init__.py +1 -1
- relenv/__main__.py +1 -1
- relenv/_resources/xz/crc32_table.c +22 -0
- relenv/_resources/xz/crc64_table.c +33 -0
- relenv/_resources/xz/readme.md +13 -4
- relenv/build/__init__.py +21 -25
- relenv/build/common/__init__.py +1 -1
- relenv/build/common/_sysconfigdata_template.py +1 -1
- relenv/build/common/builder.py +1 -1
- relenv/build/common/builders.py +1 -1
- relenv/build/common/download.py +1 -1
- relenv/build/common/install.py +6 -2
- relenv/build/common/ui.py +1 -1
- relenv/build/darwin.py +1 -1
- relenv/build/linux.py +1 -1
- relenv/build/windows.py +9 -1
- relenv/buildenv.py +1 -1
- relenv/check.py +1 -1
- relenv/common.py +2 -4
- relenv/create.py +19 -27
- relenv/fetch.py +7 -5
- relenv/manifest.py +1 -1
- relenv/python-versions.json +43 -0
- relenv/pyversions.py +49 -1
- relenv/relocate.py +118 -4
- relenv/runtime.py +29 -20
- relenv/toolchain.py +1 -1
- {relenv-0.22.1.dist-info → relenv-0.22.3.dist-info}/METADATA +1 -1
- relenv-0.22.3.dist-info/RECORD +51 -0
- {relenv-0.22.1.dist-info → relenv-0.22.3.dist-info}/WHEEL +1 -1
- tests/__init__.py +1 -1
- tests/_pytest_typing.py +1 -1
- tests/conftest.py +1 -1
- tests/test_build.py +3 -7
- tests/test_common.py +2 -2
- tests/test_create.py +1 -1
- tests/test_downloads.py +1 -1
- tests/test_fips_photon.py +1 -1
- tests/test_module_imports.py +1 -1
- tests/test_pyversions_runtime.py +78 -1
- tests/test_relocate.py +86 -1
- tests/test_relocate_module.py +1 -1
- tests/test_relocate_tools.py +152 -0
- tests/test_runtime.py +73 -1
- tests/test_verify_build.py +10 -5
- relenv-0.22.1.dist-info/RECORD +0 -48
- {relenv-0.22.1.dist-info → relenv-0.22.3.dist-info}/entry_points.txt +0 -0
- {relenv-0.22.1.dist-info → relenv-0.22.3.dist-info}/licenses/LICENSE.md +0 -0
- {relenv-0.22.1.dist-info → relenv-0.22.3.dist-info}/licenses/NOTICE +0 -0
- {relenv-0.22.1.dist-info → relenv-0.22.3.dist-info}/top_level.txt +0 -0
relenv/relocate.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
"""
|
|
4
4
|
A script to ensure the proper rpaths are in place for the relenv environment.
|
|
@@ -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
|
-
[
|
|
276
|
+
[readelf, "-d", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
197
277
|
)
|
|
198
278
|
return parse_readelf_d(proc.stdout.decode())
|
|
199
279
|
|
|
@@ -264,6 +344,35 @@ def is_in_dir(
|
|
|
264
344
|
return os.path.realpath(filepath).startswith(os.path.realpath(directory) + os.sep)
|
|
265
345
|
|
|
266
346
|
|
|
347
|
+
def remove_rpath(path: str | os.PathLike[str]) -> bool:
|
|
348
|
+
"""
|
|
349
|
+
Remove the rpath from a given ELF file.
|
|
350
|
+
|
|
351
|
+
:param path: The path to an ELF file
|
|
352
|
+
:type path: str
|
|
353
|
+
|
|
354
|
+
:return: True if successful, False otherwise
|
|
355
|
+
:rtype: bool
|
|
356
|
+
"""
|
|
357
|
+
old_rpath = parse_rpath(path)
|
|
358
|
+
if not old_rpath:
|
|
359
|
+
# No RPATH to remove
|
|
360
|
+
return True
|
|
361
|
+
|
|
362
|
+
log.info("Remove RPATH from %s (was: %s)", path, old_rpath)
|
|
363
|
+
patchelf = _get_patchelf_binary()
|
|
364
|
+
proc = subprocess.run(
|
|
365
|
+
[patchelf, "--remove-rpath", path],
|
|
366
|
+
stderr=subprocess.PIPE,
|
|
367
|
+
stdout=subprocess.PIPE,
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
if proc.returncode:
|
|
371
|
+
log.error("Failed to remove RPATH from %s: %s", path, proc.stderr.decode())
|
|
372
|
+
return False
|
|
373
|
+
return True
|
|
374
|
+
|
|
375
|
+
|
|
267
376
|
def patch_rpath(
|
|
268
377
|
path: str | os.PathLike[str],
|
|
269
378
|
new_rpath: str,
|
|
@@ -291,8 +400,9 @@ def patch_rpath(
|
|
|
291
400
|
if new_rpath not in old_rpath:
|
|
292
401
|
patched_rpath = ":".join([new_rpath] + old_rpath)
|
|
293
402
|
log.info("Set RPATH=%s %s", patched_rpath, path)
|
|
403
|
+
patchelf = _get_patchelf_binary()
|
|
294
404
|
proc = subprocess.run(
|
|
295
|
-
[
|
|
405
|
+
[patchelf, "--force-rpath", "--set-rpath", patched_rpath, path],
|
|
296
406
|
stderr=subprocess.PIPE,
|
|
297
407
|
stdout=subprocess.PIPE,
|
|
298
408
|
)
|
|
@@ -325,6 +435,7 @@ def handle_elf(
|
|
|
325
435
|
root = libs
|
|
326
436
|
proc = subprocess.run(["ldd", path], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
327
437
|
needs_rpath = False
|
|
438
|
+
|
|
328
439
|
for line in proc.stdout.decode().splitlines():
|
|
329
440
|
if line.find("=>") == -1:
|
|
330
441
|
log.debug("Skip ldd output line: %s", line)
|
|
@@ -371,7 +482,9 @@ def handle_elf(
|
|
|
371
482
|
log.info("Adjust rpath of %s to %s", path, relpath)
|
|
372
483
|
patch_rpath(path, relpath)
|
|
373
484
|
else:
|
|
374
|
-
|
|
485
|
+
# No relenv libraries are linked, so RPATH is not needed
|
|
486
|
+
# Remove any existing RPATH to avoid security/correctness issues
|
|
487
|
+
remove_rpath(path)
|
|
375
488
|
|
|
376
489
|
|
|
377
490
|
def main(
|
|
@@ -420,6 +533,7 @@ def main(
|
|
|
420
533
|
if path in processed:
|
|
421
534
|
continue
|
|
422
535
|
log.debug("Checking %s", path)
|
|
536
|
+
|
|
423
537
|
if is_macho(path):
|
|
424
538
|
log.info("Found Mach-O %s", path)
|
|
425
539
|
_ = handle_macho(path, libs_dir, rpath_only)
|
relenv/runtime.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
"""
|
|
4
4
|
This code is run when initializing the python interperter in a Relenv environment.
|
|
@@ -384,25 +384,30 @@ def install_wheel_wrapper(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
|
384
384
|
direct_url,
|
|
385
385
|
requested,
|
|
386
386
|
)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
-
|
|
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
|
relenv/toolchain.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: relenv
|
|
3
|
-
Version: 0.22.
|
|
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,51 @@
|
|
|
1
|
+
relenv/__init__.py,sha256=LmJZKtCS7hgyl9VIIT4qiIXj8YX1fJKY0qJUi2UtwgA,325
|
|
2
|
+
relenv/__main__.py,sha256=gLR88_GYmdpyuppoaCq-W0SPetEOQFisQglcQZSGROg,1538
|
|
3
|
+
relenv/buildenv.py,sha256=I7ZyWxO6RZveaCQtqwBPpUW1XdMLwmuPfww5dfzRgwM,4120
|
|
4
|
+
relenv/check.py,sha256=m3KkTS6LQkvRwexVqqGQ4_A7GQU-dAmcF8EQDu_K67c,1133
|
|
5
|
+
relenv/common.py,sha256=zkNmhZr-Dp9-aMPSO2FNiF9vrDqGOCVQgDjIClaDalA,35826
|
|
6
|
+
relenv/create.py,sha256=6Q3m1MkeYyeH48UM6wYhvTlhHRRA73ZA34LfvNe0Ylk,7934
|
|
7
|
+
relenv/fetch.py,sha256=cnkPJiPlttiWOm7Olt4R23RAu_IzzcUsq1H-yHoPbow,2791
|
|
8
|
+
relenv/manifest.py,sha256=xyMHWao_I3j7gCyweES3I50830efacxZI3KumuPfyvU,1057
|
|
9
|
+
relenv/python-versions.json,sha256=vRDqf2sKys1zCnJBzZCCtaUnn5d_Ow6-vj3XcX8y2hE,15827
|
|
10
|
+
relenv/pyversions.py,sha256=euMZcELZsM612c4D3PD9dad4ScFbh0S54dRnbf7BHJg,43032
|
|
11
|
+
relenv/relocate.py,sha256=7usm94iC68AsbFl4IJ6iL_6p4Cb_HUeygAdfLrelCwU,16503
|
|
12
|
+
relenv/runtime.py,sha256=BQ96USuzT04umQjlnDt0johYTsfSVg1IMrfVx8xyfdI,40326
|
|
13
|
+
relenv/toolchain.py,sha256=vbL79grOsThEsstYpx1fZxo3EuliDj5UHcsTpEgdWXo,929
|
|
14
|
+
relenv/_resources/xz/config.h,sha256=2IofvMqyAa8xBaQb6QGStiUugMmjyUCq_Of5drh7fro,4117
|
|
15
|
+
relenv/_resources/xz/crc32_table.c,sha256=L7Loi256KVn8QDuteZVGh5lhH2spTCIDOEc_7tnSJqs,641
|
|
16
|
+
relenv/_resources/xz/crc64_table.c,sha256=--hRinyLqzfn6qTTQfRYBu5osdtucwJjH-lFRE4_xzY,1058
|
|
17
|
+
relenv/_resources/xz/readme.md,sha256=Esbs4NhnCwCpCfk_iRc9AgeiKoqW--HnSME7THjYivA,577
|
|
18
|
+
relenv/_scripts/install_vc_build.ps1,sha256=ir1bcz7rNOuFw4E5_AqBidiKS0SpPViXW7zaanRPCoM,7629
|
|
19
|
+
relenv/build/__init__.py,sha256=0Gre5oainAdkw4Bfc54V4Dwrjz4a5_hiICeEil9D-Cw,6157
|
|
20
|
+
relenv/build/darwin.py,sha256=n1ff-NLFlcN3_fqeJUOPnBC6HODT5Y-gvWjQ3UpdXsE,7136
|
|
21
|
+
relenv/build/linux.py,sha256=UD1b-bTri9lF3iaoibOFO2vzF-6GpubO38_65hvmqtY,26161
|
|
22
|
+
relenv/build/windows.py,sha256=xJ6hoH-MjhMxkS6_Pj5piUGtX41tTRcLkHJuUPWsuQA,16508
|
|
23
|
+
relenv/build/common/__init__.py,sha256=PUAaLAv_1zIGJcGb77jGhSZ1PdawJF6DH4RMRKamh7U,1004
|
|
24
|
+
relenv/build/common/_sysconfigdata_template.py,sha256=NaJkmEKIj8Uhf7Cs82FyrMd2CyZVh-Ri8mE3Pm8jxo0,1955
|
|
25
|
+
relenv/build/common/builder.py,sha256=vFujUxp_cq-miBZD6RLS3yta7158DbiQM0zZg593IDQ,31000
|
|
26
|
+
relenv/build/common/builders.py,sha256=2ajiN3gjjIsQ6TFuEvUCzizL_XKnvx0F9qeuak658ho,4772
|
|
27
|
+
relenv/build/common/download.py,sha256=oFmwYhdRlWvQMPju5ByjCTEsbxmH8ENFXilnZKY7c6U,10479
|
|
28
|
+
relenv/build/common/install.py,sha256=nXFxncnJQKlaell3Ps3LnjmVyGpS8KVuRWSMWrdjJHA,19299
|
|
29
|
+
relenv/build/common/ui.py,sha256=FebuZVSioSHGzTUyOSepFRkZzKN-SxchjXsM5tTLApI,14192
|
|
30
|
+
relenv-0.22.3.dist-info/licenses/LICENSE.md,sha256=T0SRk3vJM1YcAJjDz9vsX9gsCRatAVSBS7LeU0tklRM,9919
|
|
31
|
+
relenv-0.22.3.dist-info/licenses/NOTICE,sha256=Ns0AybPHBsgJKJJfjE6YnGgWEQQ9F7lQ6QNlYLlQT3E,548
|
|
32
|
+
tests/__init__.py,sha256=FdZLbBoaMwwgwYwDI9ozQnuT7FHQ7ae8MhQrsP3tVyQ,70
|
|
33
|
+
tests/_pytest_typing.py,sha256=cOGrU7it8H-2F198AT3bHf1Un8K3PzYvQgDs2PCmFb8,1204
|
|
34
|
+
tests/conftest.py,sha256=tccQCx1DLwftaJCnNLrBkyOmj-a_13nvayUG0KEkSzg,2628
|
|
35
|
+
tests/test_build.py,sha256=WpcO5_C1hxflgz5B0j5mB4v6ZVYZe-t8VOQRiQUUJvI,14827
|
|
36
|
+
tests/test_common.py,sha256=CJPpgP0euIOTFatZ2GmrR-TrJ1d2Hv_W8iaVpkNcuAc,18008
|
|
37
|
+
tests/test_create.py,sha256=iW26qyApMHTfOVa_X2LFsWeBIaaGo1pk8m1xaSOhGdA,6849
|
|
38
|
+
tests/test_downloads.py,sha256=A4caZk4HUdMPPS83toxx7FM6sXy4etxMqeKcDDx-gNc,3528
|
|
39
|
+
tests/test_fips_photon.py,sha256=Y39E1LUndDn3_w4Ir9gkYwxAIHlH8fWPvDI1GRNFh_g,1397
|
|
40
|
+
tests/test_module_imports.py,sha256=erwMhcRK-HMFjmVQoewQSLBtjes2KpZfiFAXgvK_Nt4,1354
|
|
41
|
+
tests/test_pyversions_runtime.py,sha256=CTlGySCCLTuk1I4KFftr2S37gnPvpYubNun8oIRv5BU,8978
|
|
42
|
+
tests/test_relocate.py,sha256=eWgocPhQoU9zAItdLN9yp45j0ura6IPsCmmkuLOOCGU,13402
|
|
43
|
+
tests/test_relocate_module.py,sha256=LslZSILgSAgLZ_wSepIlTFqb7AIkXTNGLWiPiNh5ffE,7802
|
|
44
|
+
tests/test_relocate_tools.py,sha256=bOHFlkebeZFfH0ZF0rg2Agd0unI-7GFHxkzmPL3reIw,5622
|
|
45
|
+
tests/test_runtime.py,sha256=QBIiX89JBJHoNAGqghZFtH6iLzvtKbQyqonNzYWK7gE,74285
|
|
46
|
+
tests/test_verify_build.py,sha256=F34nBwEvUif7XTpLOTD9tSIGz2kzf3egvelpcajnLc0,67927
|
|
47
|
+
relenv-0.22.3.dist-info/METADATA,sha256=6vmSfsbuLB0DSSmXkCIYgzRN-znakGbKDRH3Ds08-a8,1360
|
|
48
|
+
relenv-0.22.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
49
|
+
relenv-0.22.3.dist-info/entry_points.txt,sha256=dO66nWPPWl8ALWWnZFlHKAo6mfPFuQid7purYWL2ddc,48
|
|
50
|
+
relenv-0.22.3.dist-info/top_level.txt,sha256=P4Ro6JLZE53ZdsQ76o2OzBcpb0MaVJmbfr0HAn9WF8M,13
|
|
51
|
+
relenv-0.22.3.dist-info/RECORD,,
|
tests/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
tests/_pytest_typing.py
CHANGED
tests/conftest.py
CHANGED
tests/test_build.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import hashlib
|
|
4
4
|
import logging
|
|
@@ -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
|
-
#
|
|
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."""
|
tests/test_common.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -529,7 +529,7 @@ def test_toolchain_uses_cache_without_relenv_data(
|
|
|
529
529
|
def test_copyright_headers() -> None:
|
|
530
530
|
"""Verify all Python source files have the correct copyright header."""
|
|
531
531
|
expected_header = (
|
|
532
|
-
"# Copyright 2022-
|
|
532
|
+
"# Copyright 2022-2026 Broadcom.\n" "# SPDX-License-Identifier: Apache-2.0\n"
|
|
533
533
|
)
|
|
534
534
|
|
|
535
535
|
# Find all Python files in relenv/ and tests/
|
tests/test_create.py
CHANGED
tests/test_downloads.py
CHANGED
tests/test_fips_photon.py
CHANGED
tests/test_module_imports.py
CHANGED
tests/test_pyversions_runtime.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
from __future__ import annotations
|
|
@@ -175,3 +175,80 @@ def test_detect_xz_versions(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
175
175
|
assert "5.6.3" in versions
|
|
176
176
|
# Verify sorting (latest first)
|
|
177
177
|
assert versions[0] == "5.8.1"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_resolve_python_version_none_defaults_to_latest_310() -> None:
|
|
181
|
+
"""Test that None resolves to the latest 3.10 version."""
|
|
182
|
+
result = pyversions.resolve_python_version(None)
|
|
183
|
+
assert result.startswith("3.10.")
|
|
184
|
+
# Verify it's a valid version in the registry
|
|
185
|
+
versions = pyversions.python_versions("3.10")
|
|
186
|
+
assert pyversions.Version(result) in versions
|
|
187
|
+
# Verify it's the latest 3.10 version
|
|
188
|
+
latest = sorted(list(versions.keys()))[-1]
|
|
189
|
+
assert result == str(latest)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_resolve_python_version_partial_minor() -> None:
|
|
193
|
+
"""Test that partial versions (3.10) resolve to latest micro."""
|
|
194
|
+
result = pyversions.resolve_python_version("3.10")
|
|
195
|
+
assert result.startswith("3.10.")
|
|
196
|
+
# Verify it resolves to the latest micro version
|
|
197
|
+
versions = pyversions.python_versions("3.10")
|
|
198
|
+
latest = sorted(list(versions.keys()))[-1]
|
|
199
|
+
assert result == str(latest)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_resolve_python_version_different_minors() -> None:
|
|
203
|
+
"""Test resolution works for different minor versions."""
|
|
204
|
+
result_311 = pyversions.resolve_python_version("3.11")
|
|
205
|
+
assert result_311.startswith("3.11.")
|
|
206
|
+
|
|
207
|
+
result_313 = pyversions.resolve_python_version("3.13")
|
|
208
|
+
assert result_313.startswith("3.13.")
|
|
209
|
+
|
|
210
|
+
# Verify they're different
|
|
211
|
+
assert result_311 != result_313
|
|
212
|
+
|
|
213
|
+
# Verify each is the latest for its minor version
|
|
214
|
+
versions_311 = pyversions.python_versions("3.11")
|
|
215
|
+
latest_311 = sorted(list(versions_311.keys()))[-1]
|
|
216
|
+
assert result_311 == str(latest_311)
|
|
217
|
+
|
|
218
|
+
versions_313 = pyversions.python_versions("3.13")
|
|
219
|
+
latest_313 = sorted(list(versions_313.keys()))[-1]
|
|
220
|
+
assert result_313 == str(latest_313)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test_resolve_python_version_full_version() -> None:
|
|
224
|
+
"""Test that full versions are validated and returned as-is."""
|
|
225
|
+
# Get any valid version from the registry
|
|
226
|
+
all_versions = pyversions.python_versions()
|
|
227
|
+
some_version = str(next(iter(all_versions)))
|
|
228
|
+
|
|
229
|
+
result = pyversions.resolve_python_version(some_version)
|
|
230
|
+
assert result == some_version
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def test_resolve_python_version_invalid_full_version() -> None:
|
|
234
|
+
"""Test that invalid full versions raise RuntimeError."""
|
|
235
|
+
with pytest.raises(RuntimeError, match="Unknown version"):
|
|
236
|
+
pyversions.resolve_python_version("3.10.999")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_resolve_python_version_invalid_minor_version() -> None:
|
|
240
|
+
"""Test that invalid minor versions raise RuntimeError."""
|
|
241
|
+
with pytest.raises(RuntimeError, match="Unknown minor version"):
|
|
242
|
+
pyversions.resolve_python_version("3.99")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_resolve_python_version_consistency() -> None:
|
|
246
|
+
"""Test that resolve_python_version is idempotent for full versions."""
|
|
247
|
+
# Get a valid full version from the registry
|
|
248
|
+
all_versions = pyversions.python_versions()
|
|
249
|
+
some_version = str(next(iter(all_versions)))
|
|
250
|
+
|
|
251
|
+
# Resolving a full version twice should give the same result
|
|
252
|
+
first = pyversions.resolve_python_version(some_version)
|
|
253
|
+
second = pyversions.resolve_python_version(first)
|
|
254
|
+
assert first == second
|
tests/test_relocate.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import pathlib
|
|
4
4
|
import shutil
|
|
@@ -16,6 +16,7 @@ from relenv.relocate import (
|
|
|
16
16
|
main,
|
|
17
17
|
parse_readelf_d,
|
|
18
18
|
patch_rpath,
|
|
19
|
+
remove_rpath,
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
pytestmark = [
|
|
@@ -276,3 +277,87 @@ def test_handle_elf_rpath_only(tmp_path: pathlib.Path) -> None:
|
|
|
276
277
|
assert not (proj.libs_dir / "fake.so.2").exists()
|
|
277
278
|
assert patch_rpath_mock.call_count == 1
|
|
278
279
|
patch_rpath_mock.assert_called_with(str(pybin), "$ORIGIN/../lib")
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def test_remove_rpath_with_existing_rpath(tmp_path: pathlib.Path) -> None:
|
|
283
|
+
"""Test that remove_rpath removes an existing RPATH."""
|
|
284
|
+
path = str(tmp_path / "test.so")
|
|
285
|
+
with patch("subprocess.run", return_value=MagicMock(returncode=0)):
|
|
286
|
+
with patch(
|
|
287
|
+
"relenv.relocate.parse_rpath",
|
|
288
|
+
return_value=["/some/absolute/path"],
|
|
289
|
+
):
|
|
290
|
+
assert remove_rpath(path) is True
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def test_remove_rpath_no_existing_rpath(tmp_path: pathlib.Path) -> None:
|
|
294
|
+
"""Test that remove_rpath succeeds when there's no RPATH to remove."""
|
|
295
|
+
path = str(tmp_path / "test.so")
|
|
296
|
+
with patch("relenv.relocate.parse_rpath", return_value=[]):
|
|
297
|
+
assert remove_rpath(path) is True
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def test_remove_rpath_failed(tmp_path: pathlib.Path) -> None:
|
|
301
|
+
"""Test that remove_rpath returns False when patchelf fails."""
|
|
302
|
+
path = str(tmp_path / "test.so")
|
|
303
|
+
with patch("subprocess.run", return_value=MagicMock(returncode=1)):
|
|
304
|
+
with patch(
|
|
305
|
+
"relenv.relocate.parse_rpath",
|
|
306
|
+
return_value=["/some/absolute/path"],
|
|
307
|
+
):
|
|
308
|
+
assert remove_rpath(path) is False
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def test_handle_elf_removes_rpath_when_no_relenv_libs(tmp_path: pathlib.Path) -> None:
|
|
312
|
+
"""Test that handle_elf removes RPATH for binaries linking only to system libs."""
|
|
313
|
+
proj = LinuxProject(tmp_path / "proj")
|
|
314
|
+
module = proj.add_simple_elf("array.so", "lib", "python3.10", "lib-dynload")
|
|
315
|
+
|
|
316
|
+
# ldd output showing only system libraries
|
|
317
|
+
ldd_ret = """
|
|
318
|
+
linux-vdso.so.1 => linux-vdso.so.1 (0x0123456789)
|
|
319
|
+
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x0123456789)
|
|
320
|
+
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0123456789)
|
|
321
|
+
""".encode()
|
|
322
|
+
|
|
323
|
+
with proj:
|
|
324
|
+
with patch("subprocess.run", return_value=MagicMock(stdout=ldd_ret)):
|
|
325
|
+
with patch("relenv.relocate.remove_rpath") as remove_rpath_mock:
|
|
326
|
+
with patch("relenv.relocate.patch_rpath") as patch_rpath_mock:
|
|
327
|
+
handle_elf(
|
|
328
|
+
str(module), str(proj.libs_dir), True, str(proj.root_dir)
|
|
329
|
+
)
|
|
330
|
+
# Should remove RPATH, not patch it
|
|
331
|
+
assert remove_rpath_mock.call_count == 1
|
|
332
|
+
assert patch_rpath_mock.call_count == 0
|
|
333
|
+
remove_rpath_mock.assert_called_with(str(module))
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def test_handle_elf_sets_rpath_when_relenv_libs_present(tmp_path: pathlib.Path) -> None:
|
|
337
|
+
"""Test that handle_elf sets RPATH for binaries linking to relenv libs."""
|
|
338
|
+
proj = LinuxProject(tmp_path / "proj")
|
|
339
|
+
module = proj.add_simple_elf("_ssl.so", "lib", "python3.10", "lib-dynload")
|
|
340
|
+
libssl = proj.libs_dir / "libssl.so.3"
|
|
341
|
+
libssl.touch()
|
|
342
|
+
|
|
343
|
+
# ldd output showing relenv-built library
|
|
344
|
+
ldd_ret = """
|
|
345
|
+
linux-vdso.so.1 => linux-vdso.so.1 (0x0123456789)
|
|
346
|
+
libssl.so.3 => {libssl} (0x0123456789)
|
|
347
|
+
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x0123456789)
|
|
348
|
+
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0123456789)
|
|
349
|
+
""".format(
|
|
350
|
+
libssl=libssl
|
|
351
|
+
).encode()
|
|
352
|
+
|
|
353
|
+
with proj:
|
|
354
|
+
with patch("subprocess.run", return_value=MagicMock(stdout=ldd_ret)):
|
|
355
|
+
with patch("relenv.relocate.remove_rpath") as remove_rpath_mock:
|
|
356
|
+
with patch("relenv.relocate.patch_rpath") as patch_rpath_mock:
|
|
357
|
+
handle_elf(
|
|
358
|
+
str(module), str(proj.libs_dir), True, str(proj.root_dir)
|
|
359
|
+
)
|
|
360
|
+
# Should patch RPATH, not remove it
|
|
361
|
+
assert patch_rpath_mock.call_count == 1
|
|
362
|
+
assert remove_rpath_mock.call_count == 0
|
|
363
|
+
patch_rpath_mock.assert_called_with(str(module), "$ORIGIN/../..")
|
tests/test_relocate_module.py
CHANGED