android build: patch hostpython3 to not install setuptools
What changed, and why it matters
This change adjusts how the Electrum Android app is built so that the build process installs only one specific, checked version of a helper library called setuptools, instead of accidentally ending up with two copies. Having two copies could make builds unpredictable from one run to the next. The patch itself is a build-hygiene fix aimed at reproducibility, not a direct fix for an active security flaw.
Treat as a normal build-maintenance patch. Reviewers can verify the patch matches the upstream CPython 3.12 change (gh-95299 / PR 101039) and that the pinned setuptools dependency remains hash-pinned. No urgent security response is indicated by the commit itself.
Security signals we found
Build reproducibility / deterministic build hardening
Supply-chain adjacent: prevents co-installation of unpinned and pinned versions of setuptools during Android build
No runtime code change to Electrum wallet logic
No CVE, advisory, or exploit referenced in commit
Evidence from the diff
The commit adds a patch to Electrum’s Android build recipe for hostpython3 (CPython 3.11). The patch removes setuptools from the ensurepip bootstrap package list, backporting CPython 3.12’s gh-95299 behavior. The build already installs a hash-pinned setuptools separately, but pip’s –target mode cannot uninstall old dist-info directories, so the unpinned bundled setuptools (79.0.1) could remain alongside the pinned version. Which dist-info was used then depended on os.listdir() ordering, breaking deterministic/reproducible builds. The patch is a minimal workaround; the commit notes the same issue could affect other packages installed via –target.
Changed components
contrib/android/p4a_recipes/hostpython3/__init__.pycontrib/android/p4a_recipes/hostpython3/patches/cpython-311-ensurepip-no-setuptools.patchElectrum Android build pipeline (hostpython3 / python-for-android)Inspect captured patch +39 / −0
diff --git a/contrib/android/p4a_recipes/hostpython3/__init__.py b/contrib/android/p4a_recipes/hostpython3/__init__.py
index 2bce6e8..45618c6 100644
--- a/contrib/android/p4a_recipes/hostpython3/__init__.py
+++ b/contrib/android/p4a_recipes/hostpython3/__init__.py
@@ -8,6 +8,7 @@ util = load_source('util', os.path.join(os.path.dirname(os.path.dirname(__file__
assert HostPython3Recipe.depends == []
assert HostPython3Recipe.python_depends == []
+assert HostPython3Recipe.patches == []
class HostPython3RecipePinned(util.InheritedRecipeMixin, HostPython3Recipe):
@@ -18,6 +19,11 @@ class HostPython3RecipePinned(util.InheritedRecipeMixin, HostPython3Recipe):
# use official releases from python.org that have sigs, instead of auto-generated archives from github
url = 'https://www.python.org/ftp/python/{version}/Python-{version}.tgz'
+ # TODO: remove patch once CPython >= 3.12 is used (no more bundled setuptools)
+ patches = [
+ os.path.join(os.path.dirname(__file__), "patches", "cpython-311-ensurepip-no-setuptools.patch"),
+ ]
+
# this property overrides the default hostpython dependencies for PyProjectRecipe recipies
pyproject_base_dependencies = [
HashPinnedDependency(package="build==1.4.0",
diff --git a/contrib/android/p4a_recipes/hostpython3/patches/cpython-311-ensurepip-no-setuptools.patch b/contrib/android/p4a_recipes/hostpython3/patches/cpython-311-ensurepip-no-setuptools.patch
new file mode 100644
index 0000000..8402bc9
--- /dev/null
+++ b/contrib/android/p4a_recipes/hostpython3/patches/cpython-311-ensurepip-no-setuptools.patch
@@ -0,0 +1,33 @@
+Backport of Python 3.12's removal of setuptools from ensurepip (gh-95299,
+https://github.com/python/cpython/pull/101039) onto CPython 3.11.
+
+ensurepip is only used to bootstrap pip into hostpython. Without this patch
+it would also install its bundled, unpinned setuptools, which would later
+coexist with the hash-pinned setuptools we install ourselves: pip's
+"--target" mode cannot uninstall, so the stale setuptools-79.0.1.dist-info
+would survive next to the pinned version and which distribution gets picked
+up during the build depends on os.listdir() order, breaking reproducibility.
+
+The now-unreferenced setuptools wheel in Lib/ensurepip/_bundled/ is left in
+the source tree ("patch" cannot delete binary files); it is never installed.
+
+The deleted _SETUPTOOLS_VERSION line changes between CPython 3.11.x releases,
+so this patch is coupled to the CPython version pinned in our hostpython3
+recipe and must be refreshed together with it. Remove the patch when
+hostpython3 is >= 3.12.
+
+--- a/Lib/ensurepip/__init__.py
++++ b/Lib/ensurepip/__init__.py
+@@ -9,11 +9,9 @@
+
+
+ __all__ = ["version", "bootstrap"]
+-_PACKAGE_NAMES = ('setuptools', 'pip')
+-_SETUPTOOLS_VERSION = "79.0.1"
++_PACKAGE_NAMES = ('pip',)
+ _PIP_VERSION = "24.0"
+ _PROJECTS = [
+- ("setuptools", _SETUPTOOLS_VERSION, "py3"),
+ ("pip", _PIP_VERSION, "py3"),
+ ]
+
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.