Skip to content

Commit eeddb24

Browse files
authored
Merge pull request #1103 from inclement/ndk-15-inclement
Fixes for NDK 15+
2 parents 922625c + 144bff9 commit eeddb24

File tree

14 files changed

+1340
-76
lines changed

14 files changed

+1340
-76
lines changed

pythonforandroid/archs.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from os.path import (join, dirname)
1+
from os.path import (exists, join, dirname)
22
from os import environ, uname
33
import sys
44
from distutils.spawn import find_executable
@@ -33,13 +33,32 @@ def include_dirs(self):
3333
def get_env(self, with_flags_in_cc=True):
3434
env = {}
3535

36-
env["CFLAGS"] = " ".join([
37-
"-DANDROID", "-mandroid", "-fomit-frame-pointer",
38-
"--sysroot", self.ctx.ndk_platform])
36+
env['CFLAGS'] = ' '.join([
37+
'-DANDROID', '-mandroid', '-fomit-frame-pointer'
38+
' -D__ANDROID_API__={}'.format(self.ctx._android_api),
39+
])
40+
env['LDFLAGS'] = ' '
41+
42+
sysroot = join(self.ctx._ndk_dir, 'sysroot')
43+
if exists(sysroot):
44+
# post-15 NDK per
45+
# https://android.googlesource.com/platform/ndk/+/ndk-r15-release/docs/UnifiedHeaders.md
46+
env['CFLAGS'] += ' -isystem {}/sysroot/usr/include/{}'.format(
47+
self.ctx.ndk_dir, self.ctx.toolchain_prefix)
48+
else:
49+
sysroot = self.ctx.ndk_platform
50+
env['CFLAGS'] += ' -I{}'.format(self.ctx.ndk_platform)
51+
env['CFLAGS'] += ' -isysroot {} '.format(sysroot)
52+
env['CFLAGS'] += '-I' + join(self.ctx.get_python_install_dir(),
53+
'include/python{}'.format(
54+
self.ctx.python_recipe.version[0:3])
55+
)
56+
57+
env['LDFLAGS'] += '--sysroot {} '.format(self.ctx.ndk_platform)
3958

4059
env["CXXFLAGS"] = env["CFLAGS"]
4160

42-
env["LDFLAGS"] = " ".join(['-lm', '-L' + self.ctx.get_libs_dir(self.arch)])
61+
env["LDFLAGS"] += " ".join(['-lm', '-L' + self.ctx.get_libs_dir(self.arch)])
4362

4463
if self.ctx.ndk == 'crystax':
4564
env['LDFLAGS'] += ' -L{}/sources/crystax/libs/{} -lcrystax'.format(self.ctx.ndk_dir, self.arch)

pythonforandroid/build.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -666,12 +666,16 @@ def biglink(ctx, arch):
666666
info('target {}'.format(join(ctx.get_libs_dir(arch.arch),
667667
'libpymodules.so')))
668668
do_biglink = copylibs_function if ctx.copy_libs else biglink_function
669-
do_biglink(
670-
join(ctx.get_libs_dir(arch.arch), 'libpymodules.so'),
671-
obj_dir.split(' '),
672-
extra_link_dirs=[join(ctx.bootstrap.build_dir,
673-
'obj', 'local', arch.arch)],
674-
env=env)
669+
670+
# Move to the directory containing crtstart_so.o and crtend_so.o
671+
# This is necessary with newer NDKs? A gcc bug?
672+
with current_directory(join(ctx.ndk_platform, 'usr', 'lib')):
673+
do_biglink(
674+
join(ctx.get_libs_dir(arch.arch), 'libpymodules.so'),
675+
obj_dir.split(' '),
676+
extra_link_dirs=[join(ctx.bootstrap.build_dir,
677+
'obj', 'local', arch.arch)],
678+
env=env)
675679

676680

677681
def biglink_function(soname, objs_paths, extra_link_dirs=[], env=None):

pythonforandroid/recipes/hostpython2/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from pythonforandroid.toolchain import Recipe, shprint, current_directory, info, warning
33
from os.path import join, exists
44
from os import chdir
5+
import os
56
import sh
67

78

@@ -36,6 +37,8 @@ def build_arch(self, arch):
3637
'hostpgen')
3738
return
3839

40+
if 'LIBS' in os.environ:
41+
os.environ.pop('LIBS')
3942
configure = sh.Command('./configure')
4043

