1 #!/bin/bash
2 #
3 # USAGE
4 # osx-app [-s] [-l /path/to/libraries] -py /path/to/python/modules [-l /path/to/libraries] -b /path/to/bin/inkscape -p /path/to/Info.plist
5 #
6 # This script attempts to build an Inkscape.app package for OS X, resolving
7 # dynamic libraries, etc.
8 #
9 # If the '-s' option is given, then the libraries and executable are stripped.
10 #
11 # The Info.plist file can be found in the base inkscape directory once
12 # configure has been run.
13 #
14 # AUTHORS
15 # Kees Cook <kees@outflux.net>
16 # Michael Wybrow <mjwybrow@users.sourceforge.net>
17 # Jean-Olivier Irisson <jo.irisson@gmail.com>
18 #
19 # Copyright (C) 2005 Kees Cook
20 # Copyright (C) 2005-2009 Michael Wybrow
21 # Copyright (C) 2007-2009 Jean-Olivier Irisson
22 #
23 # Released under GNU GPL, read the file 'COPYING' for more information
24 #
25 # Thanks to GNUnet's "build_app" script for help with library dep resolution.
26 # https://gnunet.org/svn/GNUnet/contrib/OSX/build_app
27 #
28 # NB:
29 # When packaging Inkscape for OS X, configure should be run with the
30 # "--enable-osxapp" option which sets the correct paths for support
31 # files inside the app bundle.
32 #
34 # Defaults
35 strip=false
36 add_python=false
37 python_dir=""
39 # If LIBPREFIX is not already set (by osx-build.sh for example) set it to blank (one should use the command line argument to set it correctly)
40 if [ -z $LIBPREFIX ]; then
41 LIBPREFIX=""
42 fi
45 # Help message
46 #----------------------------------------------------------
47 help()
48 {
49 echo -e "
50 Create an app bundle for OS X
52 \033[1mUSAGE\033[0m
53 $0 [-s] [-l /path/to/libraries] -py /path/to/python/modules -b /path/to/bin/inkscape -p /path/to/Info.plist
55 \033[1mOPTIONS\033[0m
56 \033[1m-h,--help\033[0m
57 display this help message
58 \033[1m-s\033[0m
59 strip the libraries and executables from debugging symbols
60 \033[1m-py,--with-python\033[0m
61 add python modules (numpy, lxml) from given directory
62 inside the app bundle
63 \033[1m-l,--libraries\033[0m
64 specify the path to the librairies Inkscape depends on
65 (typically /sw or /opt/local)
66 \033[1m-b--binary\033[0m
67 specify the path to Inkscape's binary. By default it is in
68 Build/bin/ at the base of the source code directory
69 \033[1m-p,--plist\033[0m
70 specify the path to Info.plist. Info.plist can be found
71 in the base directory of the source code once configure
72 has been run
74 \033[1mEXAMPLE\033[0m
75 $0 -s -py ~/python-modules -l /opt/local -b ../../Build/bin/inkscape -p ../../Info.plist
76 "
77 }
80 # Parse command line arguments
81 #----------------------------------------------------------
82 while [ "$1" != "" ]
83 do
84 case $1 in
85 -py|--with-python)
86 add_python=true
87 python_dir="$2"
88 shift 1 ;;
89 -s)
90 strip=true ;;
91 -l|--libraries)
92 LIBPREFIX="$2"
93 shift 1 ;;
94 -b|--binary)
95 binary="$2"
96 shift 1 ;;
97 -p|--plist)
98 plist="$2"
99 shift 1 ;;
100 -h|--help)
101 help
102 exit 0 ;;
103 *)
104 echo "Invalid command line option: $1"
105 exit 2 ;;
106 esac
107 shift 1
108 done
110 echo -e "\n\033[1mCREATE INKSCAPE APP BUNDLE\033[0m\n"
112 # Safety tests
114 if [ "x$binary" == "x" ]; then
115 echo "Inkscape binary path not specified." >&2
116 exit 1
117 fi
119 if [ ! -x "$binary" ]; then
120 echo "Inkscape executable not not found at $binary." >&2
121 exit 1
122 fi
124 if [ "x$plist" == "x" ]; then
125 echo "Info.plist file not specified." >&2
126 exit 1
127 fi
129 if [ ! -f "$plist" ]; then
130 echo "Info.plist file not found at $plist." >&2
131 exit 1
132 fi
134 PYTHONPACKURL="http://inkscape.modevia.com/macosx-snap/Python-packages.dmg"
136 if [ "x$python_dir" == "x" ]; then
137 echo "Python modules directory not specified." >&2
138 echo "Python modules can be downloaded from:" >&2
139 echo " $PYTHONPACKURL" >&2
140 exit 1
141 fi
143 if [ ! -e "$python_dir/i386" -o ! -e "$python_dir/ppc" ]; then
144 echo "Directory does not appear to contain the i386 and ppc python modules:" >&2
145 echo " $python_dir" >&2
146 echo "Python modules can be downloaded from:" >&2
147 echo " $PYTHONPACKURL" >&2
148 exit 1
149 fi
151 if [ ! -e "$LIBPREFIX" ]; then
152 echo "Cannot find the directory containing the libraires: $LIBPREFIX" >&2
153 exit 1
154 fi
156 if [ ! -e "$LIBPREFIX/share/themes/Clearlooks-Quicksilver" ]; then
157 echo "Missing Clearlooks -- please install gtk2-clearlooks and try again." >&2
158 exit 1
159 fi
161 if [ ! -e "$LIBPREFIX/lib/gnome-vfs-2.0" ]; then
162 echo "Missing gnome-vfs -- please install gnome-vfs and try again." >&2
163 exit 1
164 fi
166 if ! pkg-config --modversion ImageMagick >/dev/null 2>&1; then
167 echo "Missing ImageMagick -- please install ImageMagick and try again." >&2
168 exit 1
169 fi
171 if [ ! -e "$LIBPREFIX/lib/aspell-0.60/en.dat" ]; then
172 echo "Missing aspell en dictionary -- please install at least 'aspell-dict-en', but" >&2
173 echo "preferably all dictionaries ('aspell-dict-*') and try again." >&2
174 exit 1
175 fi
178 # Handle some version specific details.
179 VERSION=`/usr/bin/sw_vers | grep ProductVersion | cut -f2 -d'.'`
180 if [ "$VERSION" -ge "4" ]; then
181 # We're on Tiger (10.4) or later.
182 # XCode behaves a little differently in Tiger and later.
183 XCODEFLAGS="-configuration Deployment"
184 SCRIPTEXECDIR="ScriptExec/build/Deployment/ScriptExec.app/Contents/MacOS"
185 EXTRALIBS=""
186 else
187 # Panther (10.3) or earlier.
188 XCODEFLAGS="-buildstyle Deployment"
189 SCRIPTEXECDIR="ScriptExec/build/ScriptExec.app/Contents/MacOS"
190 EXTRALIBS=""
191 fi
194 # Package always has the same name. Version information is stored in
195 # the Info.plist file which is filled in by the configure script.
196 package="Inkscape.app"
198 # Remove a previously existing package if necessary
199 if [ -d $package ]; then
200 echo "Removing previous Inkscape.app"
201 rm -Rf $package
202 fi
205 # Set the 'macosx' directory, usually the current directory.
206 resdir=`pwd`
209 # Prepare Package
210 #----------------------------------------------------------
211 pkgexec="$package/Contents/MacOS"
212 pkgbin="$package/Contents/Resources/bin"
213 pkglib="$package/Contents/Resources/lib"
214 pkglocale="$package/Contents/Resources/locale"
215 pkgpython="$package/Contents/Resources/python/site-packages/"
216 pkgresources="$package/Contents/Resources"
218 mkdir -p "$pkgexec"
219 mkdir -p "$pkgbin"
220 mkdir -p "$pkglib"
221 mkdir -p "$pkglocale"
222 mkdir -p "$pkgpython"
224 mkdir -p "$pkgresources/Dutch.lprj"
225 mkdir -p "$pkgresources/English.lprj"
226 mkdir -p "$pkgresources/French.lprj"
227 mkdir -p "$pkgresources/German.lprj"
228 mkdir -p "$pkgresources/Italian.lprj"
229 mkdir -p "$pkgresources/Spanish.lprj"
230 mkdir -p "$pkgresources/fi.lprj"
231 mkdir -p "$pkgresources/no.lprj"
232 mkdir -p "$pkgresources/sv.lprj"
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
240 cd "$resdir/ScriptExec"
241 echo -e "\033[1mBuilding launcher...\033[0m\n"
242 xcodebuild $XCODEFLAGS clean build
243 )
244 cp "$resdir/$SCRIPTEXECDIR/ScriptExec" "$pkgexec/Inkscape"
247 # Copy all files into the bundle
248 #----------------------------------------------------------
249 echo -e "\n\033[1mFilling app bundle...\033[0m\n"
251 binary_name=`basename "$binary"`
252 binary_dir=`dirname "$binary"`
254 # Inkscape's binary
255 binpath="$pkgbin/inkscape-bin"
256 cp -v "$binary" "$binpath"
257 # TODO Add a "$verbose" variable and command line switch, which sets wether these commands are verbose or not
259 # Share files
260 rsync -av "$binary_dir/../share/$binary_name"/* "$pkgresources/"
261 cp "$plist" "$package/Contents/Info.plist"
262 rsync -av "$binary_dir/../share/locale"/* "$pkgresources/locale"
264 # Copy GTK shared mime information
265 mkdir -p "$pkgresources/share"
266 cp -rp "$LIBPREFIX/share/mime" "$pkgresources/share/"
268 # Icons and the rest of the script framework
269 rsync -av --exclude ".svn" "$resdir"/Resources/* "$pkgresources/"
271 # Add python modules if requested
272 if [ ${add_python} = "true" ]; then
273 # copy python site-packages. They need to be organized in a hierarchical set of directories, by architecture and python major+minor version, e.g. i386/2.3/ for Ptyhon 2.3 on Intel; ppc/2.4/ for Python 2.4 on PPC
274 cp -rvf "$python_dir"/* "$pkgpython"
275 fi
277 # PkgInfo must match bundle type and creator code from Info.plist
278 echo "APPLInks" > $package/Contents/PkgInfo
280 # Pull in extra requirements for Pango and GTK
281 pkgetc="$package/Contents/Resources/etc"
282 mkdir -p $pkgetc/pango
283 cp $LIBPREFIX/etc/pango/pangox.aliases $pkgetc/pango/
284 # Need to adjust path and quote in case of spaces in path.
285 sed -e "s,$LIBPREFIX,\"\${CWD},g" -e 's,\.so ,.so" ,g' $LIBPREFIX/etc/pango/pango.modules > $pkgetc/pango/pango.modules
286 cat > $pkgetc/pango/pangorc <<END_PANGO
287 [Pango]
288 ModuleFiles=\${HOME}/.inkscape-etc/pango.modules
289 [PangoX]
290 AliasFiles=\${HOME}/.inkscape-etc/pangox.aliases
291 END_PANGO
293 # We use a modified fonts.conf file so only need the dtd
294 mkdir -p $pkgetc/fonts
295 cp $LIBPREFIX/etc/fonts/fonts.dtd $pkgetc/fonts/
296 cp -r $LIBPREFIX/etc/fonts/conf.avail $pkgetc/fonts/
297 cp -r $LIBPREFIX/etc/fonts/conf.d $pkgetc/fonts/
299 mkdir -p $pkgetc/gtk-2.0
300 sed -e "s,$LIBPREFIX,\${CWD},g" $LIBPREFIX/etc/gtk-2.0/gdk-pixbuf.loaders > $pkgetc/gtk-2.0/gdk-pixbuf.loaders
301 sed -e "s,$LIBPREFIX,\${CWD},g" $LIBPREFIX/etc/gtk-2.0/gtk.immodules > $pkgetc/gtk-2.0/gtk.immodules
303 for item in gnome-vfs-mime-magic gnome-vfs-2.0
304 do
305 cp -r $LIBPREFIX/etc/$item $pkgetc/
306 done
308 pango_version=`pkg-config --variable=pango_module_version pango`
309 mkdir -p $pkglib/pango/$pango_version/modules
310 cp $LIBPREFIX/lib/pango/$pango_version/modules/*.so $pkglib/pango/$pango_version/modules/
312 gtk_version=`pkg-config --variable=gtk_binary_version gtk+-2.0`
313 mkdir -p $pkglib/gtk-2.0/$gtk_version/{engines,immodules,loaders,printbackends}
314 cp -r $LIBPREFIX/lib/gtk-2.0/$gtk_version/* $pkglib/gtk-2.0/$gtk_version/
316 mkdir -p $pkglib/gnome-vfs-2.0/modules
317 cp $LIBPREFIX/lib/gnome-vfs-2.0/modules/*.so $pkglib/gnome-vfs-2.0/modules/
319 imagemagick_version=`pkg-config --modversion ImageMagick`
320 cp -r "$LIBPREFIX/lib/ImageMagick-$imagemagick_version" "$pkglib/"
321 cp -r "$LIBPREFIX/share/ImageMagick-$imagemagick_version" "$pkgresources/share/"
323 # Copy aspell dictionary files:
324 cp -r "$LIBPREFIX/lib/aspell-0.60" "$pkglib/"
325 cp -r "$LIBPREFIX/share/aspell" "$pkgresources/share/"
327 # Find out libs we need from fink, darwinports, or from a custom install
328 # (i.e. $LIBPREFIX), then loop until no changes.
329 a=1
330 nfiles=0
331 endl=true
332 while $endl; do
333 echo -e "\033[1mLooking for dependencies.\033[0m Round" $a
334 libs="`otool -L $pkglib/gtk-2.0/$gtk_version/{engines,immodules,loaders,printbackends}/*.{dylib,so} $pkglib/pango/$pango_version/modules/* $pkglib/gnome-vfs-2.0/modules/* $package/Contents/Resources/lib/* $pkglib/ImageMagick/modules-Q16/{filters,coders}/*.so $binary 2>/dev/null | fgrep compatibility | cut -d\( -f1 | grep $LIBPREFIX | sort | uniq`"
335 cp -f $libs $package/Contents/Resources/lib
336 let "a+=1"
337 nnfiles=`ls $package/Contents/Resources/lib | wc -l`
338 if [ $nnfiles = $nfiles ]; then
339 endl=false
340 else
341 nfiles=$nnfiles
342 fi
343 done
345 # Add extra libraries of necessary
346 for libfile in $EXTRALIBS
347 do
348 cp -f $libfile $package/Contents/Resources/lib
349 done
352 # Strip libraries and executables if requested
353 #----------------------------------------------------------
354 if [ "$strip" = "true" ]; then
355 echo -e "\n\033[1mStripping debugging symbols...\033[0m\n"
356 chmod +w "$pkglib"/*.dylib
357 strip -x "$pkglib"/*.dylib
358 strip -ur "$binpath"
359 fi
361 # NOTE: This works for all the dylibs but causes GTK to crash at startup.
362 # Instead we leave them with their original install_names and set
363 # DYLD_LIBRARY_PATH within the app bundle before running Inkscape.
364 #
365 fixlib () {
366 libPath="`echo $2 | sed 's/.*Resources//'`"
367 pkgPath="`echo $2 | sed 's/Resources\/.*/Resources/'`"
368 # Fix a given executable or library to be relocatable
369 if [ ! -d "$1" ]; then
370 libs="`otool -L $1 | fgrep compatibility | cut -d\( -f1`"
371 for lib in $libs; do
372 echo " $lib"
373 base=`echo $lib | awk -F/ '{print $NF}'`
374 first=`echo $lib | cut -d/ -f1-3`
375 relative=`echo $lib | cut -d/ -f4-`
376 to=@executable_path/../$relative
377 if [ $first != /usr/lib -a $first != /usr/X11 -a $first != /System/Library ]; then
378 /usr/bin/install_name_tool -change $lib $to $1
379 if [ "`echo $lib | fgrep libcrypto`" = "" ]; then
380 /usr/bin/install_name_tool -id $to $1
381 for ll in $libs; do
382 base=`echo $ll | awk -F/ '{print $NF}'`
383 first=`echo $ll | cut -d/ -f1-3`
384 relative=`echo $ll | cut -d/ -f4-`
385 to=@executable_path/../$relative
386 if [ $first != /usr/lib -a $first != /usr/X11 -a $first != /System/Library -a "`echo $ll | fgrep libcrypto`" = "" ]; then
387 /usr/bin/install_name_tool -change $ll $to $pkgPath/$relative
388 fi
389 done
390 fi
391 fi
392 done
393 fi
394 }
396 rewritelibpaths () {
397 #
398 # Fix package deps
399 (cd "$package/Contents/Resources/lib/gtk-2.0/2.10.0/loaders"
400 for file in *.so; do
401 echo "Rewriting dylib paths for $file..."
402 fixlib "$file" "`pwd`"
403 done
404 )
405 (cd "$package/Contents/Resources/lib/gtk-2.0/2.10.0/engines"
406 for file in *.so; do
407 echo "Rewriting dylib paths for $file..."
408 fixlib "$file" "`pwd`"
409 done
410 )
411 (cd "$package/Contents/Resources/lib/gtk-2.0/2.10.0/immodules"
412 for file in *.so; do
413 echo "Rewriting dylib paths for $file..."
414 fixlib "$file" "`pwd`"
415 done
416 )
417 (cd "$package/Contents/Resources/lib/gtk-2.0/2.10.0/printbackends"
418 for file in *.so; do
419 echo "Rewriting dylib paths for $file..."
420 fixlib "$file" "`pwd`"
421 done
422 )
423 (cd "$package/Contents/Resources/lib/gnome-vfs-2.0/modules"
424 for file in *.so; do
425 echo "Rewriting dylib paths for $file..."
426 fixlib "$file" "`pwd`"
427 done
428 )
429 (cd "$package/Contents/Resources/lib/pango/1.6.0/modules"
430 for file in *.so; do
431 echo "Rewriting dylib paths for $file..."
432 fixlib "$file" "`pwd`"
433 done
434 )
435 (cd "$package/Contents/Resources/lib/ImageMagick/modules-Q16/filters"
436 for file in *.so; do
437 echo "Rewriting dylib paths for $file..."
438 fixlib "$file" "`pwd`"
439 done
440 )
441 (cd "$package/Contents/Resources/lib/ImageMagick/modules-Q16/coders"
442 for file in *.so; do
443 echo "Rewriting dylib paths for $file..."
444 fixlib "$file" "`pwd`"
445 done
446 )
447 (cd "$package/Contents/Resources/bin"
448 for file in *; do
449 echo "Rewriting dylib paths for $file..."
450 fixlib "$file" "`pwd`"
451 done
452 cd ../lib
453 for file in *.dylib; do
454 echo "Rewriting dylib paths for $file..."
455 fixlib "$file" "`pwd`"
456 done
457 )
458 }
460 PATHLENGTH=`echo $LIBPREFIX | wc -c`
461 if [ "$PATHLENGTH" -ge "50" ]; then
462 # If the LIBPREFIX path is long enough to allow
463 # path rewriting, then do this.
464 rewritelibpaths
465 else
466 echo "Could not rewrite dylb paths for bundled libraries. This requires" >&2
467 echo "Macports to be installed in a PREFIX of at least 50 characters in length." >&2
468 echo "" >&2
469 echo "The package will still work if the following line is uncommented in" >&2
470 echo "Inkscape.app/Contents/Resources/bin/inkscape:" >&2
471 echo ' export DYLD_LIBRARY_PATH="$TOP/lib"' >&2
472 exit 1
474 fi
476 exit 0