Skip to content

Commit 7fb74fe

Browse files
committed
add copy libs option
1 parent fdb2b5f commit 7fb74fe

File tree

5 files changed

+189
-19
lines changed

5 files changed

+189
-19
lines changed

pythonforandroid/build.py

Lines changed: 145 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from __future__ import print_function
2+
13
from os.path import (join, realpath, dirname, expanduser, exists,
2-
split)
3-
from os import environ
4+
split, isdir)
5+
from os import environ, listdir
46
import os
57
import glob
68
import sys
@@ -421,6 +423,7 @@ def __init__(self):
421423
self.toolchain_version = None
422424

423425
self.local_recipes = None
426+
self.copy_libs = False
424427

425428
# root of the toolchain
426429
self.setup_dirs()
@@ -485,6 +488,9 @@ def get_libs_dir(self, arch):
485488
ensure_dir(join(self.libs_dir, arch))
486489
return join(self.libs_dir, arch)
487490

491+
def has_lib(self, arch, lib):
492+
return exists(join(self.get_libs_dir(arch), lib))
493+
488494
def has_package(self, name, arch=None):
489495
try:
490496
recipe = Recipe.get_recipe(name, self)
@@ -617,6 +623,7 @@ def biglink(ctx, arch):
617623
if not len(files):
618624
info('{} recipe has no biglinkable files, skipping'
619625
.format(recipe.name))
626+
continue
620627
info('{} recipe has object files, copying'.format(recipe.name))
621628
files.append(obj_dir)
622629
shprint(sh.cp, '-r', *files)
@@ -631,7 +638,8 @@ def biglink(ctx, arch):
631638
info('Biglinking')
632639
info('target {}'.format(join(ctx.get_libs_dir(arch.arch),
633640
'libpymodules.so')))
634-
biglink_function(
641+
do_biglink = copylibs_function if ctx.copy_libs else biglink_function
642+
do_biglink(
635643
join(ctx.get_libs_dir(arch.arch), 'libpymodules.so'),
636644
obj_dir.split(' '),
637645
extra_link_dirs=[join(ctx.bootstrap.build_dir,
@@ -684,3 +692,137 @@ def biglink_function(soname, objs_paths, extra_link_dirs=[], env=None):
684692
cc = cc.bake(*cc_name.split()[1:])
685693

686694
shprint(cc, '-shared', '-O3', '-o', soname, *unique_args, _env=env)
695+
696+
697+
def copylibs_function(soname, objs_paths, extra_link_dirs=[], env=None):
698+
print('objs_paths are', objs_paths)
699+
700+
re_needso = re.compile(r'^.*\(NEEDED\)\s+Shared library: \[lib(.*)\.so\]\s*$')
701+
blacklist_libs = (
702+
'c',
703+
'stdc++',
704+
'dl',
705+
'python2.7',
706+
'sdl',
707+
'sdl_image',
708+
'sdl_ttf',
709+
'z',
710+
'm',
711+
'GLESv2',
712+
'jpeg',
713+
'png',
714+
'log',
715+
716+
# bootstrap takes care of sdl2 libs (if applicable)
717+
'SDL2',
718+
'SDL2_ttf',
719+
'SDL2_image',
720+
'SDL2_mixer',
721+
)
722+
found_libs = []
723+
sofiles = []
724+
if env and 'READELF' in env:
725+
readelf = env['READELF']
726+
elif 'READELF' in os.environ:
727+
readelf = os.environ['READELF']
728+
else:
729+
readelf = sh.which('readelf').strip()
730+
readelf = sh.Command(readelf).bake('-d')
731+
732+
dest = dirname(soname)
733+
734+
for directory in objs_paths:
735+
for fn in os.listdir(directory):
736+
fn = join(directory, fn)
737+
738+
if not fn.endswith('.libs'):
739+
continue
740+
741+
dirfn = fn[:-1] + 'dirs'
742+
if not exists(dirfn):
743+
continue
744+
745+
with open(fn) as f:
746+
libs = f.read().strip().split(' ')
747+
needed_libs = [lib for lib in libs
748+
if lib and
749+
lib not in blacklist_libs and
750+
lib not in found_libs]
751+
752+
while needed_libs:
753+
print('need libs:\n\t' + '\n\t'.join(needed_libs))
754+
755+
start_needed_libs = needed_libs[:]
756+
found_sofiles = []
757+
758+
with open(dirfn) as f:
759+
libdirs = f.read().split()
760+
for libdir in libdirs:
761+
if not needed_libs:
762+
break
763+
764+
if libdir == dest:
765+
# don't need to copy from dest to dest!
766+
continue
767+
768+
libdir = libdir.strip()
769+
print('scanning', libdir)
770+
for lib in needed_libs[:]:
771+
if lib in found_libs:
772+
continue
773+
774+
if lib.endswith('.a'):
775+
needed_libs.remove(lib)
776+
found_libs.append(lib)
777+
continue
778+
779+
lib_a = 'lib' + lib + '.a'
780+
libpath_a = join(libdir, lib_a)
781+
lib_so = 'lib' + lib + '.so'
782+
libpath_so = join(libdir, lib_so)
783+
plain_so = lib + '.so'
784+
plainpath_so = join(libdir, plain_so)
785+
786+
sopath = None
787+
if exists(libpath_so):
788+
sopath = libpath_so
789+
elif exists(plainpath_so):
790+
sopath = plainpath_so
791+
792+
if sopath:
793+
print('found', lib, 'in', libdir)
794+
found_sofiles.append(sopath)
795+
needed_libs.remove(lib)
796+
found_libs.append(lib)
797+
continue
798+
799+
if exists(libpath_a):
800+
print('found', lib, '(static) in', libdir)
801+
needed_libs.remove(lib)
802+
found_libs.append(lib)
803+
continue
804+
805+
for sofile in found_sofiles:
806+
print('scanning dependencies for', sofile)
807+
out = readelf(sofile)
808+
for line in out.splitlines():
809+
needso = re_needso.match(line)
810+
if needso:
811+
lib = needso.group(1)
812+
if (lib not in needed_libs
813+
and lib not in found_libs
814+
and lib not in blacklist_libs):
815+
needed_libs.append(needso.group(1))
816+
817+
sofiles += found_sofiles
818+
819+
if needed_libs == start_needed_libs:
820+
raise RuntimeError(
821+
'Failed to locate needed libraries!\n\t' +
822+
'\n\t'.join(needed_libs))
823+
824+
print('Copying libraries')
825+
cp = sh.cp.bake('-t', dest)
826+
for lib in sofiles:
827+
shprint(cp, lib)
828+

pythonforandroid/recipe.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,10 +908,12 @@ def get_recipe_env(self, arch):
908908
if self.ctx.python_recipe.from_crystax:
909909
env['LDSHARED'] = env['CC'] + ' -shared'
910910
else:
911-
env['LDSHARED'] = join(self.ctx.root_dir, 'tools', 'liblink')
911+
env['LDSHARED'] = join(self.ctx.root_dir, 'tools', 'liblink.sh')
912912
# shprint(sh.whereis, env['LDSHARED'], _env=env)
913913
env['LIBLINK'] = 'NOTNONE'
914914
env['NDKPLATFORM'] = self.ctx.ndk_platform
915+
if self.ctx.copy_libs:
916+
env['COPYLIBS'] = '1'
915917

916918
# Every recipe uses its own liblink path, object files are
917919
# collected and biglinked later

pythonforandroid/toolchain.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,11 @@ def __init__(self):
268268
dest='local_recipes', default='./p4a-recipes',
269269
help='Directory to look for local recipes')
270270

271+
add_boolean_option(
272+
parser, ['copy-libs'],
273+
default=False,
274+
description='Copy libraries instead of using biglink (Android 4.3+)')
275+
271276
self._read_configuration()
272277

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

304309
self.ctx.local_recipes = args.local_recipes
310+
self.ctx.copy_libs = args.copy_libs
305311

306312
getattr(self, args.command)(unknown)
307313

pythonforandroid/tools/liblink

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ libs = [ ]
1010
objects = [ ]
1111
output = None
1212

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

1415
i = 1
1516
while i < len(sys.argv):
@@ -62,19 +63,35 @@ while i < len(sys.argv):
6263
objects.append(opt)
6364

6465

65-
f = open(output, "w")
66-
f.close()
67-
6866
print('liblink path is', str(environ.get('LIBLINK_PATH')))
69-
output = join(environ.get('LIBLINK_PATH'), basename(output))
70-
71-
f = open(output + ".libs", "w")
72-
f.write(" ".join(libs))
73-
f.close()
74-
75-
sys.exit(subprocess.call([
76-
environ.get('LD'), '-r',
77-
'-o', output + '.o'
78-
#, '-arch', environ.get('ARCH')
79-
] + objects))
80-
67+
abs_output = join(environ.get('LIBLINK_PATH'), basename(output))
68+
69+
if not copylibs:
70+
f = open(output, "w")
71+
f.close()
72+
73+
output = abs_output
74+
75+
f = open(output + ".libs", "w")
76+
f.write(" ".join(libs))
77+
f.close()
78+
79+
sys.exit(subprocess.call([
80+
environ.get('LD'), '-r',
81+
'-o', output + '.o'
82+
#, '-arch', environ.get('ARCH')
83+
] + objects))
84+
else:
85+
with open(abs_output + '.libs', 'w') as f_libs:
86+
with open(abs_output + '.libdirs', 'w') as f_libdirs:
87+
for l in libs:
88+
if l[1] == 'l':
89+
f_libs.write(l[2:])
90+
f_libs.write(' ')
91+
else:
92+
f_libdirs.write(l[2:])
93+
f_libdirs.write(' ')
94+
95+
libargs = ' '.join(["'%s'" % arg for arg in sys.argv[1:]])
96+
cmd = '%s -shared %s %s' % (environ['CC'], environ['LDFLAGS'], libargs)
97+
sys.exit(subprocess.call(cmd, shell=True))

pythonforandroid/tools/liblink.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
PYTHONPATH= python `dirname $0`/liblink "$@"

0 commit comments

Comments
 (0)