Skip to content

Commit 50e3011

Browse files
committed
[python] Fix build for case-insensitive FS 🍎
It turns out that the generated python binary for some builds are named `python.exe` instead of `python`. This depends on the File System where the build happens. It will be named `python.exe` when the FS is case-insensitive (Mac OSX and Cygwin), but it will be named `python` when the FS is case-sensitive (most GNU Linux distributions). The proposed solution consists in make a copy of the generated python binary with a given name (python3 or python2) so this way To achieve this goal we refactor a little our `HostPythonRecipe`:   - add private property `HostPythonRecipe._exe_name` (the name of the python executable based on major version)   - add public property `HostPythonRecipe.python_exe` (the full path of the python executable)   - implement `HostPythonRecipe.should_build` And also it's affected the `GuestPythonRecipe`, because we need to use the generated python executable by `HostPythonRecipe`:   - add private property `GuestPythonRecipe._libpython` (python's library name with extension...hardcoded for now...)   - implement `GuestPythonRecipe.should_build`... to check the library instead of the executable so we avoid conflicts with case-insensitive FS We also need: - fix `PythonRecipe.real_hostpython_location` because the name of our host python executable will depend on major version   - fix python2 interpreter (fix-interpreter-version.patch) Note: the variation of the name of the python's executable is mentioned at python's build docs (https://github.com/python/cpython/blob/3.7/README.rst#build-instructions) Note: @TheSin- , ¡¡¡thanks for your debugging sessions!!!
1 parent 982469a commit 50e3011

File tree

4 files changed

+82
-30
lines changed

4 files changed

+82
-30
lines changed

pythonforandroid/python.py

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
build our python3 and python2 recipes and his corresponding hostpython recipes.
44
'''
55

6-
from os.path import dirname, exists, join
6+
from os.path import dirname, exists, join, isfile
77
from multiprocessing import cpu_count
88
from shutil import copy2
99
from os import environ
@@ -205,6 +205,17 @@ def add_flags(include_flags, link_dirs, link_libs):
205205
recipe.link_dirs_flags(arch), recipe.link_libs_flags())
206206
return env
207207

208+
@property
209+
def _libpython(self):
210+
'''return the python's library name (with extension)'''
211+
py_version = self.major_minor_version_string
212+
if self.major_minor_version_string[0] == '3':
213+
py_version += 'm'
214+
return 'libpython{version}.so'.format(version=py_version)
215+
216+
def should_build(self, arch):
217+
return not isfile(join(self.link_root(arch.arch), self._libpython))
218+
208219
def prebuild_arch(self, arch):
209220
super(TargetPythonRecipe, self).prebuild_arch(arch)
210221
self.ctx.python_recipe = self
@@ -243,13 +254,11 @@ def build_arch(self, arch):
243254
exec_prefix=sys_exec_prefix)).split(' '),
244255
_env=env)
245256

246-
if not exists('python'):
247-
py_version = self.major_minor_version_string
248-
if self.major_minor_version_string[0] == '3':
249-
py_version += 'm'
250-
shprint(sh.make, 'all', '-j', str(cpu_count()),
251-
'INSTSONAME=libpython{version}.so'.format(
252-
version=py_version), _env=env)
257+
shprint(
258+
sh.make, 'all', '-j', str(cpu_count()),
259+
'INSTSONAME={lib_name}'.format(lib_name=self._libpython),
260+
_env=env
261+
)
253262

254263
# TODO: Look into passing the path to pyconfig.h in a
255264
# better way, although this is probably acceptable
@@ -382,6 +391,30 @@ class HostPythonRecipe(Recipe):
382391
'''The default url to download our host python recipe. This url will
383392
change depending on the python version set in attribute :attr:`version`.'''
384393

394+
@property
395+
def _exe_name(self):
396+
'''
397+
Returns the name of the python executable depending on the version.
398+
'''
399+
if not self.version:
400+
raise BuildInterruptingException(
401+
'The hostpython recipe must have set version'
402+
)
403+
version = self.version.split('.')[0]
404+
return 'python{major_version}'.format(major_version=version)
405+
406+
@property
407+
def python_exe(self):
408+
'''Returns the full path of the hostpython executable.'''
409+
return join(self.get_path_to_python(), self._exe_name)
410+
411+
def should_build(self, arch):
412+
if exists(self.python_exe):
413+
# no need to build, but we must set hostpython for our Context
414+
self.ctx.hostpython = self.python_exe
415+
return False
416+
return True
417+
385418
def get_build_container_dir(self, arch=None):
386419
choices = self.check_recipe_choices()
387420
dir_name = '-'.join([self.name] + choices)
@@ -404,22 +437,28 @@ def build_arch(self, arch):
404437
build_dir = join(recipe_build_dir, self.build_subdir)
405438
ensure_dir(build_dir)
406439

