Distributing WxWidgets Applications-Distributing WxMac Programs

From WxWiki
Jump to: navigation, search

(This page is a work in progress)

Creating a Bundle

On Mac, it's nice to distribute your applications as one self-contained 'Bundle'. A 'bundle' is basically a directory with a given structure, typically named 'foo.app', which shows up as a drag-and-droppable, startable 'program'.

See WxMac-specific_topics#Building_a_MacOSX_application_bundle for more information...

Gotcha: when you change Info.plist, sometimes the information in it is still cached somewhere. A reboot might help, I'm not sure how to do this gracefully.

Statically compiling

I (ChoJin) used macport to install wxwidgets, but since there isn't any variant (yet) to force a static version I added my own using the following command line:

 sudo port edit wxwidgets

Then add the following lines at the bottom of the file:

 variant chojin description { build a static version of the libraries with some other options... } {
     configure.args-append       --enable-std_iostreams
     configure.args-append       --disable-shared
     configure.args-delete       --with-sdl
     configure.args-delete       --with-opengl
     set installtype release-static
 }

And save and quit

Feel free to remove the following two lines from the variant if you need/want SDL and OpenGL:

   configure.args-delete       --with-sdl
   configure.args-delete       --with-opengl

Or to rename the variant from "chojin" to your own :)

By the way, by default, the following options are used by the port: --with-libjpeg --with-libtiff --with-libpng --with-zlib --with-sdl --with-opengl --with-mac --disable-sdltest --enable-unicode --enable-display --enable-monolithic so you don't need to specify them.

You can then install wxwigets using the following command line:

 sudo port install wxwidgets +chojin

But if you want to compile wxwidgets manually without using macport you could use (for example):

 ./configure --with-libjpeg --with-libtiff --with-libpng --with-zlib --with-mac --disable-sdltest --enable-unicode --enable-display --enable-monolithic --enable-std_iostreams --disable-shared --prefix=/Users/arnoutengelen/local

Now wx-config should link with the static version of wxwidgets. The problem is it still compiles with a bunch of dynamic libraries (iconv, libpng, libjpeg and so on)

I therefore used the following code in my Makefile (ideally, wx-config should be patched to do it automatically, but I was too lazy to give it a try):

 WXCONFIGLIBS := $(shell wx-config --libs)
 WXCONFIGLIBS := $(WXCONFIGLIBS:-lpng=/opt/local/lib/libpng.a)
 WXCONFIGLIBS := $(WXCONFIGLIBS:-lz=/opt/local/lib/libz.a)
 WXCONFIGLIBS := $(WXCONFIGLIBS:-ljpeg=/opt/local/lib/libjpeg.a)
 WXCONFIGLIBS := $(WXCONFIGLIBS:-ltiff=/opt/local/lib/libtiff.a)
 WXCONFIGLIBS := $(WXCONFIGLIBS:-liconv=/opt/local/lib/libiconv.a)
 LDFLAGS := $(WXCONFIGLIBS)

And use LDFLAGS in your linking line.

you can use "otool -L your_binary" to double check you're not linking anymore with any non-standard dynamic libraries.

Enjoy! it worked for me just fine :)

Including shared libraries in the bundle

Luckily the site at http://doc.trolltech.com/qq/qq09-mac-deployment.html explains how to include the shared libraries in the bundle itself:

  • Create a Contents/Frameworks directory in the bundle
  • Use 'otool -L' (the Mac alternative for 'ldd') to see which shared libs are required
  • Copy the shared libraries into it
  • For the library:
     install_name_tool -id @executable_path/../Frameworks/whatever.dylib demo.app/Contents/Frameworks/whatever.dylib
     
 And for the executable:
 
     install_name_tool -change /path/to/whatever.dylib @executable_path/../Frameworks/whatever.dylib demo.app/Contents/MacOS/demo
  • check if that worked with otool

(untested right now..)

Including shared libraries in the bundle (Verified)

The problem of the method above is that the dynamic libraries themselves depend on the library. With patching the executable only it's not done.

All library-internal cross references have to be removed (replaced) too. The library is linked internally to the major.minor.micro named versions of the dynamic libraries (e.g. libwx_mac_qa-2.6.0.dylib).

To accomplish this task I've written a bash script which is listed here (I haven't found a sourcecode highlighting function for this wiki -- is there any?):

     WXLIBPOSTFIX=*wx*2.6.0.dylib
     APP=WorldApp
     
     WXLIBDIR=../../../wxwidgets/build-dynamic/lib
     BINDIR=./build/Release/$APP.app/Contents/MacOS
     LIBDEFDIR=/usr/local/lib
     
     echo "Copying dynamic libraries to " $BINDIR " ..."
     cp $WXLIBDIR/*.dylib $BINDIR
     
     echo "Changing directory to " $BINDIR " ..."
     export TMP=$PWD
     cd $BINDIR
     
     # patch all wx dynlibs and Saracon executable
     for file in `ls $WXLIBPOSTFIX`
     do
           # patch all library internal cross references
           echo "Patching " $file "..."
           for fileother in `ls $WXLIBPOSTFIX `
           do
                 # library
                 echo "  Patching " $fileother " with " $file "..."
                 install_name_tool  -change $LIBDEFDIR/$file @executable_path/$file  $fileother
           done
           # patch current library itself
           install_name_tool  -id  @executable_path/$file  $file
           # patch executable
           install_name_tool  -change $LIBDEFDIR/$file @executable_path/$file $APP
     done
     cd $TMP

This script can be added for instance to the build phases of XCode or called from within of a makefile. Make sure that's a bash shell. Others might cause errors. Furthermore the executable has to be linked with the

 -headerpad_max_install_names

option enabled to ensure install_name_tool will work correctly (otherwise complications may arise due to the fact that there is no room for the new pathnames in the executable)

The following variables are required:

  • WXLIBPOSTFIX: The wx dylib pattern which matches the desired version and wx-internal cross references.
  • APP: The executable name.
  • WXLIBDIR: Directory of the wxWidgets dylibs (can also be a system directory when installed)
  • BINDIR: Directory of the executable AND dylibs (it's a limitation of this script but it can be adapted easily)
  • LIBDEFDIR: Default dylib directory when linking (ususally it's '/usr/local/lib'). It can be found out when inspecting the crash report...

Creating a .DMG

http://www.stepwise.com/Articles/Technical/2001-03-29.01.html describes how to package your application as a DMG. This seems to be slightly outdated though. The steps I'm using are:

  • hdiutil create -megabytes 10 -layout NONE Pathalizer.dmg
  • hdid -nomount Pathalizer.dmg
  • newfs_hfs -v pathalizer /dev/disk1
  • hdiutil eject /dev/disk1
  • hdid Pathalizer.dmg
  • cp -r Pathalize.app /Volumes/pathalizer/
  • eject using finder

See also WxMac-specific_topics#Packaging_your_Mac_application_up_for_distribution

See also

 http://developer.apple.com/technotes/tn2002/tn2071.html 
 http://developer.apple.com/documentation/DeveloperTools/Conceptual/cross_development