Skip to content

Commit d39275a

Browse files
author
Jonas Thiem
committed
Fix Cython not working out of the box for regular package install
1 parent 81a8bf6 commit d39275a

File tree

2 files changed

+78
-26
lines changed

2 files changed

+78
-26
lines changed

pythonforandroid/build.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from os.path import (join, realpath, dirname, expanduser, exists,
44
split, isdir)
55
from os import environ
6+
import copy
67
import os
78
import glob
89
import sys
@@ -13,7 +14,7 @@
1314
from pythonforandroid.util import (ensure_dir, current_directory, BuildInterruptingException)
1415
from pythonforandroid.logger import (info, warning, info_notify, info_main, shprint)
1516
from pythonforandroid.archs import ArchARM, ArchARMv7_a, ArchAarch_64, Archx86, Archx86_64
16-
from pythonforandroid.recipe import Recipe
17+
from pythonforandroid.recipe import CythonRecipe, Recipe
1718

1819
DEFAULT_ANDROID_API = 15
1920

@@ -682,17 +683,51 @@ def run_pymodules_install(ctx, modules):
682683
line = '{}\n'.format(module)
683684
fileh.write(line)
684685

685-
info('Installing Python modules with pip')
686-
info('If this fails with a message about /bin/false, this '
687-
'probably means the package cannot be installed with '
688-
'pip as it needs a compilation recipe.')
686+
base_env = copy.copy(os.environ)
687+
base_env["PYTHONPATH"] = ctx.get_site_packages_dir()
688+
689+
info('Upgrade pip to latest version')
690+
shprint(sh.bash, '-c', (
691+
"source venv/bin/activate && pip install -U pip"
692+
), _env=copy.copy(base_env))
689693

690-
# This bash method is what old-p4a used
691-
# It works but should be replaced with something better
694+
info('Install Cython in case one of the modules needs it to build')
695+
shprint(sh.bash, '-c', (
696+
"venv/bin/pip install Cython"
697+
), _env=copy.copy(base_env))
698+
699+
# Get environment variables for build (with CC/compiler set):
700+
standard_recipe = CythonRecipe()
701+
standard_recipe.ctx = ctx
702+
# (note: following line enables explicit -lpython... linker options)
703+
standard_recipe.call_hostpython_via_targetpython = False
704+
recipe_env = standard_recipe.get_recipe_env(ctx.archs[0])
705+
env = copy.copy(base_env)
706+
env.update(recipe_env)
707+
708+
info('Installing Python modules with pip')
709+
info('IF THIS FAILS, THE MODULES MAY NEED A RECIPE. '
710+
'A reason for this is often modules compiling '
711+
'native code that is unaware of Android cross-compilation '
712+
'and does not work without additional '
713+
'changes / workarounds.')
714+
715+
# Make sure our build package dir is available, and the virtualenv
716+
# site packages come FIRST (for the proper pip version):
717+
env["PYTHONPATH"] += ":" + ctx.get_site_packages_dir()
718+
env["PYTHONPATH"] = os.path.abspath(join(
719+
ctx.build_dir, "venv", "lib",
720+
"python" + ctx.python_recipe.major_minor_version_string,
721+
"site-packages")) + ":" + env["PYTHONPATH"]
692722
shprint(sh.bash, '-c', (
693-
"env CC=/bin/false CXX=/bin/false "
694-
"PYTHONPATH={0} venv/bin/pip install --target '{0}' --no-deps -r requirements.txt"
695-
).format(ctx.get_site_packages_dir()))
723+
"source venv/bin/activate && " +
724+
"pip install -v --target '{0}' --no-deps -r requirements.txt"
725+
).format(ctx.get_site_packages_dir().replace("'", "'\"'\"'")),
726+
_env=copy.copy(env))
727+
728+
# Strip object files after potential Cython or native code builds:
729+
standard_recipe.strip_object_files(ctx.archs[0], env,
730+
build_dir=ctx.build_dir)
696731

697732

698733
def biglink(ctx, arch):

