lbry-android-sdk/p4a/pythonforandroid/recipes/hostpython3/__init__.py
2022-12-02 15:15:34 -05:00

144 lines
5 KiB
Python

import sh
import os
from multiprocessing import cpu_count
from pathlib import Path
from os.path import join
from pythonforandroid.logger import shprint
from pythonforandroid.recipe import Recipe
from pythonforandroid.util import (
BuildInterruptingException,
current_directory,
ensure_dir,
)
from pythonforandroid.prerequisites import OpenSSLPrerequisite
HOSTPYTHON_VERSION_UNSET_MESSAGE = (
'The hostpython recipe must have set version'
)
SETUP_DIST_NOT_FIND_MESSAGE = (
'Could not find Setup.dist or Setup in Python build'
)
class HostPython3Recipe(Recipe):
'''
The hostpython3's recipe.
.. versionchanged:: 2019.10.06.post0
Refactored from deleted class ``python.HostPythonRecipe`` into here.
.. versionchanged:: 0.6.0
Refactored into the new class
:class:`~pythonforandroid.python.HostPythonRecipe`
'''
version = '3.9.9'
name = 'hostpython3'
build_subdir = 'native-build'
'''Specify the sub build directory for the hostpython3 recipe. Defaults
to ``native-build``.'''
url = 'https://www.python.org/ftp/python/{version}/Python-{version}.tgz'
'''The default url to download our host python recipe. This url will
change depending on the python version set in attribute :attr:`version`.'''
patches = ['patches/pyconfig_detection.patch']
@property
def _exe_name(self):
'''
Returns the name of the python executable depending on the version.
'''
if not self.version:
raise BuildInterruptingException(HOSTPYTHON_VERSION_UNSET_MESSAGE)
return f'python{self.version.split(".")[0]}'
@property
def python_exe(self):
'''Returns the full path of the hostpython executable.'''
return join(self.get_path_to_python(), self._exe_name)
def get_recipe_env(self, arch=None):
env = os.environ.copy()
openssl_prereq = OpenSSLPrerequisite()
if env.get("PKG_CONFIG_PATH", ""):
env["PKG_CONFIG_PATH"] = os.pathsep.join(
[openssl_prereq.pkg_config_location, env["PKG_CONFIG_PATH"]]
)
else:
env["PKG_CONFIG_PATH"] = openssl_prereq.pkg_config_location
return env
def should_build(self, arch):
if Path(self.python_exe).exists():
# no need to build, but we must set hostpython for our Context
self.ctx.hostpython = self.python_exe
return False
return True
def get_build_container_dir(self, arch=None):
choices = self.check_recipe_choices()
dir_name = '-'.join([self.name] + choices)
return join(self.ctx.build_dir, 'other_builds', dir_name, 'desktop')
def get_build_dir(self, arch=None):
'''
.. note:: Unlike other recipes, the hostpython build dir doesn't
depend on the target arch
'''
return join(self.get_build_container_dir(), self.name)
def get_path_to_python(self):
return join(self.get_build_dir(), self.build_subdir)
def build_arch(self, arch):
env = self.get_recipe_env(arch)
recipe_build_dir = self.get_build_dir(arch.arch)
# Create a subdirectory to actually perform the build
build_dir = join(recipe_build_dir, self.build_subdir)
ensure_dir(build_dir)
# Configure the build
with current_directory(build_dir):
if not Path('config.status').exists():
shprint(sh.Command(join(recipe_build_dir, 'configure')), _env=env)
with current_directory(recipe_build_dir):
# Create the Setup file. This copying from Setup.dist is
# the normal and expected procedure before Python 3.8, but
# after this the file with default options is already named "Setup"
setup_dist_location = join('Modules', 'Setup.dist')
if Path(setup_dist_location).exists():
shprint(sh.cp, setup_dist_location,
join(build_dir, 'Modules', 'Setup'))
else:
# Check the expected file does exist
setup_location = join('Modules', 'Setup')
if not Path(setup_location).exists():
raise BuildInterruptingException(
SETUP_DIST_NOT_FIND_MESSAGE
)
shprint(sh.make, '-j', str(cpu_count()), '-C', build_dir, _env=env)
# make a copy of the python executable giving it the name we want,
# because we got different python's executable names depending on
# the fs being case-insensitive (Mac OS X, Cygwin...) or
# case-sensitive (linux)...so this way we will have an unique name
# for our hostpython, regarding the used fs
for exe_name in ['python.exe', 'python']:
exe = join(self.get_path_to_python(), exe_name)
if Path(exe).is_file():
shprint(sh.cp, exe, self.python_exe)
break
self.ctx.hostpython = self.python_exe
recipe = HostPython3Recipe()