Black Box: fix "reveal" button location, explain what's meant by the
[sgt/puzzles] / mkfiles.pl
index 8c23127..1421a92 100755 (executable)
 #    mainly because I was too scared to go anywhere near it.
 #  - sbcsgen.pl is still run at startup.
 
+# Other things undone:
+#  - special-define objects (foo.o[PREPROCSYMBOL]) are not
+#    supported in the mac or vcproj makefiles.
+
 use FileHandle;
 use Cwd;
 
@@ -39,6 +43,8 @@ $project_name = "project"; # this is a good enough default
 %programs = (); # maps prog name + type letter to listref of objects/resources
 %groups = (); # maps group name to listref of objects/resources
 
+@allobjs = (); # all object file names
+
 while (<IN>) {
   # Skip comments (unless the comments belong, for example because
   # they're part of a diversion).
@@ -51,6 +57,7 @@ while (<IN>) {
   if ($_[0] eq "!name") { $project_name = $_[1]; next; }
   if ($_[0] eq "!srcdir") { push @srcdirs, $_[1]; next; }
   if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;}
+  if ($_[0] eq "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;}
   if ($_[0] eq "!begin") {
       if (&mfval($_[1])) {
          $divert = \$makefile_extra{$_[1]};
@@ -91,10 +98,11 @@ while (<IN>) {
     if ($groups{$i}) {
       foreach $j (@{$groups{$i}}) { unshift @objs, $j; }
     } elsif (($i eq "[G]" or $i eq "[C]" or $i eq "[M]" or
-              $i eq "[X]" or $i eq "[U]") and defined $prog) {
-      $type = substr($i,1,1);
+              $i eq "[X]" or $i eq "[U]" or $i eq "[MX]") and defined $prog) {
+      $type = substr($i,1,(length $i)-2);
     } else {
       push @$listref, $i;
+      push @allobjs, $i;
     }
   }
   if ($prog and $type) {
@@ -107,6 +115,35 @@ while (<IN>) {
 
 close IN;
 
+# Find object file names with predefines (in square brackets after
+# the module name), and decide on actual object names for them.
+foreach $i (@allobjs) {
+  if ($i !~ /\[/) {
+    $objname{$i} = $i;
+    $srcname{$i} = $i;
+    $usedobjname{$i} = 1;
+  }
+}
+foreach $i (@allobjs) {
+  if ($i =~ /^(.*)\[([^\]]*)/) {
+    $defs{$i} = [ split ",",$2 ];
+    $srcname{$i} = $s = $1;
+    $index = 1;
+    while (1) {
+      $maxlen = length $s;
+      $maxlen = 8 if $maxlen < 8;
+      $chop = $maxlen - length $index;
+      $chop = length $s if $chop > length $s;
+      $chop = 0 if $chop < 0;
+      $name = substr($s, 0, $chop) . $index;
+      $index++, next if $usedobjname{$name};
+      $objname{$i} = $name;
+      $usedobjname{$name} = 1;
+      last;
+    }
+  }
+}
+
 # Now retrieve the complete list of objects and resource files, and
 # construct dependency data for them. While we're here, expand the
 # object list for each program, and complain if its type isn't set.
@@ -120,25 +157,26 @@ foreach $i (@prognames) {
   @list = grep { $status = ($prev ne $_); $prev=$_; $status }
           sort @{$programs{$i}};
   $programs{$i} = [@list];
-  foreach $j (@list) {
-    # Dependencies for "x" start with "x.c".
+  foreach $jj (@list) {
+    $j = $srcname{$jj};
+    # Dependencies for "x" start with "x.c" or "x.m" (depending on
+    # which one exists).
     # Dependencies for "x.res" start with "x.rc".
     # Dependencies for "x.rsrc" start with "x.r".
     # Both types of file are pushed on the list of files to scan.
     # Libraries (.lib) don't have dependencies at all.
     if ($j =~ /^(.*)\.res$/) {
       $file = "$1.rc";
-      $depends{$j} = [$file];
+      $depends{$jj} = [$file];
       push @scanlist, $file;
     } elsif ($j =~ /^(.*)\.rsrc$/) {
       $file = "$1.r";
-      $depends{$j} = [$file];
+      $depends{$jj} = [$file];
       push @scanlist, $file;
-    } elsif ($j =~ /\.lib$/) {
-      # libraries don't have dependencies
-    } else {
+    } elsif ($j !~ /\./) {
       $file = "$j.c";
-      $depends{$j} = [$file];
+      $file = "$j.m" unless &findfile($file);
+      $depends{$jj} = [$file];
       push @scanlist, $file;
     }
   }
@@ -208,7 +246,7 @@ sub mfval($) {
     # Returns true if the argument is a known makefile type. Otherwise,
     # prints a warning and returns false;
     if (grep { $type eq $_ }
-       ("vc","vcproj","cygwin","borland","lcc","gtk","mpw")) {
+       ("vc","vcproj","cygwin","borland","lcc","gtk","mpw","osx")) {
            return 1;
        }
     warn "$.:unknown makefile type '$type'\n";
@@ -230,14 +268,16 @@ sub dirpfx {
 
 sub findfile {
   my ($name) = @_;
-  my $dir, $i, $outdir = "";
+  my $dir;
+  my $i;
+  my $outdir = undef;
   unless (defined $findfilecache{$name}) {
     $i = 0;
     foreach $dir (@srcdirs) {
       $outdir = $dir, $i++ if -f "$dir$name";
     }
     die "multiple instances of source file $name\n" if $i > 1;
-    $findfilecache{$name} = $outdir . $name;
+    $findfilecache{$name} = (defined $outdir ? $outdir . $name : undef);
   }
   return $findfilecache{$name};
 }
@@ -247,7 +287,8 @@ sub objects {
   my @ret;
   my ($i, $x, $y);
   @ret = ();
-  foreach $i (@{$programs{$prog}}) {
+  foreach $ii (@{$programs{$prog}}) {
+    $i = $objname{$ii};
     $x = "";
     if ($i =~ /^(.*)\.(res|rsrc)/) {
       $y = $1;
@@ -255,7 +296,7 @@ sub objects {
     } elsif ($i =~ /^(.*)\.lib/) {
       $y = $1;
       ($x = $ltmpl) =~ s/X/$y/;
-    } else {
+    } elsif ($i !~ /\./) {
       ($x = $otmpl) =~ s/X/$i/;
     }
     push @ret, $x if $x ne "";
@@ -263,6 +304,20 @@ sub objects {
   return join " ", @ret;
 }
 
+sub special {
+  my ($prog, $suffix) = @_;
+  my @ret;
+  my ($i, $x, $y);
+  @ret = ();
+  foreach $ii (@{$programs{$prog}}) {
+    $i = $objname{$ii};
+    if (substr($i, (length $i) - (length $suffix)) eq $suffix) {
+      push @ret, $i;
+    }
+  }
+  return join " ", @ret;
+}
+
 sub splitline {
   my ($line, $width, $splitchar) = @_;
   my ($result, $len);
@@ -283,7 +338,9 @@ sub deps {
   my @deps, @ret;
   @ret = ();
   $depchar ||= ':';
-  foreach $i (sort keys %depends) {
+  foreach $ii (sort keys %depends) {
+    $i = $objname{$ii};
+    next if $specialobj{$mftyp}->{$i};
     if ($i =~ /^(.*)\.(res|rsrc)/) {
       next if !defined $rtmpl;
       $y = $1;
@@ -291,13 +348,13 @@ sub deps {
     } else {
       ($x = $otmpl) =~ s/X/$i/;
     }
-    @deps = @{$depends{$i}};
+    @deps = @{$depends{$ii}};
     @deps = map {
       $_ = &findfile($_);
       s/\//$dirsep/g;
       $_ = $prefix . $_;
     } @deps;
-    push @ret, {obj => $x, deps => [@deps]};
+    push @ret, {obj => $x, deps => [@deps], defs => $defs{$ii}};
   }
   return @ret;
 }
@@ -309,7 +366,7 @@ sub prognames {
   @ret = ();
   foreach $n (@prognames) {
     ($prog, $type) = split ",", $n;
-    push @ret, $n if index($types, $type) >= 0;
+    push @ret, $n if index(":$types:", ":$type:") >= 0;
   }
   return @ret;
 }
@@ -321,7 +378,7 @@ sub progrealnames {
   @ret = ();
   foreach $n (@prognames) {
     ($prog, $type) = split ",", $n;
-    push @ret, $prog if index($types, $type) >= 0;
+    push @ret, $prog if index(":$types:", ":$type:") >= 0;
   }
   return @ret;
 }
@@ -330,7 +387,7 @@ sub manpages {
   my ($types,$suffix) = @_;
 
   # assume that all UNIX programs have a man page
-  if($suffix eq "1" && $types =~ /X/) {
+  if($suffix eq "1" && $types =~ /:X:/) {
     return map("$_.1", &progrealnames($types));
   }
   return ();
@@ -339,6 +396,7 @@ sub manpages {
 # Now we're ready to output the actual Makefiles.
 
 if (defined $makefiles{'cygwin'}) {
+    $mftyp = 'cygwin';
     $dirpfx = &dirpfx($makefiles{'cygwin'}, "/");
 
     ##-- CygWin makefile
@@ -379,9 +437,9 @@ if (defined $makefiles{'cygwin'}) {
     "%.res.o: %.rc\n".
     "\t\$(RC) \$(FWHACK) \$(RCFL) \$(RCFLAGS) \$< \$\@\n".
     "\n";
-    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC"));
+    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
     print "\n\n";
-    foreach $p (&prognames("GC")) {
+    foreach $p (&prognames("G:C")) {
       ($prog, $type) = split ",", $p;
       $objstr = &objects($p, "X.o", "X.res.o", undef);
       print &splitline($prog . ".exe: " . $objstr), "\n";
@@ -394,6 +452,13 @@ if (defined $makefiles{'cygwin'}) {
     foreach $d (&deps("X.o", "X.res.o", $dirpfx, "/")) {
       print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
         "\n";
+      if ($d->{obj} =~ /\.res\.o$/) {
+       print "\t\$(RC) \$(FWHACK) \$(RCFL) \$(RCFLAGS) \$< \$\@\n";
+      } else {
+       $deflist = join "", map { " -D$_" } @{$d->{defs}};
+       print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS)" .
+           " \$(CFLAGS)$deflist -c \$< -o \$\@\n";
+      }
     }
     print "\n";
     print $makefile_extra{'cygwin'};
@@ -406,6 +471,7 @@ if (defined $makefiles{'cygwin'}) {
 
 ##-- Borland makefile
 if (defined $makefiles{'borland'}) {
+    $mftyp = 'borland';
     $dirpfx = &dirpfx($makefiles{'borland'}, "\\");
 
     %stdlibs = (  # Borland provides many Win32 API libraries intrinsically
@@ -441,26 +507,17 @@ if (defined $makefiles{'borland'}) {
     "!if !\$d(BCB)\n".
     "BCB = \$(MAKEDIR)\\..\n".
     "!endif\n".
-    "\n".
-    ".c.obj:\n".
-    &splitline("\tbcc32 -w-aus -w-ccc -w-par -w-pia \$(COMPAT) \$(FWHACK)".
-              " \$(XFLAGS) \$(CFLAGS) ".
-              (join " ", map {"-I$dirpfx$_"} @srcdirs) .
-              " /c \$*.c",69)."\n".
-    ".rc.res:\n".
-    &splitline("\tbrcc32 \$(FWHACK) \$(RCFL) -i \$(BCB)\\include -r".
-      " -DNO_WINRESRC_H -DWIN32 -D_WIN32 -DWINVER=0x0401 \$*.rc",69)."\n".
     "\n";
-    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC"));
+    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
     print "\n\n";
-    foreach $p (&prognames("GC")) {
+    foreach $p (&prognames("G:C")) {
       ($prog, $type) = split ",", $p;
       $objstr = &objects($p, "X.obj", "X.res", undef);
       print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n";
       my $ap = ($type eq "G") ? "-aa" : "-ap";
       print "\tilink32 $ap -Gn -L\$(BCB)\\lib \@$prog.rsp\n\n";
     }
-    foreach $p (&prognames("GC")) {
+    foreach $p (&prognames("G:C")) {
       ($prog, $type) = split ",", $p;
       print $prog, ".rsp: \$(MAKEFILE)\n";
       $objstr = &objects($p, "X.obj", undef, undef);
@@ -491,6 +548,17 @@ if (defined $makefiles{'borland'}) {
     foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) {
       print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
         "\n";
+      if ($d->{obj} =~ /\.res$/) {
+       print &splitline("\tbrcc32 \$(FWHACK) \$(RCFL) " .
+                        "-i \$(BCB)\\include -r -DNO_WINRESRC_H -DWIN32".
+                        " -D_WIN32 -DWINVER=0x0401 \$*.rc",69)."\n";
+      } else {
+       $deflist = join "", map { " -D$_" } @{$d->{defs}};
+       print &splitline("\tbcc32 -w-aus -w-ccc -w-par -w-pia \$(COMPAT)" .
+                        " \$(FWHACK) \$(XFLAGS) \$(CFLAGS)$deflist ".
+                        (join " ", map {"-I$dirpfx$_"} @srcdirs) .
+                        " /o$d->{obj} /c ".$d->{deps}->[0],69)."\n";
+      }
     }
     print "\n";
     print $makefile_extra{'borland'};
@@ -509,6 +577,7 @@ if (defined $makefiles{'borland'}) {
 }
 
 if (defined $makefiles{'vc'}) {
+    $mftyp = 'vc';
     $dirpfx = &dirpfx($makefiles{'vc'}, "\\");
 
     ##-- Visual C++ makefile
@@ -527,21 +596,16 @@ if (defined $makefiles{'vc'}) {
       "# C compilation flags\n".
       "CFLAGS = /nologo /W3 /O1 /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401\n".
       "LFLAGS = /incremental:no /fixed\n".
-      "\n".
-      ".c.obj:\n".
-      "\tcl \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) /c \$*.c\n".
-      ".rc.res:\n".
-      "\trc \$(FWHACK) \$(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 \$*.rc\n".
       "\n";
-    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC"));
+    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
     print "\n\n";
-    foreach $p (&prognames("GC")) {
+    foreach $p (&prognames("G:C")) {
        ($prog, $type) = split ",", $p;
        $objstr = &objects($p, "X.obj", "X.res", undef);
        print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n";
        print "\tlink \$(LFLAGS) -out:$prog.exe -map:$prog.map \@$prog.rsp\n\n";
     }
-    foreach $p (&prognames("GC")) {
+    foreach $p (&prognames("G:C")) {
        ($prog, $type) = split ",", $p;
        print $prog, ".rsp: \$(MAKEFILE)\n";
        $objstr = &objects($p, "X.obj", "X.res", "X.lib");
@@ -563,6 +627,14 @@ if (defined $makefiles{'vc'}) {
     foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) {
        print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
          "\n";
+       if ($d->{obj} =~ /\.res$/) {
+           print "\trc \$(FWHACK) \$(RCFL) -r -DWIN32 -D_WIN32 ".
+             "-DWINVER=0x0400 ".$d->{deps}->[0]."\n";
+       } else {
+           $deflist = join "", map { " /D$_" } @{$d->{defs}};
+           print "\tcl \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS)$deflist".
+             " /c ".$d->{deps}->[0]." /Fo$d->{obj}\n";
+       }
     }
     print "\n";
     print $makefile_extra{'vc'};
@@ -588,6 +660,7 @@ if (defined $makefiles{'vc'}) {
 }
 
 if (defined $makefiles{'vcproj'}) {
+    $mftyp = 'vcproj';
 
     $orig_dir = cwd;
 
@@ -608,7 +681,7 @@ if (defined $makefiles{'vcproj'}) {
     %all_object_deps = map {$_->{obj} => $_->{deps}} @deps;
     # Create the project files
     # Get names of all Windows projects (GUI and console)
-    my @prognames = &prognames("GC");
+    my @prognames = &prognames("G:C");
     foreach $progname (@prognames) {
        create_project(\%all_object_deps, $progname);
     }
@@ -843,6 +916,7 @@ if (defined $makefiles{'vcproj'}) {
 }
 
 if (defined $makefiles{'gtk'}) {
+    $mftyp = 'gtk';
     $dirpfx = &dirpfx($makefiles{'gtk'}, "/");
 
     ##-- X/GTK/Unix makefile
@@ -859,11 +933,17 @@ if (defined $makefiles{'gtk'}) {
     "# You can define this path to point at your tools if you need to\n".
     "# TOOLPATH = /opt/gcc/bin\n".
     "CC = \$(TOOLPATH)cc\n".
+    "# You can manually set this to `gtk-config' or `pkg-config gtk+-1.2'\n".
+    "# (depending on what works on your system) if you want to enforce\n".
+    "# building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0'\n".
+    "# if you want to enforce 2.0. The default is to try 2.0 and fall back\n".
+    "# to 1.2 if it isn't found.\n".
+    "GTK_CONFIG = sh -c 'pkg-config gtk+-2.0 \$\$0 2>/dev/null || gtk-config \$\$0'\n".
     "\n".
     &splitline("CFLAGS = -O2 -Wall -Werror -g " .
               (join " ", map {"-I$dirpfx$_"} @srcdirs) .
-              " `gtk-config --cflags`")."\n".
-    "XLDFLAGS = `gtk-config --libs`\n".
+              " `\$(GTK_CONFIG) --cflags`")."\n".
+    "XLDFLAGS = `\$(GTK_CONFIG) --libs`\n".
     "ULDFLAGS =#\n".
     "INSTALL=install\n",
     "INSTALL_PROGRAM=\$(INSTALL)\n",
@@ -871,17 +951,13 @@ if (defined $makefiles{'gtk'}) {
     "prefix=/usr/local\n",
     "exec_prefix=\$(prefix)\n",
     "bindir=\$(exec_prefix)/bin\n",
+    "gamesdir=\$(exec_prefix)/games\n",
     "mandir=\$(prefix)/man\n",
     "man1dir=\$(mandir)/man1\n",
-    "\n".
-    ".SUFFIXES:\n".
-    "\n".
-    "%.o:\n".
-    "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n".
     "\n";
-    print &splitline("all:" . join "", map { " $_" } &progrealnames("XU"));
+    print &splitline("all:" . join "", map { " $_" } &progrealnames("X:U"));
     print "\n\n";
-    foreach $p (&prognames("XU")) {
+    foreach $p (&prognames("X:U")) {
       ($prog, $type) = split ",", $p;
       $objstr = &objects($p, "X.o", undef, undef);
       print &splitline($prog . ": " . $objstr), "\n";
@@ -892,15 +968,19 @@ if (defined $makefiles{'gtk'}) {
     foreach $d (&deps("X.o", undef, $dirpfx, "/")) {
       print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
           "\n";
+      $deflist = join "", map { " -D$_" } @{$d->{defs}};
+      print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS)$deflist" .
+         " -c \$< -o \$\@\n";
     }
     print "\n";
     print $makefile_extra{'gtk'};
     print "\nclean:\n".
-    "\trm -f *.o". (join "", map { " $_" } &progrealnames("XU")) . "\n";
+    "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:U")) . "\n";
     select STDOUT; close OUT;
 }
 
 if (defined $makefiles{'mpw'}) {
+    $mftyp = 'mpw';
     ##-- MPW Makefile
     open OUT, ">$makefiles{'mpw'}"; select OUT;
     print
@@ -1041,6 +1121,7 @@ if (defined $makefiles{'mpw'}) {
 }
 
 if (defined $makefiles{'lcc'}) {
+    $mftyp = 'lcc';
     $dirpfx = &dirpfx($makefiles{'lcc'}, "\\");
 
     ##-- lcc makefile
@@ -1064,16 +1145,10 @@ if (defined $makefiles{'lcc'}) {
       "\n".
     "\n".
     "# Get include directory for resource compiler\n".
-    "\n".
-    ".c.obj:\n".
-    &splitline("\tlcc -O -p6 \$(COMPAT) \$(FWHACK)".
-      " \$(XFLAGS) \$(CFLAGS)  \$*.c",69)."\n".
-    ".rc.res:\n".
-    &splitline("\tlrc \$(FWHACK) \$(RCFL) -r \$*.rc",69)."\n".
     "\n";
-    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC"));
+    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
     print "\n\n";
-    foreach $p (&prognames("GC")) {
+    foreach $p (&prognames("G:C")) {
       ($prog, $type) = split ",", $p;
       $objstr = &objects($p, "X.obj", "X.res", undef);
       print &splitline("$prog.exe: " . $objstr ), "\n";
@@ -1087,6 +1162,13 @@ if (defined $makefiles{'lcc'}) {
     foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) {
       print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
         "\n";
+      if ($d->{obj} =~ /\.res$/) {
+       print &splitline("\tlrc \$(FWHACK) \$(RCFL) -r \$*.rc",69)."\n";
+      } else {
+       $deflist = join "", map { " -D$_" } @{$d->{defs}};
+       print &splitline("\tlcc -O -p6 \$(COMPAT) \$(FWHACK) \$(XFLAGS)".
+                        " \$(CFLAGS)$deflist ".$d->{deps}->[0]." -o \$\@",69)."\n";
+      }
     }
     print "\n";
     print $makefile_extra{'lcc'};
@@ -1097,3 +1179,81 @@ if (defined $makefiles{'lcc'}) {
 
     select STDOUT; close OUT;
 }
+
+if (defined $makefiles{'osx'}) {
+    $mftyp = 'osx';
+    $dirpfx = &dirpfx($makefiles{'osx'}, "/");
+
+    ##-- Mac OS X makefile
+    open OUT, ">$makefiles{'osx'}"; select OUT;
+    print
+    "# Makefile for $project_name under Mac OS X.\n".
+    "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
+    # gcc command line option is -D not /D
+    ($_ = $help) =~ s/=\/D/=-D/gs;
+    print $_;
+    print
+    "CC = \$(TOOLPATH)gcc\n".
+    "\n".
+    &splitline("CFLAGS = -O2 -Wall -Werror -g " .
+              (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
+    "LDFLAGS = -framework Cocoa\n".
+    &splitline("all:" . join "", map { " $_" } &progrealnames("MX:U")) .
+    "\n" .
+    $makefile_extra{'osx'} .
+    "\n".
+    ".SUFFIXES: .o .c .m\n".
+    "\n";
+    print "\n\n";
+    foreach $p (&prognames("MX")) {
+      ($prog, $type) = split ",", $p;
+      $objstr = &objects($p, "X.o", undef, undef);
+      $icon = &special($p, ".icns");
+      $infoplist = &special($p, "info.plist");
+      print "${prog}.app:\n\tmkdir -p \$\@\n";
+      print "${prog}.app/Contents: ${prog}.app\n\tmkdir -p \$\@\n";
+      print "${prog}.app/Contents/MacOS: ${prog}.app/Contents\n\tmkdir -p \$\@\n";
+      $targets = "${prog}.app/Contents/MacOS/$prog";
+      if (defined $icon) {
+       print "${prog}.app/Contents/Resources: ${prog}.app/Contents\n\tmkdir -p \$\@\n";
+       print "${prog}.app/Contents/Resources/${prog}.icns: ${prog}.app/Contents/Resources $icon\n\tcp $icon \$\@\n";
+       $targets .= " ${prog}.app/Contents/Resources/${prog}.icns";
+      }
+      if (defined $infoplist) {
+       print "${prog}.app/Contents/Info.plist: ${prog}.app/Contents/Resources $infoplist\n\tcp $infoplist \$\@\n";
+       $targets .= " ${prog}.app/Contents/Info.plist";
+      }
+      $targets .= " \$(${prog}_extra)";
+      print &splitline("${prog}: $targets", 69) . "\n\n";
+      print &splitline("${prog}.app/Contents/MacOS/$prog: ".
+                      "${prog}.app/Contents/MacOS " . $objstr), "\n";
+      $libstr = &objects($p, undef, undef, "-lX");
+      print &splitline("\t\$(CC)" . $mw . " \$(LDFLAGS) -o \$@ " .
+                       $objstr . " $libstr", 69), "\n\n";
+    }
+    foreach $p (&prognames("U")) {
+      ($prog, $type) = split ",", $p;
+      $objstr = &objects($p, "X.o", undef, undef);
+      print &splitline($prog . ": " . $objstr), "\n";
+      $libstr = &objects($p, undef, undef, "-lX");
+      print &splitline("\t\$(CC)" . $mw . " \$(ULDFLAGS) -o \$@ " .
+                      $objstr . " $libstr", 69), "\n\n";
+    }
+    foreach $d (&deps("X.o", undef, $dirpfx, "/")) {
+      print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
+          "\n";
+      $deflist = join "", map { " -D$_" } @{$d->{defs}};
+      if ($d->{deps}->[0] =~ /\.m$/) {
+       print "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(XFLAGS)".
+           " \$(CFLAGS)$deflist -c \$< -o \$\@\n";
+      } else {
+       print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS)$deflist" .
+           " -c \$< -o \$\@\n";
+      }
+    }
+    print "\nclean:\n".
+    "\trm -f *.o *.dmg". (join "", map { " $_" } &progrealnames("U")) . "\n".
+    "\trm -rf *.app\n";
+    select STDOUT; close OUT;
+}