Skip to content

Commit

Permalink
p4a support for creating aar libraries
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
zworkb committed Mar 29, 2020
1 parent c47f697 commit 13287b2
Show file tree
Hide file tree
Showing 20 changed files with 553 additions and 183 deletions.
35 changes: 30 additions & 5 deletions pythonforandroid/bdistapk.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@ def argv_contains(t):
return False


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

user_options = []

@property
def package_type(self):
raise NotImplementedError("Subclass must define package_type")

def initialize_options(self):
for option in self.user_options:
setattr(self, option[0].strip('=').replace('-', '_'), None)

option_dict = self.distribution.get_option_dict('apk')
option_dict = self.distribution.get_option_dict(self.package_type)

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

def finalize_options(self):

setup_options = self.distribution.get_option_dict('apk')
setup_options = self.distribution.get_option_dict(self.package_type)
for (option, (source, value)) in setup_options.items():
if source == 'command line':
continue
Expand Down Expand Up @@ -76,7 +79,7 @@ def run(self):
self.prepare_build_dir()

from pythonforandroid.entrypoints import main
sys.argv[1] = 'apk'
sys.argv[1] = self.package_type
main()

def prepare_build_dir(self):
Expand Down Expand Up @@ -128,6 +131,28 @@ def prepare_build_dir(self):
)


class BdistAPK(Bdist):
"""
distutil command handler for 'apk'
"""
description = 'Create an APK with python-for-android'

@property
def package_type(self):
return "apk"


class BdistAAR(Bdist):
"""
distutil command handler for 'aar'
"""
description = 'Create an AAR with python-for-android'

@property
def package_type(self):
return "aar"


def _set_user_options():
# This seems like a silly way to do things, but not sure if there's a
# better way to pass arbitrary options onwards to p4a
Expand Down
37 changes: 21 additions & 16 deletions pythonforandroid/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pythonforandroid.recipe import Recipe


def copy_files(src_root, dest_root, override=True):
def copy_files(src_root, dest_root, override=True, symlink=False):
for root, dirnames, filenames in walk(src_root):
for filename in filenames:
subdir = normpath(root.replace(src_root, ""))
Expand All @@ -29,7 +29,10 @@ def copy_files(src_root, dest_root, override=True):
if override and os.path.exists(dest_file):
os.unlink(dest_file)
if not os.path.exists(dest_file):
shutil.copy(src_file, dest_file)
if symlink:
os.symlink(src_file, dest_file)
else:
shutil.copy(src_file, dest_file)
else:
os.makedirs(dest_file)

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

def prepare_build_dir(self):
'''Ensure that a build dir exists for the recipe. This same single
dir will be used for building all different archs.'''
"""Ensure that a build dir exists for the recipe. This same single
dir will be used for building all different archs."""
self.build_dir = self.get_build_dir()
self.common_dir = self.get_common_dir()
copy_files(join(self.bootstrap_dir, 'build'), self.build_dir)
copy_files(join(self.common_dir, 'build'), self.build_dir,
override=False)
if self.ctx.symlink_java_src:
info('Symlinking java src instead of copying')
shprint(sh.rm, '-r', join(self.build_dir, 'src'))
shprint(sh.mkdir, join(self.build_dir, 'src'))
for dirn in listdir(join(self.bootstrap_dir, 'build', 'src')):
shprint(sh.ln, '-s', join(self.bootstrap_dir, 'build', 'src', dirn),
join(self.build_dir, 'src'))

# get all bootstrap names along the __mro__, cutting off Bootstrap and object
classes = self.__class__.__mro__[:-2]
bootstrap_names = [cls.name for cls in classes] + ['common']
bootstrap_dirs = [
join(self.ctx.root_dir, 'bootstraps', bootstrap_name)
for bootstrap_name
in reversed(bootstrap_names)
]
# now do a cumulative copy of all bootstrap dirs
for bootstrap_dir in bootstrap_dirs:
copy_files(join(bootstrap_dir, 'build'), self.build_dir, symlink=self.ctx.symlink_java_src)

with current_directory(self.build_dir):
with open('project.properties', 'w') as fileh:
fileh.write('target=android-{}'.format(self.ctx.android_api))
Expand Down
7 changes: 4 additions & 3 deletions pythonforandroid/bootstraps/common/build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ def compile_dir(dfn, optimize_python=True):
def make_package(args):
# If no launcher is specified, require a main.py/main.pyo:
if (get_bootstrap_name() != "sdl" or args.launcher is None) and \
get_bootstrap_name() != "webview":
get_bootstrap_name() not in ["webview", "service_library"]:
# (webview doesn't need an entrypoint, apparently)
if args.private is None or (
not exists(join(realpath(args.private), 'main.py')) and
Expand Down Expand Up @@ -529,8 +529,9 @@ def make_package(args):
aars=aars,
jars=jars,
android_api=android_api,
build_tools_version=build_tools_version
)
build_tools_version=build_tools_version,
is_library=get_bootstrap_name() == 'service_library',
)

