Skip to content

Commit e8873f0

Browse files
committed
p4a support for creating aar libraries
* ported @xuhcc's PR 1063 to recent p4a master * added python3.8 support to pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java * openssl recipe switched to version '1.1.1e' because in 1.1.1 there was a print without () * bootstraps dirs are cumulatively copied based on their inheritance, can be arbitrarily deep * add symlink param to copy_files, when set the copy target are symlinked * support for the aar directive in buildozer * create a 'p4a aar' command, so that lots of cluttering conditionals can be moved away from toolchain.apk() * began to remove ant support (@inclement allowed me to do so) * renamed library bootstrap to service_library, because that describe it better * test setup setup_testlib_service.py
1 parent 22eb6c9 commit e8873f0

File tree

20 files changed

+552
-165
lines changed

20 files changed

+552
-165
lines changed

pythonforandroid/bdistapk.py

+30-5
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@ def argv_contains(t):
1515
return False
1616

1717

18-
class BdistAPK(Command):
19-
description = 'Create an APK with python-for-android'
18+
class Bdist(Command):
2019

2120
user_options = []
2221

22+
@property
23+
def package_type(self):
24+
raise NotImplementedError("Subclass must define package_type")
25+
2326
def initialize_options(self):
2427
for option in self.user_options:
2528
setattr(self, option[0].strip('=').replace('-', '_'), None)
2629

27-
option_dict = self.distribution.get_option_dict('apk')
30+
option_dict = self.distribution.get_option_dict(self.package_type)
2831

2932
# This is a hack, we probably aren't supposed to loop through
3033
# the option_dict so early because distutils does exactly the
@@ -35,7 +38,7 @@ def initialize_options(self):
3538

3639
def finalize_options(self):
3740

38-
setup_options = self.distribution.get_option_dict('apk')
41+
setup_options = self.distribution.get_option_dict(self.package_type)
3942
for (option, (source, value)) in setup_options.items():
4043
if source == 'command line':
4144
continue
@@ -76,7 +79,7 @@ def run(self):
7679
self.prepare_build_dir()
7780

7881
from pythonforandroid.entrypoints import main
79-
sys.argv[1] = 'apk'
82+
sys.argv[1] = self.package_type
8083
main()
8184

8285
def prepare_build_dir(self):
@@ -128,6 +131,28 @@ def prepare_build_dir(self):
128131
)
129132

130133

134+
class BdistAPK(Bdist):
135+
"""
136+
distutil command handler for 'apk'
137+
"""
138+
description = 'Create an APK with python-for-android'
139+
140+
@property
141+
def package_type(self):
142+
return "apk"
143+
144+
145+
class BdistAAR(Bdist):
146+
"""
147+
distutil command handler for 'aar'
148+
"""
149+
description = 'Create an AAR with python-for-android'
150+
151+
@property
152+
def package_type(self):
153+
return "aar"
154+
155+
131156
def _set_user_options():
132157
# This seems like a silly way to do things, but not sure if there's a
133158
# better way to pass arbitrary options onwards to p4a

pythonforandroid/bootstrap.py

+21-16
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from pythonforandroid.recipe import Recipe
1515

1616

17-
def copy_files(src_root, dest_root, override=True):
17+
def copy_files(src_root, dest_root, override=True, symlink=False):
1818
for root, dirnames, filenames in walk(src_root):
1919
for filename in filenames:
2020
subdir = normpath(root.replace(src_root, ""))
@@ -29,7 +29,10 @@ def copy_files(src_root, dest_root, override=True):
2929
if override and os.path.exists(dest_file):
3030
os.unlink(dest_file)
3131
if not os.path.exists(dest_file):
32-
shutil.copy(src_file, dest_file)
32+
if symlink:
33+
os.symlink(src_file, dest_file)
34+
else:
35+
shutil.copy(src_file, dest_file)
3336
else:
3437
os.makedirs(dest_file)
3538

