Using pyqtdeploy0.5 on Linux to cross compile a PyQt app for Android: Part 2

Summary

In Part 1:

  • installed tools
  • built static libraries

In this part 2:

  • use pyqtdeploy to freeze Python code and generate a Qt Creator project
  • use QtCreator to compile and link the app

Refresh pyqtdeploy installation

You may need this since:

  •  pyqtdeploy is actively developed,
  •  the cross-compiling feature is the bleeding edge of pyqtdeploy development.

This assumes you cloned the repository (instead of installing a snapshot via pip3.)

>cd ~/Downloads/pyqtdeploy*
>hg pull
>hg update
>sudo python setup.py install

Use pyqtdeploy to generate Qt Creator project

Start pyqtdeploy

Open a terminal and enter:

>mkdir pensoolAndroidBuild
>cd pensoolAndroidBuild
>pyqtdeploy pensool.pdy

where pensool is the name of my/your app.  The pyqtdeploy GUI should open.  Here we made a new directory to build in.  Pyqtdeploy can create many files in that directory.

Configuring your pyqtdeploy project

Glossing over the details, this is similar to pyqtdeploy that is NOT cross-compiling.  (For example, nowhere do you see the word “Android”)  The main difference is that under the “Locations” tab, the “Target Python Locations” section points to a directory which contains cross-compiled artifacts (e.g. a SYSROOT of /home/bootch/aRoot.)

pyqtdeploy ‘Build’

After you have configured pyqtdeploy, choose the ‘Build’ tab.  Uncheck most of the options (i.e. ‘Run Qmake’ and ‘Run make’) since we will be doing that soon (I don’t know, maybe those would work, but I prefer to see any errors from inside QtCreator.)  Choose the ‘Build’ button.  This creates a qt creator project file (.pro)

Build using Qt Creator

Start Qt Creator

Navigate to the project created by pyqtdeploy (say ~/pensoolAndroidBuild/build/Pensool.pro) and choose the project.

The first time you open a project, Qt Creator tells you it has not been configured, and offers a choice of configurations.  Configure the project for Android.

Choose the ‘Build’ button.  (Now Qt Creator shows you a list of Android AVD’s.  You should have one if you have tested Qt Creator’s ability to cross-compile a C++ helloworld example.)

Choose an AVD.  (Now Qt Creator proceeds to compile and you may get errors.  See below, the FAQ. )

 FAQ Frequent errors

These are problems I encountered during the build phase in Qt Creator

Qt skips incompatible Qt libraries (built for the wrong architecture or word size)

This is because Qt Creator is building (linking) for a different architecture than the libraries.  It can be 32-bit versus 64-bit,  or Intel versus ARM architecture?  Typical error message from Qt Creator:

arm-linux-androideabi/bin/ld: warning: skipping incompatible /home/bootch/aRoot/lib/python3.4/site-packages/PyQt5/libQtGui.a while searching for QtGui
/home/bootch/android-ndk-r10/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.8/../../../../arm-linux-androideabi/bin/ld: error: cannot find -lQtGui

How to know what architecture a library is built for (on Linux):

>objdump -f libfoo.a | grep ^architecture
>objdump -f libfoo.a       (a verbose alternative)

If that says ‘i386’, it was built  for the Intel architecture.  If it says architecture UNKNOWN, it is probably for the ARM architecture?  There is a different tool on the Mac.

Anyway, if the libraries differ in either 32-bit/62-bit or architecture, you probably built some of the static libraries incorrectly (without configuring Qt Creator to use the ARM kit by default?)

Optional Python modules are missing at link time

I got this:

main.o:main.cpp:extension_modules: error: undefined reference to 'PyInit__posixsubprocess'
main.o:main.cpp:extension_modules: error: undefined reference to 'PyInit_select'
mathmodule.o:mathmodule.c:function m_log2: error: undefined reference to 'log2'
collect2: error: ld returned 1 exit status

