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