Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 145 additions & 3 deletions pythonforandroid/build.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import print_function

from os.path import (join, realpath, dirname, expanduser, exists,
split)
from os import environ
split, isdir)
from os import environ, listdir
import os
import glob
import sys
Expand Down Expand Up @@ -421,6 +423,7 @@ def __init__(self):
self.toolchain_version = None

self.local_recipes = None
self.copy_libs = False

# root of the toolchain
self.setup_dirs()
Expand Down Expand Up @@ -485,6 +488,9 @@ def get_libs_dir(self, arch):
ensure_dir(join(self.libs_dir, arch))
return join(self.libs_dir, arch)

def has_lib(self, arch, lib):
return exists(join(self.get_libs_dir(arch), lib))

def has_package(self, name, arch=None):
try:
recipe = Recipe.get_recipe(name, self)
Expand Down Expand Up @@ -617,6 +623,7 @@ def biglink(ctx, arch):
if not len(files):
info('{} recipe has no biglinkable files, skipping'
.format(recipe.name))
continue
info('{} recipe has object files, copying'.format(recipe.name))
files.append(obj_dir)
shprint(sh.cp, '-r', *files)
Expand All @@ -631,7 +638,8 @@ def biglink(ctx, arch):
info('Biglinking')
info('target {}'.format(join(ctx.get_libs_dir(arch.arch),
'libpymodules.so')))
biglink_function(
do_biglink = copylibs_function if ctx.copy_libs else biglink_function
do_biglink(
join(ctx.get_libs_dir(arch.arch), 'libpymodules.so'),
obj_dir.split(' '),
extra_link_dirs=[join(ctx.bootstrap.build_dir,
Expand Down Expand Up @@ -684,3 +692,137 @@ def biglink_function(soname, objs_paths, extra_link_dirs=[], env=None):
cc = cc.bake(*cc_name.split()[1:])

shprint(cc, '-shared', '-O3', '-o', soname, *unique_args, _env=env)


def copylibs_function(soname, objs_paths, extra_link_dirs=[], env=None):
print('objs_paths are', objs_paths)

re_needso = re.compile(r'^.*\(NEEDED\)\s+Shared library: \[lib(.*)\.so\]\s*$')
blacklist_libs = (
'c',
'stdc++',
'dl',
'python2.7',
'sdl',
'sdl_image',
'sdl_ttf',
'z',
'm',
'GLESv2',
'jpeg',
'png',
'log',

# bootstrap takes care of sdl2 libs (if applicable)
'SDL2',
'SDL2_ttf',
'SDL2_image',
'SDL2_mixer',
)
found_libs = []
sofiles = []
if env and 'READELF' in env:
readelf = env['READELF']
elif 'READELF' in os.environ:
readelf = os.environ['READELF']
else:
readelf = sh.which('readelf').strip()
readelf = sh.Command(readelf).bake('-d')

dest = dirname(soname)

for directory in objs_paths:
for fn in os.listdir(directory):
fn = join(directory, fn)

if not fn.endswith('.libs'):
continue

dirfn = fn[:-1] + 'dirs'
if not exists(dirfn):
continue

with open(fn) as f:
libs = f.read().strip().split(' ')
needed_libs = [lib for lib in libs
if lib and
lib not in blacklist_libs and
lib not in found_libs]

while needed_libs:
print('need libs:\n\t' + '\n\t'.join(needed_libs))

start_needed_libs = needed_libs[:]
found_sofiles = []

with open(dirfn) as f:
libdirs = f.read().split()
for libdir in libdirs:
if not needed_libs:
break

if libdir == dest:
# don't need to copy from dest to dest!
continue

libdir = libdir.strip()
print('scanning', libdir)
for lib in needed_libs[:]:
if lib in found_libs:
continue

if lib.endswith('.a'):
needed_libs.remove(lib)
found_libs.append(lib)
continue

lib_a = 'lib' + lib + '.a'
libpath_a = join(libdir, lib_a)
lib_so = 'lib' + lib + '.so'
libpath_so = join(libdir, lib_so)
plain_so = lib + '.so'
plainpath_so = join(libdir, plain_so)

sopath = None
if exists(libpath_so):
sopath = libpath_so
elif exists(plainpath_so):
sopath = plainpath_so

if sopath:
print('found', lib, 'in', libdir)
found_sofiles.append(sopath)
needed_libs.remove(lib)
found_libs.append(lib)
continue

if exists(libpath_a):
print('found', lib, '(static) in', libdir)
needed_libs.remove(lib)
found_libs.append(lib)
continue

for sofile in found_sofiles:
print('scanning dependencies for', sofile)
out = readelf(sofile)
for line in out.splitlines():
needso = re_needso.match(line)
if needso:
lib = needso.group(1)
if (lib not in needed_libs
and lib not in found_libs
and lib not in blacklist_libs):
needed_libs.append(needso.group(1))

sofiles += found_sofiles

if needed_libs == start_needed_libs:
raise RuntimeError(
'Failed to locate needed libraries!\n\t' +
'\n\t'.join(needed_libs))

print('Copying libraries')
cp = sh.cp.bake('-t', dest)
for lib in sofiles:
shprint(cp, lib)

4 changes: 3 additions & 1 deletion pythonforandroid/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -908,10 +908,12 @@ def get_recipe_env(self, arch):
if self.ctx.python_recipe.from_crystax:
env['LDSHARED'] = env['CC'] + ' -shared'
else:
env['LDSHARED'] = join(self.ctx.root_dir, 'tools', 'liblink')
env['LDSHARED'] = join(self.ctx.root_dir, 'tools', 'liblink.sh')
# shprint(sh.whereis, env['LDSHARED'], _env=env)
env['LIBLINK'] = 'NOTNONE'
env['NDKPLATFORM'] = self.ctx.ndk_platform
if self.ctx.copy_libs:
env['COPYLIBS'] = '1'

# Every recipe uses its own liblink path, object files are
# collected and biglinked later
Expand Down
6 changes: 6 additions & 0 deletions pythonforandroid/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,11 @@ def __init__(self):
dest='local_recipes', default='./p4a-recipes',
help='Directory to look for local recipes')

add_boolean_option(
parser, ['copy-libs'],
default=False,
description='Copy libraries instead of using biglink (Android 4.3+)')

self._read_configuration()

args, unknown = parser.parse_known_args(sys.argv[1:])
Expand Down Expand Up @@ -302,6 +307,7 @@ def __init__(self):
exit(1)

self.ctx.local_recipes = args.local_recipes
self.ctx.copy_libs = args.copy_libs

getattr(self, args.command)(unknown)

Expand Down
47 changes: 32 additions & 15 deletions pythonforandroid/tools/liblink
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ libs = [ ]
objects = [ ]
output = None

copylibs = environ.get('COPYLIBS', '0') == '1'

i = 1
while i < len(sys.argv):
Expand Down Expand Up @@ -62,19 +63,35 @@ while i < len(sys.argv):
objects.append(opt)


f = open(output, "w")
f.close()

print('liblink path is', str(environ.get('LIBLINK_PATH')))
output = join(environ.get('LIBLINK_PATH'), basename(output))

f = open(output + ".libs", "w")
f.write(" ".join(libs))
f.close()

sys.exit(subprocess.call([
environ.get('LD'), '-r',
'-o', output + '.o'
#, '-arch', environ.get('ARCH')
] + objects))

abs_output = join(environ.get('LIBLINK_PATH'), basename(output))

if not copylibs:
f = open(output, "w")
f.close()

output = abs_output

f = open(output + ".libs", "w")
f.write(" ".join(libs))
f.close()

sys.exit(subprocess.call([
environ.get('LD'), '-r',
'-o', output + '.o'
#, '-arch', environ.get('ARCH')
] + objects))
else:
with open(abs_output + '.libs', 'w') as f_libs:
with open(abs_output + '.libdirs', 'w') as f_libdirs:
for l in libs:
if l[1] == 'l':
f_libs.write(l[2:])
f_libs.write(' ')
else:
f_libdirs.write(l[2:])
f_libdirs.write(' ')

libargs = ' '.join(["'%s'" % arg for arg in sys.argv[1:]])
cmd = '%s -shared %s %s' % (environ['CC'], environ['LDFLAGS'], libargs)
sys.exit(subprocess.call(cmd, shell=True))
3 changes: 3 additions & 0 deletions pythonforandroid/tools/liblink.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

PYTHONPATH= python `dirname $0`/liblink "$@"