1- from os .path import basename , dirname , exists , isdir , isfile , join , realpath , split , sep
1+ from os .path import basename , dirname , exists , isdir , isfile , join , realpath , split
22import glob
33
44import hashlib
@@ -843,7 +843,6 @@ class PythonRecipe(Recipe):
843843
844844 def __init__ (self , * args , ** kwargs ):
845845 super ().__init__ (* args , ** kwargs )
846-
847846 if 'python3' not in self .depends :
848847 # We ensure here that the recipe depends on python even it overrode
849848 # `depends`. We only do this if it doesn't already depend on any
@@ -893,12 +892,12 @@ def folder_name(self):
893892
894893 def get_recipe_env (self , arch = None , with_flags_in_cc = True ):
895894 env = super ().get_recipe_env (arch , with_flags_in_cc )
896-
897895 env ['PYTHONNOUSERSITE' ] = '1'
898-
899896 # Set the LANG, this isn't usually important but is a better default
900897 # as it occasionally matters how Python e.g. reads files
901898 env ['LANG' ] = "en_GB.UTF-8"
899+ # Binaries made by packages installed by pip
900+ env ["PATH" ] = join (self .hostpython_site_dir , "bin" ) + ":" + env ["PATH" ]
902901
903902 if not self .call_hostpython_via_targetpython :
904903 env ['CFLAGS' ] += ' -I{}' .format (
@@ -978,27 +977,35 @@ def install_hostpython_package(self, arch):
978977 '--install-lib=Lib/site-packages' ,
979978 _env = env , * self .setup_extra_args )
980979
981- @property
982- def python_version (self ):
983- return Recipe .get_recipe ("python3" , self .ctx ).version
980+ def get_python_formatted_version (self ):
981+ parsed_version = packaging .version .parse (self .ctx .python_recipe .version )
982+ return f"{ parsed_version .major } .{ parsed_version .minor } "
983+
984+ def install_hostpython_prerequisites (self , packages = None , force_upgrade = True ):
985+ if not packages :
986+ packages = self .hostpython_prerequisites
984987
985- def install_hostpython_prerequisites (self , force_upgrade = True ):
986- if len (self .hostpython_prerequisites ) == 0 :
988+ if len (packages ) == 0 :
987989 return
990+
988991 pip_options = [
989992 "install" ,
990- * self . hostpython_prerequisites ,
993+ * packages ,
991994 "--target" , self .hostpython_site_dir , "--python-version" ,
992- self .python_version ,
995+ self .ctx . python_recipe . version ,
993996 # Don't use sources, instead wheels
994997 "--only-binary=:all:" ,
995- "--no-deps"
998+ # "--no-deps"
996999 ]
9971000 if force_upgrade :
9981001 pip_options .append ("--upgrade" )
9991002 # Use system's pip
10001003 shprint (sh .pip , * pip_options )
10011004
1005+ def restore_hostpython_prerequisites (self , package ):
1006+ original_version = Recipe .get_recipe (package , self .ctx ).version
1007+ self .install_hostpython_prerequisites (packages = [package + "==" + original_version ])
1008+
10021009
10031010class CompiledComponentsPythonRecipe (PythonRecipe ):
10041011 pre_build_ext = False
@@ -1157,7 +1164,148 @@ def get_recipe_env(self, arch, with_flags_in_cc=True):
11571164 return env
11581165
11591166
1160- class RustCompiledComponentsRecipe (PythonRecipe ):
1167+ class PyProjectRecipe (PythonRecipe ):
1168+ '''Recipe for projects which containes `pyproject.toml`'''
1169+
1170+ # Extra args to pass to `python -m build ...`
1171+ extra_build_args = []
1172+ call_hostpython_via_targetpython = False
1173+
1174+ def __init__ (self , * arg , ** kwargs ):
1175+ super ().__init__ (* arg , ** kwargs )
1176+
1177+ def get_recipe_env (self , arch , ** kwargs ):
1178+ self .ctx .python_recipe .python_exe = join (
1179+ self .ctx .python_recipe .get_build_dir (arch ), "android-build" , "python3" )
1180+ return super ().get_recipe_env (arch , ** kwargs )
1181+
1182+ def build_arch (self , arch ):
1183+ self .install_hostpython_prerequisites (
1184+ packages = ["build[virtualenv]" , "pip" ] + self .hostpython_prerequisites
1185+ )
1186+ build_dir = self .get_build_dir (arch .arch )
1187+ env = self .get_recipe_env (arch , with_flags_in_cc = True )
1188+ built_wheel = None
1189+ # make build dir separatly
1190+ sub_build_dir = join (build_dir , "p4a_android_build" )
1191+ ensure_dir (sub_build_dir )
1192+ # copy hostpython to built python to ensure correct libs and includes
1193+ shprint (sh .cp , self .real_hostpython_location , self .ctx .python_recipe .python_exe )
1194+
1195+ build_args = [
1196+ "-m" ,
1197+ "build" ,
1198+ # "--no-isolation",
1199+ # "--skip-dependency-check",
1200+ "--wheel" ,
1201+ "--config-setting" ,
1202+ "builddir={}" .format (sub_build_dir ),
1203+ ] + self .extra_build_args
1204+
1205+ with current_directory (build_dir ):
1206+ shprint (
1207+ sh .Command (self .ctx .python_recipe .python_exe ), * build_args , _env = env
1208+ )
1209+ built_wheel = realpath (glob .glob ("dist/*.whl" )[0 ])
1210+
1211+ info ("Unzipping built wheel '{}'" .format (basename (built_wheel )))
1212+
1213+ with zipfile .ZipFile (built_wheel , "r" ) as zip_ref :
1214+ zip_ref .extractall (self .ctx .get_python_install_dir (arch .arch ))
1215+ info ("Successfully installed '{}'" .format (basename (built_wheel )))
1216+
1217+
1218+ class MesonRecipe (PyProjectRecipe ):
1219+ '''Recipe for projects which uses meson as build system'''
1220+
1221+ meson_version = "1.4.0"
1222+ ninja_version = "1.11.1.1"
1223+
1224+ def sanitize_flags (self , * flag_strings ):
1225+ return " " .join (flag_strings ).strip ().split (" " )
1226+
1227+ def get_recipe_meson_options (self , arch ):
1228+ """Writes python dict to meson config file"""
1229+ env = self .get_recipe_env (arch , with_flags_in_cc = True )
1230+ return {
1231+ "binaries" : {
1232+ "c" : arch .get_clang_exe (with_target = True ),
1233+ "cpp" : arch .get_clang_exe (with_target = True , plus_plus = True ),
1234+ "ar" : self .ctx .ndk .llvm_ar ,
1235+ "strip" : self .ctx .ndk .llvm_strip ,
1236+ },
1237+ "built-in options" : {
1238+ "c_args" : self .sanitize_flags (env ["CFLAGS" ], env ["CPPFLAGS" ]),
1239+ "cpp_args" : self .sanitize_flags (env ["CXXFLAGS" ], env ["CPPFLAGS" ]),
1240+ "c_link_args" : self .sanitize_flags (env ["LDFLAGS" ]),
1241+ "cpp_link_args" : self .sanitize_flags (env ["LDFLAGS" ]),
1242+ },
1243+ "properties" : {
1244+ "needs_exe_wrapper" : True ,
1245+ "sys_root" : self .ctx .ndk .sysroot
1246+ },
1247+ "host_machine" : {
1248+ "cpu_family" : {
1249+ "arm64-v8a" : "aarch64" ,
1250+ "armeabi-v7a" : "arm" ,
1251+ "x86_64" : "x86_64" ,
1252+ "x86" : "x86"
1253+ }[arch .arch ],
1254+ "cpu" : {
1255+ "arm64-v8a" : "aarch64" ,
1256+ "armeabi-v7a" : "armv7" ,
1257+ "x86_64" : "x86_64" ,
1258+ "x86" : "i686"
1259+ }[arch .arch ],
1260+ "endian" : "little" ,
1261+ "system" : "android" ,
1262+ }
1263+ }
1264+
1265+ def write_build_options (self , arch ):
1266+ option_data = ""
1267+ build_options = self .get_recipe_meson_options (arch )
1268+ for key in build_options .keys ():
1269+ data_chunk = "[{}]" .format (key )
1270+ for subkey in build_options [key ].keys ():
1271+ value = build_options [key ][subkey ]
1272+ if isinstance (value , int ):
1273+ value = str (value )
1274+ elif isinstance (value , str ):
1275+ value = "'{}'" .format (value )
1276+ elif isinstance (value , bool ):
1277+ value = "true" if value else "false"
1278+ elif isinstance (value , list ):
1279+ value = "['" + "', '" .join (value ) + "']"
1280+ data_chunk += "\n " + subkey + " = " + value
1281+ option_data += data_chunk + "\n \n "
1282+ return option_data
1283+
1284+ def ensure_args (self , * args ):
1285+ for arg in args :
1286+ if arg not in self .extra_build_args :
1287+ self .extra_build_args .append (arg )
1288+
1289+ def build_arch (self , arch ):
1290+ cross_file = join (self .get_build_dir (arch ), "android.meson.cross" )
1291+ info ("Writing cross file at: {}" .format (cross_file ))
1292+ # write cross config file
1293+ with open (cross_file , "w" ) as file :
1294+ file .write (self .write_build_options (arch ))
1295+ file .close ()
1296+ # set cross file
1297+ self .ensure_args ('-Csetup-args=--cross-file' , '-Csetup-args={}' .format (cross_file ))
1298+ # ensure ninja and meson
1299+ for dep in [
1300+ "ninja=={}" .format (self .ninja_version ),
1301+ "meson=={}" .format (self .meson_version ),
1302+ ]:
1303+ if dep not in self .hostpython_prerequisites :
1304+ self .hostpython_prerequisites .append (dep )
1305+ super ().build_arch (arch )
1306+
1307+
1308+ class RustCompiledComponentsRecipe (PyProjectRecipe ):
11611309 # Rust toolchain codes
11621310 # https://doc.rust-lang.org/nightly/rustc/platform-support.html
11631311 RUST_ARCH_CODES = {
@@ -1167,41 +1315,10 @@ class RustCompiledComponentsRecipe(PythonRecipe):
11671315 "x86" : "i686-linux-android" ,
11681316 }
11691317
1170- # Build python wheel using `maturin` instead
1171- # of default `python -m build [...]`
1172- use_maturin = False
1173-
1174- # Directory where to find built wheel
1175- # For normal build: "dist/*.whl"
1176- # For maturin: "target/wheels/*-linux_*.whl"
1177- built_wheel_pattern = None
1178-
11791318 call_hostpython_via_targetpython = False
11801319
1181- def __init__ (self , * arg , ** kwargs ):
1182- super ().__init__ (* arg , ** kwargs )
1183- self .append_deps_if_absent (["python3" ])
1184- self .set_default_hostpython_deps ()
1185- if not self .built_wheel_pattern :
1186- self .built_wheel_pattern = (
1187- "target/wheels/*-linux_*.whl"
1188- if self .use_maturin
1189- else "dist/*.whl"
1190- )
1191-
1192- def set_default_hostpython_deps (self ):
1193- if not self .use_maturin :
1194- self .hostpython_prerequisites += ["build" , "setuptools_rust" , "wheel" , "pyproject_hooks" ]
1195- else :
1196- self .hostpython_prerequisites += ["maturin" ]
1197-
1198- def append_deps_if_absent (self , deps ):
1199- for dep in deps :
1200- if dep not in self .depends :
1201- self .depends .append (dep )
1202-
1203- def get_recipe_env (self , arch ):
1204- env = super ().get_recipe_env (arch )
1320+ def get_recipe_env (self , arch , ** kwargs ):
1321+ env = super ().get_recipe_env (arch , ** kwargs )
12051322
12061323 # Set rust build target
12071324 build_target = self .RUST_ARCH_CODES [arch .arch ]
@@ -1220,7 +1337,7 @@ def get_recipe_env(self, arch):
12201337 self .ctx .ndk_api ,
12211338 ),
12221339 )
1223- realpython_dir = Recipe . get_recipe ( "python3" , self .ctx ) .get_build_dir (arch .arch )
1340+ realpython_dir = self .ctx . python_recipe .get_build_dir (arch .arch )
12241341
12251342 env ["RUSTFLAGS" ] = "-Clink-args=-L{} -L{}" .format (
12261343 self .ctx .get_libs_dir (arch .arch ), join (realpython_dir , "android-build" )
@@ -1243,10 +1360,6 @@ def get_recipe_env(self, arch):
12431360 )
12441361 return env
12451362
1246- def get_python_formatted_version (self ):
1247- parsed_version = packaging .version .parse (self .python_version )
1248- return f"{ parsed_version .major } .{ parsed_version .minor } "
1249-
12501363 def check_host_deps (self ):
12511364 if not hasattr (sh , "rustup" ):
12521365 error (
@@ -1258,41 +1371,7 @@ def check_host_deps(self):
12581371
12591372 def build_arch (self , arch ):
12601373 self .check_host_deps ()
1261- self .install_hostpython_prerequisites ()
1262- build_dir = self .get_build_dir (arch .arch )
1263- env = self .get_recipe_env (arch )
1264- built_wheel = None
1265-
1266- # Copy the exec with version info
1267- hostpython_exec = join (
1268- sep ,
1269- * self .hostpython_location .split (sep )[:- 1 ],
1270- "python{}" .format (self .get_python_formatted_version ()),
1271- )
1272- shprint (sh .cp , self .hostpython_location , hostpython_exec )
1273-
1274- with current_directory (build_dir ):
1275- if self .use_maturin :
1276- shprint (
1277- sh .Command (join (self .hostpython_site_dir , "bin" , "maturin" )),
1278- "build" , "--interpreter" , hostpython_exec , "--skip-auditwheel" ,
1279- _env = env ,
1280- )
1281- else :
1282- shprint (
1283- sh .Command (hostpython_exec ),
1284- "-m" , "build" , "--no-isolation" , "--skip-dependency-check" , "--wheel" ,
1285- _env = env ,
1286- )
1287- # Find the built wheel
1288- built_wheel = realpath (glob .glob (self .built_wheel_pattern )[0 ])
1289-
1290- info ("Unzipping built wheel '{}'" .format (basename (built_wheel )))
1291-
1292- # Unzip .whl file into site-packages
1293- with zipfile .ZipFile (built_wheel , "r" ) as zip_ref :
1294- zip_ref .extractall (self .ctx .get_python_install_dir (arch .arch ))
1295- info ("Successfully installed '{}'" .format (basename (built_wheel )))
1374+ super ().build_arch (arch )
12961375
12971376
12981377class TargetPythonRecipe (Recipe ):
0 commit comments