@@ -56,6 +56,52 @@ def get_toolchain_versions(ndk_dir, arch):
56
56
return toolchain_versions , toolchain_path_exists
57
57
58
58
59
+ def select_and_check_toolchain_version (sdk_dir , ndk_dir , arch , ndk_sysroot_exists , py_platform ):
60
+ toolchain_versions , toolchain_path_exists = get_toolchain_versions (ndk_dir , arch )
61
+ ok = ndk_sysroot_exists and toolchain_path_exists
62
+ toolchain_versions .sort ()
63
+
64
+ toolchain_versions_gcc = []
65
+ for toolchain_version in toolchain_versions :
66
+ if toolchain_version [0 ].isdigit ():
67
+ # GCC toolchains begin with a number
68
+ toolchain_versions_gcc .append (toolchain_version )
69
+
70
+ if toolchain_versions :
71
+ info ('Found the following toolchain versions: {}' .format (
72
+ toolchain_versions ))
73
+ info ('Picking the latest gcc toolchain, here {}' .format (
74
+ toolchain_versions_gcc [- 1 ]))
75
+ toolchain_version = toolchain_versions_gcc [- 1 ]
76
+ else :
77
+ warning ('Could not find any toolchain for {}!' .format (
78
+ arch .toolchain_prefix ))
79
+ ok = False
80
+
81
+ # Modify the path so that sh finds modules appropriately
82
+ environ ['PATH' ] = (
83
+ '{ndk_dir}/toolchains/{toolchain_prefix}-{toolchain_version}/'
84
+ 'prebuilt/{py_platform}-x86/bin/:{ndk_dir}/toolchains/'
85
+ '{toolchain_prefix}-{toolchain_version}/prebuilt/'
86
+ '{py_platform}-x86_64/bin/:{ndk_dir}:{sdk_dir}/'
87
+ 'tools:{path}' ).format (
88
+ sdk_dir = sdk_dir , ndk_dir = ndk_dir ,
89
+ toolchain_prefix = arch .toolchain_prefix ,
90
+ toolchain_version = toolchain_version ,
91
+ py_platform = py_platform , path = environ .get ('PATH' ))
92
+
93
+ for executable in ("pkg-config" , "autoconf" , "automake" , "libtoolize" ,
94
+ "tar" , "bzip2" , "unzip" , "make" , "gcc" , "g++" ):
95
+ if not sh .which (executable ):
96
+ warning (f"Missing executable: { executable } is not installed" )
97
+
98
+ if not ok :
99
+ raise BuildInterruptingException (
100
+ 'python-for-android cannot continue due to the missing executables above' )
101
+
102
+ return toolchain_version
103
+
104
+
59
105
def get_targets (sdk_dir ):
60
106
if exists (join (sdk_dir , 'tools' , 'bin' , 'avdmanager' )):
61
107
avdmanager = sh .Command (join (sdk_dir , 'tools' , 'bin' , 'avdmanager' ))
@@ -253,8 +299,6 @@ def prepare_build_environment(self,
253
299
if self ._build_env_prepared :
254
300
return
255
301
256
- ok = True
257
-
258
302
# Work out where the Android SDK is
259
303
sdk_dir = None
260
304
if user_sdk_dir :
@@ -385,55 +429,13 @@ def prepare_build_environment(self,
385
429
386
430
self .ndk_standalone = get_ndk_standalone (self .ndk_dir )
387
431
self .ndk_sysroot , ndk_sysroot_exists = get_ndk_sysroot (self .ndk_dir )
388
- ok = ok and ndk_sysroot_exists
389
432
self .ndk_include_dir = join (self .ndk_sysroot , 'usr' , 'include' )
390
433
391
434
for arch in self .archs :
392
-
393
- toolchain_versions , toolchain_path_exists = get_toolchain_versions (
394
- self .ndk_dir , arch )
395
- ok = ok and toolchain_path_exists
396
- toolchain_versions .sort ()
397
-
398
- toolchain_versions_gcc = []
399
- for toolchain_version in toolchain_versions :
400
- if toolchain_version [0 ].isdigit ():
401
- # GCC toolchains begin with a number
402
- toolchain_versions_gcc .append (toolchain_version )
403
-
404
- if toolchain_versions :
405
- info ('Found the following toolchain versions: {}' .format (
406
- toolchain_versions ))
407
- info ('Picking the latest gcc toolchain, here {}' .format (
408
- toolchain_versions_gcc [- 1 ]))
409
- toolchain_version = toolchain_versions_gcc [- 1 ]
410
- else :
411
- warning ('Could not find any toolchain for {}!' .format (
412
- arch .toolchain_prefix ))
413
- ok = False
414
-
415
- # Modify the path so that sh finds modules appropriately
416
- environ ['PATH' ] = (
417
- '{ndk_dir}/toolchains/{toolchain_prefix}-{toolchain_version}/'
418
- 'prebuilt/{py_platform}-x86/bin/:{ndk_dir}/toolchains/'
419
- '{toolchain_prefix}-{toolchain_version}/prebuilt/'
420
- '{py_platform}-x86_64/bin/:{ndk_dir}:{sdk_dir}/'
421
- 'tools:{path}' ).format (
422
- sdk_dir = self .sdk_dir , ndk_dir = self .ndk_dir ,
423
- toolchain_prefix = arch .toolchain_prefix ,
424
- toolchain_version = toolchain_version ,
425
- py_platform = py_platform , path = environ .get ('PATH' ))
426
-
427
- for executable in ("pkg-config" , "autoconf" , "automake" , "libtoolize" ,
428
- "tar" , "bzip2" , "unzip" , "make" , "gcc" , "g++" ):
429
- if not sh .which (executable ):
430
- warning (f"Missing executable: { executable } is not installed" )
431
-
432
- if not ok :
433
- raise BuildInterruptingException (
434
- 'python-for-android cannot continue due to the missing executables above' )
435
-
436
- self .toolchain_version = toolchain_version # We assume that the toolchain version is the same for all the archs
435
+ # We assume that the toolchain version is the same for all the archs.
436
+ self .toolchain_version = select_and_check_toolchain_version (
437
+ self .sdk_dir , self .ndk_dir , arch , ndk_sysroot_exists , py_platform
438
+ )
437
439
438
440
def __init__ (self ):
439
441
self .include_dirs = []
@@ -604,10 +606,11 @@ def build_recipes(build_order, python_modules, ctx, project_dir,
604
606
recipe .postbuild_arch (arch )
605
607
606
608
info_main ('# Installing pure Python modules' )
607
- run_pymodules_install (
608
- ctx , python_modules , project_dir ,
609
- ignore_setup_py = ignore_project_setup_py
610
- )
609
+ for arch in ctx .archs :
610
+ run_pymodules_install (
611
+ ctx , arch , python_modules , project_dir ,
612
+ ignore_setup_py = ignore_project_setup_py
613
+ )
611
614
612
615
613
616
def project_has_setup_py (project_dir ):
@@ -728,7 +731,7 @@ def run_setuppy_install(ctx, project_dir, env=None, arch=None):
728
731
os .remove ("._tmp_p4a_recipe_constraints.txt" )
729
732
730
733
731
- def run_pymodules_install (ctx , modules , project_dir = None ,
734
+ def run_pymodules_install (ctx , arch , modules , project_dir = None ,
732
735
ignore_setup_py = False ):
733
736
""" This function will take care of all non-recipe things, by:
734
737
@@ -740,119 +743,118 @@ def run_pymodules_install(ctx, modules, project_dir=None,
740
743
741
744
"""
742
745
743
- info ('*** PYTHON PACKAGE / PROJECT INSTALL STAGE ***' )
746
+ info ('*** PYTHON PACKAGE / PROJECT INSTALL STAGE FOR ARCH: {} ***' . format ( arch ) )
744
747
745
- for arch in ctx .archs :
746
- modules = [m for m in modules if ctx .not_has_package (m , arch )]
748
+ modules = [m for m in modules if ctx .not_has_package (m , arch )]
747
749
748
- # We change current working directory later, so this has to be an absolute
749
- # path or `None` in case that we didn't supply the `project_dir` via kwargs
750
- project_dir = abspath (project_dir ) if project_dir else None
750
+ # We change current working directory later, so this has to be an absolute
751
+ # path or `None` in case that we didn't supply the `project_dir` via kwargs
752
+ project_dir = abspath (project_dir ) if project_dir else None
751
753
752
- # Bail out if no python deps and no setup.py to process:
753
- if not modules and (
754
- ignore_setup_py or
755
- project_dir is None or
756
- not project_has_setup_py (project_dir )
757
- ):
758
- info ('No Python modules and no setup.py to process, skipping' )
759
- return
754
+ # Bail out if no python deps and no setup.py to process:
755
+ if not modules and (
756
+ ignore_setup_py or
757
+ project_dir is None or
758
+ not project_has_setup_py (project_dir )
759
+ ):
760
+ info ('No Python modules and no setup.py to process, skipping' )
761
+ return
760
762
761
- # Output messages about what we're going to do:
762
- if modules :
763
- info (
764
- "The requirements ({}) don\' t have recipes, attempting to "
765
- "install them with pip" .format (', ' .join (modules ))
766
- )
767
- info (
768
- "If this fails, it may mean that the module has compiled "
769
- "components and needs a recipe."
770
- )
771
- if project_dir is not None and \
772
- project_has_setup_py (project_dir ) and not ignore_setup_py :
763
+ # Output messages about what we're going to do:
764
+ if modules :
765
+ info (
766
+ "The requirements ({}) don\' t have recipes, attempting to "
767
+ "install them with pip" .format (', ' .join (modules ))
768
+ )
769
+ info (
770
+ "If this fails, it may mean that the module has compiled "
771
+ "components and needs a recipe."
772
+ )
773
+ if project_dir is not None and \
774
+ project_has_setup_py (project_dir ) and not ignore_setup_py :
775
+ info (
776
+ "Will process project install, if it fails then the "
777
+ "project may not be compatible for Android install."
778
+ )
779
+
780
+ # Use our hostpython to create the virtualenv
781
+ host_python = sh .Command (ctx .hostpython )
782
+ with current_directory (join (ctx .build_dir )):
783
+ shprint (host_python , '-m' , 'venv' , 'venv' )
784
+
785
+ # Prepare base environment and upgrade pip:
786
+ base_env = dict (copy .copy (os .environ ))
787
+ base_env ["PYTHONPATH" ] = ctx .get_site_packages_dir (arch )
788
+ info ('Upgrade pip to latest version' )
789
+ shprint (sh .bash , '-c' , (
790
+ "source venv/bin/activate && pip install -U pip"
791
+ ), _env = copy .copy (base_env ))
792
+
793
+ # Install Cython in case modules need it to build:
794
+ info ('Install Cython in case one of the modules needs it to build' )
795
+ shprint (sh .bash , '-c' , (
796
+ "venv/bin/pip install Cython"
797
+ ), _env = copy .copy (base_env ))
798
+
799
+ # Get environment variables for build (with CC/compiler set):
800
+ standard_recipe = CythonRecipe ()
801
+ standard_recipe .ctx = ctx
802
+ # (note: following line enables explicit -lpython... linker options)
803
+ standard_recipe .call_hostpython_via_targetpython = False
804
+ recipe_env = standard_recipe .get_recipe_env (ctx .archs [0 ])
805
+ env = copy .copy (base_env )
806
+ env .update (recipe_env )
807
+
808
+ # Make sure our build package dir is available, and the virtualenv
809
+ # site packages come FIRST (so the proper pip version is used):
810
+ env ["PYTHONPATH" ] += ":" + ctx .get_site_packages_dir (arch )
811
+ env ["PYTHONPATH" ] = os .path .abspath (join (
812
+ ctx .build_dir , "venv" , "lib" ,
813
+ "python" + ctx .python_recipe .major_minor_version_string ,
814
+ "site-packages" )) + ":" + env ["PYTHONPATH" ]
815
+
816
+ # Install the manually specified requirements first:
817
+ if not modules :
818
+ info ('There are no Python modules to install, skipping' )
819
+ else :
820
+ info ('Creating a requirements.txt file for the Python modules' )
821
+ with open ('requirements.txt' , 'w' ) as fileh :
822
+ for module in modules :
823
+ key = 'VERSION_' + module
824
+ if key in environ :
825
+ line = '{}=={}\n ' .format (module , environ [key ])
826
+ else :
827
+ line = '{}\n ' .format (module )
828
+ fileh .write (line )
829
+
830
+ info ('Installing Python modules with pip' )
773
831
info (
774
- "Will process project install, if it fails then the "
775
- "project may not be compatible for Android install."
832
+ "IF THIS FAILS, THE MODULES MAY NEED A RECIPE. "
833
+ "A reason for this is often modules compiling "
834
+ "native code that is unaware of Android cross-compilation "
835
+ "and does not work without additional "
836
+ "changes / workarounds."
776
837
)
777
838
778
- # Use our hostpython to create the virtualenv
779
- host_python = sh .Command (ctx .hostpython )
780
- with current_directory (join (ctx .build_dir )):
781
- shprint (host_python , '-m' , 'venv' , 'venv' )
782
-
783
- # Prepare base environment and upgrade pip:
784
- base_env = dict (copy .copy (os .environ ))
785
- base_env ["PYTHONPATH" ] = ctx .get_site_packages_dir (arch )
786
- info ('Upgrade pip to latest version' )
787
839
shprint (sh .bash , '-c' , (
788
- "source venv/bin/activate && pip install -U pip"
789
- ), _env = copy .copy (base_env ))
840
+ "venv/bin/pip " +
841
+ "install -v --target '{0}' --no-deps -r requirements.txt"
842
+ ).format (ctx .get_site_packages_dir (arch ).replace ("'" , "'\" '\" '" )),
843
+ _env = copy .copy (env ))
790
844
791
- # Install Cython in case modules need it to build:
792
- info ('Install Cython in case one of the modules needs it to build' )
793
- shprint (sh .bash , '-c' , (
794
- "venv/bin/pip install Cython"
795
- ), _env = copy .copy (base_env ))
796
-
797
- # Get environment variables for build (with CC/compiler set):
798
- standard_recipe = CythonRecipe ()
799
- standard_recipe .ctx = ctx
800
- # (note: following line enables explicit -lpython... linker options)
801
- standard_recipe .call_hostpython_via_targetpython = False
802
- recipe_env = standard_recipe .get_recipe_env (ctx .archs [0 ])
803
- env = copy .copy (base_env )
804
- env .update (recipe_env )
805
-
806
- # Make sure our build package dir is available, and the virtualenv
807
- # site packages come FIRST (so the proper pip version is used):
808
- env ["PYTHONPATH" ] += ":" + ctx .get_site_packages_dir (arch )
809
- env ["PYTHONPATH" ] = os .path .abspath (join (
810
- ctx .build_dir , "venv" , "lib" ,
811
- "python" + ctx .python_recipe .major_minor_version_string ,
812
- "site-packages" )) + ":" + env ["PYTHONPATH" ]
813
-
814
- # Install the manually specified requirements first:
815
- if not modules :
816
- info ('There are no Python modules to install, skipping' )
817
- else :
818
- info ('Creating a requirements.txt file for the Python modules' )
819
- with open ('requirements.txt' , 'w' ) as fileh :
820
- for module in modules :
821
- key = 'VERSION_' + module
822
- if key in environ :
823
- line = '{}=={}\n ' .format (module , environ [key ])
824
- else :
825
- line = '{}\n ' .format (module )
826
- fileh .write (line )
827
-
828
- info ('Installing Python modules with pip' )
829
- info (
830
- "IF THIS FAILS, THE MODULES MAY NEED A RECIPE. "
831
- "A reason for this is often modules compiling "
832
- "native code that is unaware of Android cross-compilation "
833
- "and does not work without additional "
834
- "changes / workarounds."
835
- )
836
-
837
- shprint (sh .bash , '-c' , (
838
- "venv/bin/pip " +
839
- "install -v --target '{0}' --no-deps -r requirements.txt"
840
- ).format (ctx .get_site_packages_dir (arch ).replace ("'" , "'\" '\" '" )),
841
- _env = copy .copy (env ))
842
-
843
- # Afterwards, run setup.py if present:
844
- if project_dir is not None and (
845
- project_has_setup_py (project_dir ) and not ignore_setup_py
846
- ):
847
- run_setuppy_install (ctx , project_dir , env , arch .arch )
848
- elif not ignore_setup_py :
849
- info ("No setup.py found in project directory: " + str (project_dir ))
850
-
851
- # Strip object files after potential Cython or native code builds:
852
- if not ctx .with_debug_symbols :
853
- standard_recipe .strip_object_files (
854
- arch , env , build_dir = ctx .build_dir
855
- )
845
+ # Afterwards, run setup.py if present:
846
+ if project_dir is not None and (
847
+ project_has_setup_py (project_dir ) and not ignore_setup_py
848
+ ):
849
+ run_setuppy_install (ctx , project_dir , env , arch .arch )
850
+ elif not ignore_setup_py :
851
+ info ("No setup.py found in project directory: " + str (project_dir ))
852
+
853
+ # Strip object files after potential Cython or native code builds:
854
+ if not ctx .with_debug_symbols :
855
+ standard_recipe .strip_object_files (
856
+ arch , env , build_dir = ctx .build_dir
857
+ )
856
858
857
859
858
860
def biglink (ctx , arch ):
0 commit comments