Using pyqtdeploy on MacOS to cross compile a PyQt app for iOS: part 2

This is work in progress that I will periodically update.  I have separate blogs about deploying on mobile platforms, Android and iOS.  These are related and you might read both.

History of this post:

  • originally written for pyqtdeploy version 0.5, Qt 5.3, PyQt5.3
  • Dec. 2014 updated for latest versions

Recent changes:

  • pyqtdeploy now uses a separate command pyqtdeploycli for use on a command line
  • PyQt when used with a QML program that uses QGraphicsItems might need to be patched

Rehash

This is part two of three parts:

  1. install Qt and Xcode (and Python?), then test build and deploy an example Qt app for iOS
  2. cross compile static libraries for iOS (using pyqtdeploy.)
  3. use pyqtdeploy to cross compile and package your app.

Using the same machine to package for MacOS and iOS

Can you do it?  You can, by segregating libraries for different targets. Ideally, I would not use the same machine to package for MacOS and iOS.  Instead, I would create a separate virtual machine in which to package for iOS.  Then I would not need to think or worry as much about the segregation.  When you use the same machine for many activities (development and packaging for many platforms) there is always the chance that you mess something up, halting another activity.  Virtual machines help you isolate the activities.  (For an example of a mess, you download a shared artifact such as Qt that introduces bugs on one target platform.  For another example, you inadvertently share an artifact that should not be shared across target platforms, say through improper PATH’s.)

I only have one physical Apple desktop (that is new enough to run MacOS OSX 10.9.) It is not where I do most of my programming (yet), only where I package my app for distribution.  I have already used it to package my app for MacOS.  Unfortunately, when I tried to install OSX 10.9 Mavericks to a VirtualBox VM, I failed.  I suspect my hardware is too old (one of the earliest Mac’s that supports OSX 10.9.)

Segregating an iOS build

Eventually create a directory structure like this (say in home):

ios
   iRoot
      python
   Downloads
      Python-3.4.0
      PyQt-gpl-5.3.1
      sip-4.16.1
   pensoolBuild
      pensool.pdy
      build
         pensool.pro

The ‘ios’ directory groups subdirectores related to cross compiling to iOS. iRoot is a stand-in for the root of the target device.  Many build objects are placed (installed) here during the cross build, read from here during the final packaging, and installed to similar places on the target device.  Also known as SYSROOT. In ios/Downloads:

  • Python-3.4.0 is a copy of the Python source distribution
  • PyQt-gpl-a123 is a copy of the PyQt source distribution
  • sip-4.16.1 is a copy of the SIP source distribution

All of these directories are downloaded.  Then building occurs in the directory.  Build install products go to subdirectories of ~/ios/iRoot pensoolBuild groups directories related to pyqtdeploying your app.  You run pyqtdeploy as a GUI here, passing pensool.pdy.  Pyqtdeploy creates the build subdirectory, populating it with a pensool.pro and source files. Then you run qmake.  It installs your app to iRoot, ready to download to the iosSimulator or a real device.

Downloading the source for the libraries to be built statically

Again, I am segregating these directories in ~/ios/Downloads to prevent any mixing. Download python, pyqt, and sip.  Download the ‘Gzipped source tarball’ for Python. If you already have built your app for MacOS desktop, you already have these directories.  You probably can just copy the directories, and rebuild (the makefiles should clean where necessary) but for safety you might want to download fresh copies. In my case, new releases of Python (3.4.1) and SIP (4.16.2) existed past my prior downloads.  I chose to stick with the versions I used to build for the desktop.

Refreshing Qt

If you already have an up-to-date Qt, including iOS stuff, skip this.  Suppose there is a new release of Qt.  Reinstall it using the online installer, say to ~/Qt.  (You may need to move the old installation directory to the trash.)

When you install, it should recognize your platform and offer to install iOS related stuff:

  • a kit for Qt Creator,
  • prebuilt frameworks (ready to bundle into an iOS package.)

Note that iOS does not allow dynamic libraries.  Hence Qt for iOS by default, as installed by the on-line installer, is built as static libraries. Thus, the Qt/5.4/ios directory is a read-only input for an iOS packaging (IOW, you should not need to build Qt statically for iOS.)

Updating PyQt for the host

To run pyqtdeploy requires Python3 and PyQt dynamic libraries on the host (regardless of target.)  Note that this is separate from statically building PyQt for target iOS.

