Stop the analysis pass in Loopy's redraw routine from being
[sgt/puzzles] / mkfiles.pl
index f5a3d9b..625b9ab 100755 (executable)
 #    mainly because I was too scared to go anywhere near it.
 #  - sbcsgen.pl is still run at startup.
 
-use FileHandle;
+# Other things undone:
+#  - special-define objects (foo.o[PREPROCSYMBOL]) are not
+#    supported in the mac or vcproj makefiles.
+
+use warnings;
+use IO::Handle;
 use Cwd;
 
-open IN, "Recipe" or do {
+@filestack = ();
+$in = new IO::Handle;
+open $in, "Recipe" or do {
     # We want to deal correctly with being run from one of the
     # subdirs in the source tree. So if we can't find Recipe here,
     # try one level up.
     chdir "..";
-    open IN, "Recipe" or die "unable to open Recipe file\n";
+    open $in, "Recipe" or die "unable to open Recipe file\n";
 };
+push @filestack, $in;
 
 # HACK: One of the source files in `charset' is auto-generated by
 # sbcsgen.pl. We need to generate that _now_, before attempting
@@ -39,30 +47,57 @@ $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
 
-while (<IN>) {
-  # Skip comments (unless the comments belong, for example because
-  # they're part of a diversion).
-  next if /^\s*#/ and !defined $divert;
+@allobjs = (); # all object file names
 
+readinput: while (1) {
+  $in = $filestack[$#filestack];
+  while (not defined ($_ = <$in>)) {
+    close $filestack[$#filestack];
+    pop @filestack;
+    last readinput if 0 == scalar @filestack;
+    $in = $filestack[$#filestack];
+  }
   chomp;
-  split;
+  @_ = split;
+
+  # If we're gathering help text, keep doing so.
+  if (defined $divert) {
+      if ((defined $_[0]) && $_[0] eq "!end") {
+         $divert = undef;
+      } else {
+         ${$divert} .= "$_\n";
+      }
+      next;
+  }
+  # Skip comments and blank lines.
+  next if /^\s*#/ or scalar @_ == 0;
+
   if ($_[0] eq "!begin" and $_[1] eq "help") { $divert = \$help; next; }
-  if ($_[0] eq "!end") { $divert = undef; next; }
   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])) {
+      if ($_[1] =~ /^>(.*)/) {
+         $divert = \$auxfiles{$1};
+      } elsif (&mfval($_[1])) {
          $divert = \$makefile_extra{$_[1]};
-      } else {
-         $divert = \$dummy;
       }
       next;
   }
-  # If we're gathering help text, keep doing so.
-  if (defined $divert) { ${$divert} .= "$_\n"; next; }
-  # Ignore blank lines.
-  next if scalar @_ == 0;
+  if ($_[0] eq "!include") {
+      @newfiles = ();
+      for ($i = 1; $i <= $#_; $i++) {
+         push @newfiles, (sort glob $_[$i]);
+      }
+      for ($i = $#newfiles; $i >= 0; $i--) {
+         $file = $newfiles[$i];
+         $f = new IO::Handle;
+         open $f, "<$file" or die "unable to open include file '$file'\n";
+         push @filestack, $f;
+      }
+      next;
+  }
 
   # Now we have an ordinary line. See if it's an = line, a : line
   # or a + line.
@@ -73,6 +108,11 @@ while (<IN>) {
     $prog = undef;
     die "$.: unexpected + line\n" if !defined $lastlistref;
   } elsif ($_[1] eq "=") {
+    $groups{$_[0]} = [];
+    $listref = $groups{$_[0]};
+    $prog = undef;
+    shift @objs; # eat the group name
+  } elsif ($_[1] eq "+=") {
     $groups{$_[0]} = [] if !defined $groups{$_[0]};
     $listref = $groups{$_[0]};
     $prog = undef;
@@ -82,7 +122,7 @@ while (<IN>) {
     $prog = $_[0];
     shift @objs; # eat the program name
   } else {
-    die "$.: unrecognised line type\n";
+    die "$.: unrecognised line type: '$_'\n";
   }
   shift @objs; # eat the +, the = or the :
 
@@ -94,7 +134,28 @@ while (<IN>) {
               $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;
+      if ($i =~ /\?$/) {
+       # Object files with a trailing question mark are optional:
+       # the build can proceed fine without them, so we only use
+       # them if their primary source files are present.
+       $i =~ s/\?$//;
+       $i = undef unless defined &finddep($i);
+      } elsif ($i =~ /\|/) {
+       # Object file descriptions containing a vertical bar are
+       # lists of choices: we use the _first_ one whose primary
+       # source file is present.
+       @options = split /\|/, $i;
+       $j = undef;
+       foreach $k (@options) {
+         $j=$k, last if defined &finddep($k);
+       }
+       die "no alternative found for $i\n" unless defined $j;
+       $i = $j;
+      }
+      if (defined $i) {
+       push @$listref, $i;
+       push @allobjs, $i;
+      }
     }
   }
   if ($prog and $type) {
@@ -105,7 +166,40 @@ while (<IN>) {
   $lastlistref = $listref;
 }
 
-close IN;
+foreach $aux (sort keys %auxfiles) {
+    open AUX, ">$aux";
+    print AUX $auxfiles{$aux};
+    close AUX;
+}
+
+# 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
@@ -116,29 +210,15 @@ close IN;
 foreach $i (@prognames) {
   ($prog, $type) = split ",", $i;
   # Strip duplicate object names.
-  $prev = undef;
+  $prev = '';
   @list = grep { $status = ($prev ne $_); $prev=$_; $status }
           sort @{$programs{$i}};
   $programs{$i} = [@list];
-  foreach $j (@list) {
-    # 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];
-      push @scanlist, $file;
-    } elsif ($j =~ /^(.*)\.rsrc$/) {
-      $file = "$1.r";
-      $depends{$j} = [$file];
-      push @scanlist, $file;
-    } elsif ($j !~ /\./) {
-      $file = "$j.c";
-      $file = "$j.m" unless &findfile($file);
-      $depends{$j} = [$file];
+  foreach $jj (@list) {
+    $j = $srcname{$jj};
+    $file = &finddep($j);
+    if (defined $file) {
+      $depends{$jj} = [$file];
       push @scanlist, $file;
     }
   }
@@ -162,7 +242,6 @@ foreach $i (@prognames) {
 while (scalar @scanlist > 0) {
   $file = shift @scanlist;
   next if defined $further{$file}; # skip if we've already done it
-  $resource = ($file =~ /\.rc$/ ? 1 : 0);
   $further{$file} = [];
   $dirfile = &findfile($file);
   open IN, "$dirfile" or die "unable to open source file $file\n";
@@ -191,7 +270,7 @@ foreach $i (keys %depends) {
   while (scalar @scanlist > 0) {
     $file = shift @scanlist;
     foreach $j (@{$further{$file}}) {
-      if ($dep{$j} != 1) {
+      if (!$dep{$j}) {
         $dep{$j} = 1;
         push @{$depends{$i}}, $j;
         push @scanlist, $j;
@@ -208,7 +287,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","osx")) {
+       ("vc","vcproj","cygwin","borland","lcc","gtk","mpw","nestedvm","osx","wce","gnustep")) {
            return 1;
        }
     warn "$.:unknown makefile type '$type'\n";
@@ -220,7 +299,8 @@ sub mfval($) {
 sub dirpfx {
     my ($path) = shift @_;
     my ($sep) = shift @_;
-    my $ret = "", $i;
+    my $ret = "";
+    my $i;
     while (($i = index $path, $sep) >= 0) {
        $path = substr $path, ($i + length $sep);
        $ret .= "..$sep";
@@ -244,12 +324,40 @@ sub findfile {
   return $findfilecache{$name};
 }
 
+sub finddep {
+  my $j = shift @_;
+  my $file;
+  # Find the first dependency of an object.
+
+  # 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";
+  } elsif ($j =~ /^(.*)\.rsrc$/) {
+    $file = "$1.r";
+  } elsif ($j !~ /\./) {
+    $file = "$j.c";
+    $file = "$j.m" unless &findfile($file);
+  } else {
+    # For everything else, we assume it's its own dependency.
+    $file = $j;
+  }
+  $file = undef unless &findfile($file);
+  return $file;
+}
+
 sub objects {
   my ($prog, $otmpl, $rtmpl, $ltmpl, $prefix, $dirsep) = @_;
   my @ret;
   my ($i, $x, $y);
+  ($otmpl, $rtmpl, $ltmpl) = map { defined $_ ? $_ : "" } ($otmpl, $rtmpl, $ltmpl);
   @ret = ();
-  foreach $i (@{$programs{$prog}}) {
+  foreach $ii (@{$programs{$prog}}) {
+    $i = $objname{$ii};
     $x = "";
     if ($i =~ /^(.*)\.(res|rsrc)/) {
       $y = $1;
@@ -270,7 +378,8 @@ sub special {
   my @ret;
   my ($i, $x, $y);
   @ret = ();
-  foreach $i (@{$programs{$prog}}) {
+  foreach $ii (@{$programs{$prog}}) {
+    $i = $objname{$ii};
     if (substr($i, (length $i) - (length $suffix)) eq $suffix) {
       push @ret, $i;
     }
@@ -280,12 +389,14 @@ sub special {
 
 sub splitline {
   my ($line, $width, $splitchar) = @_;
-  my ($result, $len);
+  my $result = "";
+  my $len;
   $len = (defined $width ? $width : 76);
   $splitchar = (defined $splitchar ? $splitchar : '\\');
   while (length $line > $len) {
     $line =~ /^(.{0,$len})\s(.*)$/ or $line =~ /^(.{$len,}?\s(.*)$/;
-    $result .= $1 . " ${splitchar}\n\t\t";
+    $result .= $1;
+    $result .= " ${splitchar}\n\t\t" if $2 ne '';
     $line = $2;
     $len = 60;
   }
@@ -295,10 +406,13 @@ sub splitline {
 sub deps {
   my ($otmpl, $rtmpl, $prefix, $dirsep, $depchar, $splitchar) = @_;
   my ($i, $x, $y);
-  my @deps, @ret;
+  my @deps;
+  my @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;
@@ -306,13 +420,15 @@ sub deps {
     } else {
       ($x = $otmpl) =~ s/X/$i/;
     }
-    @deps = @{$depends{$i}};
+    @deps = @{$depends{$ii}};
+    # Skip things which are their own dependency.
+    next if grep { $_ eq $i } @deps;
     @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;
 }
@@ -354,6 +470,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,20 +496,13 @@ if (defined $makefiles{'cygwin'}) {
     "# RCINC = --include-dir c:\\cygwin\\include\\\n".
     "\n".
     &splitline("CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT".
-      " -D_NO_OLDNAMES -DNO_MULTIMON " .
+      " -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP " .
               (join " ", map {"-I$dirpfx$_"} @srcdirs)) .
               "\n".
     "LDFLAGS = -mno-cygwin -s\n".
     &splitline("RCFLAGS = \$(RCINC) --define WIN32=1 --define _WIN32=1".
-      " --define WINVER=0x0400 --define MINGW32_FIX=1")."\n".
-    "\n".
-    ".SUFFIXES:\n".
-    "\n".
-    "%.o: %.c\n".
-    "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n".
-    "\n".
-    "%.res.o: %.rc\n".
-    "\t\$(RC) \$(FWHACK) \$(RCFL) \$(RCFLAGS) \$< \$\@\n".
+      " --define WINVER=0x0400 --define MINGW32_FIX=1 " .
+       (join " ", map {"--include $dirpfx$_"} @srcdirs) )."\n".
     "\n";
     print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
     print "\n\n";
@@ -409,9 +519,16 @@ 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) \$(CFLAGS)" .
+           " \$(XFLAGS)$deflist -c \$< -o \$\@\n";
+      }
     }
     print "\n";
-    print $makefile_extra{'cygwin'};
+    print $makefile_extra{'cygwin'} || "";
     print "\nclean:\n".
     "\trm -f *.o *.exe *.res.o *.map\n".
     "\n";
@@ -421,6 +538,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
@@ -456,21 +574,12 @@ 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("G:C"));
     print "\n\n";
     foreach $p (&prognames("G:C")) {
       ($prog, $type) = split ",", $p;
-      $objstr = &objects($p, "X.obj", "X.res", undef);
+      $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";
@@ -506,9 +615,20 @@ 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) \$(CFLAGS) \$(XFLAGS)$deflist ".
+                        (join " ", map {"-I$dirpfx$_"} @srcdirs) .
+                        " /o$d->{obj} /c ".$d->{deps}->[0],69)."\n";
+      }
     }
     print "\n";
-    print $makefile_extra{'borland'};
+    print $makefile_extra{'borland'} || "";
     print "\nclean:\n".
     "\t-del *.obj\n".
     "\t-del *.exe\n".
@@ -524,6 +644,7 @@ if (defined $makefiles{'borland'}) {
 }
 
 if (defined $makefiles{'vc'}) {
+    $mftyp = 'vc';
     $dirpfx = &dirpfx($makefiles{'vc'}, "\\");
 
     ##-- Visual C++ makefile
@@ -540,13 +661,8 @@ if (defined $makefiles{'vc'}) {
       "MAKEFILE = Makefile.vc\n".
       "\n".
       "# C compilation flags\n".
-      "CFLAGS = /nologo /W3 /O1 /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401\n".
+      "CFLAGS = /nologo /W3 /O1 /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401 /I.\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("G:C"));
     print "\n\n";
@@ -578,9 +694,125 @@ 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 -fo".$d->{obj}." ".$d->{deps}->[0]."\n";
+       } else {
+           $deflist = join "", map { " /D$_" } @{$d->{defs}};
+           print "\tcl \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist".
+             " /c ".$d->{deps}->[0]." /Fo$d->{obj}\n";
+       }
+    }
+    print "\n";
+    print $makefile_extra{'vc'} || "";
+    print "\nclean: tidy\n".
+      "\t-del *.exe\n\n".
+      "tidy:\n".
+      "\t-del *.obj\n".
+      "\t-del *.res\n".
+      "\t-del *.pch\n".
+      "\t-del *.aps\n".
+      "\t-del *.ilk\n".
+      "\t-del *.pdb\n".
+      "\t-del *.rsp\n".
+      "\t-del *.dsp\n".
+      "\t-del *.dsw\n".
+      "\t-del *.ncb\n".
+      "\t-del *.opt\n".
+      "\t-del *.plg\n".
+      "\t-del *.map\n".
+      "\t-del *.idb\n".
+      "\t-del debug.log\n";
+    select STDOUT; close OUT;
+}
+
+if (defined $makefiles{'wce'}) {
+    $mftyp = 'wce';
+    $dirpfx = &dirpfx($makefiles{'wce'}, "\\");
+
+    ##-- eMbedded Visual C PocketPC makefile
+    open OUT, ">$makefiles{'wce'}"; select OUT;
+    print
+      "# Makefile for $project_name on PocketPC using eMbedded Visual C.\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";
+    print $help;
+    print
+      "\n".
+      "# If you rename this file to `Makefile', you should change this line,\n".
+      "# so that the .rsp files still depend on the correct makefile.\n".
+      "MAKEFILE = Makefile.wce\n".
+      "\n".
+      "# This makefile expects the environment to have been set up by one\n".
+      "# of the PocketPC batch files wcearmv4.bat and wceemulator.bat. No\n".
+      "# other build targets are currently supported, because they would\n".
+      "# need a section in this if statement.\n".
+      "!if \"\$(TARGETCPU)\" == \"emulator\"\n".
+      "PLATFORM_DEFS=/D \"_i386_\" /D \"i_386_\" /D \"_X86_\" /D \"x86\"\n".
+      "CC=cl\n".
+      "BASELIBS=commctrl.lib coredll.lib corelibc.lib aygshell.lib\n".
+      "MACHINE=IX86\n".
+      "!else\n".
+      "PLATFORM_DEFS=/D \"ARM\" /D \"_ARM_\" /D \"ARMV4\"\n".
+      "CC=clarm\n".
+      "BASELIBS=commctrl.lib coredll.lib aygshell.lib\n".
+      "MACHINE=ARM\n".
+      "!endif\n".
+      "\n".
+      "# C compilation flags\n".
+      "CFLAGS = /nologo /W3 /O1 /MC /D _WIN32_WCE=420 /D \"WIN32_PLATFORM_PSPC=400\" /D UNDER_CE=420 \\\n".
+      "         \$(PLATFORM_DEFS) \\\n".
+      "         /D \"UNICODE\" /D \"_UNICODE\" /D \"NDEBUG\" /D \"NO_HTMLHELP\"\n".
+      "\n".
+      "LFLAGS = /nologo /incremental:no \\\n".
+      "         /base:0x00010000 /stack:0x10000,0x1000 /entry:WinMainCRTStartup \\\n".
+      "         /nodefaultlib:libc.lib /nodefaultlib:libcmt.lib /nodefaultlib:msvcrt.lib /nodefaultlib:OLDNAMES.lib \\\n".
+      "         /subsystem:windowsce,4.20 /align:4096 /MACHINE:\$(MACHINE)\n".
+      "\n".
+      "RCFL = /d UNDER_CE=420 /d _WIN32_WCE=420 /d \"WIN32_PLATFORM_PSPC=400\" \\\n".
+      "       \$(PLATFORM_DEFS) \\\n".
+      "       /d \"NDEBUG\" /d \"UNICODE\" /d \"_UNICODE\"\n".
+      "\n";
+    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G"));
+    print "\n\n";
+    foreach $p (&prognames("G")) {
+       ($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("G")) {
+       ($prog, $type) = split ",", $p;
+       print $prog, ".rsp: \$(MAKEFILE)\n";
+       $objstr = &objects($p, "X.obj", "X.res", undef);
+       @objlist = split " ", $objstr;
+       @objlines = ("");
+       foreach $i (@objlist) {
+           if (length($objlines[$#objlines] . " $i") > 50) {
+               push @objlines, "";
+           }
+           $objlines[$#objlines] .= " $i";
+       }
+       print "\techo \$(BASELIBS) > $prog.rsp\n";
+       for ($i=0; $i<=$#objlines; $i++) {
+           print "\techo$objlines[$i] >> $prog.rsp\n";
+       }
+       print "\n";
+    }
+    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 -fo".
+             $d->{obj}." ".$d->{deps}->[0]."\n";
+       } else {
+           $deflist = join "", map { " /D$_" } @{$d->{defs}};
+           print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist".
+             " /c ".$d->{deps}->[0]." /Fo$d->{obj}\n";
+       }
     }
     print "\n";
-    print $makefile_extra{'vc'};
+    print $makefile_extra{'wce'} || "";
     print "\nclean: tidy\n".
       "\t-del *.exe\n\n".
       "tidy:\n".
@@ -603,6 +835,7 @@ if (defined $makefiles{'vc'}) {
 }
 
 if (defined $makefiles{'vcproj'}) {
+    $mftyp = 'vcproj';
 
     $orig_dir = cwd;
 
@@ -858,6 +1091,7 @@ if (defined $makefiles{'vcproj'}) {
 }
 
 if (defined $makefiles{'gtk'}) {
+    $mftyp = 'gtk';
     $dirpfx = &dirpfx($makefiles{'gtk'}, "/");
 
     ##-- X/GTK/Unix makefile
@@ -873,7 +1107,7 @@ if (defined $makefiles{'gtk'}) {
     "\n".
     "# You can define this path to point at your tools if you need to\n".
     "# TOOLPATH = /opt/gcc/bin\n".
-    "CC = \$(TOOLPATH)cc\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".
@@ -881,47 +1115,48 @@ if (defined $makefiles{'gtk'}) {
     "# 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 " .
+    &splitline("CFLAGS := -O2 -Wall -Werror -ansi -pedantic -g " .
               (join " ", map {"-I$dirpfx$_"} @srcdirs) .
-              " `\$(GTK_CONFIG) --cflags`")."\n".
-    "XLDFLAGS = `\$(GTK_CONFIG) --libs`\n".
-    "ULDFLAGS =#\n".
+              " `\$(GTK_CONFIG) --cflags` \$(CFLAGS)")."\n".
+    "XLIBS = `\$(GTK_CONFIG) --libs` -lm\n".
+    "ULIBS = -lm#\n".
     "INSTALL=install\n",
     "INSTALL_PROGRAM=\$(INSTALL)\n",
     "INSTALL_DATA=\$(INSTALL)\n",
     "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("X:U"));
+    print &splitline("all:" . join "", map { " \$(BINPREFIX)$_" }
+                     &progrealnames("X:U"));
     print "\n\n";
     foreach $p (&prognames("X:U")) {
       ($prog, $type) = split ",", $p;
       $objstr = &objects($p, "X.o", undef, undef);
-      print &splitline($prog . ": " . $objstr), "\n";
+      print &splitline("\$(BINPREFIX)" . $prog . ": " . $objstr), "\n";
       $libstr = &objects($p, undef, undef, "-lX");
-      print &splitline("\t\$(CC)" . $mw . " \$(${type}LDFLAGS) -o \$@ " .
-                       $objstr . " $libstr", 69), "\n\n";
+      print &splitline("\t\$(CC) -o \$@ $objstr $libstr \$(XLFLAGS) \$(${type}LIBS)", 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}};
+      print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" .
+         " -c \$< -o \$\@\n";
     }
     print "\n";
-    print $makefile_extra{'gtk'};
+    print $makefile_extra{'gtk'} || "";
     print "\nclean:\n".
-    "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:U")) . "\n";
+    "\trm -f *.o". (join "", map { " \$(BINPREFIX)$_" } &progrealnames("X:U")) . "\n";
     select STDOUT; close OUT;
 }
 
 if (defined $makefiles{'mpw'}) {
+    $mftyp = 'mpw';
     ##-- MPW Makefile
     open OUT, ">$makefiles{'mpw'}"; select OUT;
     print
@@ -1062,6 +1297,7 @@ if (defined $makefiles{'mpw'}) {
 }
 
 if (defined $makefiles{'lcc'}) {
+    $mftyp = 'lcc';
     $dirpfx = &dirpfx($makefiles{'lcc'}, "\\");
 
     ##-- lcc makefile
@@ -1085,12 +1321,6 @@ 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("G:C"));
     print "\n\n";
@@ -1108,9 +1338,16 @@ 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) \$(CFLAGS)".
+                        " \$(XFLAGS)$deflist ".$d->{deps}->[0]." -o \$\@",69)."\n";
+      }
     }
     print "\n";
-    print $makefile_extra{'lcc'};
+    print $makefile_extra{'lcc'} || "";
     print "\nclean:\n".
     "\t-del *.obj\n".
     "\t-del *.exe\n".
@@ -1119,8 +1356,63 @@ if (defined $makefiles{'lcc'}) {
     select STDOUT; close OUT;
 }
 
+if (defined $makefiles{'nestedvm'}) {
+    $mftyp = 'nestedvm';
+    $dirpfx = &dirpfx($makefiles{'nestedvm'}, "/");
+
+    ##-- NestedVM makefile
+    open OUT, ">$makefiles{'nestedvm'}"; select OUT;
+    print
+    "# Makefile for $project_name under NestedVM.\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
+    "\n".
+    "# This path points at the nestedvm root directory\n".
+    "NESTEDVM = /opt/nestedvm\n".
+    "# You can define this path to point at your tools if you need to\n".
+    "TOOLPATH = \$(NESTEDVM)/upstream/install/bin\n".
+    "CC = \$(TOOLPATH)/mips-unknown-elf-gcc\n".
+    "\n".
+    &splitline("CFLAGS = -O2 -Wall -Werror -DSLOW_SYSTEM -g " .
+              (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
+    "\n";
+    print &splitline("all:" . join "", map { " $_.jar" } &progrealnames("X"));
+    print "\n\n";
+    foreach $p (&prognames("X")) {
+      ($prog, $type) = split ",", $p;
+      $objstr = &objects($p, "X.o", undef, undef);
+      $objstr =~ s/gtk\.o/nestedvm\.o/g;
+      print &splitline($prog . ".mips: " . $objstr), "\n";
+      $libstr = &objects($p, undef, undef, "-lX");
+      print &splitline("\t\$(CC) \$(${type}LDFLAGS) -o \$@ " .
+                       $objstr . " $libstr -lm", 69), "\n\n";
+    }
+    foreach $d (&deps("X.o", undef, $dirpfx, "/")) {
+      $oobjs = $d->{obj};
+      $ddeps= join " ", @{$d->{deps}};
+      $oobjs =~ s/gtk/nestedvm/g;
+      $ddeps =~ s/gtk/nestedvm/g;
+      print &splitline(sprintf("%s: %s", $oobjs, $ddeps)),
+          "\n";
+      $deflist = join "", map { " -D$_" } @{$d->{defs}};
+      print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" .
+         " -c \$< -o \$\@\n";
+    }
+    print "\n";
+    print $makefile_extra{'nestedvm'} || "";
+    print "\nclean:\n".
+    "\trm -rf *.o *.mips *.class *.html *.jar org applet.manifest\n";
+    select STDOUT; close OUT;
+}
+
 if (defined $makefiles{'osx'}) {
+    $mftyp = 'osx';
     $dirpfx = &dirpfx($makefiles{'osx'}, "/");
+    @osxarchs = ('i386');
 
     ##-- Mac OS X makefile
     open OUT, ">$makefiles{'osx'}"; select OUT;
@@ -1133,25 +1425,20 @@ if (defined $makefiles{'osx'}) {
     print $_;
     print
     "CC = \$(TOOLPATH)gcc\n".
+    "LIPO = \$(TOOLPATH)lipo\n".
     "\n".
     &splitline("CFLAGS = -O2 -Wall -Werror -g " .
               (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
     "LDFLAGS = -framework Cocoa\n".
-    &splitline("all:" . join "", map { " $_" } &progrealnames("MX")) .
-    "\n" .
-    $makefile_extra{'osx'} .
-    "\n".
+    &splitline("all:" . join "", map { " $_" } &progrealnames("MX:U")) .
+    "\n";
+    print $makefile_extra{'osx'} || "";
+    print "\n".
     ".SUFFIXES: .o .c .m\n".
-    "\n".
-    ".c.o:\n".
-    "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n".
-    ".m.o:\n".
-    "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\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";
@@ -1169,18 +1456,136 @@ if (defined $makefiles{'osx'}) {
       }
       $targets .= " \$(${prog}_extra)";
       print &splitline("${prog}: $targets", 69) . "\n\n";
+      $libstr = &objects($p, undef, undef, "-lX");
+      $archbins = "";
+      foreach $arch (@osxarchs) {
+       $objstr = &objects($p, "X.${arch}.o", undef, undef);
+       print &splitline("${prog}.${arch}.bin: " . $objstr), "\n";
+       print &splitline("\t\$(CC) -arch ${arch} -mmacosx-version-min=10.4 \$(LDFLAGS) -o \$@ " .
+                       $objstr . " $libstr", 69), "\n\n";
+       $archbins .= " ${prog}.${arch}.bin";
+      }
       print &splitline("${prog}.app/Contents/MacOS/$prog: ".
-                      "${prog}.app/Contents/MacOS " . $objstr), "\n";
+                      "${prog}.app/Contents/MacOS" . $archbins), "\n";
+      print &splitline("\t\$(LIPO) -create $archbins -output \$@", 69), "\n\n";
+    }
+    foreach $p (&prognames("U")) {
+      ($prog, $type) = split ",", $p;
       $libstr = &objects($p, undef, undef, "-lX");
-      print &splitline("\t\$(CC)" . $mw . " \$(LDFLAGS) -o \$@ " .
+      $archbins = "";
+      foreach $arch (@osxarchs) {
+       $objstr = &objects($p, "X.${arch}.o", undef, undef);
+       print &splitline("${prog}.${arch}: " . $objstr), "\n";
+       print &splitline("\t\$(CC) -arch ${arch} -mmacosx-version-min=10.4 \$(ULDFLAGS) -o \$@ " .
                        $objstr . " $libstr", 69), "\n\n";
+       $archbins .= " ${prog}.${arch}";
+      }
+      print &splitline("${prog}:" . $archbins), "\n";
+      print &splitline("\t\$(LIPO) -create $archbins -output \$@", 69), "\n\n";
+    }
+    foreach $arch (@osxarchs) {
+      foreach $d (&deps("X.${arch}.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) -arch $arch -mmacosx-version-min=10.4 -x objective-c \$(COMPAT) \$(FWHACK) \$(CFLAGS)".
+             " \$(XFLAGS)$deflist -c \$< -o \$\@\n";
+        } else {
+         print "\t\$(CC) -arch $arch -mmacosx-version-min=10.4 \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" .
+             " -c \$< -o \$\@\n";
+        }
+      }
+    }
+    print "\nclean:\n".
+    "\trm -f *.o *.dmg". (join "", map { my $a=$_; (" $a", map { " ${a}.$_" } @osxarchs) } &progrealnames("U")) . "\n".
+    "\trm -rf *.app\n";
+    select STDOUT; close OUT;
+}
+
+if (defined $makefiles{'gnustep'}) {
+    $mftyp = 'gnustep';
+    $dirpfx = &dirpfx($makefiles{'gnustep'}, "/");
+
+    ##-- GNUstep makefile (use with 'gs_make -f Makefile.gnustep')
+
+    # This is a pretty evil way to do things. In an ideal world, I'd
+    # use the approved GNUstep makefile mechanism which just defines a
+    # variable or two saying what source files go into what binary and
+    # then includes application.make. Unfortunately, that has the
+    # automake-ish limitation that it doesn't let you choose different
+    # command lines for each object, so I can't arrange for all those
+    # files with -DTHIS and -DTHAT to Just Work.
+    #
+    # A simple if ugly fix would be to have mkfiles.pl construct a
+    # directory full of stub C files of the form '#define thing',
+    # '#include "real_source_file"', and then reference those in this
+    # makefile. That would also make it easy to build a proper
+    # automake makefile.
+    open OUT, ">$makefiles{'gnustep'}"; select OUT;
+    print
+    "# Makefile for $project_name under GNUstep.\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
+    "NEEDS_GUI=yes\n".
+    "include \$(GNUSTEP_MAKEFILES)/common.make\n".
+    "include \$(GNUSTEP_MAKEFILES)/rules.make\n".
+    "include \$(GNUSTEP_MAKEFILES)/Instance/rules.make\n".
+    "\n".
+    &splitline("all::" . join "", map { " $_" } &progrealnames("MX:U")) .
+    "\n";
+    print $makefile_extra{'gnustep'} || "";
+    print "\n".
+    ".SUFFIXES: .o .c .m\n".
+    "\n";
+    print "\n\n";
+    foreach $p (&prognames("MX")) {
+      ($prog, $type) = split ",", $p;
+      $icon = &special($p, ".icns");
+      $infoplist = &special($p, "info.plist");
+      print "${prog}.app:\n\tmkdir -p \$\@\n";
+      $targets = "${prog}.app ${prog}.app/$prog";
+      if (defined $icon) {
+       print "${prog}.app/Resources: ${prog}.app\n\tmkdir -p \$\@\n";
+       print "${prog}.app/Resources/${prog}.icns: ${prog}.app/Resources $icon\n\tcp $icon \$\@\n";
+       $targets .= " ${prog}.app/Resources/${prog}.icns";
+      }
+      if (defined $infoplist) {
+       print "${prog}.app/Info.plist: ${prog}.app $infoplist\n\tcp $infoplist \$\@\n";
+       $targets .= " ${prog}.app/Info.plist";
+      }
+      $targets .= " \$(${prog}_extra)";
+      print &splitline("${prog}: $targets", 69) . "\n\n";
+      $libstr = &objects($p, undef, undef, "-lX");
+      $objstr = &objects($p, "X.o", undef, undef);
+      print &splitline("${prog}.app/$prog: " . $objstr), "\n";
+      print &splitline("\t\$(CC) \$(ALL_LDFLAGS) -o \$@ " . $objstr . " \$(ALL_LIB_DIRS) $libstr \$(ALL_LIBS)", 69), "\n\n";
+    }
+    foreach $p (&prognames("U")) {
+      ($prog, $type) = split ",", $p;
+      $libstr = &objects($p, undef, undef, "-lX");
+      $objstr = &objects($p, "X.o", undef, undef);
+      print &splitline("${prog}: " . $objstr), "\n";
+      print &splitline("\t\$(CC) \$(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";
+      "\n";
+      $deflist = join "", map { " -D$_" } @{$d->{defs}};
+      if ($d->{deps}->[0] =~ /\.m$/) {
+        print "\t\$(CC) -DGNUSTEP \$(ALL_OBJCFLAGS) \$(COMPAT) \$(FWHACK) \$(OBJCFLAGS)".
+                " \$(XFLAGS)$deflist -c \$< -o \$\@\n";
+      } else {
+        print "\t\$(CC) \$(ALL_CFLAGS) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" .
+              " -c \$< -o \$\@\n";
+      }
     }
-    print "\nclean:\n".
-    "\trm -f *.o *.dmg\n".
+    print "\nclean::\n".
+    "\trm -f *.o ". (join " ", &progrealnames("U")) . "\n".
     "\trm -rf *.app\n";
     select STDOUT; close OUT;
 }