Skip to content

Commit 7afd7da

Browse files
committed
[WIP] toolchain: auto resolve deps
1 parent 6494ac1 commit 7afd7da

File tree

3 files changed

+113
-11
lines changed

3 files changed

+113
-11
lines changed

pythonforandroid/recipe.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,7 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True):
979979
env['LANG'] = "en_GB.UTF-8"
980980

981981
# Binaries made by packages installed by pip
982-
self.patch_shebangs(self._host_recipe.local_bin, self.real_hostpython_location)
982+
self.patch_shebangs(self._host_recipe.local_bin, self._host_recipe.python_exe)
983983
env["PATH"] = self._host_recipe.local_bin + ":" + self._host_recipe.site_bin + ":" + env["PATH"]
984984

985985
host_env = self.get_hostrecipe_env(arch)
@@ -1022,10 +1022,9 @@ def install_python_package(self, arch, name=None, env=None, is_dir=True):
10221022

10231023
info('Installing {} into site-packages'.format(self.name))
10241024

1025-
hostpython = sh.Command(self.hostpython_location)
10261025
hpenv = env.copy()
10271026
with current_directory(self.get_build_dir(arch.arch)):
1028-
shprint(hostpython, '-m', 'pip', 'install', '.',
1027+
shprint(self._host_recipe.pip, 'install', '.',
10291028
'--compile', '--target',
10301029
self.ctx.get_python_install_dir(arch.arch),
10311030
_env=hpenv, *self.setup_extra_args
@@ -1045,8 +1044,7 @@ def hostpython_site_dir(self):
10451044

10461045
def install_hostpython_package(self, arch):
10471046
env = self.get_hostrecipe_env(arch)
1048-
real_hostpython = sh.Command(self.real_hostpython_location)
1049-
shprint(real_hostpython, '-m', 'pip', 'install', '.',
1047+
shprint(self._host_recipe.pip, 'install', '.',
10501048
'--compile',
10511049
'--root={}'.format(self._host_recipe.site_root),
10521050
_env=env, *self.setup_extra_args)
@@ -1075,8 +1073,7 @@ def install_hostpython_prerequisites(self, packages=None, force_upgrade=True):
10751073
pip_options.append("--upgrade")
10761074
# Use system's pip
10771075
pip_env = self.get_hostrecipe_env()
1078-
pip_env["HOME"] = "/tmp"
1079-
shprint(sh.Command(self.real_hostpython_location), "-m", "pip", *pip_options, _env=pip_env)
1076+
shprint(self._host_recipe.pip, *pip_options, _env=pip_env)
10801077

10811078
def restore_hostpython_prerequisites(self, packages):
10821079
_packages = []
@@ -1270,10 +1267,14 @@ def get_recipe_env(self, arch, **kwargs):
12701267
return env
12711268

12721269
def get_wheel_platform_tag(self, arch):
1270+
# https://peps.python.org/pep-0738/#packaging
1271+
# official python only supports 64 bit:
1272+
# android_21_arm64_v8a
1273+
# android_21_x86_64
12731274
return f"android_{self.ctx.ndk_api}_" + {
1274-
"armeabi-v7a": "arm",
1275-
"arm64-v8a": "aarch64",
1275+
"arm64-v8a": "arm64_v8a",
12761276
"x86_64": "x86_64",
1277+
"armeabi-v7a": "arm",
12771278
"x86": "i686",
12781279
}[arch.arch]
12791280

pythonforandroid/recipes/hostpython3/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ def site_dir(self):
113113
f"usr/local/lib/python{p_version.major}.{p_version.minor}/site-packages/"
114114
)
115115

116+
@property
117+
def _pip(self):
118+
return join(self.local_bin, "pip3")
119+
120+
@property
121+
def pip(self):
122+
return sh.Command(self._pip)
123+
116124
def build_arch(self, arch):
117125
env = self.get_recipe_env(arch)
118126

@@ -160,10 +168,12 @@ def build_arch(self, arch):
160168

161169
ensure_dir(self.site_root)
162170
self.ctx.hostpython = self.python_exe
171+
163172
if build_configured:
173+
164174
shprint(
165175
sh.Command(self.python_exe), "-m", "ensurepip", "--root", self.site_root, "-U",
166-
_env={"HOME": "/tmp"}
176+
_env={"PATH": self.local_bin}
167177
)
168178

169179

pythonforandroid/toolchain.py

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from functools import wraps
1212
import glob
1313
import logging
14+
import json
1415
import os
1516
from os import environ
1617
from os.path import (join, dirname, realpath, exists, expanduser, basename)
@@ -48,6 +49,9 @@
4849
max_build_tool_version,
4950
)
5051

52+
from packaging.utils import parse_wheel_filename
53+
from packaging.requirements import Requirement
54+
5155
user_dir = dirname(realpath(os.path.curdir))
5256
toolchain_dir = dirname(__file__)
5357
sys.path.insert(0, join(toolchain_dir, "tools", "external"))
@@ -121,6 +125,92 @@ def dist_from_args(ctx, args):
121125
allow_replace_dist=args.allow_replace_dist)
122126

