2022-12-02 21:15:34 +01:00
|
|
|
import sh
|
|
|
|
import os
|
2017-08-13 03:24:00 +02:00
|
|
|
|
2022-12-02 21:15:34 +01:00
|
|
|
from multiprocessing import cpu_count
|
|
|
|
from pathlib import Path
|
|
|
|
from os.path import join
|
2017-08-13 03:24:00 +02:00
|
|
|
|
2022-12-02 21:15:34 +01:00
|
|
|
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):
|
2019-03-30 21:58:45 +01:00
|
|
|
'''
|
|
|
|
The hostpython3's recipe.
|
2017-08-13 03:24:00 +02:00
|
|
|
|
2022-12-02 21:15:34 +01:00
|
|
|
.. versionchanged:: 2019.10.06.post0
|
|
|
|
Refactored from deleted class ``python.HostPythonRecipe`` into here.
|
|
|
|
|
2019-03-30 21:58:45 +01:00
|
|
|
.. versionchanged:: 0.6.0
|
|
|
|
Refactored into the new class
|
|
|
|
:class:`~pythonforandroid.python.HostPythonRecipe`
|
|
|
|
'''
|
2022-12-02 21:15:34 +01:00
|
|
|
|
|
|
|
version = '3.9.9'
|
2017-08-13 03:24:00 +02:00
|
|
|
name = 'hostpython3'
|
2022-12-02 21:15:34 +01:00
|
|
|
|
|
|
|
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
|
2017-08-13 03:24:00 +02:00
|
|
|
|
|
|
|
|
2022-12-02 21:15:34 +01:00
|
|
|
recipe = HostPython3Recipe()
|