Skip to content

Commit 5dbc706

Browse files
authored
Support statically linked CPython. (#2472)
This pulls in a new vendored version of Pip with the recent fix in pypa/pip#12716 applied in pex-tool/pip#13. As a result, Pex can run using vendored Pip under statically linked musl libc CPython interpreters. This opens the door to bootstrapping newer unpatched Pip's that also have this same fix (versions 24.2 and later; see: #2471). Fixes #2017
1 parent aaa4e43 commit 5dbc706

File tree

4 files changed

+99
-5
lines changed

4 files changed

+99
-5
lines changed

pex/vendor/__init__.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -207,19 +207,24 @@ def iter_vendor_specs(filter_requires_python=None):
207207
# We shell out to pip at buildtime to resolve and install dependencies.
208208
# N.B.: We're currently using a patched version of Pip 20.3.4 housed at
209209
# https://github.com/pex-tool/pip/tree/pex/patches/generation-2.
210-
# It has 2 patches:
210+
# It has 3 patches:
211211
# 1.) https://github.com/pex-tool/pip/commit/06f462537c981116c763c1ba40cf40e9dd461bcf
212212
# The patch works around a bug in `pip download --constraint...` tracked at
213213
# https://github.com/pypa/pip/issues/9283 and fixed by https://github.com/pypa/pip/pull/9301
214214
# there and https://github.com/pex-tool/pip/pull/8 in our fork.
215215
# 2.) https://github.com/pex-tool/pip/commit/386a54f097ece66775d0c7f34fd29bb596c6b0be
216216
# This is a cherry-pick of
217-
# https://github.com/pex-tool/pip/commit/00fb5a0b224cde08e3e5ca034247baadfb646468
217+
# https://github.com/pypa/pip/commit/00fb5a0b224cde08e3e5ca034247baadfb646468
218218
# (https://github.com/pypa/pip/pull/9533) from upstream that upgrades Pip's vendored
219219
# packaging to 20.9 to pick up support for mac universal2 wheels.
220+
# 3.) https://github.com/pex-tool/pip/commit/00827ec9f4275a7786425cf006466c56f4cbd862
221+
# This is a cherry-pick of
222+
# https://github.com/pypa/pip/commit/601bcf82eccfbc15c1ff6cc735aafb2c9dab81a5
223+
# (https://github.com/pypa/pip/pull/12716) from upstream that fixes glibc version probing on
224+
# musl libc systems.
220225
yield VendorSpec.git(
221226
repo="https://github.com/pex-tool/pip",
222-
commit="386a54f097ece66775d0c7f34fd29bb596c6b0be",
227+
commit="00827ec9f4275a7786425cf006466c56f4cbd862",
223228
project_name="pip",
224229
rewrite=False,
225230
)

pex/vendor/_vendored/pip/.layout.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"fingerprint": "120267325b80f5c4b4adac019eb6617ab3319395c043d2871eedf70dd6ae2954", "record_relpath": "pip-20.3.4.dist-info/RECORD", "root_is_purelib": true, "stash_dir": ".prefix"}
1+
{"fingerprint": "23b678435eebb1c7423541b5663bdc518833a4b75a3a3a2192466a37cd7b1861", "record_relpath": "pip-20.3.4.dist-info/RECORD", "root_is_purelib": true, "stash_dir": ".prefix"}

pex/vendor/_vendored/pip/pip/_internal/utils/glibc.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,20 @@ def glibc_version_string_ctypes():
4949
# manpage says, "If filename is NULL, then the returned handle is for the
5050
# main program". This way we can let the linker do the work to figure out
5151
# which libc our process is actually using.
52-
process_namespace = ctypes.CDLL(None)
52+
#
53+
# We must also handle the special case where the executable is not a
54+
# dynamically linked executable. This can occur when using musl libc,
55+
# for example. In this situation, dlopen() will error, leading to an
56+
# OSError. Interestingly, at least in the case of musl, there is no
57+
# errno set on the OSError. The single string argument used to construct
58+
# OSError comes from libc itself and is therefore not portable to
59+
# hard code here. In any case, failure to call dlopen() means we
60+
# can't proceed, so we bail on our attempt.
61+
try:
62+
process_namespace = ctypes.CDLL(None)
63+
except OSError:
64+
return None
65+
5366
try:
5467
gnu_get_libc_version = process_namespace.gnu_get_libc_version
5568
except AttributeError:

tests/integration/test_issue_2017.py

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Copyright 2024 Pex project contributors.
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
4+
from __future__ import absolute_import
5+
6+
import os.path
7+
import shutil
8+
import subprocess
9+
import tarfile
10+
from textwrap import dedent
11+
12+
import pytest
13+
14+
from pex.common import is_exe, safe_open
15+
from pex.compatibility import urlparse
16+
from pex.fetcher import URLFetcher
17+
from pex.pep_440 import Version
18+
from pex.pip.version import PipVersion
19+
from pex.typing import TYPE_CHECKING
20+
from testing import IS_LINUX, run_pex_command
21+
22+
if TYPE_CHECKING:
23+
from typing import Any
24+
25+
26+
# TODO(John Sirois): Include a test of >= Pip 24.2 when Pex adds support for it.
27+
# See: https://github.com/pex-tool/pex/issues/2471
28+
@pytest.mark.skipif(
29+
PipVersion.DEFAULT > PipVersion.VENDORED and PipVersion.DEFAULT.version < Version("24.2"),
30+
reason=(
31+
"Although Pex's vendored Pip is patched to handle statically linked musl libc CPython, no "
32+
"version of Pip Pex supports handles these Pythons until Pip 24.2"
33+
),
34+
)
35+
@pytest.mark.skipif(
36+
not IS_LINUX,
37+
reason="This test tests statically linked musl libc CPython which is only available for Linux.",
38+
)
39+
def test_statically_linked_musl_libc_cpython_support(tmpdir):
40+
# type: (Any) -> None
41+
42+
pbs_distribution_url = (
43+
"https://github.com/indygreg/python-build-standalone/releases/download/20221220/"
44+
"cpython-3.10.9+20221220-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
45+
)
46+
pbs_distribution = os.path.join(
47+
str(tmpdir),
48+
os.path.basename(urlparse.urlparse(pbs_distribution_url).path),
49+
)
50+
with URLFetcher().get_body_stream(pbs_distribution_url) as read_fp, open(
51+
pbs_distribution, "wb"
52+
) as write_fp:
53+
shutil.copyfileobj(read_fp, write_fp)
54+
with tarfile.open(pbs_distribution) as tf:
55+
tf.extractall(str(tmpdir))
56+
statically_linked_musl_libc_cpython = os.path.join(str(tmpdir), "python", "bin", "python3")
57+
assert is_exe(statically_linked_musl_libc_cpython)
58+
59+
pex = os.path.join(str(tmpdir), "pex")
60+
run_pex_command(
61+
args=["fortune==1.1.1", "-c", "fortune", "-o", pex],
62+
python=statically_linked_musl_libc_cpython,
63+
).assert_success()
64+
65+
fortune_db = os.path.join(str(tmpdir), "fortunes")
66+
with safe_open(fortune_db, "w") as fp:
67+
fp.write(
68+
dedent(
69+
"""\
70+
A day for firm decisions!!!!! Or is it?
71+
%
72+
"""
73+
)
74+
)
75+
output = subprocess.check_output(args=[pex, fortune_db])
76+
assert b"A day for firm decisions!!!!! Or is it?\n" == output, output.decode("utf-8")

0 commit comments

Comments
 (0)