From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / OSX / build-fntable.pl
index f5e3b1f3dc69fc07c4d49854707b38783ef833d9..dc69dfecc7468f036caf80c7619b057a6b04140c 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/perl -w
-# Copyright © 2012 Jamie Zawinski <jwz@jwz.org>
+# Copyright © 2012-2015 Jamie Zawinski <jwz@jwz.org>
 #
 # Permission to use, copy, modify, distribute, and sell this software and its
 # documentation for any purpose is hereby granted without fee, provided that
@@ -9,7 +9,7 @@
 # software for any purpose.  It is provided "as is" without express or 
 # implied warranty.
 #
-# Generates a .h file that lists all the function tables we use, because
+# Generates a .c file that lists all the function tables we use, because
 # CFBundleGetDataPointerForName doesn't work in "Archive" builds.
 # What a crock of shit.
 #
@@ -23,39 +23,111 @@ require 5;
 use strict;
 
 my $progname = $0; $progname =~ s@.*/@@g;
-my $version = q{ $Revision: 1.1 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
+my ($version) = ('$Revision: 1.5 $' =~ m/\s(\d[.\d]+)\s/s);
 
 my $verbose = 1;
 
-sub build_h($$) {
-  my ($app_dir, $outfile) = @_;
+# List of savers not included in the iOS build.
+#
+my %disable = (
+   'extrusion'         => 1,
+   'lcdscrub'          => 1,
+   'lockward'          => 1,
+   'webcollage'                => 1,
+   'testx11'           => 1,
+  );
+
+# Parse the RETIRED_EXES variable from the Makefiles to populate %disable.
+# Duplicated in ../hacks/munge-ad.pl.
+#
+sub parse_makefiles() {
+  foreach my $mf ( "../hacks/Makefile.in", "../hacks/glx/Makefile.in" ) {
+    open (my $in, '<', $mf) || error ("$mf: $!");
+    print STDERR "$progname: reading $mf\n" if ($verbose > 1);
+    local $/ = undef;  # read entire file
+    my $body = <$in>;
+    close $in;
+
+    $body =~ s/\\\n//gs;
+    my ($var)  = ($body =~ m/^RETIRED_EXES\s*=\s*(.*)$/mi);
+    my ($var2) = ($body =~ m/^RETIRED_GL_EXES\s*=\s*(.*)$/mi);
+    error ("no RETIRED_EXES in $mf") unless $var;
+    $var .= " $var2" if $var2;
+    foreach my $hack (split (/\s+/, $var)) {
+      $disable{$hack} = 2;
+    }
+  }
+}
+
+
+sub build_h($) {
+  my ($outfile) = @_;
 
-  opendir (my $dh, $app_dir) || error ("$app_dir: $!");
-  print STDERR "$progname: scanning $app_dir...\n" if ($verbose > 1);
+  parse_makefiles();
 
-  my @names = ();
-  foreach (sort (readdir ($dh))) {
-    next unless (m/^(.*)\.xml$/);
-    push @names, $1;
+  my @schemes = glob('xscreensaver.xcodeproj/xcuserdata/' .
+                     '*.xcuserdatad/xcschemes/*.xcscheme');
+  error ("no scheme files") unless (@schemes);
+
+  my %names = ();
+
+  foreach my $s (@schemes) {
+    open (my $in, '<', $s) || error ("$s: $!");
+    local $/ = undef;  # read entire file
+    my $body = <$in>;
+    close $in;
+    my ($name) = ($body =~ m@BuildableName *= *"([^\"<>]+?)\.saver"@s);
+    next unless $name;
+    $name = lc($name);
+    if ($disable{$name}) {
+      print STDERR "$progname: skipping $name\n" if ($verbose > 1);
+      next;
+    }
+    print STDERR "$progname: found $name\n" if ($verbose > 1);
+    $names{$name} = 1;
   }
-  closedir $dh;
+
+  my @names = sort (keys %names);
+  error ("too few names") if (@names < 100);
 
   my $suf = 'xscreensaver_function_table';
 
-  my $body = "extern struct $suf";
-  foreach my $s (@names) {
-    $body .= "\n *${s}_${suf},";
+  my $body = ("/* Generated file, do not edit.\n" .
+              "   Created: " . localtime() . " by $progname $version.\n" .
+              " */\n" .
+              "\n" .
+              "#import <Foundation/Foundation.h>\n" .
+              "#import <UIKit/UIKit.h>\n" .
+              "\n" .
+              "extern NSDictionary *make_function_table_dict(void);\n" .
+              "\n");
+
+  $body .= "extern struct $suf";
+  foreach my $s (@names, 'testx11') {
+    $body .= "\n ${s}_${suf},";
   }
   $body =~ s/,\s*$/;/s;
 
-  $body .= ("\n\n" .
-            "static NSDictionary *make_function_tables_dict(void)\n{\n" .
-            "  return\n    [NSDictionary dictionaryWithObjectsAndKeys:\n");
-  foreach my $s (@names) {
-    $body .= "\t[NSValue valueWithPointer:&${s}_${suf}], @\"${s}\",\n";
+  sub line($$) {
+    my ($s, $suf) = @_;
+    return "\t[NSValue valueWithPointer:&${s}_${suf}], @\"${s}\",\n";
   }
-  $body .= ("\tnil];\n" .
-            "}\n");
+
+  $body .= ("\n\n" .
+            "NSDictionary *make_function_table_dict(void)\n{\n" .
+            "  return\n    [NSDictionary dictionaryWithObjectsAndKeys:\n" .
+            "\n" .
+            "#if defined(APPLE2_ONLY)\n" .
+            " " . line('apple2', $suf) .
+            "#elif defined(PHOSPHOR_ONLY)\n" .
+            " " . line('phosphor', $suf) .
+            "#elif defined(TESTX11_ONLY)\n" .
+            " " . line('testx11', $suf) .
+            "#else\n");
+  foreach my $s (@names) { $body .= line($s, $suf); }
+  $body .= ("#endif\n" .
+            "\tnil];\n" .
+            "}\n\n");
 
   my $obody = '';
   if (open (my $in, '<', $outfile)) {
@@ -64,7 +136,11 @@ sub build_h($$) {
     close $in;
   }
 
-  if ($obody eq $body) {
+  # strip comments/date for diff.
+  my ($body2, $obody2) = ($body, $obody);
+  foreach ($body2, $obody2) { s@/\*.*?\*/@@gs; }
+
+  if ($body2 eq $obody2) {
     print STDERR "$progname: $outfile: unchanged\n" if ($verbose > 1);
   } else {
     my $file_tmp = "$outfile.tmp";
@@ -88,25 +164,24 @@ sub error($) {
 }
 
 sub usage() {
-  print STDERR "usage: $progname [--verbose] program.app output.h\n";
+  print STDERR "usage: $progname [--verbose] output.c\n";
   exit 1;
 }
 
 sub main() {
 
-  my ($app, $out);
+  my ($out);
   while ($_ = $ARGV[0]) {
     shift @ARGV;
     if    (m/^--?verbose$/s)  { $verbose++; }
     elsif (m/^-v+$/)          { $verbose += length($_)-1; }
     elsif (m/^--?q(uiet)?$/s) { $verbose = 0; }
     elsif (m/^-/s)            { usage(); }
-    elsif (! $app)            { $app = $_; }
     elsif (! $out)            { $out = $_; }
     else                      { usage(); }
   }
-  usage() unless ($out && $app);
-  build_h ($app, $out);
+  usage() unless ($out);
+  build_h ($out);
 }
 
 main();