Difference between revisions of "How-To Qt5.x Max AppStore"

From Owl
Jump to: navigation, search
(Feedback)
(Upload)
 
Line 320: Line 320:
  
 
===Upload===
 
===Upload===
Before you can upload your package you need to create a new version for you App with http://itunesconnect.apple.com. After that you can use the ''Application Loader'' to upload your package {{TT|*.pkg}}.
+
Before you can upload your package you need to create a new version for you App with http://itunesconnect.apple.com. After that you can use the ''Application Loader'' to upload your package *.pkg file.
 
 
Good luck! :-)
 

Latest revision as of 11:27, 6 December 2015

Overview

This is a How-To for getting your Qt based application successfully into the Mac AppStore.

If you want to include QtWebKit or QtWebKitWidgets in your AppStore application you will face several problems - if not skip the ICU section and continue with Qt 5.3.

Problems

If you use Qt as it is (without any modification) and use Application Loader to upload it to the AppStore you will probably get Apple's response:

Use of non-public APIs not permitted. Following non-public APIs are included: <source lang=bash> 'usr/lib/libSystem.B.dylib' +++ : bootstrap_look_up2 +++ : bootstrap_register2 'usr/lib/libicucore.A.dylib' +++ : ubrk_getRuleStatus +++ : ubrk_setUText </source> What does it mean? One or more Qt libraries uses APIs which are not declared public by Apple. In this case the QtWebKit implementation is making trouble (actually it is not a problem of Qt as Apple's own Safari App uses these APIs). It is not possible (please contact me if I am wrong) to use the WebKit2 implementation as there is currently no workaround for the bootstrap_* issues. However Qt delivers both: WebKit1 and WebKit2 - so we can use WebKit1. The ubrk_* issues can be fixed by compiling your own ICU libs and we let Qt link against them.

So we need to patch Qt and this How-To shows what has to be done.

ICU

First of all you need the latest ICU libs. Download them from http://site.icu-project.org/download. In this example we use version 53.1 but you can use any recent version. We also need to patch some files otherwise we will face linking problems with Qt later. Skip this section if you are not intent to use QtWebKit and/or QtWebKitWidgets. <source lang=bash> $ tar -zxf icu-53_1-src.tar $ cd icu/source </source> By default ICU will add a version number suffix for symbols and therefore e.g. _UCNV_FROM_U_CALLBACK_ESCAPE becomes _UCNV_FROM_U_CALLBACK_ESCAPE_53. In WebKit those symbols are referenced without the version suffix and therefore Qt will not be able to link against the self-compiled ICU library. So we need to disable the version suffix: <source lang=bash> $ ./configure --prefix=/usr/local/icu53.1 --enable-renaming=no </source> After configuring ICU, it will prompt you with the following: <source lang=C> /* ICU customizations: put these lines at the top of uconfig.h */

/* -DU_DISABLE_RENAMING=1 */
  1. define U_DISABLE_RENAMING 1

</source> So add #define U_DISABLE_RENAMING 1 to the file common/unicode/uconfig.h: <source lang=C> /*

  • Copyright (C) 2002-2014, International Business Machines
  • Corporation and others. All Rights Reserved.
  • file name: uconfig.h
  • encoding: US-ASCII
  • tab size: 8 (not used)
  • indentation:4
  • created on: 2002sep19
  • created by: Markus W. Scherer
  • /
  1. ifndef __UCONFIG_H__
  2. define __UCONFIG_H__

// Add it here:

  1. define U_DISABLE_RENAMING 1

[...] </source> Now compile ICU and install them: <source lang=bash> $ make -j4 [...] $ sudo make install </source> The ICU libraries should now be available in /usr/local/icu53.1/libs.

Qt 5.3

For Qt we need some more work todo. Change into the directory of Qt: <source lang=bash> $ cd qt-everywhere-enterprise-src-5.3.0 </source>

Patch

To remove the dependency from QtDeclarative patch the file qt.pro in the top level directory (IMHO there is absolutely no reason why these libs should be dependent and link against QtDeclarative - many projects don't use QtQuick at all): <source lang=diff> --- qt.pro.org 2014-05-26 12:09:34.000000000 +0200 +++ qt.pro 2014-05-26 11:09:03.000000000 +0200 @@ -65,10 +65,10 @@

addModule(qtxmlpatterns, qtbase)
addModule(qtdeclarative, qtbase, qtsvg qtxmlpatterns)
addModule(qtquickcontrols, qtdeclarative)

-addModule(qtmultimedia, qtdeclarative) +addModule(qtmultimedia, qtbase)

addModule(qtwinextras, qtbase, qtdeclarative qtmultimedia)
addModule(qtactiveqt, qtbase)

-addModule(qt3d, qtdeclarative) +addModule(qt3d, qtbase)

addModule(qtjsondb, qtdeclarative)
addModule(qtsystems, qtbase, qtdeclarative)
addModule(qtlocation, qtbase, qt3d qtsystems qtmultimedia)

@@ -76,7 +76,7 @@

addModule(qtconnectivity, qtbase $$ANDROID_EXTRAS, qtdeclarative)
addModule(qtfeedback, qtdeclarative, qtmultimedia)
addModule(qtpim, qtdeclarative, qtjsondb)

-addModule(qtwebkit, qtdeclarative, qtlocation qtmultimedia qtsensors, WebKit.pro) +addModule(qtwebkit, qtbase, qtlocation qtmultimedia qtsensors, WebKit.pro)

addModule(qttools, qtbase, qtdeclarative qtactiveqt qtwebkit)
addModule(qtwebkit-examples, qtwebkit qttools)
addModule(qtimageformats, qtbase)

</source> If you don't use QtQuick rename the following directories to prevent Qt from building them: <source lang=bash> $ mv qtdeclarative qtdeclarative.org $ mv qtquick1 qtquick1.org $ mv qtquickcontrols qtquickcontrols.org </source> If you are not using QtWebKit and/or QtWebKitWidgets continue with Configure. For using WebKit patch file qtwebkit/Source/WTF/WTF.pri to link against our own ICU libs: <source lang=diff> --- qtwebkit/Source/WTF/WTF.pri.org 2014-05-23 17:21:30.000000000 +0200 +++ qtwebkit/Source/WTF/WTF.pri 2014-05-23 17:23:43.000000000 +0200 @@ -11,8 +11,8 @@

mac {
    # Mac OS does ship libicu but not the associated header files.
    # Therefore WebKit provides adequate header files.

- INCLUDEPATH = $${ROOT_WEBKIT_DIR}/Source/WTF/icu $$INCLUDEPATH - LIBS += -licucore + INCLUDEPATH = /usr/local/icu53.1/include $$INCLUDEPATH + LIBS += -licui18n -licuuc -licudata -L/usr/local/icu53.1/lib

} else {
    contains(QT_CONFIG,icu) {
        win32: LIBS += -licuin -licuuc -licudt

</source> Now we need to disable the WebKit2 implementation, patch file qtwebkit/Tools/qmake/mkspecs/features/configure.prf: <source lang=diff> --- qtwebkit/Tools/qmake/mkspecs/features/configure.prf.org 2014-05-23 17:36:24.000000000 +0200 +++ qtwebkit/Tools/qmake/mkspecs/features/configure.prf 2014-05-23 17:37:46.000000000 +0200 @@ -49,7 +49,6 @@

    WEBKIT_CONFIG += \
        build_webkit1 \

- build_webkit2 \

        build_tests \
        $$WEBKIT_TOOLS_CONFIG

</source>

Configure

Now we can configure and build Qt (prefix it to your own needs) and omit the -commercial and -confirm-license switch if you are using the non-commercial version: <source lang=bash> $ ./configure -prefix /usr/local/qt/5.3.0 -no-icu -release -strip -commercial -confirm-license -no-rpath -nomake examples [...] $ make -j4 </source>

Install

After building Qt we install it in the predefined directory: <source lang=bash> $ sudo make install </source>

Post checks

Some Qt versions (mainly prior 5.3.0) do not build the path ID for all Qt frameworks correctly. In our case the path ../lib/libicudata.53.1.dylib of QtWebKit.framework will make trouble as it is relative. We can fix this using install_name_tool. To find out all relevant frameworks use otool: <source lang=bash> $ cd /usr/local/qt/5.3.0/lib $ otool -L Qt*/Qt*[^prl] | grep "\.\.\/" # for all files containing e.g. '../' [...] $ otool -L Qt*/Qt*[^prl] | grep "/usr/local/qt" # for all files containing absolute paths [...] </source> After identifying the wrong frameworks we use install_name_tool (this is an example fixing the ICU path but is representative for any other lib containing wrong paths): <source lang=bash> $ cd /usr/local/qt/5.3.0/lib/QtWebKit.framework $ sudo install_name_tool -change ../lib/libicudata.53.1.dylib libicudata.53.dylib QtWebKit </source> Checking again with otool and grep no files should be listed anymore.

Bundle

Applications in Mac OS X are organized in a so called bundle which represents a well defined file system structure: <source lang=bash> MyApp.app/

 Contents/
   Info.plist     # information about your app
   Frameworks/    # place of our Qt libs
   MacOS/         # application(s)
   PlugIns/       # plug-ins and your own libs (*dylib)
   Resources/     # entitlements, readme, icons, translations
   SharedSupport/ # optional files, documents

</source> For creating such a bundle you can use macdeployqt see http://qt-project.org/doc/qt-5/macosx-deployment.html. However this tool is not sufficient in many cases, so we will develop our own deployment script.

Entitlements

The AppStore requires an entitlements file containing several security options for our application. We store this file in $HOME/src/myapp.entitlements. An example could be: <source lang=xml> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.app-sandbox</key> <true/> <key>com.apple.security.files.downloads.read-write</key> <true/> <key>com.apple.security.files.user-selected.read-write</key> <true/> <key>com.apple.security.network.client</key> <true/>

       <key>com.apple.security.print</key>

<true/>

       <key>com.apple.security.files.bookmarks.app-scope</key>
       <true/>

</dict> </plist> </source> This file will be used later for signing our application.

Structure

First we create the directories and copy all the files we need (this shell script is an example but you can use it and change it for your own needs): <source lang=bash>

  1. !/bin/bash

MYAPP="MyApp" DEST="$HOME/bundles/$MYAPP.app" # Our final App directory SRC="$HOME/src/$MYAPP.app" # Usually the App directory created by our Qt *.pro file ENTITLEMENTS="$HOME/src/$MYAPP.entitlements"

ICUDIR="/usr/local/icu53.1" ICULIBS="libicui18n.53 libicudata.53 libicuuc.53" QTDIR="/usr/local/qt/5.3.0" QTLIBS="QtCore QtNetwork QtSql QtGui QtSvg QtScript QtOpenGL QtWidgets QtWebKit QtWebKitWidgets \

 QtPrintSupport QtXml QtPositioning QtSensors QtMultimedia QtMultimediaWidgets" # QtQml QtQuick

PLUGINS="sqldrivers imageformats iconengines platforms printsupport accessible \

 position mediaservice" # playlistformats sensors sensorgestures bearer audio
  1. make clean & create pathes

rm -rf $DEST cp -Rp $SRC $DEST mkdir -p $DEST/Contents/Frameworks $DEST/Contents/PlugIns $DEST/Contents/SharedSupport cp -p $ENTITLEMENTS $DEST/Contents/Resources/

  1. copy Qt libs, plug-ins and ICU

for L in $QTLIBS ; do

 cp -Rp $QTDIR/lib/$L.framework $DEST/$MYAPP.app/Contents/Frameworks
 # remove all unnecessary header files:
 rm -f $DEST/$MYAPP.app/Contents/Frameworks/$L.framework/Headers
 rm -rf $DEST/$MYAPP.app/Contents/Frameworks/$L.framework/Versions/5/Headers

done for P in $PLUGINS ; do

 cp -Rp $QTDIR/plugins/$P/*.dylib $DEST/$MYAPP.app/Contents/PlugIns/$P/

done for I in $ICULIBS ; do

   cp -p $ICUDIR/lib/$I.dylib $DEST/$MYAPP.app/Contents/PlugIns/icu/

done

  1. copy own application libs if necessary to /Contents/PlugIns/myapp/

</source>

Library paths

We need to change the library IDs and paths of all our libs for our application to create an exclusive fully self-contained bundle using again install_name_tool - it provides the variable @executable_path to offer a path relative to our executable in /Contents/MacOS: <source lang=bash> DISTPLUGINS=`cd $DES/$MYAPP.app/Contents/PlugIns; ls -1 */*.dylib` # extract all our *.dylib libs

for I in $QTLIBS ; do

 install_name_tool -id "@executable_path/../Frameworks/$I.framework/Versions/5/$I"\
  "$DEST/$MYAPP.app/Contents/Frameworks/$I.framework/Versions/5/$I"
 install_name_tool -change $I.framework/Versions/5/$I\
   @executable_path/../Frameworks/$I.framework/Versions/5/$I\
   $DEST/$MYAPP.app/Contents/MacOS/$MYAPP # change references to Qt frameworks
 for L in $QTLIBS ; do # change all lib references in all Qt frameworks
   if [ $L = $I ] ; then continue; fi 
   install_name_tool -change $I.framework/Versions/5/$I\
     @executable_path/../Frameworks/$I.framework/Versions/5/$I\
     $DEST/$MYAPP.app/Contents/Frameworks/$L.framework/Versions/5/$L
 done

done

for P in $DISTPLUGINS ; do # change ID for all *.dylib libs

 install_name_tool -id "@executable_path/../PlugIns/$I" "$DEST/$MYAPP.app/Contents/PlugIns/$P"
 for L in $QTLIBS ; do # change any reference to Qt in our *.dylib libs
   install_name_tool -change $L.framework/Versions/5/$L\
     @executable_path/../Frameworks/$L.framework/Versions/5/$L\
     $DEST/$MYAPP.app/Contents/PlugIns/$P
 done

done

for L in $ICULIBS ; do

 install_name_tool -id "@executable_path/../PlugIns/icu/$L.dylib"\
   "$DEST/$MYAPP.app/Contents/PlugIns/icu/$L.dylib"
   for I in $ICULIBS ; do # change all references in ICU libs
     if [ $I = $L ] ; then continue; fi
     install_name_tool -change "$I.dylib" "@executable_path/../PlugIns/icu/$I.dylib"\
       "$DEST/$MYAPP.app/Contents/PlugIns/icu/$L.dylib"
   done

done

  1. we do the same for additional own libs in /Contents/PlugIns/myapp

</source> Check if every lib, framework and especially our executable contains the correct lib references i.e. no /usr/local/qt/... is part of a path. Again use otool -L.

Code sign

For uploading successfully to the Mac AppStore each of our files must be signed. By registering with Apple you get a Developer Application and a Developer Installer certificate. For signing the files we need the Developer Application certificate and for packaging the Developer Installer certificate. <source lang=bash> APPLCERT="3rd Party Mac Developer Application: <your ID>" INSTCERT="3rd Party Mac Developer Installer: <your ID>" DOMAIN="com.yourdomain" # must be the domain registered for this App

for I in $QTLIBS ; do # signing the Qt frameworks

 codesign -s "$APPLCERT" -v -i "$DOMAIN.$I" \
   $DEST/$MYAPP.app/Contents/Frameworks/$I.framework/Versions/5/$I

done for I in $DISTPLUGINS ; do # signing all *.dylib libs

 BN=`basename $I .dylib`
 codesign -s "$APPLCERT" -v -i "$DOMAIN.$BN" \
   $DEST/$MYAPP.app/Contents/PlugIns/$I

done </source> Finally we need to sign our executable using the entitlements file: <source lang=bash> codesign -s "$APPLCERT" -v -i "$DOMAIN.$MYAPP" --entitlements \

 "$DEST/$MYAPP.app/Contents/Resources/$MYAPP.entitlements" \
 "$DEST/$MYAPP.app/Contents/MacOS/$MYAPP"

</source> This step will only be successful if any library and framework is code signed.

Package

If everything went successful until now we can package our application to get it ready for the AppStore upload. Therefor we use productbuild (take care of the $INSTCERT variable): <source lang=bash> productbuild --component "$DEST/$MYAPP.app" /Applications \

 --sign "$INSTCERT" "$DEST/$MYAPP.pkg"

</source> To check our created package we can use: <source lang=bash> $ sudo installer -store -pkg MyApp.pkg -target / </source>

Upload

Before you can upload your package you need to create a new version for you App with http://itunesconnect.apple.com. After that you can use the Application Loader to upload your package *.pkg file.