4144
shprint(configure)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
""" ifaddrs for Android
2+
"""
3+
from os.path import join, exists
4+
import sh
5+
from pythonforandroid.logger import info, shprint
6+
7+
from pythonforandroid.toolchain import (CompiledComponentsPythonRecipe,
8+
current_directory)
9+
10+
class IFAddrRecipe(CompiledComponentsPythonRecipe):
11+
version = 'master'
12+
url = 'git+https://github.com/morristech/android-ifaddrs.git'
13+
depends = [('hostpython2', 'hostpython3'), ('python2', 'python3crystax')]
14+
15+
call_hostpython_via_targetpython = False
16+
site_packages_name = 'ifaddrs'
17+
generated_libraries = ['libifaddrs.so']
18+
19+
def should_build(self, arch):
20+
"""It's faster to build than check"""
21+
return not (exists(join(self.ctx.libs_dir, arch.arch, 'libifaddrs.so'))
22+
and exists(join(self.ctx.get_python_install_dir(), 'lib'
23+
"libifaddrs.so"))
24+
)
25+
26+
def prebuild_arch(self, arch):
27+
"""Make the build and target directories"""
28+
path = self.get_build_dir(arch.arch)
29+
if not exists(path):
30+
info("creating {}".format(path))
31+
shprint(sh.mkdir, '-p', path)
32+
33+
def build_arch(self, arch):
34+
"""simple shared compile"""
35+
env = self.get_recipe_env(arch, with_flags_in_cc=False)
36+
for path in (self.get_build_dir(arch.arch),
37+
join(self.ctx.python_recipe.get_build_dir(arch.arch), 'Lib'),
38+
join(self.ctx.python_recipe.get_build_dir(arch.arch), 'Include'),
39+
):
40+
if not exists(path):
41+
info("creating {}".format(path))
42+
shprint(sh.mkdir, '-p', path)
43+
cli = env['CC'].split()
44+
cc = sh.Command(cli[0])
45+
46+
with current_directory(self.get_build_dir(arch.arch)):
47+
cflags = env['CFLAGS'].split()
48+
cflags.extend(['-I.', '-c', '-l.', 'ifaddrs.c', '-I.'])
49+
shprint(cc, *cflags, _env=env)
50+
51+
cflags = env['CFLAGS'].split()
52+
cflags.extend(['-shared', '-I.', 'ifaddrs.o', '-o', 'libifaddrs.so'])
53+
cflags.extend(env['LDFLAGS'].split())
54+
shprint(cc, *cflags, _env=env)
55+
56+
shprint(sh.cp, 'libifaddrs.so', self.ctx.get_libs_dir(arch.arch))
57+
shprint(sh.cp, "libifaddrs.so", join(self.ctx.get_python_install_dir(), 'lib'))
58+
# drop header in to the Python include directory
59+
shprint(sh.cp, "ifaddrs.h", join(self.ctx.get_python_install_dir(),
60+
'include/python{}'.format(
61+
self.ctx.python_recipe.version[0:3]
62+
)
63+
)
64+
)
65+
include_path = join(self.ctx.python_recipe.get_build_dir(arch.arch), 'Include')
66+
shprint(sh.cp, "ifaddrs.h", include_path)
67+
68+
recipe = IFAddrRecipe()
Lines changed: 74 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,83 @@
1+
from os.path import exists, join
12
from pythonforandroid.recipe import Recipe
2-
from pythonforandroid.logger import shprint
3+
from pythonforandroid.logger import info, shprint
34
from pythonforandroid.util import current_directory
4-
from os.path import exists, join
55
import sh
6-
import glob
76

87