123127

128+
def is_wheel_platform_independent(whl_name):
129+
name, version, build, tags = parse_wheel_filename(whl_name)
130+
return all(tag.platform == "any" for tag in tags)
131+
132+
133+
def process_python_modules(build_order, modules):
134+
"""
135+
idk what this does
136+
"""
137+
modules = list(modules)
138+
build_order = list(build_order)
139+
_requirement_names = []
140+
processed_modules = []
141+
142+
for module in modules+build_order:
143+
try:
144+
_requirement_names.append(Requirement(module).name)
145+
except Exception:
146+
processed_modules.append(module)
147+
if module in modules:
148+
modules.remove(module)
149+
150+
if len(processed_modules) > 0:
151+
warning(f'Ignored by module resolver : {processed_modules}')
152+
153+
processed_modules.extend(modules)
154+
155+
# need a tempfile for pip report output
156+
path = os.path.realpath(".pip_report.json")
157+
158+
if not exists(path):
159+
shprint(
160+
sh.pip, 'install', *modules,
161+
'--dry-run', '--break-system-packages', '--ignore-installed',
162+
'--report', path, '-q'
163+
)
164+
165+
with open(path, "r") as f:
166+
try:
167+
report = json.load(f)
168+
except Exception:
169+
os.remove(path)
170+
return process_python_modules(modules)
171+
172+
info('Extra resolved pure python dependencies :')
173+
174+
ignored_str = " (ignored)"
175+
# did we find any non pure python package?
176+
any_not_pure_python = False
177+
178+
info(" ")
179+
for module in report["install"]:
180+
181+
mname = module["metadata"]["name"]
182+
mver = module["metadata"]["version"]
183+
filename = basename(module["download_info"]["url"])
184+
pure_python = True
185+
186+
if (filename.endswith(".whl") and not is_wheel_platform_independent(filename)):
187+
any_not_pure_python = True
188+
pure_python = False
189+
190+
# does this module matches any recipe name?
191+
if mname.lower() in _requirement_names:
192+
continue
193+
194+
color = Out_Fore.GREEN if pure_python else Out_Fore.RED
195+
ignored = "" if pure_python else ignored_str
196+
197+
info(
198+
f" {color}{mname}{Out_Fore.WHITE} : "
199+
f"{Out_Style.BRIGHT}{mver}{Out_Style.RESET_ALL}"
200+
f"{ignored}"
201+
)
202+
203+
if pure_python:
204+
processed_modules.append(f"{mname}=={mver}")
205+
info(" ")
206+
207+
if any_not_pure_python:
208+
warning("Some packages were ignored because they are not pure Python.")
209+
warning("To install the ignored packages, explicitly list them in your requirements file.")
210+
211+
return processed_modules
212+
213+
124214
def build_dist_from_args(ctx, dist, args):
125215
"""Parses out any bootstrap related arguments, and uses them to build
126216
a dist."""
@@ -134,8 +224,9 @@ def build_dist_from_args(ctx, dist, args):
134224
blacklist=blacklist
135225
))
136226
assert set(build_order).intersection(set(python_modules)) == set()
227+
137228
ctx.recipe_build_order = build_order
138-
ctx.python_modules = python_modules
229+
ctx.python_modules = process_python_modules(build_order, python_modules)
139230

140231
info('The selected bootstrap is {}'.format(bs.name))
141232
info_main('# Creating dist with {} bootstrap'.format(bs.name))

0 commit comments

Comments
 (0)