aboutsummaryrefslogtreecommitdiff
path: root/ports/darwin_dmg/package_darwin_dmg.pl
blob: 00ea95bbc1bec6b81429c96e9a3d31d97be17c82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#!/usr/bin/perl
use strict;
use warnings;
use Carp;
use Getopt::Long;
use Data::Dumper; $Data::Dumper::Useqq = 1;

my $USAGE = <<"END_OF_USAGE";
$0 yyyy mm
    --build
        Build Rakudo (MoarVM) from the tarball.
        Defaults to only packaging what is already built.
    -v
    --verbose
        Report each command that is run.
    --time
        Report timings for each step.
    YYYY
        4 digit year
    MM
        2 digit month, 01..12
END_OF_USAGE

=begin comments

Does the .dmg packaging procedure really need to be this complex?
Yes! The single-line process that we *wish* we could use does not allow:
    * symlink to /Applications (so they have to know to drag-and-drop outside the window)
    * background images
    * click-through licenses
    * auto-open the Finder window after .dmg is opened.

TODO:
    Add background images
        Camelia
        Arrow to show drag-and-drop to Applications (like in DMGs from Processing.org)
    Add a .dmg-specific HOW_TO_INSTALL.txt
    Demo code (as opposed to Example code that we already have)
    Click on .app for REPL
    Remove the underscore from /Applications/Rakudo, changing it to a space.
        This will require upstream changes to Rakudo, but Parrot itself seems OK with an embedded space.
    Include all the dependent libs; gmp, pcre, opengl, zlib, gettext, icu, libffi,readline
        So far, we build with these libs disabled to allow Rakudo to run on systems that lack the libs.

=end comments

=cut


GetOptions(
    'build'     => \( my $opt_build_rakudo ),
    'time'      => \( my $opt_time  ),
    'verbose|v' => \( my $opt_verbose ),
    'help|h'    => \( my $opt_help ),
) or die $USAGE;

print $USAGE and exit(0) if $opt_help;

my ( $yyyy, $mm ) = @ARGV; # Version

die $USAGE if @ARGV != 2
           or $yyyy !~ m{ \A \d{4} \z }msx
           or $mm   !~ m{ \A \d{2} \z }msx
           or $mm < 1 or $mm > 12;

# Sadly, Apple decided to remove the `-i` / `--addicon` option from the `sips`
# utility.  Therefore, use of Cocoa is required, which we do via Python,
# which has the added advantage of creating a _set_ of icons from the source
# image, scaling as necessary to create a 512 x 512 top resolution icon
# (whereas sips -i created a single, 128 x 128 icon).
#
# Thanks go to https://github.com/mklement0/fileicon/blob/master/bin/fileicon
# and https://apple.stackexchange.com/a/161984/28668
#
# Note: setIcon_forFile_options_() seemingly always indicates True, even with
# invalid image files, so  we attempt no error handling in the Python code.
sub set_icon {
    croak if not @_;
    my ($img_file, $target_file) = @_;

    print "Setting icon for $target_file\n" if $opt_verbose;

    my $rc = system(qq{/usr/bin/python - "$img_file" "$target_file" <<'EOF'
import Cocoa
import sys

Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(Cocoa.NSImage.alloc().initWithContentsOfFile_(sys.argv[1].decode('utf-8')), sys.argv[2].decode('utf-8'), 0)
EOF
    });
    die if $rc != 0;
}

sub run {
    croak if not @_;
    my (@command) = @_;

    print "> @command\n" if $opt_verbose;

    my $start = time;
    my $rc = system(@command);
    die if $rc != 0;
    my $stop = time;

    printf ">! %4d\t%s\n", $stop-$start, "@command" if $opt_time;
    return;
}


my $temp_dir     = 'Temp_build';
my $temp_dmg     = 'temp';
my $vol_name     = 'Rakudo';
# XXX rename to install_dir?
my $src_dir      = '/Applications/Rakudo';

my $vol_dir      = "/Volumes/$vol_name";
my $tar_dir      = "rakudo-star-$yyyy.$mm";
my $tar_file     = "rakudo-star-$yyyy.$mm.tar.gz";
my $final_dmg    = "rakudo-star-$yyyy.$mm";
my $license_path = "../../../LICENSE";
#my $license_path = "$src_dir/share/doc/rakudo/LICENSE";


