Changes between Initial Version and Version 1 of PyOtherSideForAndroidNotesArchive


Ignore:
Timestamp:
Nov 15, 2014, 8:37:09 PM (9 years ago)
Author:
Martin Kolman
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • PyOtherSideForAndroidNotesArchive

    v1 v1  
     1= !PyOtherSide for Android development note archive =
     2
     3A litle out of date development notes from the !PyOtherSide for Android porting project. !PyOtherSide now works on Android and you can check the main [wiki:PyOtherSideForAndroid PyOtherSide for Android article] for more details.
     4
     5== !PyOtherSide ==
     6
     7=== Setting up ===
     8This are the version of software versions I have used for my experiments, it is possible other versions might also work, but this is what I have worked with.
     9
     10'''Get !PyOtherSide 1.3 source code'''
     11
     12{{{wget https://thp.io/2011/pyotherside/pyotherside-1.3.0.tar.gz}}}
     13
     14'''Get & install the Qt 5.3 Android SDK'''
     15
     16Basically just follow [http://qt-project.org/doc/qt-5/androidgs.html the guide] on the Qt wiki to get the Qt 5 Android SDK & it's dependencies installed.
     17
     18'''Configure an Android device to be accessible by ADB'''
     19I've tested the resulting software on my HP TouchPad running Cyanogenmod Android 4.0, accessible with ADB. So this 'guide/howto' expect that you also have an Android device accessible over ADB. The Android emulator might also work, but I haven't tried that.
     20
     21=== Compiling for Android ===
     22
     23==== Preparing the !PyOtherSide !QtCreator project ====
     24'''If you don't care and just want the project tarball, skip to the end of this section. :)'''
     25
     26
     27Unpack the !PyOtherSide tarball and enter the resulting directory:
     28{{{
     29tar -xvf pyotherside-1.3.0.tar.gz
     30cd pyotherside-1.3.0
     31}}}
     32
     33Next you need a Python 3 compiled for Android, so you can build !PyOtherSide against it. I have found such Python 3 distribution on the [https://code.google.com/p/android-python27/ Python 2.7 for Android project] website and used it drop in library bundle for compiling !PyOtherSide.
     34
     35'''Note about the precompiled Python 3.2 libs'''
     36
     37They originally come from these files:
     38
     39[https://code.google.com/p/android-python27/source/browse/apk_python32/res/raw/python_32.zip https://code.google.com/p/android-python27/source/browse/apk_python32/res/raw/python_32.zip]
     40
     41[https://code.google.com/p/android-python27/source/browse/apk_python32/res/raw/python_extras_32.zip https://code.google.com/p/android-python27/source/browse/apk_python32/res/raw/python_extras_32.zip]
     42
     43The binaries have been modified to drop .so versioning which is not supported on Android and breaks compilation:
     44
     45{{{rpl -R -e libpython3.2m.so.1.0 "libpython3.2.so\x00\x00\x00\x00\x00" .}}}
     46
     47'''End of the note about the precompiled Python 3.2 libs'''
     48
     49So download the lib bundle tarball to the Pyotherside folder and unpack it here:
     50{{{
     51wget http://www.modrana.org/platforms/android/pyotherside/guide/python3_android_libs_bundle.tar.gz
     52tar xvf python3_android_libs_bundle.tar.gz
     53}}}
     54
     55When you are done, there should be a ''libs'' folder in your ''pyotherside-1.3.0 folder''.
     56
     57In the next step we need to tell the project to use the Android compiled libraries instead of the system ones, so edit the ''src/src.pro'' file and replace the line reading:
     58{{{
     59INCLUDEPATH += .
     60}}}
     61With this:
     62{{{
     63INCLUDEPATH += $$PWD/../libs/python32/build_dependencies/ .
     64LIBS += -L$$PWD/../libs/python32 -lpython3.2 -ldl
     65}}}
     66
     67Then in ''tests/tests.pro'' replace:
     68{{{
     69INCLUDEPATH += . ../src
     70}}}
     71
     72With:
     73
     74{{{
     75INCLUDEPATH += $$PWD/../libs/python32/build_dependencies/ . ../src
     76LIBS += -L$$PWD/../libs/python32 -lpython3.2
     77}}}
     78
     79This tells the project to use our locally available Python 3 compiled for Android instead of the system-wide Python 3 when building !PyOtherSide.
     80
     81We also need to prevent fetching some other local Python stuff. Therefore in ''python.pri'' (directly in the ''pyotherside-1.3.0'' folder) comment out these lines:
     82
     83{{{
     84isEmpty(PYTHON_CONFIG) {
     85    PYTHON_CONFIG = python3-config
     86}
     87}}}
     88
     89And these lines:
     90{{{
     91QMAKE_LIBS += $$system($$PYTHON_CONFIG --ldflags)
     92QMAKE_CXXFLAGS += $$system($$PYTHON_CONFIG --includes)
     93}}}
     94
     95The resulting python.pri file should look like this:
     96
     97{{{
     98#isEmpty(PYTHON_CONFIG) {
     99#    PYTHON_CONFIG = python3-config
     100#}
     101
     102message(PYTHON_CONFIG = $$PYTHON_CONFIG)
     103
     104#QMAKE_LIBS += $$system($$PYTHON_CONFIG --ldflags)
     105#QMAKE_CXXFLAGS += $$system($$PYTHON_CONFIG --includes)
     106}}}
     107
     108And for the last step, we need to edit the ''src/global_libpython_loader.cpp'' file:
     109
     110 * changing ''#ifdef !__linux!__'' into ''#ifndef !__linux!__'' (so that the stuff we need will be used on Android)
     111
     112 * remove a GNU macro an linh.h file (both are broken on Android and break the build):
     113
     114{{{
     115#define _GNU_SOURCE
     116#include <link.h>
     117}}}
     118
     119 * add a few headers file needed for compilation on Android (found by studying compile errors):
     120
     121{{{
     122#include <dlfcn.h>
     123#include <link.h>
     124}}}
     125
     126The resulting file looks like this:
     127
     128{{{
     129/**
     130 * PyOtherSide: Asynchronous Python 3 Bindings for Qt 5
     131 * Copyright (c) 2013, Thomas Perl <m@thp.io>
     132 *
     133 * Permission to use, copy, modify, and/or distribute this software for any
     134 * purpose with or without fee is hereby granted, provided that the above
     135 * copyright notice and this permission notice appear in all copies.
     136 *
     137 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
     138 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
     139 * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
     140 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
     141 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
     142 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     143 * PERFORMANCE OF THIS SOFTWARE.
     144 **/
     145
     146#include "global_libpython_loader.h"
     147
     148namespace GlobalLibPythonLoader {
     149
     150#ifndef __linux__
     151
     152#include <stdio.h>
     153#include <string.h>
     154
     155#include <dlfcn.h>
     156#include <link.h>
     157
     158static int load_python_globally_callback(struct dl_phdr_info *info, size_t size, void *data)
     159{
     160    int major, minor;
     161    const char *basename = strrchr(info->dlpi_name, '/');
     162    int *success = (int *)data;
     163
     164    if (basename != NULL) {
     165        if (sscanf(basename, "/libpython%d.%d.so", &major, &minor) != 2) {
     166            if (sscanf(basename, "/libpython%d.%dm.so", &major, &minor) != 2) {
     167                return 0;
     168            }
     169        }
     170
     171        void *pylib = dlopen(info->dlpi_name, RTLD_GLOBAL | RTLD_NOW);
     172        if (pylib != NULL) {
     173            *success = 1;
     174        } else {
     175            fprintf(stderr, "Could not load python library '%s': %s\n",
     176                    info->dlpi_name, dlerror());
     177        }
     178    }
     179
     180    return 0;
     181}
     182
     183bool loadPythonGlobally()
     184{
     185    int success = 0;
     186    dl_iterate_phdr(load_python_globally_callback, &success);
     187    return success;
     188}
     189
     190
     191#else /* __linux__ */
     192
     193bool loadPythonGlobally()
     194{
     195    /* On non-Linux systems, no need to load globally */
     196    return true;
     197}
     198
     199#endif /* __linux__ */
     200
     201}; /* namespace GlobalLibPythonLoader */
     202
     203}}}
     204
     205This last change makes !PyOtherSide compile with the crippled Bionic C library used on Android.
     206
     207'''The resulting project should look like contents of this tarball:'''
     208
     209[http://www.modrana.org/platforms/android/pyotherside/guide/pyotherside-1.3.0_android_project.tar.gz http://www.modrana.org/platforms/android/pyotherside/guide/pyotherside-1.3.0_android_project.tar.gz]
     210
     211
     212==== Building the !PyOtherSide project in !QtCreator ====
     213
     214Now the !PyOtherSide project should be ready to be opened in !QtCreator (make sure you are running the one corresponding to the Qt 5.3 Android SDK you have downloaded and installed previously). So start the right !QtCreator and open the ''pyotherside-1.3.0/pyotherside.pro'' file.
     215
     216You should be able to build the !PyOtherSide project by pressing the "rebuild" button in !QtCreator.
     217
     218=== Basic deployment ===
     219==== Theory ====
     220For a Python program using !PyOtherSide to work it needs these things present on the Android device:
     221* the application files
     222* Qt 5
     223* a Python 3 bundle
     224* the !PyOtherSide library
     225We don't need to care about Qt - its deployment is handled automatically by the Qt SDK Android support (it is either bundled in the APK, automatically copied to the device or provided by the Ministro library manager) and we don't need to care about it. The SDK also handles the deployment of the application files.
     226
     227But we need to get Python 3 & !PyOtherSide on the android device our self. For now we will just copy the files to a fixed location on the device with ''adb'' and hardcode a path pointing to them in the example application. This should be enough for getting a proof of concept of (hopefully) working !PyOtherSide on Android.
     228
     229In the future it should be possible deploy these files from a standalone APK without any need for manual intervention or rooting - either by using the new asset mechanism or the old method (used by the !PySide for Android port) of bundling zip files that are unpacked on first start by modified Java boilerplate.
     230
     231==== Deploying Python 3 & !PyOtherSide to the Android device ====
     232
     233Download the pre-prepared tarball with Python 3 and !PyOtherSide:
     234
     235{{{
     236wget http://www.modrana.org/platforms/android/pyotherside/guide/python3_pyotherside_deployment_bundle.tar.gz
     237}}}
     238
     239Then unpack it:
     240
     241{{{
     242tar xvf python3_pyotherside_deployment_bundle.tar.gz
     243}}}
     244
     245And push the complete ''poside'' directory directly to the ''/data'' directory on your Android device:
     246
     247{{{
     248adb push poside /data/poside
     249}}}
     250
     251This is where the hello world example will expect it. Also, we can't deploy to the ''/data/sdcard'' directory as it isi usually mounted noexec which breaks the library loading we need to do.
     252
     253==== Deploying & running a hello world application ====
     254
     255Next download the hello world project tarball and unpack it:
     256
     257{{{
     258wget http://www.modrana.org/platforms/android/pyotherside/guide/pyotherside_hello_world_project.tar.gz
     259}}}
     260
     261It is just a normal !QtQuick Controls Android project template modified to load the needed libraries from ''/data/poside'' and with all the needed environmental variables set.
     262
     263Open the project file in the !QtCreator provided by the Qt for Android SDK
     264(the main project file is called ''A52.pro''), make sure the android kit with correct architecture for your device is selected, press the ''Run'' button and make sure your device with the deployed libs is selected as the deployment target.
     265
     266!QtCreator should now do its thing and the application should successfully start on the device, presenting you a simple ''Hello world'' button in the middle of the screen.
     267
     268Now look on the main.qml file in !QtCreator and note that while it has the {{{import io.thp.pyotherside 1.0}}} line, the {{{Python {} }}} line that actually instantiates the Python element provided by !PyOtherSide is commented out.
     269
     270Next uncomment {{{Python {} }}} line and try to deploy the application again. This time the deployment fails and the applications crashes as soon as it is started.
     271
     272==== Current status quo ====
     273
     274So this how & how far I got - !PyOtherSide can be imported in a !QtQuick application running on Android, but crashes once the Python element is instantiated.
     275
     276This guide should enable anyone get as far as I got and hopefully get far past the current roadblock! :)
     277
     278Any help with finding out how to fix the crash would be much appreciated! :)
     279
     280'''UPDATE: Works now, see the note on the top for more info. The likely cause of the crash was most probably the precompiled Python 3.2.'''