relenv 0.21.2__py3-none-any.whl → 0.22.1__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 +14 -2
- relenv/__main__.py +12 -6
- relenv/_resources/xz/config.h +148 -0
- relenv/_resources/xz/readme.md +4 -0
- relenv/build/__init__.py +28 -30
- relenv/build/common/__init__.py +50 -0
- relenv/build/common/_sysconfigdata_template.py +72 -0
- relenv/build/common/builder.py +907 -0
- relenv/build/common/builders.py +163 -0
- relenv/build/common/download.py +324 -0
- relenv/build/common/install.py +609 -0
- relenv/build/common/ui.py +432 -0
- relenv/build/darwin.py +128 -14
- relenv/build/linux.py +292 -74
- relenv/build/windows.py +123 -169
- relenv/buildenv.py +48 -17
- relenv/check.py +10 -5
- relenv/common.py +492 -165
- relenv/create.py +147 -7
- relenv/fetch.py +16 -4
- relenv/manifest.py +15 -7
- relenv/python-versions.json +350 -0
- relenv/pyversions.py +817 -30
- relenv/relocate.py +101 -55
- relenv/runtime.py +457 -282
- relenv/toolchain.py +9 -3
- {relenv-0.21.2.dist-info → relenv-0.22.1.dist-info}/METADATA +1 -1
- relenv-0.22.1.dist-info/RECORD +48 -0
- tests/__init__.py +2 -0
- tests/_pytest_typing.py +45 -0
- tests/conftest.py +42 -36
- tests/test_build.py +426 -9
- tests/test_common.py +373 -48
- tests/test_create.py +149 -6
- tests/test_downloads.py +19 -15
- tests/test_fips_photon.py +6 -3
- tests/test_module_imports.py +44 -0
- tests/test_pyversions_runtime.py +177 -0
- tests/test_relocate.py +45 -39
- tests/test_relocate_module.py +257 -0
- tests/test_runtime.py +1968 -6
- tests/test_verify_build.py +477 -34
- relenv/build/common.py +0 -1707
- relenv-0.21.2.dist-info/RECORD +0 -35
- {relenv-0.21.2.dist-info → relenv-0.22.1.dist-info}/WHEEL +0 -0
- {relenv-0.21.2.dist-info → relenv-0.22.1.dist-info}/entry_points.txt +0 -0
- {relenv-0.21.2.dist-info → relenv-0.22.1.dist-info}/licenses/LICENSE.md +0 -0
- {relenv-0.21.2.dist-info → relenv-0.22.1.dist-info}/licenses/NOTICE +0 -0
- {relenv-0.21.2.dist-info → relenv-0.22.1.dist-info}/top_level.txt +0 -0
tests/test_verify_build.py
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
# Copyright 2022-2025 Broadcom.
|
|
2
|
-
# SPDX-License-Identifier: Apache-2
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
# mypy: ignore-errors
|
|
3
4
|
"""
|
|
4
5
|
Verify relenv builds.
|
|
5
6
|
"""
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
8
9
|
import pathlib
|
|
10
|
+
import shlex
|
|
9
11
|
import shutil
|
|
10
12
|
import subprocess
|
|
11
13
|
import sys
|
|
12
14
|
import textwrap
|
|
13
15
|
import time
|
|
16
|
+
import uuid
|
|
14
17
|
|
|
15
18
|
import packaging
|
|
16
19
|
import pytest
|
|
@@ -65,6 +68,48 @@ def _install_ppbt(pexec):
|
|
|
65
68
|
assert p.returncode == 0, "Failed to extract toolchain"
|
|
66
69
|
|
|
67
70
|
|
|
71
|
+
def _setup_buildenv(pyexec, env):
|
|
72
|
+
"""
|
|
73
|
+
Setup build environment variables for compiling C extensions.
|
|
74
|
+
|
|
75
|
+
On Linux, this calls 'relenv buildenv --json' to get the proper compiler
|
|
76
|
+
flags and paths to use the relenv toolchain and bundled libraries instead
|
|
77
|
+
of system libraries.
|
|
78
|
+
|
|
79
|
+
:param pyexec: Path to the relenv Python executable
|
|
80
|
+
:param env: Environment dictionary to update with buildenv variables
|
|
81
|
+
"""
|
|
82
|
+
if sys.platform != "linux":
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
p = subprocess.run(
|
|
86
|
+
[
|
|
87
|
+
str(pyexec),
|
|
88
|
+
"-m",
|
|
89
|
+
"relenv",
|
|
90
|
+
"buildenv",
|
|
91
|
+
"--json",
|
|
92
|
+
],
|
|
93
|
+
capture_output=True,
|
|
94
|
+
)
|
|
95
|
+
try:
|
|
96
|
+
buildenv = json.loads(p.stdout)
|
|
97
|
+
except json.JSONDecodeError:
|
|
98
|
+
assert False, f"Failed to decode json: {p.stdout.decode()} {p.stderr.decode()}"
|
|
99
|
+
|
|
100
|
+
for k in buildenv:
|
|
101
|
+
env[k] = buildenv[k]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@pytest.fixture(autouse=True)
|
|
105
|
+
def _clear_ssl_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Ensure preceding tests do not leave stale certificate paths behind.
|
|
108
|
+
"""
|
|
109
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
110
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
111
|
+
|
|
112
|
+
|
|
68
113
|
@pytest.fixture(scope="module")
|
|
69
114
|
def arch():
|
|
70
115
|
return build_arch()
|
|
@@ -320,27 +365,7 @@ def test_pip_install_salt_w_package_requirements(
|
|
|
320
365
|
|
|
321
366
|
_install_ppbt(pyexec)
|
|
322
367
|
env = os.environ.copy()
|
|
323
|
-
|
|
324
|
-
# if sys.platform == "linux":
|
|
325
|
-
# p = subprocess.run(
|
|
326
|
-
# [
|
|
327
|
-
# pyexec,
|
|
328
|
-
# "-m",
|
|
329
|
-
# "relenv",
|
|
330
|
-
# "buildenv",
|
|
331
|
-
# "--json",
|
|
332
|
-
# ],
|
|
333
|
-
# capture_output=True,
|
|
334
|
-
# )
|
|
335
|
-
# try:
|
|
336
|
-
# buildenv = json.loads(p.stdout)
|
|
337
|
-
# except json.JSONDecodeError:
|
|
338
|
-
# assert (
|
|
339
|
-
# False
|
|
340
|
-
# ), f"Failed to decode json: {p.stdout.decode()} {p.stderr.decode()}"
|
|
341
|
-
# for k in buildenv:
|
|
342
|
-
# env[k] = buildenv[k]
|
|
343
|
-
|
|
368
|
+
_setup_buildenv(pyexec, env)
|
|
344
369
|
env["RELENV_BUILDENV"] = "yes"
|
|
345
370
|
env["USE_STATIC_REQUIREMENTS"] = "1"
|
|
346
371
|
p = subprocess.run(
|
|
@@ -413,7 +438,15 @@ def test_pip_install_salt_w_package_requirements(
|
|
|
413
438
|
"26.4.0",
|
|
414
439
|
],
|
|
415
440
|
)
|
|
416
|
-
def test_pip_install_pyzmq(
|
|
441
|
+
def test_pip_install_pyzmq(
|
|
442
|
+
pipexec,
|
|
443
|
+
pyexec,
|
|
444
|
+
pyzmq_version,
|
|
445
|
+
build_version,
|
|
446
|
+
arch,
|
|
447
|
+
build,
|
|
448
|
+
tmp_path: pathlib.Path,
|
|
449
|
+
) -> None:
|
|
417
450
|
|
|
418
451
|
if pyzmq_version == "23.2.0" and "3.12" in build_version:
|
|
419
452
|
pytest.xfail(f"{pyzmq_version} does not install on 3.12")
|
|
@@ -483,6 +516,157 @@ def test_pip_install_pyzmq(pipexec, pyexec, pyzmq_version, build_version, arch,
|
|
|
483
516
|
env["ZMQ_PREFIX"] = "bundled"
|
|
484
517
|
env["RELENV_BUILDENV"] = "yes"
|
|
485
518
|
env["USE_STATIC_REQUIREMENTS"] = "1"
|
|
519
|
+
|
|
520
|
+
if sys.platform == "linux":
|
|
521
|
+
fake_bsd_root = tmp_path / "fake_libbsd"
|
|
522
|
+
(fake_bsd_root / "bsd").mkdir(parents=True, exist_ok=True)
|
|
523
|
+
(fake_bsd_root / "bsd" / "string.h").write_text(
|
|
524
|
+
textwrap.dedent(
|
|
525
|
+
"""\
|
|
526
|
+
#ifndef RELENV_FAKE_BSD_STRING_H
|
|
527
|
+
#define RELENV_FAKE_BSD_STRING_H
|
|
528
|
+
|
|
529
|
+
#include <stddef.h>
|
|
530
|
+
|
|
531
|
+
#ifdef __cplusplus
|
|
532
|
+
extern "C" {
|
|
533
|
+
#endif
|
|
534
|
+
|
|
535
|
+
size_t strlcpy(char *dst, const char *src, size_t siz);
|
|
536
|
+
size_t strlcat(char *dst, const char *src, size_t siz);
|
|
537
|
+
|
|
538
|
+
#ifdef __cplusplus
|
|
539
|
+
}
|
|
540
|
+
#endif
|
|
541
|
+
|
|
542
|
+
#endif /* RELENV_FAKE_BSD_STRING_H */
|
|
543
|
+
"""
|
|
544
|
+
)
|
|
545
|
+
)
|
|
546
|
+
(fake_bsd_root / "string.c").write_text(
|
|
547
|
+
textwrap.dedent(
|
|
548
|
+
"""\
|
|
549
|
+
#include <stddef.h>
|
|
550
|
+
#include <string.h>
|
|
551
|
+
|
|
552
|
+
static size_t relenv_strlen(const char *s) {
|
|
553
|
+
size_t len = 0;
|
|
554
|
+
if (s == NULL) {
|
|
555
|
+
return 0;
|
|
556
|
+
}
|
|
557
|
+
while (s[len] != '\\0') {
|
|
558
|
+
++len;
|
|
559
|
+
}
|
|
560
|
+
return len;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
static size_t relenv_strnlen(const char *s, size_t maxlen) {
|
|
564
|
+
size_t len = 0;
|
|
565
|
+
if (s == NULL) {
|
|
566
|
+
return 0;
|
|
567
|
+
}
|
|
568
|
+
while (len < maxlen && s[len] != '\\0') {
|
|
569
|
+
++len;
|
|
570
|
+
}
|
|
571
|
+
return len;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
size_t strlcpy(char *dst, const char *src, size_t siz) {
|
|
575
|
+
size_t src_len = relenv_strlen(src);
|
|
576
|
+
if (siz == 0 || dst == NULL) {
|
|
577
|
+
return src_len;
|
|
578
|
+
}
|
|
579
|
+
size_t copy = src_len;
|
|
580
|
+
if (copy >= siz) {
|
|
581
|
+
copy = siz - 1;
|
|
582
|
+
}
|
|
583
|
+
if (copy > 0 && src != NULL) {
|
|
584
|
+
memcpy(dst, src, copy);
|
|
585
|
+
}
|
|
586
|
+
dst[copy] = '\\0';
|
|
587
|
+
return src_len;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
size_t strlcat(char *dst, const char *src, size_t siz) {
|
|
591
|
+
size_t dst_len = relenv_strnlen(dst, siz);
|
|
592
|
+
size_t src_len = relenv_strlen(src);
|
|
593
|
+
size_t initial_len = dst_len;
|
|
594
|
+
if (dst == NULL || dst_len >= siz) {
|
|
595
|
+
return initial_len + src_len;
|
|
596
|
+
}
|
|
597
|
+
size_t space = (siz > dst_len + 1) ? siz - dst_len - 1 : 0;
|
|
598
|
+
size_t copy = 0;
|
|
599
|
+
if (space > 0 && src != NULL) {
|
|
600
|
+
copy = src_len;
|
|
601
|
+
if (copy > space) {
|
|
602
|
+
copy = space;
|
|
603
|
+
}
|
|
604
|
+
if (copy > 0) {
|
|
605
|
+
memcpy(dst + dst_len, src, copy);
|
|
606
|
+
}
|
|
607
|
+
dst_len += copy;
|
|
608
|
+
}
|
|
609
|
+
dst[dst_len] = '\\0';
|
|
610
|
+
return initial_len + src_len;
|
|
611
|
+
}
|
|
612
|
+
"""
|
|
613
|
+
)
|
|
614
|
+
)
|
|
615
|
+
include_flag = f"-I{fake_bsd_root}"
|
|
616
|
+
for key in ("CFLAGS", "CXXFLAGS", "CPPFLAGS"):
|
|
617
|
+
env[key] = " ".join(filter(None, [env.get(key, ""), include_flag])).strip()
|
|
618
|
+
env["CPATH"] = ":".join(
|
|
619
|
+
filter(None, [str(fake_bsd_root), env.get("CPATH", "")])
|
|
620
|
+
)
|
|
621
|
+
for key in ("C_INCLUDE_PATH", "CPLUS_INCLUDE_PATH"):
|
|
622
|
+
env[key] = ":".join(filter(None, [str(fake_bsd_root), env.get(key, "")]))
|
|
623
|
+
cc_value = env.get("CC")
|
|
624
|
+
if cc_value:
|
|
625
|
+
cc_args = shlex.split(cc_value)
|
|
626
|
+
else:
|
|
627
|
+
cc_path = shutil.which("cc") or shutil.which("gcc")
|
|
628
|
+
assert cc_path, "C compiler not found for libbsd shim"
|
|
629
|
+
cc_args = [cc_path]
|
|
630
|
+
obj_path = fake_bsd_root / "string.o"
|
|
631
|
+
compile_result = subprocess.run(
|
|
632
|
+
cc_args
|
|
633
|
+
+ [
|
|
634
|
+
"-c",
|
|
635
|
+
"-O2",
|
|
636
|
+
"-fPIC",
|
|
637
|
+
"-o",
|
|
638
|
+
str(obj_path),
|
|
639
|
+
str(fake_bsd_root / "string.c"),
|
|
640
|
+
],
|
|
641
|
+
env=env,
|
|
642
|
+
)
|
|
643
|
+
assert compile_result.returncode == 0, "Failed to compile libbsd shim"
|
|
644
|
+
ar_value = env.get("AR")
|
|
645
|
+
if ar_value:
|
|
646
|
+
ar_args = shlex.split(ar_value)
|
|
647
|
+
else:
|
|
648
|
+
ar_path = shutil.which("ar")
|
|
649
|
+
assert ar_path, "Archiver not found for libbsd shim"
|
|
650
|
+
ar_args = [ar_path]
|
|
651
|
+
libbsd_static = fake_bsd_root / "libbsd.a"
|
|
652
|
+
archive_result = subprocess.run(
|
|
653
|
+
ar_args + ["rcs", str(libbsd_static), str(obj_path)],
|
|
654
|
+
env=env,
|
|
655
|
+
)
|
|
656
|
+
assert archive_result.returncode == 0, "Failed to archive libbsd shim"
|
|
657
|
+
lib_dir_flag = f"-L{fake_bsd_root}"
|
|
658
|
+
env["LDFLAGS"] = " ".join(
|
|
659
|
+
filter(None, [lib_dir_flag, env.get("LDFLAGS", "")])
|
|
660
|
+
).strip()
|
|
661
|
+
env["LIBS"] = " ".join(filter(None, ["-lbsd", env.get("LIBS", "")])).strip()
|
|
662
|
+
env["LIBRARY_PATH"] = ":".join(
|
|
663
|
+
filter(None, [str(fake_bsd_root), env.get("LIBRARY_PATH", "")])
|
|
664
|
+
)
|
|
665
|
+
env["ac_cv_func_strlcpy"] = "yes"
|
|
666
|
+
env["ac_cv_func_strlcat"] = "yes"
|
|
667
|
+
env["ac_cv_have_decl_strlcpy"] = "yes"
|
|
668
|
+
env["ac_cv_have_decl_strlcat"] = "yes"
|
|
669
|
+
|
|
486
670
|
p = subprocess.run(
|
|
487
671
|
[
|
|
488
672
|
str(pipexec),
|
|
@@ -826,6 +1010,7 @@ def test_shebangs(pipexec, build, minor_version):
|
|
|
826
1010
|
def test_moving_pip_installed_c_extentions(pipexec, pyexec, build, minor_version):
|
|
827
1011
|
_install_ppbt(pyexec)
|
|
828
1012
|
env = os.environ.copy()
|
|
1013
|
+
_setup_buildenv(pyexec, env)
|
|
829
1014
|
env["RELENV_DEBUG"] = "yes"
|
|
830
1015
|
env["RELENV_BUILDENV"] = "yes"
|
|
831
1016
|
p = subprocess.run(
|
|
@@ -857,11 +1042,6 @@ def test_cryptography_rpath(
|
|
|
857
1042
|
pyexec, pipexec, build, minor_version, cryptography_version
|
|
858
1043
|
):
|
|
859
1044
|
_install_ppbt(pyexec)
|
|
860
|
-
# log.warn("Extract ppbt")
|
|
861
|
-
# p = subprocess.run(
|
|
862
|
-
# [pyexec, "-c", "import ppbt; ppbt.extract()"],
|
|
863
|
-
# )
|
|
864
|
-
# assert p.returncode == 0
|
|
865
1045
|
|
|
866
1046
|
def find_library(path, search):
|
|
867
1047
|
for root, dirs, files in os.walk(path):
|
|
@@ -870,6 +1050,7 @@ def test_cryptography_rpath(
|
|
|
870
1050
|
return fname
|
|
871
1051
|
|
|
872
1052
|
env = os.environ.copy()
|
|
1053
|
+
_setup_buildenv(pyexec, env)
|
|
873
1054
|
env["RELENV_BUILDENV"] = "yes"
|
|
874
1055
|
p = subprocess.run(
|
|
875
1056
|
[
|
|
@@ -1155,7 +1336,7 @@ def test_install_python_ldap(pipexec, pyexec, build):
|
|
|
1155
1336
|
tar xvf cyrus-sasl-{saslver}.tar.gz
|
|
1156
1337
|
cd cyrus-sasl-{saslver}
|
|
1157
1338
|
./configure --prefix=$RELENV_PATH
|
|
1158
|
-
make
|
|
1339
|
+
make -j"$(nproc)"
|
|
1159
1340
|
make install
|
|
1160
1341
|
cd ..
|
|
1161
1342
|
|
|
@@ -1164,7 +1345,7 @@ def test_install_python_ldap(pipexec, pyexec, build):
|
|
|
1164
1345
|
tar xvf openldap-{ldapver}.tgz
|
|
1165
1346
|
cd openldap-{ldapver}
|
|
1166
1347
|
./configure --prefix=$RELENV_PATH
|
|
1167
|
-
make
|
|
1348
|
+
make -j"$(nproc)"
|
|
1168
1349
|
make install
|
|
1169
1350
|
cd ..
|
|
1170
1351
|
|
|
@@ -1184,6 +1365,7 @@ def test_install_python_ldap(pipexec, pyexec, build):
|
|
|
1184
1365
|
|
|
1185
1366
|
subprocess.run(["/usr/bin/bash", "buildscript.sh"], check=True)
|
|
1186
1367
|
env = os.environ.copy()
|
|
1368
|
+
_setup_buildenv(pyexec, env)
|
|
1187
1369
|
env["RELENV_DEBUG"] = "yes"
|
|
1188
1370
|
env["RELENV_BUILDENV"] = "yes"
|
|
1189
1371
|
|
|
@@ -1215,9 +1397,16 @@ def test_install_with_target_shebang(pipexec, build, minor_version):
|
|
|
1215
1397
|
check=True,
|
|
1216
1398
|
env=env,
|
|
1217
1399
|
)
|
|
1218
|
-
|
|
1400
|
+
exec_line = ""
|
|
1401
|
+
for line in pathlib.Path(extras / "bin" / "cowsay").read_text().splitlines():
|
|
1402
|
+
stripped = line.strip()
|
|
1403
|
+
if not stripped or stripped.startswith("#"):
|
|
1404
|
+
continue
|
|
1405
|
+
if stripped.startswith('"exec"'):
|
|
1406
|
+
exec_line = stripped
|
|
1407
|
+
break
|
|
1219
1408
|
assert (
|
|
1220
|
-
|
|
1409
|
+
exec_line
|
|
1221
1410
|
== '"exec" "$(dirname "$(readlink -f "$0")")/../../bin/python{}" "$0" "$@"'.format(
|
|
1222
1411
|
minor_version
|
|
1223
1412
|
)
|
|
@@ -1764,7 +1953,7 @@ def test_install_editable_package_in_extras(
|
|
|
1764
1953
|
def rockycontainer(build):
|
|
1765
1954
|
if not shutil.which("docker"):
|
|
1766
1955
|
pytest.skip(reason="No docker binary found")
|
|
1767
|
-
name = "rocky10"
|
|
1956
|
+
name = f"rocky10-{uuid.uuid4().hex}"
|
|
1768
1957
|
subprocess.run(
|
|
1769
1958
|
[
|
|
1770
1959
|
"docker",
|
|
@@ -1815,10 +2004,35 @@ def rockycontainer(build):
|
|
|
1815
2004
|
|
|
1816
2005
|
|
|
1817
2006
|
@pytest.mark.skip_on_windows
|
|
1818
|
-
def test_no_openssl_binary(rockycontainer, pipexec, pyexec):
|
|
2007
|
+
def test_no_openssl_binary(rockycontainer, pipexec, pyexec, build):
|
|
1819
2008
|
_install_ppbt(pyexec)
|
|
1820
2009
|
env = os.environ.copy()
|
|
2010
|
+
_setup_buildenv(pyexec, env)
|
|
1821
2011
|
env["RELENV_BUILDENV"] = "yes"
|
|
2012
|
+
if sys.platform == "linux":
|
|
2013
|
+
toolchain_path = pathlib.Path(env["TOOLCHAIN_PATH"])
|
|
2014
|
+
triplet = env["TRIPLET"]
|
|
2015
|
+
sysroot_lib = toolchain_path / triplet / "sysroot" / "lib"
|
|
2016
|
+
sysroot_lib.mkdir(parents=True, exist_ok=True)
|
|
2017
|
+
bz2_sources = sorted(
|
|
2018
|
+
(pathlib.Path(build) / "lib").glob("libbz2.so*"),
|
|
2019
|
+
key=lambda p: len(p.name),
|
|
2020
|
+
)
|
|
2021
|
+
if not bz2_sources:
|
|
2022
|
+
pytest.fail(
|
|
2023
|
+
"libbz2.so not found in relenv build; cryptography build cannot proceed"
|
|
2024
|
+
)
|
|
2025
|
+
for bz2_source in bz2_sources:
|
|
2026
|
+
target = sysroot_lib / bz2_source.name
|
|
2027
|
+
if target.exists() or target.is_symlink():
|
|
2028
|
+
if target.is_symlink():
|
|
2029
|
+
try:
|
|
2030
|
+
if target.readlink() == bz2_source:
|
|
2031
|
+
continue
|
|
2032
|
+
except OSError:
|
|
2033
|
+
pass
|
|
2034
|
+
target.unlink()
|
|
2035
|
+
target.symlink_to(bz2_source)
|
|
1822
2036
|
proc = subprocess.run(
|
|
1823
2037
|
[
|
|
1824
2038
|
str(pipexec),
|
|
@@ -1882,3 +2096,232 @@ def test_install_setuptools_25_2_to_25_3(pipexec, build, minor_version, pip_vers
|
|
|
1882
2096
|
],
|
|
1883
2097
|
check=True,
|
|
1884
2098
|
)
|
|
2099
|
+
|
|
2100
|
+
|
|
2101
|
+
def test_expat_version(pyexec):
|
|
2102
|
+
"""
|
|
2103
|
+
Verify that the build contains the correct expat version.
|
|
2104
|
+
|
|
2105
|
+
This validates that update_expat() successfully updated the bundled
|
|
2106
|
+
expat library to match the version in python-versions.json.
|
|
2107
|
+
|
|
2108
|
+
Works on all platforms: Linux, Darwin (macOS), and Windows.
|
|
2109
|
+
"""
|
|
2110
|
+
from relenv.build.common import get_dependency_version
|
|
2111
|
+
|
|
2112
|
+
# Map sys.platform to relenv platform names
|
|
2113
|
+
platform_map = {
|
|
2114
|
+
"linux": "linux",
|
|
2115
|
+
"darwin": "darwin",
|
|
2116
|
+
"win32": "win32",
|
|
2117
|
+
}
|
|
2118
|
+
platform = platform_map.get(sys.platform)
|
|
2119
|
+
if not platform:
|
|
2120
|
+
pytest.skip(f"Unknown platform: {sys.platform}")
|
|
2121
|
+
|
|
2122
|
+
# Get expected version from python-versions.json
|
|
2123
|
+
expat_info = get_dependency_version("expat", platform)
|
|
2124
|
+
if not expat_info:
|
|
2125
|
+
pytest.skip(f"No expat version defined in python-versions.json for {platform}")
|
|
2126
|
+
|
|
2127
|
+
expected_version = expat_info["version"]
|
|
2128
|
+
|
|
2129
|
+
# Get actual version from the build
|
|
2130
|
+
proc = subprocess.run(
|
|
2131
|
+
[str(pyexec), "-c", "import pyexpat; print(pyexpat.EXPAT_VERSION)"],
|
|
2132
|
+
capture_output=True,
|
|
2133
|
+
check=True,
|
|
2134
|
+
)
|
|
2135
|
+
|
|
2136
|
+
actual_version_str = proc.stdout.decode().strip()
|
|
2137
|
+
# Format is "expat_X_Y_Z", extract version
|
|
2138
|
+
assert actual_version_str.startswith(
|
|
2139
|
+
"expat_"
|
|
2140
|
+
), f"Unexpected EXPAT_VERSION format: {actual_version_str}"
|
|
2141
|
+
|
|
2142
|
+
# Convert "expat_2_7_3" -> "2.7.3"
|
|
2143
|
+
actual_version = actual_version_str.replace("expat_", "").replace("_", ".")
|
|
2144
|
+
|
|
2145
|
+
assert actual_version == expected_version, (
|
|
2146
|
+
f"Expat version mismatch on {platform}: expected {expected_version}, "
|
|
2147
|
+
f"found {actual_version} (from {actual_version_str})"
|
|
2148
|
+
)
|
|
2149
|
+
|
|
2150
|
+
|
|
2151
|
+
def test_sqlite_version(pyexec):
|
|
2152
|
+
"""
|
|
2153
|
+
Verify that the build contains the correct SQLite version.
|
|
2154
|
+
|
|
2155
|
+
This validates that SQLite was built with the version specified
|
|
2156
|
+
in python-versions.json.
|
|
2157
|
+
|
|
2158
|
+
Works on all platforms: Linux, Darwin (macOS), and Windows.
|
|
2159
|
+
"""
|
|
2160
|
+
from relenv.build.common import get_dependency_version
|
|
2161
|
+
|
|
2162
|
+
# Map sys.platform to relenv platform names
|
|
2163
|
+
platform_map = {
|
|
2164
|
+
"linux": "linux",
|
|
2165
|
+
"darwin": "darwin",
|
|
2166
|
+
"win32": "win32",
|
|
2167
|
+
}
|
|
2168
|
+
platform = platform_map.get(sys.platform)
|
|
2169
|
+
if not platform:
|
|
2170
|
+
pytest.skip(f"Unknown platform: {sys.platform}")
|
|
2171
|
+
|
|
2172
|
+
# Get expected version from python-versions.json
|
|
2173
|
+
sqlite_info = get_dependency_version("sqlite", platform)
|
|
2174
|
+
if not sqlite_info:
|
|
2175
|
+
pytest.skip(f"No sqlite version defined in python-versions.json for {platform}")
|
|
2176
|
+
|
|
2177
|
+
expected_version = sqlite_info["version"]
|
|
2178
|
+
|
|
2179
|
+
# Get actual version from the build
|
|
2180
|
+
proc = subprocess.run(
|
|
2181
|
+
[str(pyexec), "-c", "import sqlite3; print(sqlite3.sqlite_version)"],
|
|
2182
|
+
capture_output=True,
|
|
2183
|
+
check=True,
|
|
2184
|
+
)
|
|
2185
|
+
|
|
2186
|
+
actual_version = proc.stdout.decode().strip()
|
|
2187
|
+
|
|
2188
|
+
# SQLite version in JSON is like "3.50.4.0" but runtime shows "3.50.4"
|
|
2189
|
+
# So we need to handle both formats
|
|
2190
|
+
if expected_version.count(".") == 3:
|
|
2191
|
+
# Remove trailing .0 for comparison
|
|
2192
|
+
expected_version = ".".join(expected_version.split(".")[:3])
|
|
2193
|
+
|
|
2194
|
+
assert actual_version == expected_version, (
|
|
2195
|
+
f"SQLite version mismatch on {platform}: expected {expected_version}, "
|
|
2196
|
+
f"found {actual_version}"
|
|
2197
|
+
)
|
|
2198
|
+
|
|
2199
|
+
|
|
2200
|
+
def test_openssl_version(pyexec):
|
|
2201
|
+
"""
|
|
2202
|
+
Verify that the build contains the correct OpenSSL version.
|
|
2203
|
+
|
|
2204
|
+
This validates that OpenSSL was built with the version specified
|
|
2205
|
+
in python-versions.json.
|
|
2206
|
+
|
|
2207
|
+
Works on all platforms: Linux, Darwin (macOS), and Windows.
|
|
2208
|
+
"""
|
|
2209
|
+
import re
|
|
2210
|
+
|
|
2211
|
+
from relenv.build.common import get_dependency_version
|
|
2212
|
+
|
|
2213
|
+
# Map sys.platform to relenv platform names
|
|
2214
|
+
platform_map = {
|
|
2215
|
+
"linux": "linux",
|
|
2216
|
+
"darwin": "darwin",
|
|
2217
|
+
"win32": "win32",
|
|
2218
|
+
}
|
|
2219
|
+
platform = platform_map.get(sys.platform)
|
|
2220
|
+
if not platform:
|
|
2221
|
+
pytest.skip(f"Unknown platform: {sys.platform}")
|
|
2222
|
+
|
|
2223
|
+
# Get expected version from python-versions.json
|
|
2224
|
+
openssl_info = get_dependency_version("openssl", platform)
|
|
2225
|
+
if not openssl_info:
|
|
2226
|
+
pytest.skip(
|
|
2227
|
+
f"No openssl version defined in python-versions.json for {platform}"
|
|
2228
|
+
)
|
|
2229
|
+
|
|
2230
|
+
expected_version = openssl_info["version"]
|
|
2231
|
+
|
|
2232
|
+
# Get actual version from the build
|
|
2233
|
+
proc = subprocess.run(
|
|
2234
|
+
[str(pyexec), "-c", "import ssl; print(ssl.OPENSSL_VERSION)"],
|
|
2235
|
+
capture_output=True,
|
|
2236
|
+
check=True,
|
|
2237
|
+
)
|
|
2238
|
+
|
|
2239
|
+
actual_version_str = proc.stdout.decode().strip()
|
|
2240
|
+
# Format is "OpenSSL 3.5.4 30 Sep 2025"
|
|
2241
|
+
# Extract version number
|
|
2242
|
+
match = re.search(r"OpenSSL (\d+\.\d+\.\d+)", actual_version_str)
|
|
2243
|
+
if not match:
|
|
2244
|
+
pytest.fail(f"Could not parse OpenSSL version from: {actual_version_str}")
|
|
2245
|
+
|
|
2246
|
+
actual_version = match.group(1)
|
|
2247
|
+
|
|
2248
|
+
assert actual_version == expected_version, (
|
|
2249
|
+
f"OpenSSL version mismatch on {platform}: expected {expected_version}, "
|
|
2250
|
+
f"found {actual_version} (from {actual_version_str})"
|
|
2251
|
+
)
|
|
2252
|
+
|
|
2253
|
+
|
|
2254
|
+
def test_xz_lzma_functionality(pyexec):
|
|
2255
|
+
"""
|
|
2256
|
+
Verify that the lzma module works correctly.
|
|
2257
|
+
|
|
2258
|
+
This is especially important for Windows builds which use a config.h
|
|
2259
|
+
compatibility shim from XZ 5.4.7 to support newer XZ versions that
|
|
2260
|
+
removed MSBuild support.
|
|
2261
|
+
|
|
2262
|
+
If this test fails, it indicates that the config.h in
|
|
2263
|
+
relenv/_resources/xz/config.h is no longer compatible with the
|
|
2264
|
+
current XZ version being used.
|
|
2265
|
+
|
|
2266
|
+
Works on all platforms: Linux, Darwin (macOS), and Windows.
|
|
2267
|
+
"""
|
|
2268
|
+
# Test that lzma module loads and basic compression works
|
|
2269
|
+
test_code = """
|
|
2270
|
+
import lzma
|
|
2271
|
+
import sys
|
|
2272
|
+
|
|
2273
|
+
# Test basic compression/decompression
|
|
2274
|
+
test_data = b"Hello, World! " * 100
|
|
2275
|
+
compressed = lzma.compress(test_data)
|
|
2276
|
+
decompressed = lzma.decompress(compressed)
|
|
2277
|
+
|
|
2278
|
+
if test_data != decompressed:
|
|
2279
|
+
print("ERROR: Decompressed data does not match original", file=sys.stderr)
|
|
2280
|
+
sys.exit(1)
|
|
2281
|
+
|
|
2282
|
+
# Verify compression actually happened
|
|
2283
|
+
if len(compressed) >= len(test_data):
|
|
2284
|
+
print("ERROR: Compression did not reduce size", file=sys.stderr)
|
|
2285
|
+
sys.exit(2)
|
|
2286
|
+
|
|
2287
|
+
# Test different formats (skip FORMAT_RAW as it requires explicit filters)
|
|
2288
|
+
for fmt in [lzma.FORMAT_XZ, lzma.FORMAT_ALONE]:
|
|
2289
|
+
try:
|
|
2290
|
+
data = lzma.compress(test_data, format=fmt)
|
|
2291
|
+
result = lzma.decompress(data)
|
|
2292
|
+
if result != test_data:
|
|
2293
|
+
print(f"ERROR: Format {fmt} failed round-trip", file=sys.stderr)
|
|
2294
|
+
sys.exit(3)
|
|
2295
|
+
except Exception as e:
|
|
2296
|
+
print(f"ERROR: Format {fmt} raised exception: {e}", file=sys.stderr)
|
|
2297
|
+
sys.exit(4)
|
|
2298
|
+
|
|
2299
|
+
# Test streaming compression/decompression
|
|
2300
|
+
import io
|
|
2301
|
+
output = io.BytesIO()
|
|
2302
|
+
with lzma.LZMAFile(output, "w") as f:
|
|
2303
|
+
f.write(test_data)
|
|
2304
|
+
|
|
2305
|
+
compressed_stream = output.getvalue()
|
|
2306
|
+
input_stream = io.BytesIO(compressed_stream)
|
|
2307
|
+
with lzma.LZMAFile(input_stream, "r") as f:
|
|
2308
|
+
decompressed_stream = f.read()
|
|
2309
|
+
|
|
2310
|
+
if decompressed_stream != test_data:
|
|
2311
|
+
print("ERROR: Streaming compression/decompression failed", file=sys.stderr)
|
|
2312
|
+
sys.exit(5)
|
|
2313
|
+
|
|
2314
|
+
print("OK")
|
|
2315
|
+
"""
|
|
2316
|
+
|
|
2317
|
+
proc = subprocess.run(
|
|
2318
|
+
[str(pyexec), "-c", test_code],
|
|
2319
|
+
capture_output=True,
|
|
2320
|
+
check=False,
|
|
2321
|
+
)
|
|
2322
|
+
|
|
2323
|
+
assert proc.returncode == 0, (
|
|
2324
|
+
f"LZMA functionality test failed (exit code {proc.returncode}): "
|
|
2325
|
+
f"{proc.stderr.decode()}"
|
|
2326
|
+
)
|
|
2327
|
+
assert proc.stdout.decode().strip() == "OK"
|