@@ -109,7 +112,7 @@ def check_recipe_choices(self):
109112
and optional dependencies are being used,
110113
and returns a list of these.'''
111114
recipes = []
112-
built_recipes = self.ctx.recipe_build_order
115+
built_recipes = self.ctx.recipe_build_order or []
113116
for recipe in self.recipe_depends:
114117
if isinstance(recipe, (tuple, list)):
115118
for alternative in recipe:
@@ -138,20 +141,22 @@ def name(self):
138141
return modname.split(".", 2)[-1]
139142

140143
def prepare_build_dir(self):
141-
'''Ensure that a build dir exists for the recipe. This same single
142-
dir will be used for building all different archs.'''
144+
"""Ensure that a build dir exists for the recipe. This same single
145+
dir will be used for building all different archs."""
143146
self.build_dir = self.get_build_dir()
144-
self.common_dir = self.get_common_dir()
145-
copy_files(join(self.bootstrap_dir, 'build'), self.build_dir)
146-
copy_files(join(self.common_dir, 'build'), self.build_dir,
147-
override=False)
148-
if self.ctx.symlink_java_src:
149-
info('Symlinking java src instead of copying')
150-
shprint(sh.rm, '-r', join(self.build_dir, 'src'))
151-
shprint(sh.mkdir, join(self.build_dir, 'src'))
152-
for dirn in listdir(join(self.bootstrap_dir, 'build', 'src')):
153-
shprint(sh.ln, '-s', join(self.bootstrap_dir, 'build', 'src', dirn),
154-
join(self.build_dir, 'src'))
147+
148+
# get all bootstrap names along the __mro__, cutting off Bootstrap and object
149+
classes = self.__class__.__mro__[:-2]
150+
bootstrap_names = [cls.name for cls in classes] + ['common']
151+
bootstrap_dirs = [
152+
join(self.ctx.root_dir, 'bootstraps', bootstrap_name)
153+
for bootstrap_name
154+
in reversed(bootstrap_names)
155+
]
156+
# now do a cumulative copy of all bootstrap dirs
157+
for bootstrap_dir in bootstrap_dirs:
158+
copy_files(join(bootstrap_dir, 'build'), self.build_dir, symlink=self.ctx.symlink_java_src)
159+
155160
with current_directory(self.build_dir):
156161
with open('project.properties', 'w') as fileh:
157162
fileh.write('target=android-{}'.format(self.ctx.android_api))

pythonforandroid/bootstraps/common/build/build.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def compile_dir(dfn, optimize_python=True):
274274
def make_package(args):
275275
# If no launcher is specified, require a main.py/main.pyo:
276276
if (get_bootstrap_name() != "sdl" or args.launcher is None) and \
277-
get_bootstrap_name() != "webview":
277+
get_bootstrap_name() not in ["webview", "service_library"]:
278278
# (webview doesn't need an entrypoint, apparently)
279279
if args.private is None or (
280280
not exists(join(realpath(args.private), 'main.py')) and
@@ -529,8 +529,9 @@ def make_package(args):
529529
aars=aars,
530530
jars=jars,
531531
android_api=android_api,
532-
build_tools_version=build_tools_version
533-
)
532+
build_tools_version=build_tools_version,
533+
is_library=get_bootstrap_name() == 'service_library',
534+
)
534535

535536
# ant build templates
536537
render(

pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ protected static ArrayList<String> getLibraries(File libsDir) {
4040
libsList.add("python3.5m");
4141
libsList.add("python3.6m");
4242
libsList.add("python3.7m");
43+
libsList.add("python3.8m");
4344
libsList.add("main");
4445
return libsList;
4546
}
@@ -60,7 +61,7 @@ public static void loadLibraries(File filesDir, File libsDir) {
6061
// load, and it has failed, give a more
6162
// general error
6263
Log.v(TAG, "Library loading error: " + e.getMessage());
63-
if (lib.startsWith("python3.7") && !foundPython) {
64+
if (lib.startsWith("python3.8") && !foundPython) {
6465
throw new java.lang.RuntimeException("Could not load any libpythonXXX.so");
6566
} else if (lib.startsWith("python")) {
6667
continue;

pythonforandroid/bootstraps/common/build/templates/build.tmpl.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ allprojects {
2222
}
2323
}
2424

25+
{% if is_library %}
26+
apply plugin: 'com.android.library'
27+
{% else %}
2528
apply plugin: 'com.android.application'
29+
{% endif %}
2630

2731
android {
2832
compileSdkVersion {{ android_api }}
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
# put files here that you need to un-blacklist
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from pythonforandroid.bootstraps.service_only import ServiceOnlyBootstrap
2+
3+
4+
class ServiceLibraryBootstrap(ServiceOnlyBootstrap):
5+
6+
name = 'service_library'
7+
8+
9+
bootstrap = ServiceLibraryBootstrap()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
#define BOOTSTRAP_NAME_LIBRARY
3+
#define BOOTSTRAP_USES_NO_SDL_HEADERS
4+
5+
const char bootstrap_name[] = "service_library";
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.kivy.android;
2+
3+
import android.app.Activity;
4+
5+
// Required by PythonService class
6+
public class PythonActivity extends Activity {
7+
8+
}
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// This string is autogenerated by ChangeAppSettings.sh, do not change
2+
// spaces amount
3+
package org.renpy.android;
4+
5+
import java.io.*;
6+
7+
import android.content.Context;
8+
import android.util.Log;
9+
10+
import java.io.BufferedInputStream;
11+
import java.io.BufferedOutputStream;
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
import java.io.FileInputStream;
15+
import java.io.FileOutputStream;
16+
import java.io.File;
17+
18+
import java.util.zip.GZIPInputStream;
19+
20+
import android.content.res.AssetManager;
21+
22+
import org.kamranzafar.jtar.*;
23+
24+
public class AssetExtract {
25+
26+
private AssetManager mAssetManager = null;
27+
private Context ctx = null;
28+
29+
public AssetExtract(Context context) {
30+
ctx = context;
31+
mAssetManager = ctx.getAssets();
32+
}
33+
34+
public boolean extractTar(String asset, String target) {
35+
36+
byte buf[] = new byte[1024 * 1024];
37+
38+
InputStream assetStream = null;
39+
TarInputStream tis = null;
40+
41+
try {
42+
assetStream = mAssetManager.open(asset, AssetManager.ACCESS_STREAMING);
43+
tis = new TarInputStream(new BufferedInputStream(new GZIPInputStream(new BufferedInputStream(assetStream, 8192)), 8192));
44+
} catch (IOException e) {
45+
Log.e("python", "opening up extract tar", e);
46+
return false;
47+
}
48+
49+
while (true) {
50+
TarEntry entry = null;
51+
52+
try {
53+
entry = tis.getNextEntry();
54+
} catch ( java.io.IOException e ) {
55+
Log.e("python", "extracting tar", e);
56+
return false;
57+
}
58+
59+
if ( entry == null ) {
60+
break;
61+
}
62+
63+
Log.v("python", "extracting " + entry.getName());
64+
65+
if (entry.isDirectory()) {
66+
67+
try {
68+
new File(target +"/" + entry.getName()).mkdirs();
69+
} catch ( SecurityException e ) { };
70+
71+
continue;
72+
}
73+
74+
OutputStream out = null;
75+
String path = target + "/" + entry.getName();
76+
77+
try {
78+
out = new BufferedOutputStream(new FileOutputStream(path), 8192);
79+
} catch ( FileNotFoundException e ) {
80+
} catch ( SecurityException e ) { };
81+
82+
if ( out == null ) {
83+
Log.e("python", "could not open " + path);
84+
return false;
85+
}
86+
87+
try {
88+
while (true) {
89+
int len = tis.read(buf);
90+
91+
if (len == -1) {
92+
break;
93+
}
94+
95+
out.write(buf, 0, len);
96+
}
97+
98+
out.flush();
99+
out.close();
100+
} catch ( java.io.IOException e ) {
101+
Log.e("python", "extracting zip", e);
102+
return false;
103+
}
104+
}
105+
106+
try {
107+
tis.close();
108+
assetStream.close();
109+
} catch (IOException e) {
110+
// pass
111+
}
112+
113+
return true;
114+
}
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="{{ args.package }}"
4+
android:versionCode="{{ args.numeric_version }}"
5+
android:versionName="{{ args.version }}">
6+
7+
<!-- Android 2.3.3 -->
8+
<uses-sdk android:minSdkVersion="{{ args.min_sdk_version }}" android:targetSdkVersion="{{ android_api }}" />
9+
10+
<application>
11+
{% for name in service_names %}
12+
<service android:name="{{ args.package }}.Service{{ name|capitalize }}"
13+
android:process=":service_{{ name }}"
14+
android:exported="true" />
15+
{% endfor %}
16+
</application>
17+
18+
</manifest>

0 commit comments

Comments
 (0)