From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[xscreensaver] / OSX / build-fntable.pl
1 #!/usr/bin/perl -w
2 # Copyright © 2012-2018 Jamie Zawinski <jwz@jwz.org>
3 #
4 # Permission to use, copy, modify, distribute, and sell this software and its
5 # documentation for any purpose is hereby granted without fee, provided that
6 # the above copyright notice appear in all copies and that both that
7 # copyright notice and this permission notice appear in supporting
8 # documentation.  No representations are made about the suitability of this
9 # software for any purpose.  It is provided "as is" without express or 
10 # implied warranty.
11 #
12 # Generates a .c file that lists all the function tables we use, because
13 # CFBundleGetDataPointerForName doesn't work in "Archive" builds.
14 # What a crock of shit.
15 #
16 # There's no real way to integrate this into the Xcode build system, so
17 # run this manually each time a new saver is added to the iOS app.
18 #
19 # Created: 14-Jul-2012.
20
21 require 5;
22 #use diagnostics;       # Fails on some MacOS 10.5 systems
23 use strict;
24
25 my $progname = $0; $progname =~ s@.*/@@g;
26 my ($version) = ('$Revision: 1.6 $' =~ m/\s(\d[.\d]+)\s/s);
27
28 my $verbose = 1;
29
30 # List of savers not included in the iOS build.
31 #
32 my %disable = (
33    'extrusion'          => 1,
34    'glitchpeg'          => 1,
35    'lcdscrub'           => 1,
36    'lockward'           => 1,
37    'webcollage'         => 1,
38    'testx11'            => 1,
39   );
40
41 # Parse the RETIRED_EXES variable from the Makefiles to populate %disable.
42 # Duplicated in ../hacks/munge-ad.pl.
43 #
44 sub parse_makefiles() {
45   foreach my $mf ( "../hacks/Makefile.in", "../hacks/glx/Makefile.in" ) {
46     open (my $in, '<', $mf) || error ("$mf: $!");
47     print STDERR "$progname: reading $mf\n" if ($verbose > 1);
48     local $/ = undef;  # read entire file
49     my $body = <$in>;
50     close $in;
51
52     $body =~ s/\\\n//gs;
53     my ($var)  = ($body =~ m/^RETIRED_EXES\s*=\s*(.*)$/mi);
54     my ($var2) = ($body =~ m/^RETIRED_GL_EXES\s*=\s*(.*)$/mi);
55     error ("no RETIRED_EXES in $mf") unless $var;
56     $var .= " $var2" if $var2;
57     foreach my $hack (split (/\s+/, $var)) {
58       $disable{$hack} = 2;
59     }
60   }
61 }
62
63
64 sub build_h($) {
65   my ($outfile) = @_;
66
67   parse_makefiles();
68
69   my @schemes = glob('xscreensaver.xcodeproj/xcuserdata/' .
70                      '*.xcuserdatad/xcschemes/*.xcscheme');
71   error ("no scheme files") unless (@schemes);
72
73   my %names = ();
74
75   foreach my $s (@schemes) {
76     open (my $in, '<', $s) || error ("$s: $!");
77     local $/ = undef;  # read entire file
78     my $body = <$in>;
79     close $in;
80     my ($name) = ($body =~ m@BuildableName *= *"([^\"<>]+?)\.saver"@s);
81     next unless $name;
82     $name = lc($name);
83     if ($disable{$name}) {
84       print STDERR "$progname: skipping $name\n" if ($verbose > 1);
85       next;
86     }
87     print STDERR "$progname: found $name\n" if ($verbose > 1);
88     $names{$name} = 1;
89   }
90
91   my @names = sort (keys %names);
92   error ("too few names") if (@names < 100);
93
94   my $suf = 'xscreensaver_function_table';
95
96   my $body = ("/* Generated file, do not edit.\n" .
97               "   Created: " . localtime() . " by $progname $version.\n" .
98               " */\n" .
99               "\n" .
100               "#import <Foundation/Foundation.h>\n" .
101               "#import <UIKit/UIKit.h>\n" .
102               "\n" .
103               "extern NSDictionary *make_function_table_dict(void);\n" .
104               "\n");
105
106   $body .= "extern struct $suf";
107   foreach my $s (@names, 'testx11') {
108     $body .= "\n ${s}_${suf},";
109   }
110   $body =~ s/,\s*$/;/s;
111
112   sub line($$) {
113     my ($s, $suf) = @_;
114     return "\t[NSValue valueWithPointer:&${s}_${suf}], @\"${s}\",\n";
115   }
116
117   $body .= ("\n\n" .
118             "NSDictionary *make_function_table_dict(void)\n{\n" .
119             "  return\n    [NSDictionary dictionaryWithObjectsAndKeys:\n" .
120             "\n" .
121             "#if defined(APPLE2_ONLY)\n" .
122             " " . line('apple2', $suf) .
123             "#elif defined(PHOSPHOR_ONLY)\n" .
124             " " . line('phosphor', $suf) .
125             "#elif defined(TESTX11_ONLY)\n" .
126             " " . line('testx11', $suf) .
127             "#else\n");
128   foreach my $s (@names) { $body .= line($s, $suf); }
129   $body .= ("#endif\n" .
130             "\tnil];\n" .
131             "}\n\n");
132
133   my $obody = '';
134   if (open (my $in, '<', $outfile)) {
135     local $/ = undef;  # read entire file
136     $obody = <$in>;
137     close $in;
138   }
139
140   # strip comments/date for diff.
141   my ($body2, $obody2) = ($body, $obody);
142   foreach ($body2, $obody2) { s@/\*.*?\*/@@gs; }
143
144   if ($body2 eq $obody2) {
145     print STDERR "$progname: $outfile: unchanged\n" if ($verbose > 1);
146   } else {
147     my $file_tmp = "$outfile.tmp";
148     open (my $out, '>', $file_tmp) || error ("$file_tmp: $!");
149     print $out $body || error ("$file_tmp: $!");
150     close $out || error ("$file_tmp: $!");
151
152     if (!rename ("$file_tmp", "$outfile")) {
153       unlink "$file_tmp";
154       error ("mv \"$file_tmp\" \"$outfile\": $!");
155     }
156     print STDERR "$progname: wrote $outfile\n" if ($verbose);
157   }
158 }
159
160
161 sub error($) {
162   my ($err) = @_;
163   print STDERR "$progname: $err\n";
164   exit 1;
165 }
166
167 sub usage() {
168   print STDERR "usage: $progname [--verbose] output.c\n";
169   exit 1;
170 }
171
172 sub main() {
173
174   my ($out);
175   while ($_ = $ARGV[0]) {
176     shift @ARGV;
177     if    (m/^--?verbose$/s)  { $verbose++; }
178     elsif (m/^-v+$/)          { $verbose += length($_)-1; }
179     elsif (m/^--?q(uiet)?$/s) { $verbose = 0; }
180     elsif (m/^-/s)            { usage(); }
181     elsif (! $out)            { $out = $_; }
182     else                      { usage(); }
183   }
184   usage() unless ($out);
185   build_h ($out);
186 }
187
188 main();
189 exit 0;