If you there is a new version of Qt or PyQt, you should rebuild SIP and PyQt (the dynamic libraries.) While it is possible that the Qt API has not changed, and the old PyQt bindings will work, it is easier to rebuild than to determine whether the API has changed.  Here, PyQt installs to the native system’s python directory e.g. ~/python/    (which also should be in your PYTHONPATH env variable.)

TODO: patch sip/QtMultimediaWidgets/qgraphicsvideoitem.sip if you use QML.  Delete the code %ConvertToSubClassCode…….%End.  Do this for every copy of PyQt (the one for OSX and the one for iOS.)  You must do this before building PyQt, as the configure step is the one that generates C++ code from this patched .sip file.

These are the same commands used to build PyQt for OSX:

cd ~/Downloads/sip-4.16.5
python3 configure.py
make
make install

cd ~/Downloads/PyQt-gpl-5.4
python3 configure.py --qmake ~/Qt/5.4/clang-64/bin/qmake --sip ~/python/bin/sip
make -j4
make install

Expect tens of minutes of compiling for PyQt.  Seems like ‘sudo’ is not required.

Environment variables and paths

On OSX, I needed:

>export PYTHONPATH=/Users/bootch/python/lib/python3.4/site-packages

That points to the  installation directory for Python3.4 (which you must build from source since OSX does not ship with Python3).  If this env variable is missing, pyqtdeploy complains that it can’t find Qt modules. Also you will need to explicitly pass SYSROOT to many commands below.  I’m not sure if defining that in the environment will work instead.  SYSROOT tells many commands where to install the built products, in this case to ~/ios/iRoot Also, sip is a command that needs to be on the PATH.  Again, you need the same version of sip installed on your host as you intend to run on the target (iOS.)  I assume you have already built your app for the MacOS desktop and that sip is on PATH.  Otherwise, you can pass the –sip=/Users/bootch/python/bin/sip option to some commands below.

Updating pyqtdeploy

Probably there is a new version of pyqtdeploy since you last used it.  You always want to use the latest version, possibly even the unstable version. See about installing and upgrading pyqtdeploy in Notes on pyqtdeploy

Building Python statically for target iOS

>cd ~/ios/Down*/Pyth*
>pyqtdeploycli --package python --target ios-64 configure
>~/Qt/5.3/ios/bin/qmake sysroot=/Users/bootch/ios/iRoot
>make
>make install

(see more comments in my post about cross compiling to Android.) The result is a new directory ~/ios/iRoot/python populated with a library of the Python interpreter.

Building SIP statically for target iOS

(You should also have built SIP for the host.)

>cd ~/ios/Down*/sip*
>pyqtdeploycli --package sip --target ios-64 configure
>python3 configure.py --static --sysroot=/Users/bootch/ios/iRoot --no-tools --configuration=sip-ios.cfg
>make
>make install

(see more comments in my post about cross compiling to Android.)

(Edited: this not needed: >~/Qt/5.4/ios/bin/qmake sysroot=/Users/bootch/ios/iRoot)

Building PyQt statically for target iOS

>cd ~/ios/Down*/PyQt*
>pyqtdeploycli --package pyqt5 --target ios-64 configure
>python3 configure.py --static --verbose --sysroot=/Users/bootch/ios/iRoot --no-tools --no-qsci-api --no-designer-plugin --no-qml-plugin --configuration=pyqt5-ios.cfg --qmake=/Users/bootch/Qt/5.4/ios/bin/qmake --sip ~/python/bin/sip
>~/Qt/5.4/ios/bin/qmake PyQt5.pro sysroot=/Users/bootch/ios/iRoot 
>make # expect to wait tens of minutes
>make install

Note that you may need an intermediate step, to edit pyqt5-ios.cfg to remove extra Qt modules that don’t compile and that you don’t need. FAQ: if you get “You need a working sip on your PATH”, you can pass –sip=/Users/bootch/python/bin/sip to the configure step.

(Edits:

  • 5.3=>5.4
  • add: –sip ~/python/bin/sip
  • delete: –no-qml-plugin (I want to use QML on iOS now.)
  • add: PyQt5.pro

Continuing

Now you have static libraries for Python, PyQt, and SIP.  In the next part, we use pyqtdeploy to build a helloworld app and deploy it to an iOS simulator.

Advertisements

One thought on “Using pyqtdeploy on MacOS to cross compile a PyQt app for iOS: 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