98
class LibffiRecipe(Recipe):
10-
name = 'libffi'
11-
version = 'v3.2.1'
12-
url = 'https://github.com/atgreen/libffi/archive/{version}.zip'
13-
14-
patches = ['remove-version-info.patch']
15-
16-
def get_host(self, arch):
17-
with current_directory(self.get_build_dir(arch.arch)):
18-
host = None
19-
with open('Makefile') as f:
20-
for line in f:
21-
if line.startswith('host = '):
22-
host = line.strip()[7:]
23-
break
24-
25-
if not host or not exists(host):
26-
raise RuntimeError('failed to find build output! ({})'
27-
.format(host))
28-
29-
return host
30-
31-
def should_build(self, arch):
32-
# return not bool(glob.glob(join(self.ctx.get_libs_dir(arch.arch),
33-
# 'libffi.so*')))
34-
return not exists(join(self.ctx.get_libs_dir(arch.arch), 'libffi.so'))
35-
# return not exists(join(self.ctx.get_python_install_dir(), 'lib',
36-
# 'libffi.so'))
37-
38-
def build_arch(self, arch):
39-
env = self.get_recipe_env(arch)
40-
with current_directory(self.get_build_dir(arch.arch)):
41-
if not exists('configure'):
42-
shprint(sh.Command('./autogen.sh'), _env=env)
43-
shprint(sh.Command('autoreconf'), '-vif', _env=env)
44-
shprint(sh.Command('./configure'), '--host=' + arch.toolchain_prefix,
45-
'--prefix=' + self.ctx.get_python_install_dir(),
46-
'--enable-shared', _env=env)
47-
shprint(sh.make, '-j5', 'libffi.la', _env=env)
48-
49-
50-
# dlname = None
51-
# with open(join(host, 'libffi.la')) as f:
52-
# for line in f:
53-
# if line.startswith('dlname='):
54-
# dlname = line.strip()[8:-1]
55-
# break
56-
#
57-
# if not dlname or not exists(join(host, '.libs', dlname)):
58-
# raise RuntimeError('failed to locate shared object! ({})'
59-
# .format(dlname))
60-
61-
# shprint(sh.sed, '-i', 's/^dlname=.*$/dlname=\'libffi.so\'/', join(host, 'libffi.la'))
62-
63-
shprint(sh.cp, '-t', self.ctx.get_libs_dir(arch.arch),
64-
join(self.get_host(arch), '.libs', 'libffi.so')) #,
65-
# join(host, 'libffi.la'))
66-
67-
def get_include_dirs(self, arch):
68-
return [join(self.get_build_dir(arch.arch), self.get_host(arch), 'include')]
9+
name = 'libffi'
10+
version = 'v3.2.1'
11+
url = 'https://github.com/atgreen/libffi/archive/{version}.zip'
12+
13+
patches = ['remove-version-info.patch']
14+
15+
def get_host(self, arch):
16+
with current_directory(self.get_build_dir(arch.arch)):
17+
host = None
18+
with open('Makefile') as f:
19+
for line in f:
20+
if line.startswith('host = '):
21+
host = line.strip()[7:]
22+
break
23+
24+
if not host or not exists(host):
25+
raise RuntimeError('failed to find build output! ({})'
26+
.format(host))
27+
28+
return host
29+
30+
def should_build(self, arch):
31+
return not exists(join(self.ctx.get_libs_dir(arch.arch), 'libffi.so'))
32+
33+
def build_arch(self, arch):
34+
env = self.get_recipe_env(arch)
35+
with current_directory(self.get_build_dir(arch.arch)):
36+
if not exists('configure'):
37+
shprint(sh.Command('./autogen.sh'), _env=env)
38+
shprint(sh.Command('autoreconf'), '-vif', _env=env)
39+
shprint(sh.Command('./configure'),
40+
'--host=' + arch.toolchain_prefix,
41+
'--prefix=' + self.ctx.get_python_install_dir(),
42+
'--enable-shared', _env=env)
43+
#'--with-sysroot={}'.format(self.ctx.ndk_platform),
44+
#'--target={}'.format(arch.toolchain_prefix),
45+
46+
# ndk 15 introduces unified headers required --sysroot and
47+
# -isysroot for libraries and headers. libtool's head explodes
48+
# trying to weave them into it's own magic. The result is a link
49+
# failure tryng to link libc. We call make to compile the bits
50+
# and manually link...
51+
52+
try:
53+
shprint(sh.make, '-j5', 'libffi.la', _env=env)
54+
except sh.ErrorReturnCode_2:
55+
info("make libffi.la failed as expected")
56+
cc = sh.Command(env['CC'].split()[0])
57+
cflags = env['CC'].split()[1:]
58+
59+
cflags.extend(['-march=armv7-a', '-mfloat-abi=softfp', '-mfpu=vfp',
60+
'-mthumb', '-shared', '-fPIC', '-DPIC',
61+
'src/.libs/prep_cif.o', 'src/.libs/types.o',
62+
'src/.libs/raw_api.o', 'src/.libs/java_raw_api.o',
63+
'src/.libs/closures.o', 'src/arm/.libs/sysv.o',
64+
'src/arm/.libs/ffi.o', ]
65+
)
66+
67+
ldflags = env['LDFLAGS'].split()
68+
cflags.extend(ldflags)
69+
cflags.extend(['-Wl,-soname', '-Wl,libffi.so', '-o',
70+
'.libs/libffi.so'])
71+
72+
with current_directory(self.get_host(arch)):
73+
shprint(cc, *cflags, _env=env)
74+
75+
shprint(sh.cp, '-t', self.ctx.get_libs_dir(arch.arch),
76+
join(self.get_host(arch), '.libs', 'libffi.so'))
77+
78+
def get_include_dirs(self, arch):
79+
return [join(self.get_build_dir(arch.arch), self.get_host(arch),
80+
'include')]
6981