407-
if not exists(join(build_dir, 'python')):
408-
with current_directory(recipe_build_dir):
409-
# Configure the build
410-
with current_directory(build_dir):
411-
if not exists('config.status'):
412-
shprint(
413-
sh.Command(join(recipe_build_dir, 'configure')))
414-
415-
# Create the Setup file. This copying from Setup.dist
416-
# seems to be the normal and expected procedure.
417-
shprint(sh.cp, join('Modules', 'Setup.dist'),
418-
join(build_dir, 'Modules', 'Setup'))
419-
420-
shprint(sh.make, '-j', str(cpu_count()), '-C', build_dir)
421-
else:
422-
info('Skipping {name} ({version}) build, as it has already '
423-
'been completed'.format(name=self.name, version=self.version))
424-
425-
self.ctx.hostpython = join(build_dir, 'python')
440+
with current_directory(recipe_build_dir):
441+
# Configure the build
442+
with current_directory(build_dir):
443+
if not exists('config.status'):
444+
shprint(sh.Command(join(recipe_build_dir, 'configure')))
445+
446+
# Create the Setup file. This copying from Setup.dist
447+
# seems to be the normal and expected procedure.
448+
shprint(sh.cp, join('Modules', 'Setup.dist'),
449+
join(build_dir, 'Modules', 'Setup'))
450+
451+
shprint(sh.make, '-j', str(cpu_count()), '-C', build_dir)
452+
453+
# make a copy of the python executable giving it the name we want,
454+
# because we got different python's executable names depending on
455+
# the fs being case-insensitive (Mac OS X, Cygwin...) or
456+
# case-sensitive (linux)...so this way we will have an unique name
457+
# for our hostpython, regarding the used fs
458+
for exe_name in ['python.exe', 'python']:
459+
exe = join(self.get_path_to_python(), exe_name)
460+
if isfile(exe):
461+
shprint(sh.cp, exe, self.python_exe)
462+
break
463+
464+
self.ctx.hostpython = self.python_exe

pythonforandroid/recipe.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -773,9 +773,9 @@ def clean_build(self, arch=None):
773773
@property
774774
def real_hostpython_location(self):
775775
host_name = 'host{}'.format(self.ctx.python_recipe.name)
776-
host_build = Recipe.get_recipe(host_name, self.ctx).get_build_dir()
777776
if host_name in ['hostpython2', 'hostpython3']:
778-
return join(host_build, 'native-build', 'python')
777+
python_recipe = Recipe.get_recipe(host_name, self.ctx)
778+
return python_recipe.python_exe
779779
else:
780780
python_recipe = self.ctx.python_recipe
781781
return 'python{}'.format(python_recipe.version)

pythonforandroid/recipes/python2/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ class Python2Recipe(GuestPythonRecipe):
3232
'patches/fix-gethostbyaddr.patch',
3333
'patches/fix-posix-declarations.patch',
3434
'patches/fix-pwd-gecos.patch',
35-
'patches/fix-ctypes-util-find-library.patch']
35+
'patches/fix-ctypes-util-find-library.patch',
36+
'patches/fix-interpreter-version.patch',
37+
]
3638

3739
configure_args = ('--host={android_host}',
3840
'--build={android_build}',
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- Python-2.7.15/configure.orig 2018-04-30 00:47:33.000000000 +0200
2+
+++ Python-2.7.15/configure 2019-08-16 14:09:48.696030207 +0200
3+
@@ -2903,7 +2903,7 @@ case $host_os in *\ *) host_os=`echo "$h
4+
# pybuilddir.txt will be created by --generate-posix-vars in the Makefile
5+
rm -f pybuilddir.txt
6+
7+
-for ac_prog in python$PACKAGE_VERSION python3 python
8+
+for ac_prog in python$PACKAGE_VERSION python2 python
9+
do
10+
# Extract the first word of "$ac_prog", so it can be a program name with args.
11+
set dummy $ac_prog; ac_word=$2

0 commit comments

Comments
 (0)