1010import zipfile
1111import urllib .request
1212from urllib .request import urlretrieve
13- from os import listdir , unlink , environ , curdir , walk
13+ from os import listdir , unlink , environ , curdir , walk , chmod
1414from sys import stdout
15+ from packaging .version import Version
1516from multiprocessing import cpu_count
1617import time
1718try :
@@ -1058,6 +1059,7 @@ def install_hostpython_prerequisites(self, packages=None, force_upgrade=True):
10581059 pip_options = [
10591060 "install" ,
10601061 * packages ,
1062+ "-q" ,
10611063 ]
10621064 if force_upgrade :
10631065 pip_options .append ("--upgrade" )
@@ -1303,6 +1305,8 @@ def get_recipe_env(self, arch, **kwargs):
13031305 file .close ()
13041306
13051307 env ["DIST_EXTRA_CONFIG" ] = build_opts
1308+ python_recipe = Recipe .get_recipe ("python3" , self .ctx )
1309+ env ["INCLUDEPY" ] = python_recipe .include_root (arch .arch )
13061310 return env
13071311
13081312 @staticmethod
@@ -1374,7 +1378,6 @@ def build_arch(self, arch):
13741378 if not (isfile (join (build_dir , "pyproject.toml" )) or isfile (join (build_dir , "setup.py" ))):
13751379 warning ("Skipping build because it does not appear to be a Python project." )
13761380 return
1377-
13781381 self .install_hostpython_prerequisites (
13791382 packages = ["build[virtualenv]" , "pip" , "setuptools" , "patchelf" ] + self .hostpython_prerequisites
13801383 )
@@ -1405,7 +1408,6 @@ class MesonRecipe(PyProjectRecipe):
14051408 '''Recipe for projects which uses meson as build system'''
14061409
14071410 meson_version = "1.4.0"
1408- ninja_version = "1.11.1.1"
14091411
14101412 skip_python = False
14111413 '''If true, skips all Python build and installation steps.
@@ -1414,10 +1416,55 @@ class MesonRecipe(PyProjectRecipe):
14141416 def sanitize_flags (self , * flag_strings ):
14151417 return " " .join (flag_strings ).strip ().split (" " )
14161418
1419+ def get_python_wrapper (self , arch ):
1420+ """
1421+ Meson Python introspection runs on the host interpreter, but the
1422+ target Python (Android) cannot be executed on the build machine.
1423+
1424+ We therefore run host Python and override sysconfig data to emulate
1425+ the target Android Python environment during Meson introspection.
1426+ """
1427+ python_recipe = Recipe .get_recipe ('python3' , self .ctx )
1428+ target_prefix = python_recipe .get_python_root (arch )
1429+ python_file = join (self .ctx .root_dir , 'meson_python.py' )
1430+ _arch = {
1431+ "arm64-v8a" : ["aarch64" ],
1432+ "x86_64" : ["x86_64" ],
1433+ "armeabi-v7a" : ["arm" ],
1434+ "x86" : ["i686" ],
1435+ }[arch .arch ][0 ]
1436+
1437+ # Real values pulled from android
1438+ # PYTHON_MAJOR_VERSION -> 3
1439+ # PYTHON_MINOR_VERSION -> 14
1440+ # PLATFORM_TAG eg -> 'android-24-arm64_v8a'
1441+ # PYTHON_SUFFIX eg -> '.cpython-314-aarch64-linux-android.so'
1442+
1443+ _p_version = Version (python_recipe .version )
1444+ file_data = f"#!{ self .real_hostpython_location } "
1445+ file_data += f"\n TARGET_PYTHON_PREFIX='{ target_prefix } '"
1446+ file_data += f"\n PYTHON_MAJOR_VERSION='{ _p_version .major } '"
1447+ file_data += f"\n PYTHON_MINOR_VERSION='{ _p_version .minor } '"
1448+ file_data += f"\n PLATFORM_TAG='{ self .get_wheel_platform_tags (arch .arch , self .ctx )[0 ]} '"
1449+ file_data += f"\n PYTHON_SUFFIX='.cpython-{ _p_version .major } { _p_version .minor } -{ _arch } -linux-android.so'"
1450+
1451+ with open (python_file , "r" ) as f :
1452+ file_data += "\n " + f .read ()
1453+
1454+ wrapper_dir = join (self .get_build_dir (arch .arch ), "p4a_python_wrapper" )
1455+ ensure_dir (wrapper_dir )
1456+ wrapper_path = join (wrapper_dir , "python" )
1457+ with open (wrapper_path , "w" ) as f :
1458+ f .write (file_data )
1459+ chmod (wrapper_path , 0o755 )
1460+
1461+ return wrapper_path
1462+
14171463 def get_recipe_meson_options (self , arch ):
14181464 env = self .get_recipe_env (arch , with_flags_in_cc = True )
14191465 return {
14201466 "binaries" : {
1467+ "python" : self .get_python_wrapper (arch ),
14211468 "c" : arch .get_clang_exe (with_target = True ),
14221469 "cpp" : arch .get_clang_exe (with_target = True , plus_plus = True ),
14231470 "ar" : self .ctx .ndk .llvm_ar ,
@@ -1488,13 +1535,18 @@ def build_arch(self, arch):
14881535 self .ensure_args ('-Csetup-args=--cross-file' , '-Csetup-args={}' .format (cross_file ))
14891536 # ensure ninja and meson
14901537 for dep in [
1491- "ninja=={}" . format ( self . ninja_version ) ,
1538+ "ninja" ,
14921539 "meson=={}" .format (self .meson_version ),
14931540 ]:
14941541 if dep not in self .hostpython_prerequisites :
14951542 self .hostpython_prerequisites .append (dep )
1543+
14961544 if not self .skip_python :
14971545 super ().build_arch (arch )
1546+ else :
1547+ self .install_hostpython_prerequisites (
1548+ packages = ["build[virtualenv]" , "pip" , "setuptools" , "patchelf" ] + self .hostpython_prerequisites
1549+ )
14981550
14991551
15001552class RustCompiledComponentsRecipe (PyProjectRecipe ):
0 commit comments