@@ -56,6 +56,62 @@ 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 (
94
+ "pkg-config" ,
95
+ "autoconf" ,
96
+ "automake" ,
97
+ "libtoolize" ,
98
+ "tar" ,
99
+ "bzip2" ,
100
+ "unzip" ,
101
+ "make" ,
102
+ "gcc" ,
103
+ "g++" ,
104
+ ):
105
+ if not sh .which (executable ):
106
+ warning (f"Missing executable: { executable } is not installed" )
107
+
108
+ if not ok :
109
+ raise BuildInterruptingException (
110
+ 'python-for-android cannot continue due to the missing executables above' )
111
+
112
+ return toolchain_version
113
+
114
+
59
115
def get_targets (sdk_dir ):
60
116
if exists (join (sdk_dir , 'tools' , 'bin' , 'avdmanager' )):
61
117
avdmanager = sh .Command (join (sdk_dir , 'tools' , 'bin' , 'avdmanager' ))
@@ -253,8 +309,6 @@ def prepare_build_environment(self,
253
309
if self ._build_env_prepared :
254
310
return
255
311
256
- ok = True
257
-
258
312
# Work out where the Android SDK is
259
313
sdk_dir = None
260
314
if user_sdk_dir :
@@ -385,55 +439,13 @@ def prepare_build_environment(self,
385
439
386
440
self .ndk_standalone = get_ndk_standalone (self .ndk_dir )
387
441
self .ndk_sysroot , ndk_sysroot_exists = get_ndk_sysroot (self .ndk_dir )
388
- ok = ok and ndk_sysroot_exists
389
442
self .ndk_include_dir = join (self .ndk_sysroot , 'usr' , 'include' )
390
443
391
444
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
445
+ # We assume that the toolchain version is the same for all the archs.
446
+ self .toolchain_version = select_and_check_toolchain_version (
447
+ self .sdk_dir , self .ndk_dir , arch , ndk_sysroot_exists , py_platform
448
+ )
437
449
438
450
def __init__ (self ):
439
451
self .include_dirs = []
@@ -604,10 +616,11 @@ def build_recipes(build_order, python_modules, ctx, project_dir,
604
616
recipe .postbuild_arch (arch )
605
617
606
618
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
- )
619
+ for arch in ctx .archs :
620
+ run_pymodules_install (
621
+ ctx , arch , python_modules , project_dir ,
622
+ ignore_setup_py = ignore_project_setup_py
623
+ )
611
624
612
625
613
626
def project_has_setup_py (project_dir ):
@@ -728,7 +741,7 @@ def run_setuppy_install(ctx, project_dir, env=None, arch=None):
728
741
os .remove ("._tmp_p4a_recipe_constraints.txt" )
729
742
730
743
731
- def run_pymodules_install (ctx , modules , project_dir = None ,
744
+ def run_pymodules_install (ctx , arch , modules , project_dir = None ,
732
745
ignore_setup_py = False ):
733
746
""" This function will take care of all non-recipe things, by:
734
747
@@ -740,119 +753,118 @@ def run_pymodules_install(ctx, modules, project_dir=None,
740
753
741
754
"""
742
755
743
- info ('*** PYTHON PACKAGE / PROJECT INSTALL STAGE ***' )
756
+ info ('*** PYTHON PACKAGE / PROJECT INSTALL STAGE FOR ARCH: {} ***' . format ( arch ) )
744
757
745
- for arch in ctx .archs :
746
- modules = [m for m in modules if ctx .not_has_package (m , arch )]
758
+ modules = [m for m in modules if ctx .not_has_package (m , arch )]
747
759
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
760
+ # We change current working directory later, so this has to be an absolute
761
+ # path or `None` in case that we didn't supply the `project_dir` via kwargs
762
+ project_dir = abspath (project_dir ) if project_dir else None
751
763
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
764
+ # Bail out if no python deps and no setup.py to process:
765
+ if not modules and (
766
+ ignore_setup_py or
767
+ project_dir is None or
768
+ not project_has_setup_py (project_dir )
769
+ ):
770
+ info ('No Python modules and no setup.py to process, skipping' )
771
+ return
760
772
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 :
773
+ # Output messages about what we're going to do:
774
+ if modules :
775
+ info (
776
+ "The requirements ({}) don\' t have recipes, attempting to "
777
+ "install them with pip" .format (', ' .join (modules ))
778
+ )
779
+ info (
780
+ "If this fails, it may mean that the module has compiled "
781
+ "components and needs a recipe."
782
+ )
783
+ if project_dir is not None and \
784
+ project_has_setup_py (project_dir ) and not ignore_setup_py :
785
+ info (
786
+ "Will process project install, if it fails then the "
787
+ "project may not be compatible for Android install."
788
+ )
789
+
790
+ # Use our hostpython to create the virtualenv
791
+ host_python = sh .Command (ctx .hostpython )
792
+ with current_directory (join (ctx .build_dir )):
793
+ shprint (host_python , '-m' , 'venv' , 'venv' )
794
+
795
+ # Prepare base environment and upgrade pip:
796
+ base_env = dict (copy .copy (os .environ ))
797
+ base_env ["PYTHONPATH" ] = ctx .get_site_packages_dir (arch )
798
+ info ('Upgrade pip to latest version' )
799
+ shprint (sh .bash , '-c' , (
800
+ "source venv/bin/activate && pip install -U pip"
801
+ ), _env = copy .copy (base_env ))
802
+
803
+ # Install Cython in case modules need it to build:
804
+ info ('Install Cython in case one of the modules needs it to build' )
805
+ shprint (sh .bash , '-c' , (
806
+ "venv/bin/pip install Cython"
807
+ ), _env = copy .copy (base_env ))
808
+
809
+ # Get environment variables for build (with CC/compiler set):
810
+ standard_recipe = CythonRecipe ()
811
+ standard_recipe .ctx = ctx
812
+ # (note: following line enables explicit -lpython... linker options)
813
+ standard_recipe .call_hostpython_via_targetpython = False
814
+ recipe_env = standard_recipe .get_recipe_env (ctx .archs [0 ])
815
+ env = copy .copy (base_env )
816
+ env .update (recipe_env )
817
+
818
+ # Make sure our build package dir is available, and the virtualenv
819
+ # site packages come FIRST (so the proper pip version is used):
820
+ env ["PYTHONPATH" ] += ":" + ctx .get_site_packages_dir (arch )
821
+ env ["PYTHONPATH" ] = os .path .abspath (join (
822
+ ctx .build_dir , "venv" , "lib" ,
823
+ "python" + ctx .python_recipe .major_minor_version_string ,
824
+ "site-packages" )) + ":" + env ["PYTHONPATH" ]
825
+
826
+ # Install the manually specified requirements first:
827
+ if not modules :
828
+ info ('There are no Python modules to install, skipping' )
829
+ else :
830
+ info ('Creating a requirements.txt file for the Python modules' )
831
+ with open ('requirements.txt' , 'w' ) as fileh :
832
+ for module in modules :
833
+ key = 'VERSION_' + module
834
+ if key in environ :
835
+ line = '{}=={}\n ' .format (module , environ [key ])
836
+ else :
837
+ line = '{}\n ' .format (module )
838
+ fileh .write (line )
839
+
840
+ info ('Installing Python modules with pip' )
773
841
info (
774
- "Will process project install, if it fails then the "
775
- "project may not be compatible for Android install."
842
+ "IF THIS FAILS, THE MODULES MAY NEED A RECIPE. "
843
+ "A reason for this is often modules compiling "
844
+ "native code that is unaware of Android cross-compilation "
845
+ "and does not work without additional "
846
+ "changes / workarounds."
776
847
)
777
848
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
849
shprint (sh .bash , '-c' , (
788
- "source venv/bin/activate && pip install -U pip"
789
- ), _env = copy .copy (base_env ))
850
+ "venv/bin/pip " +
851
+ "install -v --target '{0}' --no-deps -r requirements.txt"
852
+ ).format (ctx .get_site_packages_dir (arch ).replace ("'" , "'\" '\" '" )),
853
+ _env = copy .copy (env ))
790
854
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
- )
855
+ # Afterwards, run setup.py if present:
856
+ if project_dir is not None and (
857
+ project_has_setup_py (project_dir ) and not ignore_setup_py
858
+ ):
859
+ run_setuppy_install (ctx , project_dir , env , arch .arch )
860
+ elif not ignore_setup_py :
861
+ info ("No setup.py found in project directory: " + str (project_dir ))
862
+
863
+ # Strip object files after potential Cython or native code builds:
864
+ if not ctx .with_debug_symbols :
865
+ standard_recipe .strip_object_files (
866
+ arch , env , build_dir = ctx .build_dir
867
+ )
856
868
857
869
858
870
def biglink (ctx , arch ):
0 commit comments