-
Notifications
You must be signed in to change notification settings - Fork 2k
Add support for prebuilt wheels #3280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
3c42783
1536561
60e7a5a
9239e37
4ecc0ad
bb189c7
8c9c350
3f78877
6813d66
e5ad1c2
6262c7c
b35aa7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -923,8 +923,7 @@ def real_hostpython_location(self): | |
| if host_name == 'hostpython3': | ||
| return self._host_recipe.python_exe | ||
| else: | ||
| python_recipe = self.ctx.python_recipe | ||
| return 'python{}'.format(python_recipe.version) | ||
| return 'python{}'.format(self.ctx.python_recipe.version) | ||
|
|
||
| @property | ||
| def hostpython_location(self): | ||
|
|
@@ -1248,6 +1247,59 @@ class PyProjectRecipe(PythonRecipe): | |
| extra_build_args = [] | ||
| call_hostpython_via_targetpython = False | ||
|
|
||
| def get_pip_name(self): | ||
| name_str = self.name | ||
| if self.name not in self.ctx.use_prebuilt_version_for and self.version is not None: | ||
| # Like: v2.3.0 -> 2.3.0 | ||
| cleaned_version = self.version.replace("v", "") | ||
| name_str += f"=={cleaned_version}" | ||
| return name_str | ||
|
|
||
| def get_pip_install_args(self, arch): | ||
| python_recipe = Recipe.get_recipe("python3", self.ctx) | ||
| opts = [ | ||
| "install", | ||
| self.get_pip_name(), | ||
| "--ignore-installed", | ||
| "--disable-pip-version-check", | ||
| "--python-version", | ||
| python_recipe.version, | ||
| "--only-binary=:all:", | ||
| "--no-deps", | ||
| ] | ||
| # add platform tags | ||
| tags = PyProjectRecipe.get_wheel_platform_tags(arch.arch, self.ctx) | ||
| for tag in tags: | ||
| opts.append(f"--platform={tag}") | ||
|
|
||
| # add extra index urls | ||
| for index in self.ctx.extra_index_urls: | ||
| opts.extend(["--extra-index-url", index]) | ||
|
|
||
| return opts | ||
|
|
||
| def lookup_prebuilt(self, arch): | ||
| pip_options = self.get_pip_install_args(arch) | ||
| # do not install | ||
| pip_options.extend(["--dry-run", "-q"]) | ||
| pip_env = self.get_hostrecipe_env() | ||
| try: | ||
| shprint(self._host_recipe.pip, *pip_options, _env=pip_env) | ||
| except Exception: | ||
| return False | ||
| return True | ||
|
|
||
| def check_prebuilt(self, arch, msg=""): | ||
| if self.ctx.skip_prebuilt: | ||
| return False | ||
|
|
||
| if self.lookup_prebuilt(arch): | ||
| if msg != "": | ||
| info(f"Prebuilt pip wheel found, {msg}") | ||
| return True | ||
|
|
||
| return False | ||
|
|
||
| def get_recipe_env(self, arch, **kwargs): | ||
| # Custom hostpython | ||
| self.ctx.python_recipe.python_exe = join( | ||
|
|
@@ -1259,24 +1311,42 @@ def get_recipe_env(self, arch, **kwargs): | |
|
|
||
| with open(build_opts, "w") as file: | ||
| file.write("[bdist_wheel]\nplat_name={}".format( | ||
| self.get_wheel_platform_tag(arch) | ||
| self.get_wheel_platform_tag(arch.arch) | ||
| )) | ||
| file.close() | ||
|
|
||
| env["DIST_EXTRA_CONFIG"] = build_opts | ||
| return env | ||
|
|
||
| def get_wheel_platform_tag(self, arch): | ||
| @staticmethod | ||
| def get_wheel_platform_tags(arch, ctx): | ||
| # https://peps.python.org/pep-0738/#packaging | ||
| # official python only supports 64 bit: | ||
| # android_21_arm64_v8a | ||
| # android_21_x86_64 | ||
| return f"android_{self.ctx.ndk_api}_" + { | ||
| "arm64-v8a": "arm64_v8a", | ||
| "x86_64": "x86_64", | ||
| "armeabi-v7a": "arm", | ||
| "x86": "i686", | ||
| }[arch.arch] | ||
| _suffix = { | ||
| "arm64-v8a": ["arm64_v8a", "aarch64"], | ||
| "x86_64": ["x86_64"], | ||
| "armeabi-v7a": ["arm"], | ||
| "x86": ["i686"], | ||
| }[arch] | ||
| return [f"android_{ctx.ndk_api}_" + _ for _ in _suffix] | ||
|
|
||
| def get_wheel_platform_tag(self, arch): | ||
| return PyProjectRecipe.get_wheel_platform_tags(arch, self.ctx)[0] | ||
|
|
||
| def install_prebuilt_wheel(self, arch): | ||
| info("Installing prebuilt built wheel") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe drop the "built" and go for "Installing prebuilt wheel" |
||
| destination = self.ctx.get_python_install_dir(arch.arch) | ||
| pip_options = self.get_pip_install_args(arch) | ||
| pip_options.extend(["--target", destination]) | ||
| pip_options.append("--upgrade") | ||
| pip_env = self.get_hostrecipe_env() | ||
| try: | ||
| shprint(self._host_recipe.pip, *pip_options, _env=pip_env) | ||
| except Exception: | ||
| return False | ||
| return True | ||
|
|
||
| def install_wheel(self, arch, built_wheels): | ||
| with patch_wheel_setuptools_logging(): | ||
|
|
@@ -1287,16 +1357,18 @@ def install_wheel(self, arch, built_wheels): | |
| # Fix wheel platform tag | ||
| wheel_tag = wheel_tags( | ||
| _wheel, | ||
| platform_tags=self.get_wheel_platform_tag(arch), | ||
| platform_tags=self.get_wheel_platform_tag(arch.arch), | ||
| remove=True, | ||
| ) | ||
| selected_wheel = join(built_wheel_dir, wheel_tag) | ||
|
|
||
| _dev_wheel_dir = environ.get("P4A_WHEEL_DIR", False) | ||
T-Dynamos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if _dev_wheel_dir: | ||
| ensure_dir(_dev_wheel_dir) | ||
| shprint(sh.cp, selected_wheel, _dev_wheel_dir) | ||
|
|
||
| if exists(self.ctx.save_wheel_dir): | ||
| shprint(sh.cp, selected_wheel, self.ctx.save_wheel_dir) | ||
|
|
||
| info(f"Installing built wheel: {wheel_tag}") | ||
| destination = self.ctx.get_python_install_dir(arch.arch) | ||
| with WheelFile(selected_wheel) as wf: | ||
|
|
@@ -1305,6 +1377,11 @@ def install_wheel(self, arch, built_wheels): | |
| wf.close() | ||
|
|
||
| def build_arch(self, arch): | ||
| if self.check_prebuilt(arch, "skipping build_arch"): | ||
| result = self.install_prebuilt_wheel(arch) | ||
| if result: | ||
| return | ||
| warning("Failed to install prebuilt wheel, falling back to build_arch") | ||
|
|
||
| build_dir = self.get_build_dir(arch.arch) | ||
| if not (isfile(join(build_dir, "pyproject.toml")) or isfile(join(build_dir, "setup.py"))): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,7 @@ | |
| from os.path import join | ||
|
|
||
| from packaging.version import Version | ||
| from pythonforandroid.logger import shprint | ||
| from pythonforandroid.logger import shprint, error | ||
| from pythonforandroid.recipe import Recipe | ||
| from pythonforandroid.util import ( | ||
| BuildInterruptingException, | ||
|
|
@@ -48,6 +48,16 @@ class HostPython3Recipe(Recipe): | |
|
|
||
| patches = ["fix_ensurepip.patch"] | ||
|
|
||
| # apply version guard | ||
| def download(self): | ||
| python_recipe = Recipe.get_recipe("python3", self.ctx) | ||
| if python_recipe.version != self.version: | ||
| error( | ||
| f"python3 should have same version as hostpython3, {python_recipe.version} != {self.version}" | ||
| ) | ||
| exit(1) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we want to |
||
| super().download() | ||
|
|
||
| @property | ||
| def _exe_name(self): | ||
| ''' | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -188,6 +188,7 @@ class NoAbbrevParser(argparse.ArgumentParser): | |
| This subclass alternative is follows the suggestion at | ||
| https://bugs.python.org/issue14910. | ||
| """ | ||
|
|
||
| def _get_option_tuples(self, option_string): | ||
| return [] | ||
|
|
||
|
|
@@ -267,6 +268,44 @@ def __init__(self): | |
| '--arch', help='The archs to build for.', | ||
| action='append', default=[]) | ||
|
|
||
| generic_parser.add_argument( | ||
| '--extra-index-url', | ||
| help=( | ||
| 'Extra package indexes to look for prebuilt Android wheels. ' | ||
| 'Can be used multiple times.' | ||
| ), | ||
| action='append', | ||
| default=[], | ||
| dest="extra_index_urls", | ||
| ) | ||
|
|
||
| generic_parser.add_argument( | ||
| '--skip-prebuilt', | ||
| help='Always build from source; do not use prebuilt wheels.', | ||
| action='store_true', | ||
| default=False, | ||
| dest="skip_prebuilt", | ||
| ) | ||
|
|
||
| generic_parser.add_argument( | ||
| '--use-prebuilt-version-for', | ||
| help=( | ||
| 'For these packages, ignore pinned versions and use the latest ' | ||
| 'prebuilt version from the extra index if available.' | ||
| 'Only applies to packages with a recipe.' | ||
|
Comment on lines
+294
to
+295
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor add a trailing slash after "available. " to prevent having "available.Only" |
||
| ), | ||
| action='append', | ||
| default=[], | ||
| dest="use_prebuilt_version_for", | ||
| ) | ||
|
|
||
| generic_parser.add_argument( | ||
| '--save-wheel-dir', | ||
| dest='save_wheel_dir', | ||
| default='', | ||
| help='Directory to store wheels built by PyProjectRecipe.', | ||
| ) | ||
|
|
||
| # Options for specifying the Distribution | ||
| generic_parser.add_argument( | ||
| '--dist-name', '--dist_name', | ||
|
|
@@ -672,6 +711,11 @@ def add_parser(subparsers, *args, **kwargs): | |
| self.ctx.activity_class_name = args.activity_class_name | ||
| self.ctx.service_class_name = args.service_class_name | ||
|
|
||
| self.ctx.extra_index_urls = args.extra_index_urls | ||
| self.ctx.skip_prebuilt = args.skip_prebuilt | ||
| self.ctx.use_prebuilt_version_for = args.use_prebuilt_version_for | ||
| self.ctx.save_wheel_dir = args.save_wheel_dir | ||
|
|
||
| # Each subparser corresponds to a method | ||
| command = args.subparser_name.replace('-', '_') | ||
| getattr(self, command)(args) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we have
2.3.0dev1, refs PEP 440 & https://packaging.python.org/en/latest/specifications/version-specifiers/#developmental-releasesMaybe we should use that instead: