7a9542fd74099de6104a67e2f254c0153426d698
[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/fonts/LiberationSans-Regular.ttf "$dest"
200     cp $prefix/src/dcpomatic/fonts/LiberationSans-Italic.ttf "$dest"
201     cp $prefix/src/dcpomatic/fonts/LiberationSans-Bold.ttf "$dest"
202     cp $prefix/src/dcpomatic/fonts/fonts.conf.osx "$dest"/fonts.conf
203     cp $prefix/src/dcpomatic/graphics/splash.png "$dest"
204     cp $prefix/src/dcpomatic/graphics/zoom.png "$dest"
205     cp $prefix/src/dcpomatic/graphics/zoom_all.png "$dest"
206     cp $prefix/src/dcpomatic/graphics/select.png "$dest"
207     cp $prefix/src/dcpomatic/graphics/snap.png "$dest"
208     cp $prefix/src/dcpomatic/graphics/sequence.png "$dest"
209     cp $prefix/src/dcpomatic/graphics/me.jpg "$dest"
210     cp -r $prefix/share/libdcp/xsd "$dest"
211
212     # i18n: DCP-o-matic .mo files
213     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
214         mkdir -p "$dest/$lang/LC_MESSAGES"
215         cp $prefix/src/dcpomatic/build/src/lib/mo/$lang/*.mo "$dest/$lang/LC_MESSAGES"
216         cp $prefix/src/dcpomatic/build/src/wx/mo/$lang/*.mo "$dest/$lang/LC_MESSAGES"
217         cp $prefix/src/dcpomatic/build/src/tools/mo/$lang/*.mo "$dest/$lang/LC_MESSAGES"
218     done
219
220     # i18n: wxWidgets .mo files
221     for lang in de es fr it sv nl ru pl da cs; do
222         mkdir "$dest/$lang"
223         cp $ENV/64/share/locale/$lang/LC_MESSAGES/wxstd.mo "$dest/$lang"
224     done
225 }
226
227 # param $1 list of things that link to other things
228 function relink {
229     to_relink=`echo $to_relink | sed -e "s/\+//g"`
230     local linkers=("$@")
231
232     for obj in "${linkers[@]}"; do
233         deps=`otool -L "$obj" | awk '{print $1}' | egrep "($to_relink)" | egrep "($ENV|$ROOT|boost|libicu)"`
234         changes=""
235         for dep in $deps; do
236             base=`basename $dep`
237             if [ "$TYPE" == "universal" ]; then
238                 # $dep will be a path within 64/; make a 32/ path too
239                 dep32=`echo $dep | sed -e "s/\/64\//\/32\//g"`
240                 changes="$changes -change $dep @executable_path/../Frameworks/$base -change $dep32 @executable_path/../Frameworks/$base"
241             else
242                 changes="$changes -change $dep @executable_path/../Frameworks/$base"
243             fi
244         done
245         if test "x$changes" != "x"; then
246             install_name_tool $changes -id `basename "$obj"` "$obj"
247         fi
248     done
249 }
250
251 # @param #1 .app directory
252 # @param #2 full name e.g. DCP-o-matic Batch Converter
253 # @param #3 bundle id e.g. com.dcpomatic.batch
254 function make_dmg {
255     local appdir="$1"
256     local full_name="$2"
257     local bundle_id="$3"
258     tmp_dmg=dcpomatic_tmp.dmg
259     dmg="$full_name $version.dmg"
260     vol_name=DCP-o-matic-$version
261
262     codesign --deep --force --verify --verbose --sign "Developer ID Application: Carl Hetherington (R82DXSR997)" "$appdir"
263     if [ "$?" != "0" ]; then
264         echo "Failed to sign .app"
265         exit 1
266     fi
267
268     mkdir -p $vol_name
269     cp -a "$appdir" $vol_name
270     ln -s /Applications "$vol_name/Applications"
271     cat<<EOF > "$vol_name/READ ME.txt"
272 Welcome to DCP-o-matic!  The first time you run the program there may be
273 a long (several-minute) delay while OS X checks the code for viruses and
274 other malware.  Please be patient!
275 EOF
276     cat<<EOF > "$vol_name/READ ME.de_DE.txt"
277 Beim erstmaligen Start der DCP-o-matic Anwendungen kann ein längerer
278 Verifikationsvorgang auftreten.  Dies ist von der OS X Sicherheitsumgebung
279 'Gatekeeper' verursacht.  Dieser je nach Rechner teils minutenlange
280 Verifikationsvorgang ist gegenwärtig normal und nicht zu umgehen,
281 es ist kein Programmfehler.  Warten sie die Verifikation für jede der
282 DCP-o-matic Anwendungen ab, bei weiteren Programmstarts wird sie nicht
283 mehr auftreten.
284 EOF
285
286     rm -f $tmp_dmg "$dmg"
287     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
288     attach=$(hdiutil attach -readwrite -noverify -noautoopen $tmp_dmg)
289     device=`echo $attach | egrep '^/dev/' | sed 1q | awk '{print $5}'`
290     sleep 5
291
292     echo '
293   tell application "Finder"
294     tell disk "'$vol_name'"
295            open
296            set current view of container window to icon view
297            set toolbar visible of container window to false
298            set statusbar visible of container window to false
299            set the bounds of container window to {400, 200, 940, 300}
300            set theViewOptions to the icon view options of container window
301            set arrangement of theViewOptions to not arranged
302            set icon size of theViewOptions to 64
303            set position of item "'$appdir'" of container window to {90, 80}
304            set position of item "Applications" of container window to {265, 80}
305            set position of item "READ ME.txt" of container window to {430, 80}
306            set position of item "READ ME.de_DE.txt" of container window to {595, 80}
307            close
308            open
309            update without registering applications
310            delay 5
311      end tell
312    end tell
313 ' | osascript
314
315     chmod -Rf go-w /Volumes/"$vol_name"/"$appdir"
316     sync
317
318     hdiutil eject $device
319     hdiutil convert -format UDZO $tmp_dmg -imagekey zlib-level=9 -o "$dmg"
320     sips -i "$appdir/Contents/Resources/dcpomatic2.icns"
321     DeRez -only icns "$appdir/Contents/Resources/dcpomatic2.icns" > "$appdir/Contents/Resources/DCP-o-matic.rsrc"
322     Rez -append "$appdir/Contents/Resources/DCP-o-matic.rsrc" -o "$dmg"
323     SetFile -a C "$dmg"
324     xattr -c "$dmg"
325
326     set -e
327     codesign --verify --verbose --sign "Developer ID Application: Carl Hetherington (R82DXSR997)" "$dmg"
328
329     # We only notarize thin builds, as if we're building universal binaries we must be on an OS
330     # sufficiently old that it can't notarize anyway
331     if [ "$TYPE" == "thin" ]; then
332
333         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")
334         N=0
335         while [ 1 ]; do
336             echo "Checking up on $id"
337             status=$(xcrun altool --notarization-info $id -u $APPLE_ID -p $APPLE_PASSWORD --output-format xml | grep -C1 "<key>Status</key>" | tail -n 1 | sed -e "s/   .//g")
338             echo "Got $status"
339             if [ "$status" == "<string>success</string>" ]; then
340                 break
341             fi
342             sleep 30
343             N=$((N+1))
344             if [ "$N" == "10" ]; then
345                 echo "Timed out waiting for notarization"
346                 exit 1
347             fi
348         done
349
350         xcrun stapler staple "$dmg"
351     fi
352     set +e
353
354     rm $tmp_dmg
355     rm -rf $vol_name
356 }
357
358 # @param #1 appdir
359 function setup {
360     appdir="$1"
361     approot="$appdir/Contents"
362     rm -rf "$appdir"
363     mkdir -p "$approot/MacOS"
364     mkdir -p "$approot/Frameworks"
365     mkdir -p "$approot/Resources"
366
367     to_relink="dcpomatic"
368     copy_libs "$approot/Frameworks"
369     copy_resources "$approot/Resources"
370 }
371
372 case $TYPE in
373     universal)
374         prefix=$ROOT/32
375         ;;
376     thin)
377         prefix=$ROOT
378         ;;
379 esac
380
381 # DCP-o-matic main
382 setup "DCP-o-matic 2.app"
383 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2 "$approot/MacOS"
384 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_cli "$approot/MacOS"
385 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_create "$approot/MacOS"
386 copy $ROOT bin/ffprobe "$approot/MacOS"
387 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
388 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2.Info.plist "$approot/Info.plist"
389 rl=("$approot/MacOS/dcpomatic2" "$approot/MacOS/dcpomatic2_cli" "$approot/MacOS/dcpomatic2_create" "$approot/MacOS/ffprobe" "$approot/Frameworks/"*.dylib)
390 relink "${rl[@]}"
391 make_dmg "$appdir" "DCP-o-matic" com.dcpomatic
392
393 # DCP-o-matic KDM Creator
394 setup "DCP-o-matic 2 KDM Creator.app"
395 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_kdm "$approot/MacOS"
396 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_kdm_cli "$approot/MacOS"
397 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
398 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_kdm.Info.plist "$approot/Info.plist"
399 rl=("$approot/MacOS/dcpomatic2_kdm" "$approot/MacOS/dcpomatic2_kdm_cli" "$approot/Frameworks/"*.dylib)
400 relink "${rl[@]}"
401 make_dmg "$appdir" "DCP-o-matic KDM Creator" com.dcpomatic.kdm
402
403 # DCP-o-matic Encode Server
404 setup "DCP-o-matic 2 Encode Server.app"
405 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_server "$approot/MacOS"
406 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_server_cli "$approot/MacOS"
407 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
408 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_server.Info.plist "$approot/Info.plist"
409 rl=("$approot/MacOS/dcpomatic2_server" "$approot/MacOS/dcpomatic2_server_cli" "$approot/Frameworks/"*.dylib)
410 relink "${rl[@]}"
411 make_dmg "$appdir" "DCP-o-matic Encode Server" com.dcpomatic.server
412
413 # DCP-o-matic Batch Converter
414 setup "DCP-o-matic 2 Batch converter.app"
415 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_batch "$approot/MacOS"
416 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
417 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_batch.Info.plist "$approot/Info.plist"
418 rl=("$approot/MacOS/dcpomatic2_batch" "$approot/Frameworks/"*.dylib)
419 relink "${rl[@]}"
420 make_dmg "$appdir" "DCP-o-matic Batch Converter" com.dcpomatic.batch
421
422 # DCP-o-matic Player
423 setup "DCP-o-matic 2 Player.app"
424 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_player "$approot/MacOS"
425 copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
426 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_player.Info.plist "$approot/Info.plist"
427 rl=("$approot/MacOS/dcpomatic2_player" "$approot/Frameworks/"*.dylib)
428 relink "${rl[@]}"
429 make_dmg "$appdir" "DCP-o-matic Player" com.dcpomatic.player