This might be wrong configuration of the Python build.  The Python build is configurable for optional modules (extension modules, written in C.)  This is briefly mentioned at the pyqtdeploy documents:

This will configure Python for a small sub-set of standard extension modules. Your application will probably require additional ones to be enabled. To do this you will need to make changes to the python.pro file and the config.c file (in the Modules directory). See the comments in those files for more information.

A minor quibble is that there don’t seem to be any pertinent comments in the python.pro file.  I think you just need to add additional lines in the body as in this example:

greaterThan(PY_MAJOR_VERSION, 2) {
    MOD_SOURCES = \
    ...
    Modules/_posixsubprocess.c \
    Modules/selectmodule.c \

Here, the names are names of source code files.  You can also view these names in Modules/Setup.dist.

To edit Modules/config.c, you add a line for SOME of the modules which you added a line to python.pro:  those modules whose error is like ‘PyInit…’

This process of figuring out what additional Python modules is similar to the process when you are not cross-compiling.  That is, when you built your app for the desktop, you determined what additional Python modules your app uses.  (The process there was iterative: build, see what Python complains about, add the module, and repeat.  Here, the link phase tells you all the missing modules at once, but it doesn’t tell you the names of the modules.  If you didn’t build your app for the desktop, you can determine the names of the missing modules by grepping the Python source code in the Pythonx.x/Modules directory for the function name given by the link error.

        extern PyObject* PyInit__symtable(void);
        extern PyObject* PyInit__posixsubprocess(void);
        extern PyObject* PyInit_select(void);
...
        {"_posixsubprocess", PyInit__posixsubprocess},
        {"select", PyInit_select},

In this example, _posixsubprocess is a key (and also the name of the source code file) but note that this results in two underscores in the value field.  In this example, ‘select’ is a key, but NOT the name of the source code file (which is ‘selectmodule.c’.  In other words, this key must be the same as the key defined in Modules/Setup.dist.

After you make these configurations, you should not run pyqtdeploy again (because it creates a new python.pro, overwriting your edits) but you should run qmake, make, make install again because qmake reads the python.pro file you just edited.

It seems that you can rebuild and reinstall a static Python without destroying the installed PyQt and SIP components (so you can proceed directly to Qt Creator and retry your build.)

More patches might be needed for Python modules

Again, pyqtdeploy, when helping you build Python, applies patches to the Python source code to account for differences on the Android platform (that Python.org does not support.)  If you configure Python with additional extension modules, you might need to make additional patches (until pyqtdeploy knows all the patches.)  Here are some ill-defined hacks:

SYS_getdents64

When building my app using Qt Creator I got this:

undefined SYS_getdents64

Apply this patch for Python issue 20307.

epoll_create1

If you get:

undefined EPOLL_CLOEXEC

(I tried a hack to define that variable, only to find another error:

undefined epoll_create1

so my eventual fix was to comment out a line in pyconfig.h:

/* #define HAVE_EPOLL_CREATE1 1 */

Apparently, the linux system call epoll_create1() is not implemented on Android?  And EPOLL_CLOEXEC is only needed if epool_create1() is used.

log2()

If you get

undefined log2

then in pyconfig.h make these changes:

/* #define HAVE_LOG2 1 */
#undef HAVE_LOG2

That controls whether m_log2() calls the system defined log2() or implements log2() de novo.  Apparently there is no system log2() implemented on Android?

 

Next:

Part 3 will discuss:

  • debugging your app on an AVD
  • packaging using Qt Creator

http://qt-project.org/doc/qtcreator-3.1/creator-deploying-android.html

The pyqtdeploy directory contains a file DeveloperNotes.txt.  This seems to be a discussion for a developer of pyqtdeploy (someone who is maintaining pyqtdeploy or wants to add a feature to it) but it also has useful hints about Android development.

Advertisements

One thought on “Using pyqtdeploy0.5 on Linux to cross compile a PyQt app for Android: Part 2

  1. Pingback: Deploying PyQt Python Qt apps cross platform using pyqtdeploy | plashless

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s