if ( $opt_build_rakudo ) {

    run "rm -rf '$src_dir'";

    my $ocwd = qx!pwd!;
    chomp $ocwd;

    chdir "../..";

    run "perl Configure.pl --gen-moar --prefix /Applications/Rakudo";
    run "make install";

    chdir $ocwd;

}

if ( `$src_dir/bin/perl6 -e 42.say` ne "42\n" ) {
    die "The perl6 exe will not run, so we cannot make a .dmg for it! ($src_dir/bin/perl6)\n";
}

if ( -e $vol_dir ) {
    run "diskutil eject $vol_dir";
}
if ( -e $temp_dir ) {
    run "rm -rf '$temp_dir'";
}
mkdir $temp_dir or die;
chdir $temp_dir or die;

my $size = `du -ks '$src_dir'`;
$size =~ s{ \A \s* (\d+) \t \S.* \z }{$1}msx or die;
$size += int( $size * 0.20 ); # Add 20% for file system
run "hdiutil create  '$temp_dmg' -ov -size ${size}k -fs HFS+ -volname '$vol_name' -attach";

print "Copying Rakudo files\n";
run "CpMac -r '$src_dir'    '$vol_dir'";
run "cp ../HOW_TO_INSTALL.txt  '$vol_dir/README.txt'";
run "cp -pr ../../../docs  '$vol_dir/Docs'";

run "touch                        '$vol_dir/Rakudo/Icon\r'";
run "cp ../2000px-Camelia.svg.icns $vol_dir/.VolumeIcon.icns";
set_icon "../2000px-Camelia.svg.icns", "$vol_dir/Rakudo/bin/perl6";
run "mkdir                        $vol_dir/.background";
run "cp ../installerbg.png        $vol_dir/.background"; 
run "SetFile -c icnC              '$vol_dir/.VolumeIcon.icns'";
run "SetFile -a C                 '$vol_dir'";
run "SetFile -a C                 '$vol_dir/Rakudo'";
run "SetFile -a C                 '$vol_dir/Rakudo/bin/perl6'";
run "SetFile -a V                 '$vol_dir/Rakudo/Icon\r'";


print ">>> Adjusting sizes and positions in installation window\n";
run "osascript ../adjust_installation_window.scpt";


print ">>> Waiting on .DS_STORE to be written\n";
sleep 1 while not -s "$vol_dir/.DS_STORE";

print ">>> Compressing\n";
run "diskutil eject $vol_dir";
run "hdiutil convert '$temp_dmg.dmg' -format UDBZ -o '$final_dmg'";
unlink "$temp_dmg.dmg" or die;


print ">>> Adding click-thru license and auto-open\n";
my $r_path        =  './SLA_rakudo_star.r';
my $template_path = '../SLA_rakudo_star.template.r';
create_sla_file( $license_path, $template_path, $r_path );

run "hdiutil unflatten                 '$final_dmg.dmg'";
#run "Rez Carbon.r '$r_path' -append -o '$final_dmg.dmg'"; # Carbon.r has type definitions.
run "hdiutil flatten                   '$final_dmg.dmg'";
unlink $r_path or die;

chdir '..' or die;


sub create_sla_file {
    croak 'Wrong number of arguments' if @_ != 3;
    my ( $license_path, $template_path, $output_r_path ) = @_;

    my $license_munged = '';
    open my $license_fh,  '<', $license_path or die "not found at $license_path";
    while (<$license_fh>) {
        s{"}{\\"}g;
        s{\n}{\\n};
        $license_munged .= qq{    "$_"\n};
    }
    close $license_fh or warn;

    open my $template_fh, '<', $template_path or die;
    open my $r_fh, '>', $output_r_path or die;
    {
        local $/ = undef; # Slurp mode
        local $_ = <$template_fh>;
        s{\[% ENGLISH_LICENSE %\]}{$license_munged} or die;
        print {$r_fh} $_;
    }
    close $template_fh or warn;
    close $r_fh        or warn;
}