7082

7183
recipe = LibffiRecipe()
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""
2+
android libglob
3+
available via '-lglob' LDFLAG
4+
"""
5+
from os.path import exists, join, dirname
6+
from pythonforandroid.toolchain import (CompiledComponentsPythonRecipe,
7+
current_directory)
8+
from pythonforandroid.logger import shprint, info, warning, info_main
9+
import sh
10+
11+
class LibGlobRecipe(CompiledComponentsPythonRecipe):
12+
"""Make a glob.h and glob.so for the python_install_dir()"""
13+
version = '0.0.1'
14+
url = None
15+
#
16+
# glob.h and glob.c extracted from
17+
# https://github.com/white-gecko/TokyoCabinet, e.g.:
18+
# https://raw.githubusercontent.com/white-gecko/TokyoCabinet/master/glob.h
19+
# https://raw.githubusercontent.com/white-gecko/TokyoCabinet/master/glob.c
20+
# and pushed in via patch
21+
name = 'libglob'
22+
23+
depends = [('hostpython2', 'hostpython3'), ('python2', 'python3crystax')]
24+
patches = ['glob.patch']
25+
26+
def should_build(self, arch):
27+
"""It's faster to build than check"""
28+
return True
29+
30+
def prebuild_arch(self, arch):
31+
"""Make the build and target directories"""
32+
path = self.get_build_dir(arch.arch)
33+
if not exists(path):
34+
info("creating {}".format(path))
35+
shprint(sh.mkdir, '-p', path)
36+
37+
def build_arch(self, arch):
38+
"""simple shared compile"""
39+
env = self.get_recipe_env(arch, with_flags_in_cc=False)
40+
for path in (self.get_build_dir(arch.arch),
41+
join(self.ctx.python_recipe.get_build_dir(arch.arch), 'Lib'),
42+
join(self.ctx.python_recipe.get_build_dir(arch.arch), 'Include'),
43+
):
44+
if not exists(path):
45+
info("creating {}".format(path))
46+
shprint(sh.mkdir, '-p', path)
47+
cli = env['CC'].split()
48+
cc = sh.Command(cli[0])
49+
50+
with current_directory(self.get_build_dir(arch.arch)):
51+
cflags = env['CFLAGS'].split()
52+
cflags.extend(['-I.', '-c', '-l.', 'glob.c', '-I.']) # , '-o', 'glob.o'])
53+
shprint(cc, *cflags, _env=env)
54+
55+
cflags = env['CFLAGS'].split()
56+
srindex = cflags.index('--sysroot')
57+
if srindex:
58+
cflags[srindex+1] = self.ctx.ndk_platform
59+
cflags.extend(['-shared', '-I.', 'glob.o', '-o', 'libglob.so'])
60+
shprint(cc, *cflags, _env=env)
61+
62+
shprint(sh.cp, 'libglob.so', join(self.ctx.libs_dir, arch.arch))
63+
shprint(sh.cp, "libglob.so", join(self.ctx.get_python_install_dir(), 'lib'))
64+
# drop header in to the Python include directory
65+
shprint(sh.cp, "glob.h", join(self.ctx.get_python_install_dir(),
66+
'include/python{}'.format(
67+
self.ctx.python_recipe.version[0:3]
68+
)
69+
)
70+
)
71+
include_path = join(self.ctx.python_recipe.get_build_dir(arch.arch), 'Include')
72+
shprint(sh.cp, "glob.h", include_path)
73+
74+
recipe = LibGlobRecipe()

0 commit comments

Comments
 (0)