Skip to content

Commit 3f3420d

Browse files
committed
Some code cleanup
1 parent 40816f7 commit 3f3420d

File tree

2 files changed

+172
-160
lines changed

2 files changed

+172
-160
lines changed

pythonforandroid/build.py

Lines changed: 169 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,62 @@ def get_toolchain_versions(ndk_dir, arch):
5656
return toolchain_versions, toolchain_path_exists
5757

5858

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+
59115
def get_targets(sdk_dir):
60116
if exists(join(sdk_dir, 'tools', 'bin', 'avdmanager')):
61117
avdmanager = sh.Command(join(sdk_dir, 'tools', 'bin', 'avdmanager'))
@@ -253,8 +309,6 @@ def prepare_build_environment(self,
253309
if self._build_env_prepared:
254310
return
255311

256-
ok = True
257-
258312
# Work out where the Android SDK is
259313
sdk_dir = None
260314
if user_sdk_dir:
@@ -385,55 +439,13 @@ def prepare_build_environment(self,
385439

386440
self.ndk_standalone = get_ndk_standalone(self.ndk_dir)
387441
self.ndk_sysroot, ndk_sysroot_exists = get_ndk_sysroot(self.ndk_dir)
388-
ok = ok and ndk_sysroot_exists
389442
self.ndk_include_dir = join(self.ndk_sysroot, 'usr', 'include')
390443

391444
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+
)
437449

438450
def __init__(self):
439451
self.include_dirs = []
@@ -604,10 +616,11 @@ def build_recipes(build_order, python_modules, ctx, project_dir,
604616
recipe.postbuild_arch(arch)
605617

606618
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+
)
611624

612625

613626
def project_has_setup_py(project_dir):
@@ -728,7 +741,7 @@ def run_setuppy_install(ctx, project_dir, env=None, arch=None):
728741
os.remove("._tmp_p4a_recipe_constraints.txt")
729742

730743

731-
def run_pymodules_install(ctx, modules, project_dir=None,
744+
def run_pymodules_install(ctx, arch, modules, project_dir=None,
732745
ignore_setup_py=False):
733746
""" This function will take care of all non-recipe things, by:
734747
@@ -740,119 +753,118 @@ def run_pymodules_install(ctx, modules, project_dir=None,
740753
741754
"""
742755

743-
info('*** PYTHON PACKAGE / PROJECT INSTALL STAGE ***')
756+
info('*** PYTHON PACKAGE / PROJECT INSTALL STAGE FOR ARCH: {} ***'.format(arch))
744757

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)]
747759

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
751763

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
760772

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')
773841
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."
776847
)
777848

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')
787849
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))
790854

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+
)
856868

857869

858870
def biglink(ctx, arch):

0 commit comments

Comments
 (0)