Tweak DMG creation for Apple changes.
[dcpomatic.git] / platform / osx / make_dmg.sh
1 #!/bin/bash
2 #
3 # Syntax: make_dmg.sh <environment> <builddir> <type> <apple-id> <apple-password>
4 # where <type> is universal or thin
5 #
6 # e.g. make_dmg.sh /Users/carl/osx-environment /Users/carl/cdist universal foo@bar.net opensesame
7
8 # Don't set -e here as egrep (used a few times) returns 1 if no matches
9 # were found.
10
11 version=`git describe --tags --abbrev=0 | sed -e "s/v//"`
12
13 # DMG size in megabytes
14 DMG_SIZE=256
15 ENV=$1
16 ROOT=$2
17 TYPE=$3
18 APPLE_ID=$4
19 APPLE_PASSWORD=$5
20
21 if [ "$TYPE" != "universal" -a "$TYPE" != "thin" ]; then
22     echo "Syntax: $0 <builddir> <type>"
23     echo "where <type> is universal or thin"
24     exit 1
25 fi
26
27 # This is our work area for making up the .dmgs
28 mkdir -p build/platform/osx
29 cd build/platform/osx
30
31 function copy {
32     case $TYPE in
33         universal)
34             for f in $1/32/$2; do
35                 if [ -h $f ]; then
36                     ln -s $(readlink $f) "$3/`basename $f`"
37                 else
38                     g=`echo $f | sed -e "s/\/32\//\/64\//g"`
39                     mkdir -p "$3"
40                     lipo -create $f $g -output "$3/`basename $f`"
41                 fi
42             done
43             ;;
44         thin)
45             if [ -h $1/$2 ]; then
46                 ln -s $(readlink $1/$2) "$3/`basename $f`"
47             else
48                 cp $1/$2 "$3"
49             fi
50             ;;
51     esac
52 }
53
54 function copy_lib_root {
55     case $TYPE in
56         universal)
57             for f in $ROOT/32/lib/$1*.dylib; do
58                 if [ -h $f ]; then
59                     ln -s $(readlink $f) "$2/`basename $f`"
60                 else
61                     g=`echo $f | sed -e "s/\/32\//\/64\//g"`
62                     mkdir -p "$2"
63                     lipo -create $f $g -output "$2/`basename $f`"
64                 fi
65             done
66             ;;
67         thin)
68             for f in $ROOT/lib/$1*.dylib; do
69                 if [ -h $f ]; then
70                     ln -s $(readlink $f) "$2/`basename $f`"
71                 else
72                     mkdir -p "$2"
73                     cp $f "$2"
74                 fi
75             done
76             ;;
77     esac
78     to_relink="$to_relink|$1"
79 }
80
81 function copy_lib_env {
82     case $TYPE in
83         universal)
84             for f in $ENV/32/lib/$1*.dylib; do
85                 if [ -h $f ]; then
86                     ln -s $(readlink $f) "$2/`basename $f`"
87                 else
88                     g=`echo $f | sed -e "s/\/32\//\/64\//g"`
89                     mkdir -p "$2"
90                     lipo -create $f $g -output "$2/`basename $f`"
91                 fi
92             done
93             ;;
94         thin)
95             for f in $ENV/64/lib/$1*.dylib; do
96                 if [ -h $f ]; then
97                     ln -s $(readlink $f) "$2/`basename $f`"
98                 else
99                     mkdir -p "$2"
100                     cp $f "$2"
101                 fi
102             done
103             ;;
104     esac
105     to_relink="$to_relink|$1"
106 }
107
108 # @param #1 directory to copy to
109 function copy_libs {
110     local dest="$1"
111     copy_lib_root libcxml "$dest"
112     copy_lib_root libdcp-1.0 "$dest"
113     copy_lib_root libasdcp-carl "$dest"
114     copy_lib_root libkumu-carl "$dest"
115     copy_lib_root libsub "$dest"
116     copy_lib_root libopenjp2 "$dest"
117     copy_lib_root libavdevice "$dest"
118     copy_lib_root libavformat "$dest"
119     copy_lib_root libavfilter "$dest"
120     copy_lib_root libavutil "$dest"
121     copy_lib_root libavcodec "$dest"
122     copy_lib_root libswscale "$dest"
123     copy_lib_root libpostproc "$dest"
124     copy_lib_root libswresample "$dest"
125     copy $ROOT src/dcpomatic/build/src/lib/libdcpomatic2.dylib "$dest"
126     copy $ROOT src/dcpomatic/build/src/wx/libdcpomatic2-wx.dylib "$dest"
127     copy_lib_env libboost_system "$dest"
128     copy_lib_env libboost_filesystem "$dest"
129     copy_lib_env libboost_thread "$dest"
130     copy_lib_env libboost_date_time "$dest"
131     copy_lib_env libboost_locale "$dest"
132     copy_lib_env libboost_regex "$dest"
133     copy_lib_env libxml++ "$dest"
134     copy_lib_env libxslt "$dest"
135     copy_lib_env libxml2 "$dest"
136     copy_lib_env libglibmm-2.4 "$dest"
137     copy_lib_env libgobject "$dest"
138     copy_lib_env libgthread "$dest"
139     copy_lib_env libgmodule "$dest"
140     copy_lib_env libsigc "$dest"
141     copy_lib_env libglib-2 "$dest"
142     copy_lib_env libintl "$dest"
143     copy_lib_env libsndfile "$dest"
144     copy_lib_env libssh "$dest"
145     copy_lib_env libwx "$dest"
146     copy_lib_env libfontconfig "$dest"
147     copy_lib_env libfreetype "$dest"
148     copy_lib_env libexpat "$dest"
149     copy_lib_env libltdl "$dest"
150     copy_lib_env libxmlsec1 "$dest"
151     copy_lib_env libcurl "$dest"
152     copy_lib_env libffi "$dest"
153     copy_lib_env libpango "$dest"
154     copy_lib_env libcairo "$dest"
155     copy_lib_env libpixman "$dest"
156     copy_lib_env libharfbuzz "$dest"
157     copy_lib_env libsamplerate "$dest"
158     copy_lib_env libicui18n "$dest"
159     copy_lib_env libicudata "$dest"
160     copy_lib_env libicuio "$dest"
161     copy_lib_env libicule "$dest"
162     copy_lib_env libiculx "$dest"
163     copy_lib_env libicutest "$dest"
164     copy_lib_env libicutu "$dest"
165     copy_lib_env libicuuc "$dest"
166     copy_lib_env libFLAC "$dest"
167     copy_lib_env libvorbis "$dest"
168     copy_lib_env libogg "$dest"
169     copy_lib_env libxerces-c "$dest"
170 }
171
172 # @param #1 directory to copy to
173 function copy_resources {
174     local dest="$1"
175     case $TYPE in
176         universal)
177             local prefix=$ROOT/32
178             ;;
179         thin)
180             local prefix=$ROOT
181             ;;
182     esac
183     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic_small.png "$dest"
184     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2.icns "$dest"
185     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_kdm.icns "$dest"
186     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_server.icns "$dest"
187     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_player.icns "$dest"
188     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_batch.icns "$dest"
189     cp $prefix/src/dcpomatic/graphics/osx/preferences/colour_conversions.png "$dest"
190     cp $prefix/src/dcpomatic/graphics/osx/preferences/defaults.png "$dest"
191     cp $prefix/src/dcpomatic/graphics/osx/preferences/kdm_email.png "$dest"
192     cp $prefix/src/dcpomatic/graphics/osx/preferences/email.png "$dest"
193     cp $prefix/src/dcpomatic/graphics/osx/preferences/servers.png "$dest"
194     cp $prefix/src/dcpomatic/graphics/osx/preferences/tms.png "$dest"
195     cp $prefix/src/dcpomatic/graphics/osx/preferences/keys.png "$dest"
196     cp $prefix/src/dcpomatic/graphics/osx/preferences/cover_sheet.png "$dest"
197     cp $prefix/src/dcpomatic/graphics/osx/preferences/notifications.png "$dest"
198     cp $prefix/src/dcpomatic/graphics/osx/preferences/locations.png "$dest"
199     cp $prefix/src/dcpomatic/graphics/osx/preferences/sound.png "$dest"
200     cp $prefix/src/dcpomatic/fonts/LiberationSans-Regular.ttf "$dest"
201     cp $prefix/src/dcpomatic/fonts/LiberationSans-Italic.ttf "$dest"
202     cp $prefix/src/dcpomatic/fonts/LiberationSans-Bold.ttf "$dest"
203     cp $prefix/src/dcpomatic/fonts/fonts.conf.osx "$dest"/fonts.conf
204     cp $prefix/src/dcpomatic/graphics/splash.png "$dest"
205     cp $prefix/src/dcpomatic/graphics/zoom.png "$dest"
206     cp $prefix/src/dcpomatic/graphics/zoom_all.png "$dest"
207     cp $prefix/src/dcpomatic/graphics/select.png "$dest"
208     cp $prefix/src/dcpomatic/graphics/snap.png "$dest"
209     cp $prefix/src/dcpomatic/graphics/sequence.png "$dest"
210     cp $prefix/src/dcpomatic/graphics/me.jpg "$dest"
211     cp -r $prefix/share/libdcp/xsd "$dest"
212
213     # i18n: DCP-o-matic .mo files
214     for lang in de_DE es_ES fr_FR it_IT sv_SE nl_NL ru_RU pl_PL da_DK pt_PT pt_BR sk_SK cs_CZ uk_UA zh_CN tr_TR; do
215         mkdir -p "$dest/$lang/LC_MESSAGES"
216         cp $prefix/src/dcpomatic/build/src/lib/mo/$lang/*.mo "$dest/$lang/LC_MESSAGES"
217         cp $prefix/src/dcpomatic/build/src/wx/mo/$lang/*.mo "$dest/$lang/LC_MESSAGES"
218         cp $prefix/src/dcpomatic/build/src/tools/mo/$lang/*.mo "$dest/$lang/LC_MESSAGES"
219     done
220
221     # i18n: wxWidgets .mo files
222     for lang in de es fr it sv nl ru pl da cs; do
223         mkdir "$dest/$lang"
224         cp $ENV/64/share/locale/$lang/LC_MESSAGES/wxstd.mo "$dest/$lang"
225     done
226 }
227
228 # param $1 list of things that link to other things
229 function relink {
230     to_relink=`echo $to_relink | sed -e "s/\+//g"`
231     local linkers=("$@")
232
233     for obj in "${linkers[@]}"; do
234         deps=`otool -L "$obj" | awk '{print $1}' | egrep "($to_relink)" | egrep "($ENV|$ROOT|boost|libicu)"`
235         changes=""
236         for dep in $deps; do
237             base=`basename $dep`
238             if [ "$TYPE" == "universal" ]; then
239                 # $dep will be a path within 64/; make a 32/ path too
240                 dep32=`echo $dep | sed -e "s/\/64\//\/32\//g"`
241                 changes="$changes -change $dep @executable_path/../Frameworks/$base -change $dep32 @executable_path/../Frameworks/$base"
242             else
243                 changes="$changes -change $dep @executable_path/../Frameworks/$base"
244             fi
245         done
246         if test "x$changes" != "x"; then
247             install_name_tool $changes -id `basename "$obj"` "$obj"
248         fi
249     done
250 }
251
252 # @param #1 .app directory
253 # @param #2 full name e.g. DCP-o-matic Batch Converter
254 # @param #3 bundle id e.g. com.dcpomatic.batch
255 function make_dmg {
256     local appdir="$1"
257     local full_name="$2"
258     local bundle_id="$3"
259     tmp_dmg=dcpomatic_tmp.dmg
260     dmg="$full_name $version.dmg"
261     vol_name=DCP-o-matic-$version
262
263     codesign --deep --force --verify --verbose --options runtime --sign "Developer ID Application: Carl Hetherington (R82DXSR997)" "$appdir"
264     if [ "$?" != "0" ]; then
265         echo "Failed to sign .app"
266         exit 1
267     fi
268
269     mkdir -p $vol_name
270     cp -a "$appdir" $vol_name
271     ln -s /Applications "$vol_name/Applications"
272     cat<<EOF > "$vol_name/READ ME.txt"
273 Welcome to DCP-o-matic!  The first time you run the program there may be
274 a long (several-minute) delay while OS X checks the code for viruses and
275 other malware.  Please be patient!
276 EOF
277     cat<<EOF > "$vol_name/READ ME.de_DE.txt"
278 Beim erstmaligen Start der DCP-o-matic Anwendungen kann ein längerer
279 Verifikationsvorgang auftreten.  Dies ist von der OS X Sicherheitsumgebung
280 'Gatekeeper' verursacht.  Dieser je nach Rechner teils minutenlange
281 Verifikationsvorgang ist gegenwärtig normal und nicht zu umgehen,
282 es ist kein Programmfehler.  Warten sie die Verifikation für jede der
283 DCP-o-matic Anwendungen ab, bei weiteren Programmstarts wird sie nicht
284 mehr auftreten.
285 EOF
286
287     rm -f $tmp_dmg "$dmg"
288     hdiutil create -srcfolder $vol_name -volname $vol_name -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW -size $DMG_SIZE $tmp_dmg
289     attach=$(hdiutil attach -readwrite -noverify -noautoopen $tmp_dmg)
290     device=`echo $attach | egrep '^/dev/' | sed 1q | awk '{print $5}'`
291     sleep 5
292
293     echo '
294   tell application "Finder"
295     tell disk "'$vol_name'"
296            open
297            set current view of container window to icon view
298            set toolbar visible of container window to false
299            set statusbar visible of container window to false
300            set the bounds of container window to {400, 200, 940, 300}
301            set theViewOptions to the icon view options of container window
302            set arrangement of theViewOptions to not arranged
303            set icon size of theViewOptions to 64
304            set position of item "'$appdir'" of container window to {90, 80}
305            set position of item "Applications" of container window to {265, 80}
306            set position of item "READ ME.txt" of container window to {430, 80}
307            set position of item "READ ME.de_DE.txt" of container window to {595, 80}
308            close
309            open
310            update without registering applications
311            delay 5
312      end tell
313    end tell
314 ' | osascript
315
316     chmod -Rf go-w /Volumes/"$vol_name"/"$appdir"
317     sync
318
319     hdiutil eject $device
320     hdiutil convert -format UDZO $tmp_dmg -imagekey zlib-level=9 -o "$dmg"
321     sips -i "$appdir/Contents/Resources/dcpomatic2.icns"
322     DeRez -only icns "$appdir/Contents/Resources/dcpomatic2.icns" > "$appdir/Contents/Resources/DCP-o-matic.rsrc"
323     Rez -append "$appdir/Contents/Resources/DCP-o-matic.rsrc" -o "$dmg"
324     SetFile -a C "$dmg"
325     xattr -c "$dmg"
326
327     set -e
328     codesign --verify --verbose --options runtime --sign "Developer ID Application: Carl Hetherington (R82DXSR997)" "$dmg"
329
330     # We only notarize thin builds, as if we're building universal binaries we must be on an OS
331     # sufficiently old that it can't notarize anyway
332     if [ "$TYPE" == "thin" ]; then
333
334         id=$(xcrun altool --notarize-app -t osx -f "$dmg" --primary-bundle-id $bundle_id -u $APPLE_ID -p $APPLE_PASSWORD --output-format xml | grep -C1 RequestUUID | tail -n 1 | sed -e "s/<string>//g" | sed -e "s/<\/string>//g")
335         N=0
336         while [ 1 ]; do
337             echo "Checking up on $id"
338             status=$(xcrun altool --notarization-info $id -u $APPLE_ID -p $APPLE_PASSWORD --output-format xml)
339             summary=$(echo "$status" | grep -C1 "<key>Status</key>" | tail -n 1 | sed -e "s/    .//g")
340             echo "Got $summary"
341             if [ "$summary" == "<string>invalid</string>" ]; then
342                 echo "Notarization failed."
343                 echo $status
344                 exit 1
345             fi
346             if [ "$summary" == "<string>success</string>" ]; then
347                 break
348             fi
349             sleep 30
350             N=$((N+1))
351             if [ "$N" == "30" ]; then
352                 echo "Timed out waiting for notarization"
353                 exit 1
354             fi
355         done
356
357         xcrun stapler staple "$dmg"
358     fi
359     set +e
360
361     rm $tmp_dmg
362     rm -rf $vol_name
363 }
364
365 # @param #1 appdir
366 function setup {
367     appdir="$1"
368     approot="$appdir/Contents"
369     rm -rf "$appdir"
370     mkdir -p "$approot/MacOS"
371     mkdir -p "$approot/Frameworks"
372     mkdir -p "$approot/Resources"
373
374     to_relink="dcpomatic"
375     copy_libs "$approot/Frameworks"
376     copy_resources "$approot/Resources"
377 }
378
379 case $TYPE in
380     universal)
381         prefix=$ROOT/32
382         ;;
383     thin)
384         prefix=$ROOT
385         ;;
386 esac
387
388 # DCP-o-matic main
389 setup "DCP-o-matic 2.app"
390 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2 "$approot/MacOS"
391 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_cli "$approot/MacOS"
392 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_create "$approot/MacOS"
393 copy $ROOT bin/ffprobe "$approot/MacOS"
394 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
395 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2.Info.plist "$approot/Info.plist"
396 rl=("$approot/MacOS/dcpomatic2" "$approot/MacOS/dcpomatic2_cli" "$approot/MacOS/dcpomatic2_create" "$approot/MacOS/ffprobe" "$approot/Frameworks/"*.dylib)
397 relink "${rl[@]}"
398 make_dmg "$appdir" "DCP-o-matic" com.dcpomatic
399
400 # DCP-o-matic KDM Creator
401 setup "DCP-o-matic 2 KDM Creator.app"
402 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_kdm "$approot/MacOS"
403 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_kdm_cli "$approot/MacOS"
404 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
405 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_kdm.Info.plist "$approot/Info.plist"
406 rl=("$approot/MacOS/dcpomatic2_kdm" "$approot/MacOS/dcpomatic2_kdm_cli" "$approot/Frameworks/"*.dylib)
407 relink "${rl[@]}"
408 make_dmg "$appdir" "DCP-o-matic KDM Creator" com.dcpomatic.kdm
409
410 # DCP-o-matic Encode Server
411 setup "DCP-o-matic 2 Encode Server.app"
412 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_server "$approot/MacOS"
413 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_server_cli "$approot/MacOS"
414 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
415 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_server.Info.plist "$approot/Info.plist"
416 rl=("$approot/MacOS/dcpomatic2_server" "$approot/MacOS/dcpomatic2_server_cli" "$approot/Frameworks/"*.dylib)
417 relink "${rl[@]}"
418 make_dmg "$appdir" "DCP-o-matic Encode Server" com.dcpomatic.server
419
420 # DCP-o-matic Batch Converter
421 setup "DCP-o-matic 2 Batch converter.app"
422 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_batch "$approot/MacOS"
423 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
424 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_batch.Info.plist "$approot/Info.plist"
425 rl=("$approot/MacOS/dcpomatic2_batch" "$approot/Frameworks/"*.dylib)
426 relink "${rl[@]}"
427 make_dmg "$appdir" "DCP-o-matic Batch Converter" com.dcpomatic.batch
428
429 # DCP-o-matic Player
430 setup "DCP-o-matic 2 Player.app"
431 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_player "$approot/MacOS"
432 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
433 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_player.Info.plist "$approot/Info.plist"
434 rl=("$approot/MacOS/dcpomatic2_player" "$approot/Frameworks/"*.dylib)
435 relink "${rl[@]}"
436 make_dmg "$appdir" "DCP-o-matic Player" com.dcpomatic.player