# ant build templates
render(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ protected static ArrayList<String> getLibraries(File libsDir) {
libsList.add("python3.5m");
libsList.add("python3.6m");
libsList.add("python3.7m");
libsList.add("python3.8m");
libsList.add("main");
return libsList;
}
Expand All @@ -60,7 +61,7 @@ public static void loadLibraries(File filesDir, File libsDir) {
// load, and it has failed, give a more
// general error
Log.v(TAG, "Library loading error: " + e.getMessage());
if (lib.startsWith("python3.7") && !foundPython) {
if (lib.startsWith("python3.8") && !foundPython) {
throw new java.lang.RuntimeException("Could not load any libpythonXXX.so");
} else if (lib.startsWith("python")) {
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ allprojects {
}
}

{% if is_library %}
apply plugin: 'com.android.library'
{% else %}
apply plugin: 'com.android.application'
{% endif %}

android {
compileSdkVersion {{ android_api }}
Expand Down
1 change: 0 additions & 1 deletion pythonforandroid/bootstraps/common/build/whitelist.txt
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
# put files here that you need to un-blacklist
9 changes: 9 additions & 0 deletions pythonforandroid/bootstraps/service_library/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from pythonforandroid.bootstraps.service_only import ServiceOnlyBootstrap


class ServiceLibraryBootstrap(ServiceOnlyBootstrap):

name = 'service_library'


bootstrap = ServiceLibraryBootstrap()
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

#define BOOTSTRAP_NAME_LIBRARY
#define BOOTSTRAP_USES_NO_SDL_HEADERS

const char bootstrap_name[] = "service_library";

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.kivy.android;

import android.app.Activity;

// Required by PythonService class
public class PythonActivity extends Activity {

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// This string is autogenerated by ChangeAppSettings.sh, do not change
// spaces amount
package org.renpy.android;

import java.io.*;

import android.content.Context;
import android.util.Log;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;

import java.util.zip.GZIPInputStream;

import android.content.res.AssetManager;

import org.kamranzafar.jtar.*;

public class AssetExtract {

private AssetManager mAssetManager = null;
private Context ctx = null;

public AssetExtract(Context context) {
ctx = context;
mAssetManager = ctx.getAssets();
}

public boolean extractTar(String asset, String target) {

byte buf[] = new byte[1024 * 1024];

InputStream assetStream = null;
TarInputStream tis = null;

try {
assetStream = mAssetManager.open(asset, AssetManager.ACCESS_STREAMING);
tis = new TarInputStream(new BufferedInputStream(new GZIPInputStream(new BufferedInputStream(assetStream, 8192)), 8192));
} catch (IOException e) {
Log.e("python", "opening up extract tar", e);
return false;
}

while (true) {
TarEntry entry = null;

try {
entry = tis.getNextEntry();
} catch ( java.io.IOException e ) {
Log.e("python", "extracting tar", e);
return false;
}

if ( entry == null ) {
break;
}

Log.v("python", "extracting " + entry.getName());

if (entry.isDirectory()) {

try {
new File(target +"/" + entry.getName()).mkdirs();
} catch ( SecurityException e ) { };

continue;
}

OutputStream out = null;
String path = target + "/" + entry.getName();

try {
out = new BufferedOutputStream(new FileOutputStream(path), 8192);
} catch ( FileNotFoundException e ) {
} catch ( SecurityException e ) { };

if ( out == null ) {
Log.e("python", "could not open " + path);
return false;
}

try {
while (true) {
int len = tis.read(buf);

if (len == -1) {
break;
}

out.write(buf, 0, len);
}

out.flush();
out.close();
} catch ( java.io.IOException e ) {
Log.e("python", "extracting zip", e);
return false;
}
}

try {
tis.close();
assetStream.close();
} catch (IOException e) {
// pass
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="{{ args.package }}"
android:versionCode="{{ args.numeric_version }}"
android:versionName="{{ args.version }}">

<!-- Android 2.3.3 -->
<uses-sdk android:minSdkVersion="{{ args.min_sdk_version }}" android:targetSdkVersion="{{ android_api }}" />

<application>
{% for name in service_names %}
<service android:name="{{ args.package }}.Service{{ name|capitalize }}"
android:process=":service_{{ name }}"
android:exported="true" />
{% endfor %}
</application>

</manifest>
Loading

0 comments on commit 13287b2

Please sign in to comment.