Only notarize thin OSX builds.
[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/accounts.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
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 --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 --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 | grep -C1 "<key>Status</key>" | tail -n 1 | sed -e "s/   .//g")
337             echo "Got $status"
338             if [ "$status" == "<string>success</string>" ]; then
339                 break
340             fi
341             sleep 30
342             N=$((N+1))
343             if [ "$N" == "10" ]; then
344                 echo "Timed out waiting for notarization"
345                 exit 1
346             fi
347         done
348
349         xcrun stapler staple "$dmg"
350         set +e
351
352     fi
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 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2.Info.plist "$approot/Info.plist"
388 rl=("$approot/MacOS/dcpomatic2" "$approot/MacOS/dcpomatic2_cli" "$approot/MacOS/dcpomatic2_create" "$approot/MacOS/ffprobe" "$approot/Frameworks/"*.dylib)
389 relink "${rl[@]}"
390 make_dmg "$appdir" "DCP-o-matic" com.dcpomatic
391
392 # DCP-o-matic KDM Creator
393 setup "DCP-o-matic 2 KDM Creator.app"
394 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_kdm "$approot/MacOS"
395 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_kdm_cli "$approot/MacOS"
396 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_kdm.Info.plist "$approot/Info.plist"
397 rl=("$approot/MacOS/dcpomatic2_kdm" "$approot/MacOS/dcpomatic2_kdm_cli" "$approot/Frameworks/"*.dylib)
398 relink "${rl[@]}"
399 make_dmg "$appdir" "DCP-o-matic KDM Creator" com.dcpomatic.kdm
400
401 # DCP-o-matic Encode Server
402 setup "DCP-o-matic 2 Encode Server.app"
403 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_server "$approot/MacOS"
404 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_server_cli "$approot/MacOS"
405 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_server.Info.plist "$approot/Info.plist"
406 rl=("$approot/MacOS/dcpomatic2_server" "$approot/MacOS/dcpomatic2_server_cli" "$approot/Frameworks/"*.dylib)
407 relink "${rl[@]}"
408 make_dmg "$appdir" "DCP-o-matic Encode Server" com.dcpomatic.server
409
410 # DCP-o-matic Batch Converter
411 setup "DCP-o-matic 2 Batch converter.app"
412 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_batch "$approot/MacOS"
413 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_batch.Info.plist "$approot/Info.plist"
414 rl=("$approot/MacOS/dcpomatic2_batch" "$approot/Frameworks/"*.dylib)
415 relink "${rl[@]}"
416 make_dmg "$appdir" "DCP-o-matic Batch Converter" com.dcpomatic.batch
417
418 # DCP-o-matic Player
419 setup "DCP-o-matic 2 Player.app"
420 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_player "$approot/MacOS"
421 cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_player.Info.plist "$approot/Info.plist"
422 rl=("$approot/MacOS/dcpomatic2_player" "$approot/Frameworks/"*.dylib)
423 relink "${rl[@]}"
424 make_dmg "$appdir" "DCP-o-matic Player" com.dcpomatic.player