pythonforandroid/recipe.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -779,12 +779,14 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True):
779779
hppath.append(join(dirname(self.hostpython_location), 'Lib'))
780780
hppath.append(join(hppath[0], 'site-packages'))
781781
builddir = join(dirname(self.hostpython_location), 'build')
782-
hppath += [join(builddir, d) for d in listdir(builddir)
783-
if isdir(join(builddir, d))]
784-
if 'PYTHONPATH' in env:
785-
env['PYTHONPATH'] = ':'.join(hppath + [env['PYTHONPATH']])
786-
else:
787-
env['PYTHONPATH'] = ':'.join(hppath)
782+
if exists(builddir):
783+
hppath += [join(builddir, d) for d in listdir(builddir)
784+
if isdir(join(builddir, d))]
785+
if len(hppath) > 0:
786+
if 'PYTHONPATH' in env:
787+
env['PYTHONPATH'] = ':'.join(hppath + [env['PYTHONPATH']])
788+
else:
789+
env['PYTHONPATH'] = ':'.join(hppath)
788790
return env
789791

790792
def should_build(self, arch):
@@ -955,16 +957,6 @@ def build_cython_components(self, arch):
955957

956958
env = self.get_recipe_env(arch)
957959

958-
if self.ctx.python_recipe.from_crystax:
959-
command = sh.Command('python{}'.format(self.ctx.python_recipe.version))
960-
site_packages_dirs = command(
961-
'-c', 'import site; print("\\n".join(site.getsitepackages()))')
962-
site_packages_dirs = site_packages_dirs.stdout.decode('utf-8').split('\n')
963-
if 'PYTHONPATH' in env:
964-
env['PYTHONPATH'] = env['PYTHONPATH'] + ':{}'.format(':'.join(site_packages_dirs))
965-
else:
966-
env['PYTHONPATH'] = ':'.join(site_packages_dirs)
967-
968960
with current_directory(self.get_build_dir(arch.arch)):
969961
hostpython = sh.Command(self.ctx.hostpython)
970962
shprint(hostpython, '-c', 'import sys; print(sys.path)', _env=env)
@@ -989,8 +981,15 @@ def build_cython_components(self, arch):
989981
info('First build appeared to complete correctly, skipping manual'
990982
'cythonising.')
991983

984+
self.strip_object_files(arch, env)
985+
986+
def strip_object_files(self, arch, env, build_dir=None):
987+
if build_dir is None:
988+
build_dir = self.get_build_dir(arch.arch)
989+
with current_directory(build_dir):
992990
info('Stripping object files')
993991
if self.ctx.python_recipe.name == 'python2legacy':
992+
info('Stripping object files')
994993
build_lib = glob.glob('./build/lib*')
995994
shprint(sh.find, build_lib[0], '-name', '*.o', '-exec',
996995
env['STRIP'], '{}', ';', _env=env)
@@ -1055,6 +1054,24 @@ def get_recipe_env(self, arch, with_flags_in_cc=True):
10551054
env['LIBLINK_PATH'] = liblink_path
10561055
ensure_dir(liblink_path)
10571056

1057+
# Add crystax-specific site packages:
1058+
if self.ctx.python_recipe.from_crystax:
1059+
command = sh.Command('python{}'.format(self.ctx.python_recipe.version))
1060+
site_packages_dirs = command(
1061+
'-c', 'import site; print("\\n".join(site.getsitepackages()))')
1062+
site_packages_dirs = site_packages_dirs.stdout.decode('utf-8').split('\n')
1063+
if 'PYTHONPATH' in env:
1064+
env['PYTHONPATH'] = env['PYTHONPATH'] +\
1065+
':{}'.format(':'.join(site_packages_dirs))
1066+
else:
1067+
env['PYTHONPATH'] = ':'.join(site_packages_dirs)
1068+
while env['PYTHONPATH'].find("::") > 0:
1069+
env['PYTHONPATH'] = env['PYTHONPATH'].replace("::", ":")
1070+
if env['PYTHONPATH'].endswith(":"):
1071+
env['PYTHONPATH'] = env['PYTHONPATH'][:-1]
1072+
if env['PYTHONPATH'].startswith(":"):
1073+
env['PYTHONPATH'] = env['PYTHONPATH'][1:]
1074+
10581075
return env
10591076

10601077

0 commit comments

Comments
 (0)