More Qt app bundle migration. Still doesn't work quite right.
[metze/wireshark/wip.git] / packaging / macosx / osx-app.sh
1 #!/bin/bash
2 #
3 # $Id$
4 #
5 # USAGE
6 # osx-app [-s] [-l /path/to/libraries] -bp /path/to/wireshark/bin -p /path/to/Info.plist
7 #
8 # This script attempts to build an Wireshark.app package for OS X, resolving
9 # dynamic libraries, etc.
10 # It strips the executable and libraries if '-s' is given.
11 # It adds python modules if the '-py option' is given
12 # The Info.plist file can be found in the base wireshark directory once
13 # configure has been run.
14 #
15 # AUTHORS
16 #                Kees Cook <kees@outflux.net>
17 #                Michael Wybrow <mjwybrow@users.sourceforge.net>
18 #                Jean-Olivier Irisson <jo.irisson@gmail.com>
19 #
20 # Copyright (C) 2005 Kees Cook
21 # Copyright (C) 2005-2007 Michael Wybrow
22 # Copyright (C) 2007 Jean-Olivier Irisson
23 #
24 # Released under GNU GPL, read the file 'COPYING' for more information
25 #
26 # Thanks to GNUnet's "build_app" script for help with library dep resolution.
27 #               https://gnunet.org/svn/GNUnet/contrib/OSX/build_app
28 #
29 # NB:
30 # This originally came from Inkscape; Inkscape's configure script has an
31 # "--enable-osxapp", which causes some of Inkscape's installation data
32 # files to have OS X-ish paths under Contents/Resources of the package
33 # or under /Library/Application Support.  We don't have such an option;
34 # we just put them in "bin", "etc", "lib", and "share" directories
35 # under Contents/Resources, rather than in the "bin", "etc", "lib",
36 # and "share" directories under the installation directory.
37 #
38
39 # Defaults
40 strip=false
41 binary_path="/tmp/inst/bin"
42 plist="./Info.plist"
43 util_dir="./Utilities"
44 cli_dir="$util_dir/Command Line"
45 chmodbpf_dir="$util_dir/ChmodBPF"
46 frameworks=""
47
48 # "qt" or "gtk"
49 ui_toolkit="gtk"
50 # Name of the Wireshark executable
51 wireshark_bin_name="wireshark"
52
53 binary_list="
54         capinfos
55         dftest
56         dumpcap
57         editcap
58         mergecap
59         randpkt
60         rawshark
61         text2pcap
62         tshark
63 "
64
65 # Location for libraries (macosx-setup.sh defaults to whatever the
66 # various support libraries use as their standard installation location,
67 # which is /usr/local)
68 if [ -z $LIBPREFIX ]; then
69         LIBPREFIX="/usr/local"
70 fi
71
72
73 # Help message
74 #----------------------------------------------------------
75 help()
76 {
77 echo -e "
78 Create an app bundle for OS X
79
80 USAGE
81         $0 [-s] [-l /path/to/libraries] [-qt] -bp /path/to/wireshark/binaries -p /path/to/Info.plist
82
83 OPTIONS
84         -h,--help
85                 display this help message
86         -s
87                 strip the libraries and executables from debugging symbols
88         -l,--libraries
89                 specify the path to the libraries Wireshark depends on
90                 (typically /sw or /opt/local).  By default it is
91                 /usr/local.
92         -bp,--binary-path
93                 specify the path to the Wireshark binaries. By default it
94                 is /tmp/inst/bin.
95         -p,--plist
96                 specify the path to Info.plist. Info.plist can be found
97                 in the base directory of the source code once configure
98                 has been run.
99         -sdkroot
100                 specify the root of the SDK to use
101         -qt,--qt-flavor
102                 use the Qt flavor
103
104 EXAMPLE
105         $0 -s -l /opt/local -bp ../../Build/bin -p Info.plist -sdkroot /Developer/SDKs/MacOSX10.5.sdk
106 "
107 }
108
109
110 # Parse command line arguments
111 #----------------------------------------------------------
112 while [ "$1" != "" ]
113 do
114         case $1 in
115                 -s)
116                         strip=true ;;
117                 -l|--libraries)
118                         LIBPREFIX="$2"
119                         shift 1 ;;
120                 -bp|--binary-path)
121                         binary_path="$2"
122                         shift 1 ;;
123                 -p|--plist)
124                         plist="$2"
125                         shift 1 ;;
126                 -qt|--qt-flavor)
127                         ui_toolkit="qt"
128                         wireshark_bin_name="wireshark-qt"
129                         ;;
130                 -h|--help)
131                         help
132                         exit 0 ;;
133                 -sdkroot)
134                         sdkroot="$2"
135                         shift 1 ;;
136                 *)
137                         echo "Invalid command line option: $1"
138                         exit 2 ;;
139         esac
140         shift 1
141 done
142
143 echo -e "\nCREATE WIRESHARK APP BUNDLE\n"
144
145 # Safety tests
146 if [ ! -e "$LIBPREFIX" ]; then
147         echo "Cannot find the directory containing the libraries: $LIBPREFIX" >&2
148         exit 1
149 fi
150
151 for binary in $wireshark_bin_name $binary_list ; do
152         if [ ! -x "$binary_path/$binary" ]; then
153                 echo "Couldn't find $binary (or it's not executable)" >&2
154                 exit 1
155         fi
156 done
157
158 if [ ! -f "$plist" ]; then
159         echo "Need plist file" >&2
160         exit 1
161 fi
162
163
164 # Handle some version specific details.
165 VERSION=`/usr/bin/sw_vers | grep ProductVersion | cut -f2 -d'.'`
166 if [ "$VERSION" -ge "4" ]; then
167         # We're on Tiger (10.4) or later.
168         # XCode behaves a little differently in Tiger and later.
169         XCODEFLAGS="-configuration Deployment"
170         SCRIPTEXECDIR="ScriptExec/build/Deployment/ScriptExec.app/Contents/MacOS"
171         EXTRALIBS=""
172 else
173         # Panther (10.3) or earlier.
174         XCODEFLAGS="-buildstyle Deployment"
175         SCRIPTEXECDIR="ScriptExec/build/ScriptExec.app/Contents/MacOS"
176         EXTRALIBS=""
177 fi
178
179 # Set the SDK root, if an SDK was specified.
180 # (-sdk is only supported by the xcodebuild in the version of the
181 # developer tools that came with Snow Leopard and later versions)
182 if [ ! -z "$sdkroot" ]
183 then
184         XCODEFLAGS="$XCODEFLAGS SDKROOT=$sdkroot"
185 fi
186
187 # Package always has the same name. Version information is stored in
188 # the Info.plist file which is filled in by the configure script.
189 package="Wireshark.app"
190
191 # Remove a previously existing package if necessary
192 if [ -d $package ]; then
193         echo "Removing previous Wireshark.app"
194         rm -Rf $package
195 fi
196
197 # Remove a previously existing utility directory if necessary
198 if [ -d "$util_dir" ]; then
199         echo "Removing $util_dir directory"
200         rm -Rf "$util_dir"
201 fi
202
203 # Set the 'macosx' directory, usually the current directory.
204 resdir=`pwd`
205
206
207 # Prepare Package
208 #----------------------------------------------------------
209 pkgexec="$package/Contents/MacOS"
210 pkgres="$package/Contents/Resources"
211 pkgbin="$pkgres/bin"
212 # Should pkglib be Contents/Frameworks instead?
213 pkglib="$pkgres/lib"
214 pkgplugin="$pkglib/wireshark/plugins"
215 pkgpython="$pkglib/wireshark/python"
216
217 mkdir -p "$pkgexec"
218 mkdir -p "$pkgbin"
219 mkdir -p "$pkgplugin"
220 mkdir -p "$pkgpython"
221
222 mkdir -p "$cli_dir"
223
224 if [ "$ui_toolkit" = "qt" ] ; then
225         cp "$binary_path/$wireshark_bin_name" "$pkgexec/Wireshark"
226 else
227 # Build and add the launcher
228 #----------------------------------------------------------
229         (
230                 # Build fails if CC happens to be set (to anything other than CompileC)
231                 unset CC
232
233                 cd "$resdir/ScriptExec"
234                 echo -e "Building launcher...\n"
235                 xcodebuild $XCODEFLAGS clean build
236         )
237         cp "$resdir/$SCRIPTEXECDIR/ScriptExec" "$pkgexec/Wireshark"
238
239 fi
240
241 # Copy all files into the bundle
242 #----------------------------------------------------------
243 echo -e "\nFilling app bundle and utility directory...\n"
244
245 # Wireshark executables
246 cp -v utility-launcher "$cli_dir/$binary"
247 for binary in $binary_list ; do
248         # Copy the binary to its destination
249         dest_path="$pkgbin/$binary-bin"
250         cp -v "$binary_path/$binary" "$dest_path"
251         # TODO Add a "$verbose" variable and command line switch, which sets wether these commands are verbose or not
252
253         ln -sv ./wireshark "$pkgbin/$binary"
254         ln -sv ./wireshark "$cli_dir/$binary"
255 done
256
257 # ChmodBPF
258 mkdir -p "$chmodbpf_dir"
259 cp -v ChmodBPF/* "$chmodbpf_dir"
260 chmod -R g-w "$chmodbpf_dir"
261
262 # The rest of the Wireshark installation (we handled bin above)
263 rsync -av \
264         --exclude bin/ \
265         --exclude lib/wireshark/plugins/ \
266         --exclude lib/wireshark/python/ \
267         "$binary_path/.."/* "$pkgres"
268
269 # Remove the version number from the plugin path
270 find "$binary_path/../lib/wireshark/plugins" -type f \
271         -exec cp -fv "{}" "$pkgplugin/" \;
272
273 # Remove the version number from the python path
274 find "$binary_path/../lib/wireshark/python" -type f \
275         -exec cp -fv "{}" "$pkgpython/" \;
276
277 cp "$plist" "$package/Contents/Info.plist"
278
279 # Icons and the rest of the script framework
280 res_list="
281         Wireshark.icns
282         Wiresharkdoc.icns
283         bin
284         openDoc
285 "
286
287 if [ "$ui_toolkit" = "gtk" ] ; then
288         res_list="
289                 $res_list
290                 etc
291                 script
292                 MenuBar.nib
293                 ProgressWindow.nib
294                 themes
295         "
296 fi
297
298 for rl_entry in $res_list ; do
299         rsync -av "$resdir"/Resources/$rl_entry "$package"/Contents/Resources/
300 done
301
302 # PkgInfo must match bundle type and creator code from Info.plist
303 echo "APPLWshk" > $package/Contents/PkgInfo
304
305 if [ "$ui_toolkit" = "gtk" ] ; then
306
307         # Pull in extra requirements for Pango and GTK
308         pkgetc="$package/Contents/Resources/etc"
309         mkdir -p $pkgetc/pango
310         cp $LIBPREFIX/etc/pango/pangox.aliases $pkgetc/pango/
311         # Need to adjust path and quote in case of spaces in path.
312         sed -e "s,$LIBPREFIX,\"\${CWD},g" -e 's,\.so ,.so" ,g' $LIBPREFIX/etc/pango/pango.modules > $pkgetc/pango/pango.modules
313         cat > $pkgetc/pango/pangorc <<END_PANGO
314 [Pango]
315 ModuleFiles=\${HOME}/.wireshark-etc/pango.modules
316 [PangoX]
317 AliasFiles=\${HOME}/.wireshark-etc/pangox.aliases
318 END_PANGO
319
320         # We use a modified fonts.conf file so only need the dtd
321         mkdir -p $pkgetc/fonts
322         cp $LIBPREFIX/etc/fonts/fonts.dtd $pkgetc/fonts/
323         cp -r $LIBPREFIX/etc/fonts/conf.avail $pkgetc/fonts/
324         cp -r $LIBPREFIX/etc/fonts/conf.d $pkgetc/fonts/
325
326         mkdir -p $pkgetc/gtk-2.0
327         #
328         # In newer versions of GTK+, the gdk-pixbuf library was split off from
329         # GTK+, and the gdk-pixbuf.loaders file moved, so we check for its
330         # existence here.
331         #
332         # The file is ultimately copied to the user's home directory, with
333         # the pathnames adjusted to refer to the installed package, so we
334         # always put it in the same location in the installed package,
335         # regardless of where it lives in the machine on which it's built.
336         #
337         if [ -e $LIBPREFIX/etc/gtk-2.0/gdk-pixbuf.loaders ]
338         then
339                 sed -e "s,$LIBPREFIX,\${CWD},g" $LIBPREFIX/etc/gtk-2.0/gdk-pixbuf.loaders > $pkgetc/gtk-2.0/gdk-pixbuf.loaders
340         fi
341         sed -e "s,$LIBPREFIX,\${CWD},g" $LIBPREFIX/etc/gtk-2.0/gtk.immodules > $pkgetc/gtk-2.0/gtk.immodules
342
343         pango_version=`pkg-config --variable=pango_module_version pango`
344         mkdir -p $pkglib/pango/$pango_version/modules
345         cp $LIBPREFIX/lib/pango/$pango_version/modules/*.so $pkglib/pango/$pango_version/modules/
346
347         gtk_version=`pkg-config --variable=gtk_binary_version gtk+-2.0`
348         mkdir -p $pkglib/gtk-2.0/$gtk_version/{engines,immodules,loaders}
349         cp -r $LIBPREFIX/lib/gtk-2.0/$gtk_version/* $pkglib/gtk-2.0/$gtk_version/
350
351         gdk_pixbuf_version=`pkg-config --variable=gdk_pixbuf_binary_version gdk-pixbuf-2.0`
352         if [ ! -z $gdk_pixbuf_version ]; then
353                 mkdir -p $pkglib/gdk-pixbuf-2.0/$gdk_pixbuf_version/loaders
354                 #
355                 # As per the above, check whether we have a loaders.cache file
356                 # in $LIBPREFIX/lib/gdk-pixbuf-2.0/$gdk_pixbuf_version, as
357                 # that's where the output of gdk-pixbuf-query-loaders gets
358                 # put if gdk-pixbuf and GTK+ are separated.
359                 #
360                 # The file is ultimately copied to the user's home directory,
361                 # with the pathnames adjusted to refer to the installed package,
362                 # so we always put it in the same location in the installed
363                 # package, regardless of where it lives in the machine on which
364                 # it's built.
365                 #
366                 if [ -e $LIBPREFIX/lib/gdk-pixbuf-2.0/$gdk_pixbuf_version/loaders.cache ]
367                 then
368                         sed -e "s,$LIBPREFIX,\${CWD},g" $LIBPREFIX/lib/gdk-pixbuf-2.0/$gdk_pixbuf_version/loaders.cache > $pkgetc/gtk-2.0/gdk-pixbuf.loaders
369                 fi
370                 cp -r $LIBPREFIX/lib/gdk-pixbuf-2.0/$gdk_pixbuf_version/loaders/* $pkglib/gdk-pixbuf-2.0/$gdk_pixbuf_version/loaders
371         fi
372 elif [ "$ui_toolkit" = "qt" ] ; then
373         frameworks="`otool -L $pkgexec/Wireshark 2>/dev/null | grep 'Qt.*framework' | sed -e 's:.framework/.*:.framework:' | sort | uniq`"
374         for fwk in $frameworks ; do
375                 rsync -av \
376                 --exclude "Headers/" \
377                 --exclude "*_debug*" \
378                 $fwk "$pkglib"
379         done
380 fi # GTK+ / Qt
381
382 # Find out libs we need from Fink, MacPorts, or from a custom install
383 # (i.e. $LIBPREFIX), then loop until no changes.
384 a=1
385 nfiles=0
386 endl=true
387 lib_dep_search_list="
388         $pkglib/*
389         $pkgbin/*-bin
390         "
391 if [ "$ui_toolkit" = "gtk" ] ; then
392         lib_dep_search_list="
393                 $lib_dep_search_list
394                 $pkglib/gtk-2.0/$gtk_version/loaders/*
395                 $pkglib/gtk-2.0/$gtk_version/immodules/*
396                 $pkglib/gtk-2.0/$gtk_version/engines/*.so
397                 $pkglib/pango/$pango_version/modules/*
398                 $pkglib/gdk-pixbuf-2.0/$gdk_pixbuf_version/loaders/*
399                 "
400 elif [ "$ui_toolkit" = "qt" ] ; then
401         lib_dep_search_list="
402                 $pkgexec/Wireshark
403                 $lib_dep_search_list
404                 $pkglib/Qt*.framework/Versions/[0-9]*/Qt*
405                 "
406 fi
407
408 while $endl; do
409         echo -e "Looking for dependencies. Round" $a
410         libs="`otool -L $lib_dep_search_list 2>/dev/null | fgrep compatibility | cut -d\( -f1 | grep $LIBPREFIX | sort | uniq`"
411         cp -vn $libs "$pkglib"
412         let "a+=1"
413         nnfiles=`ls "$pkglib" | wc -l`
414         if [ $nnfiles = $nfiles ]; then
415                 endl=false
416         else
417                 nfiles=$nnfiles
418         fi
419 done
420
421 # Add extra libraries of necessary
422 for libfile in $EXTRALIBS
423 do
424         cp -f $libfile "$pkglib"
425 done
426 chmod 755 "$pkglib"/*.dylib
427
428 # Strip libraries and executables if requested
429 #----------------------------------------------------------
430 if [ "$strip" = "true" ]; then
431         echo -e "\nStripping debugging symbols...\n"
432         strip -x "$pkglib"/*.dylib
433         strip -ur "$binpath"
434 fi
435
436 # NOTE: we must rpathify *all* files, *including* plugins for GTK+ etc.,
437 #       to keep GTK+ from crashing at startup.
438 #
439 rpathify_file () {
440         # Fix a given executable, library, or plugin to be relocatable
441         if [ ! -d "$1" ]; then
442                 #
443                 # OK, what type of file is this?
444                 #
445                 filetype=`otool -hv "$1" | sed -n '4p' | awk '{print $5}'`
446                 case "$filetype" in
447
448                 EXECUTE|DYLIB|BUNDLE)
449                         #
450                         # Executable, library, or plugin.  (Plugins
451                         # can be either DYLIB or BUNDLE; shared
452                         # libraries are DYLIB.)
453                         #
454                         # For DYLIB and BUNDLE, fix the shared
455                         # library identification.
456                         #
457                         if [[ "$filetype" = "DYLIB" || "$filetype" = "BUNDLE" ]]; then
458                                 echo "Changing shared library identification of $1"
459                                 base=`echo $1 | awk -F/ '{print $NF}'`
460                                 #
461                                 # The library will end up in a directory in
462                                 # the rpath; this is what we should change its
463                                 # ID to.
464                                 #
465                                 to=@rpath/$base
466                                 /usr/bin/install_name_tool -id $to $1
467                         fi
468
469                         #
470                         # Get the list of dynamic libraries on which this
471                         # file depends, and select only the libraries that
472                         # are in $LIBPREFIX, as those are the only ones
473                         # that we'll be shipping in the app bundle; the
474                         # other libraries are system-supplied or supplied
475                         # as part of X11, will be expected to be on the
476                         # system on which the bundle will be installed,
477                         # and should be referred to by their full pathnames.
478                         #
479                         libs="`otool -L $1 | egrep "$LIBPREFIX.* \(compatibility" | cut -d\( -f1`"
480                         for lib in $libs; do
481                                 #
482                                 # Get the file name of the library.
483                                 #
484                                 base=`echo $lib | awk -F/ '{print $NF}'`
485                                 #
486                                 # The library will end up in a directory in
487                                 # the rpath; this is what we should change its
488                                 # file name to.
489                                 #
490                                 to=@rpath/$base
491                                 #
492                                 # Change the reference to that library.
493                                 #
494                                 echo "Changing reference to $lib in $1"
495                                 /usr/bin/install_name_tool -change $lib $to $1
496                         done
497                         #
498                         # Rewrite framework paths
499                         #
500                         if [ "$ui_toolkit" = "qt" ] ; then
501                                 frameworks="`otool -L $1 | egrep "Qt.*framework/.* \(compatibility" | cut -d\( -f1`"
502                                 for fwk in $frameworks ; do
503                                         #
504                                         # Get the file name of the framework.
505                                         #
506                                         base=`echo $fwk | awk 'BEGIN{FS="/";OFS="/"} {print $(NF-3), $(NF-2), $(NF-1), $NF}'`
507                                         #
508                                         # The library will end up in a directory in
509                                         # the rpath; this is what we should change its
510                                         # file name to.
511                                         #
512                                         to=@rpath/$base
513                                         #
514                                         # Change the reference to that library.
515                                         #
516                                         echo "Changing reference to $fwk in $1"
517                                         /usr/bin/install_name_tool -change $fwk $to $1
518                                 done
519                         fi
520                         ;;
521                 esac
522         fi
523 }
524
525 rpathify_dir () {
526         #
527         # Make sure we *have* that directory
528         #
529         if [ -d "$1" ]; then
530                 (cd "$1"
531                 #
532                 # Make sure we *have* files to fix
533                 #
534                 files=`ls $2 2>/dev/null`
535                 if [ ! -z "$files" ]; then
536                         for file in $files; do
537                                 rpathify_file "$file" "`pwd`"
538                         done
539                 fi
540                 )
541         fi
542 }
543
544 rpathify_files () {
545         #
546         # Fix package deps
547         #
548         rpathify_dir "$pkglib" "*.dylib"
549         if [ "$ui_toolkit" = "gtk" ] ; then
550                 rpathify_dir "$pkglib/gtk-2.0/$gtk_version/loaders" "*.so"
551                 rpathify_dir "$pkglib/gtk-2.0/$gtk_version/engines" "*.so"
552                 rpathify_dir "$pkglib/gtk-2.0/$gtk_version/immodules" "*.so"
553                 rpathify_dir "$pkglib/gtk-2.0/$gtk_version/printbackends" "*.so"
554                 rpathify_dir "$pkglib/gnome-vfs-2.0/modules" "*.so"
555                 rpathify_dir "$pkglib/gdk-pixbuf-2.0/$gtk_version/loaders" "*.so"
556                 rpathify_dir "$pkglib/pango/$pango_version/modules" "*.so"
557         fi
558         rpathify_dir "$pkgbin" "*"
559         if [ "$ui_toolkit" = "qt" ] ; then
560                 rpathify_dir "$pkgexec" "Wireshark"
561                 for fwk_dir in $pkglib/Qt*.framework/Versions/[0-9]* ; do
562                         rpathify_dir "$fwk_dir" "Qt*"
563                 done
564         fi
565 }
566
567 PATHLENGTH=`echo $LIBPREFIX | wc -c`
568 if [ "$PATHLENGTH" -ge "6" ]; then
569         # If the LIBPREFIX path is long enough to allow 
570         # path rewriting, then do this.
571         # 6 is the length of @rpath, which replaces LIBPREFIX.
572         rpathify_files
573 else
574         echo "Could not rewrite dylib paths for bundled libraries.  This requires" >&2
575         echo "the support libraries to be installed in a PREFIX of at least 6 characters in length." >&2
576         echo "" >&2
577         echo "The package will still work if the following line is uncommented in" >&2
578         echo "Wireshark.app/Contents/Resources/bin/{various scripts}:" >&2
579         echo '        export DYLD_LIBRARY_PATH="$TOP/lib"' >&2
580         exit 1
581
582 fi
583
584 exit 0