Change internal name dist -> disk.
[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_lib_root liblwext4 "$dest"
126     copy_lib_root libblockdev "$dest"
127     copy $ROOT src/dcpomatic/build/src/lib/libdcpomatic2.dylib "$dest"
128     copy $ROOT src/dcpomatic/build/src/wx/libdcpomatic2-wx.dylib "$dest"
129     copy_lib_env libboost_system "$dest"
130     copy_lib_env libboost_filesystem "$dest"
131     copy_lib_env libboost_thread "$dest"
132     copy_lib_env libboost_date_time "$dest"
133     copy_lib_env libboost_locale "$dest"
134     copy_lib_env libboost_regex "$dest"
135     copy_lib_env libxml++ "$dest"
136     copy_lib_env libxslt "$dest"
137     copy_lib_env libxml2 "$dest"
138     copy_lib_env libglibmm-2.4 "$dest"
139     copy_lib_env libgobject "$dest"
140     copy_lib_env libgthread "$dest"
141     copy_lib_env libgmodule "$dest"
142     copy_lib_env libsigc "$dest"
143     copy_lib_env libglib-2 "$dest"
144     copy_lib_env libintl "$dest"
145     copy_lib_env libsndfile "$dest"
146     copy_lib_env libssh "$dest"
147     copy_lib_env libwx "$dest"
148     copy_lib_env libfontconfig "$dest"
149     copy_lib_env libfreetype "$dest"
150     copy_lib_env libexpat "$dest"
151     copy_lib_env libltdl "$dest"
152     copy_lib_env libxmlsec1 "$dest"
153     copy_lib_env libcurl "$dest"
154     copy_lib_env libffi "$dest"
155     copy_lib_env libpango "$dest"
156     copy_lib_env libcairo "$dest"
157     copy_lib_env libpixman "$dest"
158     copy_lib_env libharfbuzz "$dest"
159     copy_lib_env libsamplerate "$dest"
160     copy_lib_env libicui18n "$dest"
161     copy_lib_env libicudata "$dest"
162     copy_lib_env libicuio "$dest"
163     copy_lib_env libicule "$dest"
164     copy_lib_env libiculx "$dest"
165     copy_lib_env libicutest "$dest"
166     copy_lib_env libicutu "$dest"
167     copy_lib_env libicuuc "$dest"
168     copy_lib_env libFLAC "$dest"
169     copy_lib_env libvorbis "$dest"
170     copy_lib_env libogg "$dest"
171     copy_lib_env libxerces-c "$dest"
172     copy_lib_env libnanomsg "$dest"
173 }
174
175 # @param #1 directory to copy to
176 function copy_resources {
177     local dest="$1"
178     case $TYPE in
179         universal)
180             local prefix=$ROOT/32
181             ;;
182         thin)
183             local prefix=$ROOT
184             ;;
185     esac
186     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic_small.png "$dest"
187     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2.icns "$dest"
188     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_kdm.icns "$dest"
189     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_server.icns "$dest"
190     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_player.icns "$dest"
191     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_batch.icns "$dest"
192     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_playlist.icns "$dest"
193     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_disk.icns "$dest"
194     cp $prefix/src/dcpomatic/graphics/osx/preferences/colour_conversions.png "$dest"
195     cp $prefix/src/dcpomatic/graphics/osx/preferences/defaults.png "$dest"
196     cp $prefix/src/dcpomatic/graphics/osx/preferences/kdm_email.png "$dest"
197     cp $prefix/src/dcpomatic/graphics/osx/preferences/email.png "$dest"
198     cp $prefix/src/dcpomatic/graphics/osx/preferences/servers.png "$dest"
199     cp $prefix/src/dcpomatic/graphics/osx/preferences/tms.png "$dest"
200     cp $prefix/src/dcpomatic/graphics/osx/preferences/keys.png "$dest"
201     cp $prefix/src/dcpomatic/graphics/osx/preferences/cover_sheet.png "$dest"
202     cp $prefix/src/dcpomatic/graphics/osx/preferences/notifications.png "$dest"
203     cp $prefix/src/dcpomatic/graphics/osx/preferences/locations.png "$dest"
204     cp $prefix/src/dcpomatic/graphics/osx/preferences/sound.png "$dest"
205     cp $prefix/src/dcpomatic/fonts/LiberationSans-Regular.ttf "$dest"
206     cp $prefix/src/dcpomatic/fonts/LiberationSans-Italic.ttf "$dest"
207     cp $prefix/src/dcpomatic/fonts/LiberationSans-Bold.ttf "$dest"
208     cp $prefix/src/dcpomatic/fonts/fonts.conf.osx "$dest"/fonts.conf
209     cp $prefix/src/dcpomatic/graphics/splash.png "$dest"
210     cp $prefix/src/dcpomatic/graphics/zoom.png "$dest"
211     cp $prefix/src/dcpomatic/graphics/zoom_all.png "$dest"
212     cp $prefix/src/dcpomatic/graphics/select.png "$dest"
213     cp $prefix/src/dcpomatic/graphics/snap.png "$dest"
214     cp $prefix/src/dcpomatic/graphics/sequence.png "$dest"
215     cp $prefix/src/dcpomatic/graphics/me.jpg "$dest"
216     cp -r $prefix/share/libdcp/xsd "$dest"
217
218     # i18n: DCP-o-matic .mo files
219     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
220         mkdir -p "$dest/$lang/LC_MESSAGES"
221         cp $prefix/src/dcpomatic/build/src/lib/mo/$lang/*.mo "$dest/$lang/LC_MESSAGES"
222         cp $prefix/src/dcpomatic/build/src/wx/mo/$lang/*.mo "$dest/$lang/LC_MESSAGES"
223         cp $prefix/src/dcpomatic/build/src/tools/mo/$lang/*.mo "$dest/$lang/LC_MESSAGES"
224     done
225
226     # i18n: wxWidgets .mo files
227     for lang in de es fr it sv nl ru pl da cs; do
228         mkdir "$dest/$lang"
229         cp $ENV/64/share/locale/$lang/LC_MESSAGES/wxstd.mo "$dest/$lang"
230     done
231 }
232
233 # param $1 list of things that link to other things
234 function relink {
235     to_relink=`echo $to_relink | sed -e "s/\+//g"`
236     local linkers=("$@")
237
238     for obj in "${linkers[@]}"; do
239         deps=`otool -L "$obj" | awk '{print $1}' | egrep "($to_relink)" | egrep "($ENV|$ROOT|boost|libicu)"`
240         changes=""
241         for dep in $deps; do
242             base=`basename $dep`
243             if [ "$TYPE" == "universal" ]; then
244                 # $dep will be a path within 64/; make a 32/ path too
245                 dep32=`echo $dep | sed -e "s/\/64\//\/32\//g"`
246                 changes="$changes -change $dep @executable_path/../Frameworks/$base -change $dep32 @executable_path/../Frameworks/$base"
247             else
248                 changes="$changes -change $dep @executable_path/../Frameworks/$base"
249             fi
250         done
251         if test "x$changes" != "x"; then
252             install_name_tool $changes -id `basename "$obj"` "$obj"
253         fi
254     done
255 }
256
257 # @param #1 .app directory
258 # @param #2 full name e.g. DCP-o-matic Batch Converter
259 # @param #3 bundle id e.g. com.dcpomatic.batch
260 function make_dmg {
261     local appdir="$1"
262     local full_name="$2"
263     local bundle_id="$3"
264     tmp_dmg=dcpomatic_tmp.dmg
265     dmg="$full_name $version.dmg"
266     vol_name=DCP-o-matic-$version
267
268     codesign --deep --force --verify --verbose --options runtime --sign "Developer ID Application: Carl Hetherington (R82DXSR997)" "$appdir"
269     if [ "$?" != "0" ]; then
270         echo "Failed to sign .app"
271         exit 1
272     fi
273
274     mkdir -p $vol_name
275     cp -a "$appdir" $vol_name
276     ln -s /Applications "$vol_name/Applications"
277     cat<<EOF > "$vol_name/READ ME.txt"
278 Welcome to DCP-o-matic!  The first time you run the program there may be
279 a long (several-minute) delay while OS X checks the code for viruses and
280 other malware.  Please be patient!
281 EOF
282     cat<<EOF > "$vol_name/READ ME.de_DE.txt"
283 Beim erstmaligen Start der DCP-o-matic Anwendungen kann ein längerer
284 Verifikationsvorgang auftreten.  Dies ist von der OS X Sicherheitsumgebung
285 'Gatekeeper' verursacht.  Dieser je nach Rechner teils minutenlange
286 Verifikationsvorgang ist gegenwärtig normal und nicht zu umgehen,
287 es ist kein Programmfehler.  Warten sie die Verifikation für jede der
288 DCP-o-matic Anwendungen ab, bei weiteren Programmstarts wird sie nicht
289 mehr auftreten.
290 EOF
291
292     rm -f $tmp_dmg "$dmg"
293     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
294     attach=$(hdiutil attach -readwrite -noverify -noautoopen $tmp_dmg)
295     device=`echo $attach | egrep '^/dev/' | sed 1q | awk '{print $5}'`
296     sleep 5
297
298     echo '
299   tell application "Finder"
300     tell disk "'$vol_name'"
301            open
302            set current view of container window to icon view
303            set toolbar visible of container window to false
304            set statusbar visible of container window to false
305            set the bounds of container window to {400, 200, 940, 300}
306            set theViewOptions to the icon view options of container window
307            set arrangement of theViewOptions to not arranged
308            set icon size of theViewOptions to 64
309            set position of item "'$appdir'" of container window to {90, 80}
310            set position of item "Applications" of container window to {265, 80}
311            set position of item "READ ME.txt" of container window to {430, 80}
312            set position of item "READ ME.de_DE.txt" of container window to {595, 80}
313            close
314            open
315            update without registering applications
316            delay 5
317      end tell
318    end tell
319 ' | osascript
320
321     chmod -Rf go-w /Volumes/"$vol_name"/"$appdir"
322     sync
323
324     hdiutil eject $device
325     hdiutil convert -format UDZO $tmp_dmg -imagekey zlib-level=9 -o "$dmg"
326     sips -i "$appdir/Contents/Resources/dcpomatic2.icns"
327     DeRez -only icns "$appdir/Contents/Resources/dcpomatic2.icns" > "$appdir/Contents/Resources/DCP-o-matic.rsrc"
328     Rez -append "$appdir/Contents/Resources/DCP-o-matic.rsrc" -o "$dmg"
329     SetFile -a C "$dmg"
330     xattr -c "$dmg"
331
332     set -e
333     codesign --verify --verbose --options runtime --sign "Developer ID Application: Carl Hetherington (R82DXSR997)" "$dmg"
334
335     # We only notarize thin builds, as if we're building universal binaries we must be on an OS
336     # sufficiently old that it can't notarize anyway
337     if [ "$TYPE" == "thin" ]; then
338
339         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")
340         N=0
341         while [ 1 ]; do
342             echo "Checking up on $id"
343             status=$(xcrun altool --notarization-info $id -u $APPLE_ID -p $APPLE_PASSWORD --output-format xml)
344             summary=$(echo "$status" | grep -C1 "<key>Status</key>" | tail -n 1 | sed -e "s/    .//g")
345             echo "Got $summary"
346             if [ "$summary" == "<string>invalid</string>" ]; then
347                 echo "Notarization failed."
348                 echo $status
349                 exit 1
350             fi
351             if [ "$summary" == "<string>success</string>" ]; then
352                 break
353             fi
354             sleep 30
355             N=$((N+1))
356             if [ "$N" == "30" ]; then
357                 echo "Timed out waiting for notarization"
358                 exit 1
359             fi
360         done
361
362         xcrun stapler staple "$dmg"
363     fi
364     set +e
365
366     rm $tmp_dmg
367     rm -rf $vol_name
368 }
369
370 # @param #1 appdir
371 function setup {
372     appdir="$1"
373     approot="$appdir/Contents"
374     rm -rf "$appdir"
375     mkdir -p "$approot/MacOS"
376     mkdir -p "$approot/Frameworks"
377     mkdir -p "$approot/Resources"
378
379     to_relink="dcpomatic"
380     copy_libs "$approot/Frameworks"
381     copy_resources "$approot/Resources"
382 }
383
384 case $TYPE in
385     universal)
386         prefix=$ROOT/32
387         ;;
388     thin)
389         prefix=$ROOT
390         ;;
391 esac
392
393 # DCP-o-matic main
394 setup "DCP-o-matic 2.app"
395 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2 "$approot/MacOS"
396 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_cli "$approot/MacOS"
397 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_create "$approot/MacOS"
398 copy $ROOT bin/ffprobe "$approot/MacOS"
399 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
400 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2.Info.plist "$approot/Info.plist"
401 rl=("$approot/MacOS/dcpomatic2" "$approot/MacOS/dcpomatic2_cli" "$approot/MacOS/dcpomatic2_create" "$approot/MacOS/ffprobe" "$approot/Frameworks/"*.dylib)
402 relink "${rl[@]}"
403 make_dmg "$appdir" "DCP-o-matic" com.dcpomatic
404
405 # DCP-o-matic KDM Creator
406 setup "DCP-o-matic 2 KDM Creator.app"
407 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_kdm "$approot/MacOS"
408 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_kdm_cli "$approot/MacOS"
409 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
410 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_kdm.Info.plist "$approot/Info.plist"
411 rl=("$approot/MacOS/dcpomatic2_kdm" "$approot/MacOS/dcpomatic2_kdm_cli" "$approot/Frameworks/"*.dylib)
412 relink "${rl[@]}"
413 make_dmg "$appdir" "DCP-o-matic KDM Creator" com.dcpomatic.kdm
414
415 # DCP-o-matic Encode Server
416 setup "DCP-o-matic 2 Encode Server.app"
417 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_server "$approot/MacOS"
418 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_server_cli "$approot/MacOS"
419 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
420 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_server.Info.plist "$approot/Info.plist"
421 rl=("$approot/MacOS/dcpomatic2_server" "$approot/MacOS/dcpomatic2_server_cli" "$approot/Frameworks/"*.dylib)
422 relink "${rl[@]}"
423 make_dmg "$appdir" "DCP-o-matic Encode Server" com.dcpomatic.server
424
425 # DCP-o-matic Batch Converter
426 setup "DCP-o-matic 2 Batch converter.app"
427 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_batch "$approot/MacOS"
428 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
429 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_batch.Info.plist "$approot/Info.plist"
430 rl=("$approot/MacOS/dcpomatic2_batch" "$approot/Frameworks/"*.dylib)
431 relink "${rl[@]}"
432 make_dmg "$appdir" "DCP-o-matic Batch Converter" com.dcpomatic.batch
433
434 # DCP-o-matic Player
435 setup "DCP-o-matic 2 Player.app"
436 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_player "$approot/MacOS"
437 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
438 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_player.Info.plist "$approot/Info.plist"
439 rl=("$approot/MacOS/dcpomatic2_player" "$approot/Frameworks/"*.dylib)
440 relink "${rl[@]}"
441 make_dmg "$appdir" "DCP-o-matic Player" com.dcpomatic.player
442
443 # DCP-o-matic Playlist Editor
444 setup "DCP-o-matic 2 Playlist Editor.app"
445 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_playlist "$approot/MacOS"
446 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
447 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_playlist.Info.plist "$approot/Info.plist"
448 rl=("$approot/MacOS/dcpomatic2_playlist" "$approot/Frameworks/"*.dylib)
449 relink "${rl[@]}"
450 make_dmg "$appdir" "DCP-o-matic Playlist Editor" com.dcpomatic.playlist
451
452 # DCP-o-matic Disk Writer
453 setup "DCP-o-matic 2 Disk Writer.app"
454 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_disk "$approot/MacOS"
455 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_disk_writer "$approot/MacOS"
456 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_disk.Info.plist "$approot/Info.plist"
457 rl=("$approot/MacOS/dcpomatic2_disk" "$approot/Frameworks/"*.dylib)
458 relink "${rl[@]}"
459 rl=("$approot/MacOS/dcpomatic2_disk_writer" "$approot/Frameworks/"*.dylib)
460 relink "${rl[@]}"
461 make_dmg "$appdir" "DCP-o-matic Disk Writer" com.dcpomatic.disk