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