relenv 0.22.1__tar.gz → 0.22.2__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.
- {relenv-0.22.1/relenv.egg-info → relenv-0.22.2}/PKG-INFO +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/__init__.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/__main__.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/__init__.py +21 -25
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/common/__init__.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/common/_sysconfigdata_template.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/common/builder.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/common/builders.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/common/download.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/common/install.py +6 -2
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/common/ui.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/darwin.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/linux.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/build/windows.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/buildenv.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/check.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/common.py +2 -4
- {relenv-0.22.1 → relenv-0.22.2}/relenv/create.py +19 -27
- {relenv-0.22.1 → relenv-0.22.2}/relenv/fetch.py +7 -5
- {relenv-0.22.1 → relenv-0.22.2}/relenv/manifest.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/pyversions.py +49 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/relocate.py +34 -2
- {relenv-0.22.1 → relenv-0.22.2}/relenv/runtime.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/relenv/toolchain.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2/relenv.egg-info}/PKG-INFO +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/__init__.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/_pytest_typing.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/conftest.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_build.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_common.py +2 -2
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_create.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_downloads.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_fips_photon.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_module_imports.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_pyversions_runtime.py +78 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_relocate.py +86 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_relocate_module.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_runtime.py +1 -1
- {relenv-0.22.1 → relenv-0.22.2}/tests/test_verify_build.py +7 -5
- {relenv-0.22.1 → relenv-0.22.2}/LICENSE.md +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/MANIFEST.in +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/NOTICE +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/README.md +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/pyproject.toml +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/relenv/_resources/xz/config.h +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/relenv/_resources/xz/readme.md +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/relenv/_scripts/install_vc_build.ps1 +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/relenv/python-versions.json +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/relenv.egg-info/SOURCES.txt +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/relenv.egg-info/dependency_links.txt +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/relenv.egg-info/entry_points.txt +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/relenv.egg-info/requires.txt +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/relenv.egg-info/top_level.txt +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/setup.cfg +0 -0
- {relenv-0.22.1 → relenv-0.22.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: relenv
|
|
3
|
-
Version: 0.22.
|
|
3
|
+
Version: 0.22.2
|
|
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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
# mypy: ignore-errors
|
|
4
4
|
"""
|
|
@@ -15,8 +15,13 @@ from types import FrameType, ModuleType
|
|
|
15
15
|
|
|
16
16
|
from . import darwin, linux, windows
|
|
17
17
|
from .common import builds
|
|
18
|
-
from ..common import
|
|
19
|
-
from ..pyversions import
|
|
18
|
+
from ..common import build_arch
|
|
19
|
+
from ..pyversions import (
|
|
20
|
+
Version,
|
|
21
|
+
get_default_python_version,
|
|
22
|
+
python_versions,
|
|
23
|
+
resolve_python_version,
|
|
24
|
+
)
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
def platform_module() -> ModuleType:
|
|
@@ -62,11 +67,12 @@ def setup_parser(
|
|
|
62
67
|
"logs, src, build, and previous tarball."
|
|
63
68
|
),
|
|
64
69
|
)
|
|
70
|
+
default_version = get_default_python_version()
|
|
65
71
|
build_subparser.add_argument(
|
|
66
72
|
"--python",
|
|
67
|
-
default=
|
|
73
|
+
default=default_version,
|
|
68
74
|
type=str,
|
|
69
|
-
help="The python version [default: %(default)s]",
|
|
75
|
+
help="The python version (e.g., 3.10, 3.13.7) [default: %(default)s]",
|
|
70
76
|
)
|
|
71
77
|
build_subparser.add_argument(
|
|
72
78
|
"--no-cleanup",
|
|
@@ -146,27 +152,17 @@ def main(args: argparse.Namespace) -> None:
|
|
|
146
152
|
print(f"Unsupported platform: {sys.platform}")
|
|
147
153
|
sys.exit(1)
|
|
148
154
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
155
|
+
try:
|
|
156
|
+
build_version_str = resolve_python_version(args.python)
|
|
157
|
+
except RuntimeError as e:
|
|
158
|
+
print(f"Error: {e}")
|
|
152
159
|
pyversions = python_versions()
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
else:
|
|
160
|
-
pyversions = python_versions(args.python)
|
|
161
|
-
build_version = sorted(list(pyversions.keys()))[-1]
|
|
162
|
-
|
|
163
|
-
# print(pyversions)
|
|
164
|
-
# print(pyversions[0].major)
|
|
165
|
-
# print(pyversions[0].minor)
|
|
166
|
-
# print(pyversions[0].micro)
|
|
167
|
-
# print(pyversions[0].pre)
|
|
168
|
-
# print(pyversions[0].post)
|
|
169
|
-
# print(pyversions)
|
|
160
|
+
strversions = "\n".join([str(_) for _ in pyversions])
|
|
161
|
+
print(f"Known versions are:\n{strversions}")
|
|
162
|
+
sys.exit(1)
|
|
163
|
+
|
|
164
|
+
build_version = Version(build_version_str)
|
|
165
|
+
pyversions = python_versions()
|
|
170
166
|
print(f"Build Python {build_version}")
|
|
171
167
|
|
|
172
168
|
# XXX
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
"""
|
|
4
4
|
Installation and finalization functions for the build process.
|
|
@@ -427,7 +427,11 @@ def finalize(
|
|
|
427
427
|
:type logfp: file
|
|
428
428
|
"""
|
|
429
429
|
# Run relok8 to make sure the rpaths are relocatable.
|
|
430
|
-
relenv
|
|
430
|
+
# Modules that don't link to relenv libs will have their RPATH removed
|
|
431
|
+
relenv.relocate.main(
|
|
432
|
+
dirs.prefix,
|
|
433
|
+
log_file_name=str(dirs.logs / "relocate.py.log"),
|
|
434
|
+
)
|
|
431
435
|
# Install relenv-sysconfigdata module
|
|
432
436
|
libdir = pathlib.Path(dirs.prefix) / "lib"
|
|
433
437
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
"""
|
|
4
4
|
Common classes and values used around relenv.
|
|
@@ -34,14 +34,12 @@ from typing import (
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
# relenv package version
|
|
37
|
-
__version__ = "0.22.
|
|
37
|
+
__version__ = "0.22.2"
|
|
38
38
|
|
|
39
39
|
log = logging.getLogger(__name__)
|
|
40
40
|
|
|
41
41
|
MODULE_DIR = pathlib.Path(__file__).resolve().parent
|
|
42
42
|
|
|
43
|
-
DEFAULT_PYTHON = "3.10.18"
|
|
44
|
-
|
|
45
43
|
LINUX = "linux"
|
|
46
44
|
WIN32 = "win32"
|
|
47
45
|
DARWIN = "darwin"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
"""
|
|
4
4
|
The ``relenv create`` command.
|
|
@@ -23,7 +23,11 @@ from .common import (
|
|
|
23
23
|
format_shebang,
|
|
24
24
|
relative_interpreter,
|
|
25
25
|
)
|
|
26
|
-
from .pyversions import
|
|
26
|
+
from .pyversions import (
|
|
27
|
+
get_default_python_version,
|
|
28
|
+
python_versions,
|
|
29
|
+
resolve_python_version,
|
|
30
|
+
)
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
@contextlib.contextmanager
|
|
@@ -73,11 +77,12 @@ def setup_parser(
|
|
|
73
77
|
type=str,
|
|
74
78
|
help="The host architecture [default: %(default)s]",
|
|
75
79
|
)
|
|
80
|
+
default_version = get_default_python_version()
|
|
76
81
|
subparser.add_argument(
|
|
77
82
|
"--python",
|
|
78
|
-
default=
|
|
83
|
+
default=default_version,
|
|
79
84
|
type=str,
|
|
80
|
-
help="The python version [default: %(default)s]",
|
|
85
|
+
help="The python version (e.g., 3.10, 3.13.7) [default: %(default)s]",
|
|
81
86
|
)
|
|
82
87
|
|
|
83
88
|
|
|
@@ -106,8 +111,9 @@ def create(
|
|
|
106
111
|
else:
|
|
107
112
|
writeto = pathlib.Path(name).resolve()
|
|
108
113
|
|
|
114
|
+
# Version should be provided by main(), but handle None just in case
|
|
109
115
|
if version is None:
|
|
110
|
-
version =
|
|
116
|
+
version = get_default_python_version()
|
|
111
117
|
|
|
112
118
|
if pathlib.Path(writeto).exists():
|
|
113
119
|
raise CreateException("The requested path already exists.")
|
|
@@ -253,31 +259,17 @@ def main(args: argparse.Namespace) -> None:
|
|
|
253
259
|
"Warning: Cross compilation support is experimental and is not fully tested or working!"
|
|
254
260
|
)
|
|
255
261
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
# Full version specified (e.g., "3.12.7")
|
|
262
|
+
try:
|
|
263
|
+
create_version = resolve_python_version(args.python)
|
|
264
|
+
except RuntimeError as e:
|
|
265
|
+
print(f"Error: {e}")
|
|
261
266
|
pyversions = python_versions()
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
print(f"Known versions are:\n{strversions}")
|
|
266
|
-
sys.exit(1)
|
|
267
|
-
create_version = requested
|
|
268
|
-
else:
|
|
269
|
-
# Minor version specified (e.g., "3.12"), resolve to latest
|
|
270
|
-
pyversions = python_versions(args.python)
|
|
271
|
-
if not pyversions:
|
|
272
|
-
print(f"Unknown minor version {requested}")
|
|
273
|
-
all_versions = python_versions()
|
|
274
|
-
strversions = "\n".join([str(_) for _ in all_versions])
|
|
275
|
-
print(f"Known versions are:\n{strversions}")
|
|
276
|
-
sys.exit(1)
|
|
277
|
-
create_version = sorted(list(pyversions.keys()))[-1]
|
|
267
|
+
strversions = "\n".join([str(_) for _ in pyversions])
|
|
268
|
+
print(f"Known versions are:\n{strversions}")
|
|
269
|
+
sys.exit(1)
|
|
278
270
|
|
|
279
271
|
try:
|
|
280
|
-
create(name, arch=args.arch, version=
|
|
272
|
+
create(name, arch=args.arch, version=create_version)
|
|
281
273
|
except CreateException as exc:
|
|
282
274
|
print(exc)
|
|
283
275
|
sys.exit(1)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
# mypy: ignore-errors
|
|
4
4
|
"""
|
|
@@ -16,7 +16,6 @@ from .build import platform_module
|
|
|
16
16
|
from .common import (
|
|
17
17
|
CHECK_HOSTS,
|
|
18
18
|
DATA_DIR,
|
|
19
|
-
DEFAULT_PYTHON,
|
|
20
19
|
__version__,
|
|
21
20
|
build_arch,
|
|
22
21
|
check_url,
|
|
@@ -24,6 +23,7 @@ from .common import (
|
|
|
24
23
|
get_triplet,
|
|
25
24
|
work_dir,
|
|
26
25
|
)
|
|
26
|
+
from .pyversions import get_default_python_version, resolve_python_version
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def setup_parser(
|
|
@@ -45,11 +45,12 @@ def setup_parser(
|
|
|
45
45
|
type=str,
|
|
46
46
|
help="Architecture to download. [default: %(default)s]",
|
|
47
47
|
)
|
|
48
|
+
default_version = get_default_python_version()
|
|
48
49
|
subparser.add_argument(
|
|
49
50
|
"--python",
|
|
50
|
-
default=
|
|
51
|
+
default=default_version,
|
|
51
52
|
type=str,
|
|
52
|
-
help="The python version [default: %(default)s]",
|
|
53
|
+
help="The python version (e.g., 3.10, 3.13.7) [default: %(default)s]",
|
|
53
54
|
)
|
|
54
55
|
|
|
55
56
|
|
|
@@ -87,7 +88,8 @@ def main(args: argparse.Namespace) -> None:
|
|
|
87
88
|
"""
|
|
88
89
|
version = os.environ.get("RELENV_FETCH_VERSION", __version__)
|
|
89
90
|
triplet = get_triplet(machine=args.arch)
|
|
90
|
-
|
|
91
|
+
# args.python will be the default version or user-specified version
|
|
92
|
+
python = resolve_python_version(args.python)
|
|
91
93
|
check_hosts = CHECK_HOSTS
|
|
92
94
|
if os.environ.get("RELENV_FETCH_HOST", ""):
|
|
93
95
|
check_hosts = [os.environ["RELENV_FETCH_HOST"]]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
"""
|
|
4
4
|
Versions utility.
|
|
@@ -947,6 +947,54 @@ def python_versions(
|
|
|
947
947
|
return {version: pyversions[str(version)] for version in versions}
|
|
948
948
|
|
|
949
949
|
|
|
950
|
+
def get_default_python_version() -> str:
|
|
951
|
+
"""
|
|
952
|
+
Get the default Python version to use when none is specified.
|
|
953
|
+
|
|
954
|
+
:return: The default Python version string (e.g., "3.10.19")
|
|
955
|
+
"""
|
|
956
|
+
# Default to latest 3.10 version
|
|
957
|
+
pyversions = python_versions("3.10")
|
|
958
|
+
if not pyversions:
|
|
959
|
+
raise RuntimeError("No 3.10 versions found")
|
|
960
|
+
latest = sorted(list(pyversions.keys()))[-1]
|
|
961
|
+
return str(latest)
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
def resolve_python_version(version_spec: str | None = None) -> str:
|
|
965
|
+
"""
|
|
966
|
+
Resolve a Python version specification to a full version string.
|
|
967
|
+
|
|
968
|
+
If version_spec is None, returns the latest Python 3.10 version.
|
|
969
|
+
If version_spec is partial (e.g., "3.10"), returns the latest micro version.
|
|
970
|
+
If version_spec is full (e.g., "3.10.19"), returns it as-is after validation.
|
|
971
|
+
|
|
972
|
+
:param version_spec: Version specification (None, "3.10", or "3.10.19")
|
|
973
|
+
:return: Full version string (e.g., "3.10.19")
|
|
974
|
+
:raises RuntimeError: If the version is not found
|
|
975
|
+
"""
|
|
976
|
+
if version_spec is None:
|
|
977
|
+
# Default to latest 3.10 version
|
|
978
|
+
return get_default_python_version()
|
|
979
|
+
|
|
980
|
+
requested = Version(version_spec)
|
|
981
|
+
|
|
982
|
+
if requested.micro is not None:
|
|
983
|
+
# Full version specified - validate it exists
|
|
984
|
+
pyversions = python_versions()
|
|
985
|
+
if requested not in pyversions:
|
|
986
|
+
raise RuntimeError(f"Unknown version {requested}")
|
|
987
|
+
return str(requested)
|
|
988
|
+
else:
|
|
989
|
+
# Partial version (major.minor) - get latest micro
|
|
990
|
+
pyversions = python_versions(version_spec)
|
|
991
|
+
if not pyversions:
|
|
992
|
+
raise RuntimeError(f"Unknown minor version {requested}")
|
|
993
|
+
# Return the latest version for this major.minor
|
|
994
|
+
latest = sorted(list(pyversions.keys()))[-1]
|
|
995
|
+
return str(latest)
|
|
996
|
+
|
|
997
|
+
|
|
950
998
|
def setup_parser(
|
|
951
999
|
subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
952
1000
|
) -> None:
|
|
@@ -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.
|
|
@@ -264,6 +264,34 @@ def is_in_dir(
|
|
|
264
264
|
return os.path.realpath(filepath).startswith(os.path.realpath(directory) + os.sep)
|
|
265
265
|
|
|
266
266
|
|
|
267
|
+
def remove_rpath(path: str | os.PathLike[str]) -> bool:
|
|
268
|
+
"""
|
|
269
|
+
Remove the rpath from a given ELF file.
|
|
270
|
+
|
|
271
|
+
:param path: The path to an ELF file
|
|
272
|
+
:type path: str
|
|
273
|
+
|
|
274
|
+
:return: True if successful, False otherwise
|
|
275
|
+
:rtype: bool
|
|
276
|
+
"""
|
|
277
|
+
old_rpath = parse_rpath(path)
|
|
278
|
+
if not old_rpath:
|
|
279
|
+
# No RPATH to remove
|
|
280
|
+
return True
|
|
281
|
+
|
|
282
|
+
log.info("Remove RPATH from %s (was: %s)", path, old_rpath)
|
|
283
|
+
proc = subprocess.run(
|
|
284
|
+
["patchelf", "--remove-rpath", path],
|
|
285
|
+
stderr=subprocess.PIPE,
|
|
286
|
+
stdout=subprocess.PIPE,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
if proc.returncode:
|
|
290
|
+
log.error("Failed to remove RPATH from %s: %s", path, proc.stderr.decode())
|
|
291
|
+
return False
|
|
292
|
+
return True
|
|
293
|
+
|
|
294
|
+
|
|
267
295
|
def patch_rpath(
|
|
268
296
|
path: str | os.PathLike[str],
|
|
269
297
|
new_rpath: str,
|
|
@@ -325,6 +353,7 @@ def handle_elf(
|
|
|
325
353
|
root = libs
|
|
326
354
|
proc = subprocess.run(["ldd", path], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
327
355
|
needs_rpath = False
|
|
356
|
+
|
|
328
357
|
for line in proc.stdout.decode().splitlines():
|
|
329
358
|
if line.find("=>") == -1:
|
|
330
359
|
log.debug("Skip ldd output line: %s", line)
|
|
@@ -371,7 +400,9 @@ def handle_elf(
|
|
|
371
400
|
log.info("Adjust rpath of %s to %s", path, relpath)
|
|
372
401
|
patch_rpath(path, relpath)
|
|
373
402
|
else:
|
|
374
|
-
|
|
403
|
+
# No relenv libraries are linked, so RPATH is not needed
|
|
404
|
+
# Remove any existing RPATH to avoid security/correctness issues
|
|
405
|
+
remove_rpath(path)
|
|
375
406
|
|
|
376
407
|
|
|
377
408
|
def main(
|
|
@@ -420,6 +451,7 @@ def main(
|
|
|
420
451
|
if path in processed:
|
|
421
452
|
continue
|
|
422
453
|
log.debug("Checking %s", path)
|
|
454
|
+
|
|
423
455
|
if is_macho(path):
|
|
424
456
|
log.info("Found Mach-O %s", path)
|
|
425
457
|
_ = handle_macho(path, libs_dir, rpath_only)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: relenv
|
|
3
|
-
Version: 0.22.
|
|
3
|
+
Version: 0.22.2
|
|
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
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
@@ -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/
|
|
@@ -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
|
|
@@ -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/../..")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2022-
|
|
1
|
+
# Copyright 2022-2026 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
# mypy: ignore-errors
|
|
4
4
|
"""
|
|
@@ -180,13 +180,15 @@ def test_imports(pyexec):
|
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
def test_pip_install_salt_git(pipexec, build, build_dir, pyexec, build_version):
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
if sys.platform == "win32" and (
|
|
184
|
+
"3.10" in build_version
|
|
185
|
+
or "3.11" in build_version
|
|
186
186
|
or "3.12" in build_version
|
|
187
187
|
or "3.13" in build_version
|
|
188
188
|
):
|
|
189
|
-
pytest.xfail(
|
|
189
|
+
pytest.xfail(
|
|
190
|
+
"Salt git install fails on Windows (setup.py tries to install missing man pages)"
|
|
191
|
+
)
|
|
190
192
|
if sys.platform == "darwin" and "3.12" in build_version:
|
|
191
193
|
pytest.xfail("Salt does not work with 3.12 on macos yet")
|
|
192
194
|
if sys.platform == "darwin" and "3.13" in build_version:
|
|
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
|