shithub: puzzles

Download patch

ref: cc7f5503dc8f4ddf468e080a73028c83d1196e83
parent: 72b28b5e71b05e7775d14b1e3e2e00b82af4d87a
author: Simon Tatham <anakin@pobox.com>
date: Mon Mar 29 14:23:11 EDT 2021

Migrate to a CMake-based build system.

This completely removes the old system of mkfiles.pl + Recipe + .R
files that I used to manage the various per-platform makefiles and
other build scripts in this code base. In its place is a
CMakeLists.txt setup, which is still able to compile for Linux,
Windows, MacOS, NestedVM and Emscripten.

The main reason for doing this is because mkfiles.pl was a horrible
pile of unmaintainable cruft. It was hard to keep up to date (e.g.
didn't reliably support the latest Visual Studio project files); it
was so specific to me that nobody else could maintain it (or was even
interested in trying, and who can blame them?), and it wasn't even
easy to _use_ if you weren't me. And it didn't even produce very good
makefiles.

In fact I've been wanting to hurl mkfiles.pl in the bin for years, but
was blocked by CMake not quite being able to support my clang-cl based
system for cross-compiling for Windows on Linux. But CMake 3.20 was
released this month and fixes the last bug in that area (it had to do
with preprocessing of .rc files), so now I'm unblocked!

CMake is not perfect, but it's better at mkfiles.pl's job than
mkfiles.pl was, and it has the great advantage that lots of other
people already know about it.

Other advantages of the CMake system:

 - Easier to build with. At least for the big three platforms, it's
   possible to write down a list of build commands that's actually the
   same everywhere ("cmake ." followed by "cmake --build ."). There's
   endless scope for making your end-user cmake commands more fancy
   than that, for various advantages, but very few people _have_ to.

 - Less effort required to add a new puzzle. You just add a puzzle()
   statement to the top-level CMakeLists.txt, instead of needing to
   remember eight separate fiddly things to put in the .R file. (Look
   at the reduction in CHECKLST.txt!)

 - The 'unfinished' subdirectory is now _built_ unconditionally, even
   if the things in it don't go into the 'make install' target. So
   they won't bit-rot in future.

 - Unix build: unified the old icons makefile with the main build, so
   that each puzzle builds without an icon, runs to build its icon,
   then relinks with it.

 - Windows build: far easier to switch back and forth between debug
   and release than with the old makefiles.

 - MacOS build: CMake has its own .dmg generator, which is surely
   better thought out than my ten-line bodge.

 - net reduction in the number of lines of code in the code base. In
   fact, that's still true _even_ if you don't count the deletion of
   mkfiles.pl itself - that script didn't even have the virtue of
   allowing everything else to be done exceptionally concisely.

diff: cannot open b/cmake/platforms//null: file does not exist: 'b/cmake/platforms//null' diff: cannot open b/cmake//null: file does not exist: 'b/cmake//null' diff: cannot open b/osx//null: file does not exist: 'b/osx//null'
--- a/.gitignore
+++ b/.gitignore
@@ -93,7 +93,6 @@
 /*.dmg
 /*.res
 /*.inf
-/list.c
 /gamedesc.txt
 /build.log
 /build.out
--- a/Buildscr
+++ b/Buildscr
@@ -9,11 +9,7 @@
 
 set Version $(!builddate).$(vcsid)
 
-# Start by substituting the right version number in configure.ac.
-in puzzles do perl -i~ -pe 's/6.66/$(Version)/' configure.ac
-in puzzles do rm configure.ac~
-
-# And put it into the documentation as a versionid.
+# Put the version number into the documentation as a versionid.
 # use perl to avoid inconsistent behaviour of echo '\v'
 in puzzles do perl -e 'print "\n\\versionid Simon Tatham'\''s Portable Puzzle Collection, version $$ARGV[0]\n"' $(Version) >> puzzles.but
 in puzzles do perl -e 'print "\n\\versionid Simon Tatham'\''s Portable Puzzle Collection, version $$ARGV[0]\n"' $(Version) >> devel.but
@@ -26,36 +22,35 @@
 # icky in principle because it presumes that my version numbers don't
 # need XML escaping, but frankly, if they ever do then I should fix
 # them!)
-in puzzles do perl -i -pe 's/Unidentified build/$(Version)/' osx-info.plist
+in puzzles do perl -i -pe 's/Unidentified build/$(Version)/' osx/Info.plist
 
-ifneq "$(NOICONS)" yes then
-  # First build some local binaries, to run the icon build.
-  in puzzles do perl mkfiles.pl -U CFLAGS='-Wwrite-strings -Werror'
-  in puzzles do make -j$(nproc)
+# The very first thing we do is to make the source archive, before we
+# fill up the build directory with extra files.
+delegate -
+  # Build Windows Help and text versions of the manual for convenience.
+  in puzzles do halibut --winhelp=puzzles.hlp --text=puzzles.txt puzzles.but
+  # Build a text version of the HACKING document.
+  in puzzles do halibut --text=HACKING devel.but
+  # Get rid of some cruft that isn't really useful in a source tarball.
+  in puzzles do rm -f Buildscr CHECKLST.txt .gitignore webpage.pl
+  in . do ln -s puzzles puzzles-$(Version)
+  in . do tar -chzf puzzles-$(Version).tar.gz puzzles-$(Version)
+  return puzzles-$(Version).tar.gz
+enddelegate
 
-  # Now build the screenshots and icons.
-  in puzzles/icons do make web winicons gtkicons -j$(nproc)
-
-  # Destroy the local binaries and autoconf detritus, mostly to avoid
-  # wasting network bandwidth by transferring them to the delegate
-  # servers.
-  in puzzles do make distclean
-
+ifneq "$(NOICONS)" yes then
+  # Run enough of a native Unix build to produce the icons needed for
+  # the Windows builds and the website.
+  in puzzles do cmake -B build-icons .
+  in puzzles/build-icons do make -j$(nproc) icons VERBOSE=1
 endif
 
-# Re-run mkfiles.pl now that it knows the icons are there. (Or for the
-# first time, if we didn't bother building the icons.)
-in puzzles do perl mkfiles.pl
-
-# Rebuild the configure script.
-in puzzles do ./mkauto.sh
-
 ifneq "$(NOMACOS)" yes then
-  # Build the OS X .dmg archive.
+  # Build the OS X binaries and .dmg archive.
   delegate osx
-    in puzzles do make -f Makefile.osx clean
-    in puzzles do make -f Makefile.osx release VER=-DVER=$(Version) XFLAGS='-Wwrite-strings -Werror' -j$(nproc)
-    return puzzles/Puzzles.dmg
+    in puzzles do cmake -B build-osx -DCMAKE_BUILD_TYPE=Release .
+    in puzzles/build-osx do make -j$(nproc) package VERBOSE=1
+    return puzzles/build-osx/Puzzles.dmg
   enddelegate
 endif
 
@@ -63,71 +58,28 @@
   # Build the Windows binaries and installer, and the CHM file.
   in puzzles do make -f Makefile.doc clean
   in puzzles do make -f Makefile.doc -j$(nproc) # build help files for installer
-  in puzzles do mason.pl --args '{"version":"$(Version)","descfile":"gamedesc.txt"}' winwix.mc > puzzles.wxs
-  in puzzles do perl winiss.pl $(Version) gamedesc.txt > puzzles.iss
-  ifneq "$(VISUAL_STUDIO)" "yes" then
-    in puzzles with clangcl64 do mkdir win64 && Platform=x64 make -f Makefile.clangcl BUILDDIR=win64/ VER=-DVER=$(Version) XFLAGS='-Wwrite-strings -Werror' -j$(nproc)
-    in puzzles with clangcl32 do mkdir win32 && Platform=x86 make -f Makefile.clangcl BUILDDIR=win32/ SUBSYSVER=,5.01 VER=-DVER=$(Version) XFLAGS='-Wwrite-strings -Werror' -j$(nproc)
-    # Code-sign the binaries, if the local bob config provides a script
-    # to do so. We assume here that the script accepts an -i option to
-    # provide a 'more info' URL, and an optional -n option to provide a
-    # program name, and that it can take multiple .exe filename
-    # arguments and sign them all in place.
-    ifneq "$(cross_winsigncode)" "" in puzzles do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ win64/*.exe win32/*.exe
-    # Build installers.
-    in puzzles with wixonlinux do candle -arch x64 puzzles.wxs -dWin64=yes -dBindir=win64/ && light -ext WixUIExtension -sval puzzles.wixobj
-    in puzzles with wixonlinux do candle -arch x86 puzzles.wxs -dWin64=no -dBindir=win32/ && light -ext WixUIExtension -sval puzzles.wixobj -o puzzles32.msi
-    ifneq "$(cross_winsigncode)" "" in puzzles do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ -n "Simon Tatham's Portable Puzzle Collection Installer" puzzles.msi puzzles32.msi
-  else
-    delegate windows
-      in puzzles with visualstudio do/win nmake -f Makefile.vc clean
-      in puzzles with visualstudio do/win nmake -f Makefile.vc VER=-DVER=$(Version)
-      ifneq "$(winsigncode)" "" in puzzles do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ win64/*.exe
-      # Build installers.
-      in puzzles with wix do/win candle puzzles.wxs -dWin64=yes -dBindir=win64/  && light -ext WixUIExtension -sval puzzles.wixobj
-      in puzzles with innosetup do/win iscc puzzles.iss
-      return puzzles/win64/*.exe
-      return puzzles/puzzles.msi
-    enddelegate
-  endif
-  in puzzles do chmod +x win32/*.exe win64/*.exe
-endif
 
-# Build the Pocket PC binaries and CAB.
-#
-# NOTE: This part of the build script requires the Windows delegate
-# server to have the cabwiz program on its PATH. This will
-# typically be at
-#
-#   C:\Program Files\Windows CE Tools\WCE420\POCKET PC 2003\Tools
-#
-# but it might not be if you've installed it somewhere else, or
-# have a different version.
-#
-# NOTE ALSO: This part of the build is commented out, for the
-# moment, because cabwiz does unhelpful things when run from within
-# a bob delegate process (or, more generally, when run from any
-# terminal-based remote login to a Windows machine, including
-# Cygwin opensshd and Windows Telnet). The symptom is that cabwiz
-# just beeps and sits there. Until I figure out how to build the
-# .cab from an automated process (and I'm willing to consider silly
-# approaches such as a third-party CAB generator), I don't think I
-# can sensibly enable this build.
+  in puzzles do cmake -B build-win64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_TOOLCHAIN_FILE=$(cmake_toolchain_clangcl64) .
+  in puzzles do cmake -B build-win32 -DCMAKE_BUILD_TYPE=Release -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_TOOLCHAIN_FILE=$(cmake_toolchain_clangcl32) .
+  in puzzles/build-win64 do make -j$(nproc) VERBOSE=1
+  in puzzles/build-win32 do make -j$(nproc) VERBOSE=1
 
-#in puzzles do perl wceinf.pl gamedesc.txt > puzzles.inf
-#delegate windows
-#  in puzzles do cmd /c 'wcearmv4 & nmake -f Makefile.wce clean'
-#  in puzzles do cmd /c 'wcearmv4 & nmake -f Makefile.wce VER=-DVER=$(Version)'
-#  # Nasty piece of sh here which saves the return code from cabwiz,
-#  # outputs its errors and/or warnings, and then propagates the
-#  # return code back to bob. If only cabwiz could output to
-#  # standard error LIKE EVERY OTHER COMMAND-LINE UTILITY IN THE
-#  # WORLD, I wouldn't have to do this.
-#  in puzzles do cat puzzles.inf
-#  in puzzles do cmd /c 'wcearmv4 & bash -c cabwiz puzzles.inf /err cabwiz.err /cpu ARMV4'; a=$$?; cat cabwiz.err; exit $$a
-#  return puzzles/puzzles.armv4.cab
-#enddelegate
+  in puzzles do mason.pl --args '{"version":"$(Version)","descfile":"build-win64/gamedesc.txt"}' winwix.mc > puzzles.wxs
 
+  # Code-sign the binaries, if the local bob config provides a script
+  # to do so. We assume here that the script accepts an -i option to
+  # provide a 'more info' URL, and an optional -n option to provide a
+  # program name, and that it can take multiple .exe filename
+  # arguments and sign them all in place.
+  ifneq "$(cross_winsigncode)" "" in puzzles do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ build-win64/*.exe build-win32/*.exe
+  # Build installers.
+  in puzzles with wixonlinux do candle -arch x64 puzzles.wxs -dWin64=yes -dBindir=build-win64/ && light -ext WixUIExtension -sval puzzles.wixobj
+  in puzzles with wixonlinux do candle -arch x86 puzzles.wxs -dWin64=no -dBindir=build-win32/ && light -ext WixUIExtension -sval puzzles.wixobj -o puzzles32.msi
+  ifneq "$(cross_winsigncode)" "" in puzzles do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ -n "Simon Tatham's Portable Puzzle Collection Installer" puzzles.msi puzzles32.msi
+
+  in puzzles do chmod +x build-win32/*.exe build-win64/*.exe
+endif
+
 # Build the HTML docs.
 in puzzles do mkdir doc
 in puzzles do mkdir devel
@@ -136,12 +88,12 @@
 
 ifneq "$(NOWINDOWS)" yes then
   # Move the deliver-worthy Windows binaries (those specified in
-  # gamedesc.txt, which is generated by mkfiles.pl and helpfully
+  # gamedesc.txt, which is generated by CMakeLists.txt and helpfully
   # excludes the command-line auxiliary utilities such as solosolver,
   # and nullgame.exe) into a subdirectory for easy access.
   in puzzles do mkdir winbin64 winbin32
-  in puzzles/win64 do mv `cut -f2 -d: ../gamedesc.txt` ../winbin64
-  in puzzles/win32 do mv `cut -f2 -d: ../gamedesc.txt` ../winbin32
+  in puzzles/build-win64 do mv `cut -f2 -d: gamedesc.txt` ../winbin64
+  in puzzles/build-win32 do mv `cut -f2 -d: gamedesc.txt` ../winbin32
 
   # Make a zip file of the Windows binaries and help files.
   in puzzles do zip -j puzzles.zip winbin64/*.exe puzzles.chm puzzles.hlp puzzles.cnt
@@ -148,22 +100,22 @@
   in puzzles do zip -j puzzles32.zip winbin32/*.exe puzzles.chm puzzles.hlp puzzles.cnt
 endif
 
-# Create the source archive. (That writes the archive into the
-# _parent_ directory, so be careful when we deliver it.)
-in puzzles do ./makedist.sh $(Version)
-
 # Build the autogenerated pieces of the main web page.
-in puzzles do perl webpage.pl
+in puzzles do cmake -B build-gamedesc . -DCMAKE_TOOLCHAIN_FILE=../cmake/windows-dummy-toolchain.cmake
+in puzzles do perl webpage.pl build-gamedesc/gamedesc.txt
 
-ifneq "$(JAVA_UNFINISHED)" "" in puzzles do perl -i~ -pe 'print "!srcdir unfinished/\n" if /!srcdir icons/' Recipe
-ifneq "$(JAVA_UNFINISHED)" "" in puzzles do ln -s unfinished/group.R .
-ifneq "$(JAVA_UNFINISHED)" "" in puzzles do perl mkfiles.pl
+# Group is playable, even if still a bit unpolished and strange, so we
+# can at least make the web versions of it (which are unobtrusive if
+# you don't deliberately navigate to the web pages).
+set web_unfinished_option -DPUZZLES_ENABLE_UNFINISHED=group
 
 ifneq "$(NOJAVA)" yes then
   # Build the Java applets.
   delegate nestedvm
-    in puzzles do make -f Makefile.nestedvm NESTEDVM="$$NESTEDVM" VER=-DVER=$(Version) XFLAGS="-Wwrite-strings -Werror" -j$(nproc)
-    return puzzles/*.jar
+    in puzzles do cmake -B build-nestedvm -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../cmake/nestedvm-toolchain.cmake -DNESTEDVM="$$NESTEDVM" $(web_unfinished_option) .
+    in puzzles/build-nestedvm do make -j$(nproc) VERBOSE=1
+    return puzzles/build-nestedvm/*.jar
+    return puzzles/build-nestedvm/unfinished/group.jar
   enddelegate
 endif
 
@@ -171,11 +123,11 @@
 # have the right dependencies installed for Emscripten, I do this by a
 # delegation.
 ifneq "$(NOJS)" yes then
-  in puzzles do mkdir js # so we can tell output .js files from emcc*.js
   delegate emscripten
-    in puzzles do make -f Makefile.emcc OUTPREFIX=js/ clean
-    in puzzles do make -f Makefile.emcc OUTPREFIX=js/ XFLAGS="-Wwrite-strings -Werror" -j$(nproc)
-    return puzzles/js/*.js
+    in puzzles do emcmake cmake -B build-emscripten -DCMAKE_BUILD_TYPE=Release $(web_unfinished_option) .
+    in puzzles/build-emscripten do make -j$(nproc) VERBOSE=1
+    return puzzles/build-emscripten/*.js
+    return puzzles/build-emscripten/unfinished/group.js
   enddelegate
 
   # Build a set of wrapping HTML pages for easy testing of the
@@ -198,7 +150,7 @@
 
 # Phew, we're done. Deliver everything!
 ifneq "$(NOICONS)" yes then
-  deliver puzzles/icons/*-web.png $@
+  deliver puzzles/build-icons/icons/*-web.png $@
 endif
 ifneq "$(NOWINDOWS)" yes then
   deliver puzzles/winbin64/*.exe $@
@@ -215,13 +167,15 @@
 deliver puzzles/doc/*.html doc/$@
 deliver puzzles/devel/*.html devel/$@
 ifneq "$(NOMACOS)" yes then
-  deliver puzzles/Puzzles.dmg $@
+  deliver puzzles/build-osx/Puzzles.dmg $@
 endif
 ifneq "$(NOJAVA)" yes then
-  deliver puzzles/*.jar java/$@
+  deliver puzzles/build-nestedvm/*.jar java/$@
+  deliver puzzles/build-nestedvm/unfinished/*.jar java/$@
 endif
 ifneq "$(NOJS)" yes then
-  deliver puzzles/js/*.js js/$@
+  deliver puzzles/build-emscripten/*.js js/$@
+  deliver puzzles/build-emscripten/unfinished/*.js js/$@
   deliver puzzles/jstest/*.html jstest/$@
   deliver puzzles/html/*.html html/$@
   deliver puzzles/html/*.pl html/$@
--- a/CHECKLST.txt
+++ b/CHECKLST.txt
@@ -6,20 +6,9 @@
 
 Write the source file for the new puzzle (duhh).
 
-Create a .R file for it which:
- - defines a <puzzle>_EXTRA symbol for it if it requires auxiliary
-   object files (make sure that symbol doesn't contain the icon)
- - adds it to the `ALL' definition, to ensure it is compiled into
-   the OS X binary
- - adds it as a GTK build target, with the optional GTK icon
- - adds it as a Windows build target, with the optional resource
-   file
- - adds auxiliary solver binaries if any
- - adds it to $(GAMES) in both the automake and GTK makefiles, for
-   `make install'
- - adds it to list.c for the OS X binary
- - adds it to gamedesc.txt, with its Windows executable name, display
-   name, and slightly longer description.
+Write a puzzle() statement in CMakeLists.txt containing all the
+necessary metadata. Optionally also a solver() statement, and any
+cliprogram() statements for extra auxiliary tools.
 
 If the puzzle is by a new author, modify the copyright notice in
 LICENCE and in puzzles.but. (Also in index.html, but that's listed
@@ -47,15 +36,10 @@
 
 Make a screenshot:
  - create an appropriate save file in `icons'
- - add the puzzle name to icons/Makefile
- - set up a REDO property in icons/Makefile if the screenshot wants
-   to display a move halfway through an animation
- - set up a CROP property in icons/Makefile if the icon wants to be
-   a sub-rectangle of the whole screenshot
-
-Don't forget to `git add' the new source file, the new .R file and the
-save file in `icons', the new .html file, and any other new files that
-might have been involved.
+ - define <puzzlename>_redo in icons/icons.cmake if the screenshot
+   wants to display a move halfway through an animation
+ - define <puzzlename>_crop in icons/icons.cmake if the icon wants to
+   be a sub-rectangle of the whole screenshot
 
 Check in!
 
--- /dev/null
+++ b/CMakeLists.txt
@@ -1,0 +1,264 @@
+cmake_minimum_required(VERSION 3.16)
+
+project(puzzles
+  LANGUAGES C)
+
+include(cmake/setup.cmake)
+
+add_library(common
+  combi.c divvy.c drawing.c dsf.c findloop.c grid.c latin.c
+  laydomino.c loopgen.c malloc.c matching.c midend.c misc.c penrose.c
+  ps.c random.c sort.c tdq.c tree234.c version.c
+  ${platform_common_sources})
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+puzzle(blackbox
+  DISPLAYNAME "Black Box"
+  DESCRIPTION "Ball-finding puzzle"
+  OBJECTIVE "Find the hidden balls in the box by bouncing laser beams \
+off them.")
+
+puzzle(bridges
+  DISPLAYNAME "Bridges"
+  DESCRIPTION "Bridge-placing puzzle"
+  OBJECTIVE "Connect all the islands with a network of bridges.")
+
+puzzle(cube
+  DISPLAYNAME "Cube"
+  DESCRIPTION "Rolling cube puzzle"
+  OBJECTIVE "Pick up all the blue squares by rolling the cube over them.")
+
+puzzle(dominosa
+  DISPLAYNAME "Dominosa"
+  DESCRIPTION "Domino tiling puzzle"
+  OBJECTIVE "Tile the rectangle with a full set of dominoes.")
+solver(dominosa)
+
+puzzle(fifteen
+  DISPLAYNAME "Fifteen"
+  DESCRIPTION "Sliding block puzzle"
+  OBJECTIVE "Slide the tiles around to arrange them into order.")
+solver(fifteen)
+
+puzzle(filling
+  DISPLAYNAME "Filling"
+  DESCRIPTION "Polyomino puzzle"
+  OBJECTIVE "Mark every square with the area of its containing region.")
+solver(filling)
+
+puzzle(flip
+  DISPLAYNAME "Flip"
+  DESCRIPTION "Tile inversion puzzle"
+  OBJECTIVE "Flip groups of squares to light them all up at once.")
+
+puzzle(flood
+  DISPLAYNAME "Flood"
+  DESCRIPTION "Flood-filling puzzle"
+  OBJECTIVE "Turn the grid the same colour in as few flood fills as possible.")
+
+puzzle(galaxies
+  DISPLAYNAME "Galaxies"
+  DESCRIPTION "Symmetric polyomino puzzle"
+  OBJECTIVE "Divide the grid into rotationally symmetric regions each \
+centred on a dot.")
+solver(galaxies)
+cliprogram(galaxiespicture galaxies.c
+  COMPILE_DEFINITIONS STANDALONE_PICTURE_GENERATOR)
+
+puzzle(guess
+  DISPLAYNAME "Guess"
+  DESCRIPTION "Combination-guessing puzzle"
+  OBJECTIVE "Guess the hidden combination of colours.")
+
+puzzle(inertia
+  DISPLAYNAME "Inertia"
+  DESCRIPTION "Gem-collecting puzzle"
+  OBJECTIVE "Collect all the gems without running into any of the mines.")
+
+puzzle(keen
+  DISPLAYNAME "Keen"
+  DESCRIPTION "Arithmetic Latin square puzzle"
+  OBJECTIVE "Complete the latin square in accordance with the \
+arithmetic clues.")
+solver(keen latin.c)
+
+puzzle(lightup
+  DISPLAYNAME "Light Up"
+  DESCRIPTION "Light-bulb placing puzzle"
+  OBJECTIVE "Place bulbs to light up all the squares.")
+solver(lightup)
+
+puzzle(loopy
+  DISPLAYNAME "Loopy"
+  DESCRIPTION "Loop-drawing puzzle"
+  OBJECTIVE "Draw a single closed loop, given clues about number of \
+adjacent edges.")
+solver(loopy)
+
+puzzle(magnets
+  DISPLAYNAME "Magnets"
+  DESCRIPTION "Magnet-placing puzzle"
+  OBJECTIVE "Place magnets to satisfy the clues and avoid like poles \
+touching.")
+solver(magnets)
+
+puzzle(map
+  DISPLAYNAME "Map"
+  DESCRIPTION "Map-colouring puzzle"
+  OBJECTIVE "Colour the map so that adjacent regions are never the \
+same colour.")
+solver(map)
+
+puzzle(mines
+  DISPLAYNAME "Mines"
+  DESCRIPTION "Mine-finding puzzle"
+  OBJECTIVE "Find all the mines without treading on any of them.")
+cliprogram(mineobfusc mines.c COMPILE_DEFINITIONS STANDALONE_OBFUSCATOR)
+
+puzzle(net
+  # The Windows Net shouldn't be called 'net.exe', since Windows
+  # already has a reasonably important utility program by that name!
+  WINDOWS_EXE_NAME netgame
+
+  DISPLAYNAME "Net"
+  DESCRIPTION "Network jigsaw puzzle"
+  OBJECTIVE "Rotate each tile to reassemble the network.")
+
+puzzle(netslide
+  DISPLAYNAME "Netslide"
+  DESCRIPTION "Toroidal sliding network puzzle"
+  OBJECTIVE "Slide a row at a time to reassemble the network.")
+
+puzzle(nullgame)
+
+puzzle(palisade
+  DISPLAYNAME "Palisade"
+  DESCRIPTION "Grid-division puzzle"
+  OBJECTIVE "Divide the grid into equal-sized areas in accordance with\
+ the clues.")
+
+puzzle(pattern
+  DISPLAYNAME "Pattern"
+  DESCRIPTION "Pattern puzzle"
+  OBJECTIVE "Fill in the pattern in the grid, given only the lengths \
+of runs of black squares.")
+solver(pattern)
+cliprogram(patternpicture pattern.c
+  COMPILE_DEFINITIONS STANDALONE_PICTURE_GENERATOR)
+
+puzzle(pearl
+  DISPLAYNAME "Pearl"
+  DESCRIPTION "Loop-drawing puzzle"
+  OBJECTIVE "Draw a single closed loop, given clues about corner and \
+straight squares.")
+solver(pearl)
+cliprogram(pearlbench pearl.c COMPILE_DEFINITIONS STANDALONE_SOLVER)
+
+puzzle(pegs
+  DISPLAYNAME "Pegs"
+  DESCRIPTION "Peg solitaire puzzle"
+  OBJECTIVE "Jump pegs over each other to remove all but one.")
+
+puzzle(range
+  DISPLAYNAME "Range"
+  DESCRIPTION "Visible-distance puzzle"
+  OBJECTIVE "Place black squares to limit the visible distance from \
+each numbered cell.")
+
+puzzle(rect
+  DISPLAYNAME "Rectangles"
+  DESCRIPTION "Rectangles puzzle"
+  OBJECTIVE "Divide the grid into rectangles with areas equal to the \
+numbers.")
+
+puzzle(samegame
+  DISPLAYNAME "Same Game"
+  DESCRIPTION "Block-clearing puzzle"
+  OBJECTIVE "Clear the grid by removing touching groups of the same \
+colour squares.")
+
+puzzle(signpost
+  DISPLAYNAME "Signpost"
+  DESCRIPTION "Square-connecting puzzle"
+  OBJECTIVE "Connect the squares into a path following the arrows.")
+solver(signpost)
+
+puzzle(singles
+  DISPLAYNAME "Singles"
+  DESCRIPTION "Number-removing puzzle"
+  OBJECTIVE "Black out the right set of duplicate numbers.")
+solver(singles)
+
+puzzle(sixteen
+  DISPLAYNAME "Sixteen"
+  DESCRIPTION "Toroidal sliding block puzzle"
+  OBJECTIVE "Slide a row at a time to arrange the tiles into order.")
+
+puzzle(slant
+  DISPLAYNAME "Slant"
+  DESCRIPTION "Maze-drawing puzzle"
+  OBJECTIVE "Draw a maze of slanting lines that matches the clues.")
+solver(slant)
+
+puzzle(solo
+  DISPLAYNAME "Solo"
+  DESCRIPTION "Number placement puzzle"
+  OBJECTIVE "Fill in the grid so that each row, column and square \
+block contains one of every digit.")
+solver(solo)
+
+puzzle(tents
+  DISPLAYNAME "Tents"
+  DESCRIPTION "Tent-placing puzzle"
+  OBJECTIVE "Place a tent next to each tree.")
+solver(tents)
+
+puzzle(towers
+  DISPLAYNAME "Towers"
+  DESCRIPTION "Tower-placing Latin square puzzle"
+  OBJECTIVE "Complete the latin square of towers in accordance with \
+the clues.")
+solver(towers latin.c)
+
+puzzle(tracks
+  DISPLAYNAME "Tracks"
+  DESCRIPTION "Path-finding railway track puzzle"
+  OBJECTIVE "Fill in the railway track according to the clues.")
+solver(tracks)
+
+puzzle(twiddle
+  DISPLAYNAME "Twiddle"
+  DESCRIPTION "Rotational sliding block puzzle"
+  OBJECTIVE "Rotate the tiles around themselves to arrange them into order.")
+
+puzzle(undead
+  DISPLAYNAME "Undead"
+  DESCRIPTION "Monster-placing puzzle"
+  OBJECTIVE "Place ghosts, vampires and zombies so that the right \
+numbers of them can be seen in mirrors.")
+
+puzzle(unequal
+  DISPLAYNAME "Unequal"
+  DESCRIPTION "Latin square puzzle"
+  OBJECTIVE "Complete the latin square in accordance with the > signs.")
+solver(unequal latin.c)
+
+puzzle(unruly
+  DISPLAYNAME "Unruly"
+  DESCRIPTION "Black and white grid puzzle"
+  OBJECTIVE "Fill in the black and white grid to avoid runs of three.")
+solver(unruly)
+
+puzzle(untangle
+  DISPLAYNAME "Untangle"
+  DESCRIPTION "Planar graph layout puzzle"
+  OBJECTIVE "Reposition the points so that the lines do not cross.")
+
+add_subdirectory(unfinished)
+
+cliprogram(obfusc obfusc.c)
+cliprogram(latincheck latin.c COMPILE_DEFINITIONS STANDALONE_LATIN_TEST)
+cliprogram(matching matching.c COMPILE_DEFINITIONS STANDALONE_MATCHING_TEST)
+
+build_platform_extras()
--- a/README
+++ b/README
@@ -2,48 +2,12 @@
 puzzle collection. The collection's web site is at
 <https://www.chiark.greenend.org.uk/~sgtatham/puzzles/>.
 
-If you've obtained the source code by downloading a .tar.gz archive
-from the Puzzles web site, you should find several Makefiles in the
-source code. However, if you've checked the source code out from the
-Puzzles git repository, you won't find the Makefiles: they're
-automatically generated by `mkfiles.pl', so run that to create them.
+The puzzle collection is built using CMake <https://cmake.org/>. To
+compile in the simplest way (on any of Linux, Windows or Mac), run
+these commands in the source directory:
 
-The Makefiles include:
-
- - `Makefile.am', together with the static `configure.ac', is intended
-   as input to automake. Run `mkauto.sh' to turn these into a
-   configure script and Makefile.in, after which you can then run
-   `./configure' to create an actual Unix Makefile.
-
- - `Makefile.vc' should work under MS Visual C++ on Windows. Run
-   'nmake /f Makefile.vc' in a Visual Studio command prompt.
-
- - `Makefile.cyg' should work under Cygwin / MinGW. With appropriate
-   tweaks and setting of TOOLPATH, it should work for both compiling
-   on Windows and cross-compiling on Unix.
-
- - `Makefile.osx' should work under Mac OS X, provided the Xcode
-   tools are installed. It builds a single monolithic OS X
-   application capable of running any of the puzzles, or even more
-   than one of them at a time.
-
- - `Makefile.wce' should work under MS eMbedded Visual C++ on
-   Windows and the Pocket PC SDK; it builds Pocket PC binaries.
-
-Many of these Makefiles build a program called `nullgame' in
-addition to the actual game binaries. This program doesn't do
-anything; it's just a template for people to start from when adding
-a new game to the collection, and it's compiled every time to ensure
-that it _does_ compile and link successfully (because otherwise it
-wouldn't be much use as a template). Once it's built, you can run it
-if you really want to (but it's very boring), and then you should
-ignore it.
-
-DO NOT EDIT THE MAKEFILES DIRECTLY, if you plan to send any changes
-back to the maintainer. The makefiles are generated automatically by
-the Perl script `mkfiles.pl' from the file `Recipe' and the various
-.R files. If you need to change the makefiles as part of a patch,
-you should change Recipe, *.R, and/or mkfiles.pl.
+  cmake .
+  cmake --build .
 
 The manual is provided in Windows Help format for the Windows build;
 in text format for anyone who needs it; and in HTML for the Mac OS X
--- a/Recipe
+++ /dev/null
@@ -1,171 +1,0 @@
-# -*- makefile -*-
-# 
-# This file describes which puzzle binaries are made up from which
-# object and resource files. It is processed into the various
-# Makefiles by means of a Perl script. Makefile changes should
-# really be made by editing this file and/or the Perl script, not
-# by editing the actual Makefiles.
-
-!name puzzles
-
-!makefile gtk Makefile.gtk
-!makefile am Makefile.am
-!makefile vc Makefile.vc
-!makefile wce Makefile.wce
-!makefile cygwin Makefile.cyg
-!makefile osx Makefile.osx
-!makefile gnustep Makefile.gnustep
-!makefile nestedvm Makefile.nestedvm
-!makefile emcc Makefile.emcc
-!makefile clangcl Makefile.clangcl
-
-!srcdir icons/
-
-WINDOWS_COMMON = printing
-         + user32.lib gdi32.lib comctl32.lib comdlg32.lib winspool.lib
-WINDOWS  = windows WINDOWS_COMMON
-COMMON   = midend drawing misc malloc random version
-GTK      = gtk printing ps
-# Objects needed for auxiliary command-line programs.
-STANDALONE = nullfe random misc malloc
-
-ALL      = list
-
-LATIN_DEPS   = matching tree234
-LATIN        = latin LATIN_DEPS
-LATIN_SOLVER = latin[STANDALONE_SOLVER] LATIN_DEPS
-
-# First half of list.c.
-!begin >list.c
-/*
- * list.c: List of pointers to puzzle structures, for monolithic
- * platforms.
- *
- * This file is automatically generated by mkfiles.pl. Do not edit
- * it directly, or the changes will be lost next time mkfiles.pl runs.
- * Instead, edit Recipe and/or its *.R subfiles.
- */
-#include "puzzles.h"
-#define GAMELIST(A) \
-!end
-
-# Now each .R file adds part of the macro definition of GAMELIST to list.c.
-!include *.R
-
-# Then we finish up list.c as follows:
-!begin >list.c
-
-#define DECL(x) extern const game x;
-#define REF(x) &x,
-GAMELIST(DECL)
-const game *gamelist[] = { GAMELIST(REF) };
-const int gamecount = lenof(gamelist);
-!end
-
-# Unix standalone application for special-purpose obfuscation.
-obfusc : [U] obfusc STANDALONE
-
-# Test program built from latin.c.
-latincheck : [U] latin[STANDALONE_LATIN_TEST] LATIN_DEPS STANDALONE
-latincheck : [C] latin[STANDALONE_LATIN_TEST] LATIN_DEPS STANDALONE
-
-# Test program built from matching.c.
-matching : [U] matching[STANDALONE_MATCHING_TEST] tree234 STANDALONE
-matching : [C] matching[STANDALONE_MATCHING_TEST] tree234 STANDALONE
-
-puzzles  : [G] windows[COMBINED] WINDOWS_COMMON COMMON ALL noicon.res
-
-# Mac OS X unified application containing all the puzzles.
-Puzzles  : [MX] osx osx.icns osx-info.plist COMMON ALL
-# For OS X, we must create the online help and include it in the
-# application bundle.) Also we add -DCOMBINED to the compiler flags
-# so as to inform the code that we're building a single binary for
-# all the puzzles. Then I've also got some code in here to build a
-# distributable .dmg disk image.
-!begin osx
-Puzzles_extra = Puzzles.app/Contents/Resources/Help/index.html
-Puzzles.app/Contents/Resources/Help/index.html: \
-	Puzzles.app/Contents/Resources/Help osx-help.but puzzles.but
-	cd Puzzles.app/Contents/Resources/Help; \
-		halibut --html ../../../../osx-help.but ../../../../puzzles.but
-Puzzles.app/Contents/Resources/Help: Puzzles.app/Contents/Resources
-	mkdir -p Puzzles.app/Contents/Resources/Help
-
-release: Puzzles.dmg
-Puzzles.dmg: Puzzles
-	rm -f raw.dmg
-	hdiutil create -megabytes 5 -layout NONE raw.dmg
-	hdid -nomount raw.dmg > devicename
-	newfs_hfs -v "Simon Tatham's Puzzle Collection" `cat devicename`
-	hdiutil eject `cat devicename`
-	hdid raw.dmg | cut -f1 -d' ' > devicename
-	cp -R Puzzles.app /Volumes/"Simon Tatham's Puzzle Collection"
-	hdiutil eject `cat devicename`
-	rm -f Puzzles.dmg
-	hdiutil convert -format UDCO raw.dmg -o Puzzles.dmg
-	rm -f raw.dmg devicename
-!end
-
-!begin am
-bin_PROGRAMS = $(GAMES)
-!end
-!begin am_begin
-GAMES =
-!end
-
-# make install for Unix.
-!begin gtk
-install:
-	for i in $(GAMES); do \
-		$(INSTALL_PROGRAM) -m 755 $(BINPREFIX)$$i $(DESTDIR)$(gamesdir)/$(BINPREFIX)$$i \
-		|| exit 1; \
-	done
-!end
-!begin nestedvm
-%.tmpdir/PuzzleEngine.class: %.mips
-	mkdir -p $(patsubst %.mips,%,$<).tmpdir
-	cd $(patsubst %.mips,%,$<).tmpdir && \
-		java -cp $(NESTEDVM)/build:$(NESTEDVM)/upstream/build/classgen/build \
-		org.ibex.nestedvm.Compiler -outformat class -d . \
-		PuzzleEngine ../$<
-
-org:
-	mkdir -p org/ibex/nestedvm/util
-	cp $(NESTEDVM)/build/org/ibex/nestedvm/Registers.class org/ibex/nestedvm
-	cp $(NESTEDVM)/build/org/ibex/nestedvm/UsermodeConstants.class org/ibex/nestedvm
-	cp $(NESTEDVM)/build/org/ibex/nestedvm/Runtime*.class org/ibex/nestedvm
-	cp $(NESTEDVM)/build/org/ibex/nestedvm/util/Platform*.class org/ibex/nestedvm/util
-	cp $(NESTEDVM)/build/org/ibex/nestedvm/util/Seekable*.class org/ibex/nestedvm/util
-
-applet.manifest:
-	echo "Main-Class: PuzzleApplet" >applet.manifest
-
-PuzzleApplet.class: PuzzleApplet.java org
-	javac -source 1.7 -target 1.7 PuzzleApplet.java
-
-%.jar: %.tmpdir/PuzzleEngine.class PuzzleApplet.class applet.manifest org
-	cd $(patsubst %.jar,%,$@).tmpdir && ln -s ../applet.manifest ../org ../PuzzleApplet*.class .
-	cd $(patsubst %.jar,%,$@).tmpdir && jar cfm ../$@ applet.manifest PuzzleEngine.class PuzzleApplet*.class org
-	echo '<applet archive="'$@'" code="PuzzleApplet" width="700" height="500"></applet>' >$*.html
-!end
-
-# A benchmarking and testing target for the GTK puzzles.
-!begin gtk
-test: benchmark.html benchmark.txt
-
-benchmark.html: benchmark.txt benchmark.pl
-	./benchmark.pl benchmark.txt > $@
-
-benchmark.txt: benchmark.sh $(GAMES)
-	./benchmark.sh > $@
-
-!end
-!begin am
-test: benchmark.html benchmark.txt
-
-benchmark.html: benchmark.txt benchmark.pl
-	./benchmark.pl benchmark.txt > $@
-
-benchmark.txt: benchmark.sh $(GAMES)
-	./benchmark.sh > $@
-!end
--- a/blackbox.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-blackbox : [X] GTK COMMON blackbox blackbox-icon|no-icon
-
-blackbox : [G] WINDOWS COMMON blackbox blackbox.res|noicon.res
-
-ALL += blackbox[COMBINED]
-
-!begin am gtk
-GAMES += blackbox
-!end
-
-!begin >list.c
-    A(blackbox) \
-!end
-
-!begin >gamedesc.txt
-blackbox:blackbox.exe:Black Box:Ball-finding puzzle:Find the hidden balls in the box by bouncing laser beams off them.
-!end
--- a/bridges.R
+++ /dev/null
@@ -1,21 +1,0 @@
-# -*- makefile -*-
-
-BRIDGES_EXTRA = dsf findloop
-
-bridges  : [X] GTK COMMON bridges BRIDGES_EXTRA bridges-icon|no-icon
-
-bridges  : [G] WINDOWS COMMON bridges BRIDGES_EXTRA bridges.res|noicon.res
-
-ALL += bridges[COMBINED] BRIDGES_EXTRA
-
-!begin am gtk
-GAMES += bridges
-!end
-
-!begin >list.c
-    A(bridges) \
-!end
-
-!begin >gamedesc.txt
-bridges:bridges.exe:Bridges:Bridge-placing puzzle:Connect all the islands with a network of bridges.
-!end
--- /dev/null
+++ b/cmake/glob-symlinks.py
@@ -1,0 +1,65 @@
+#!/usr/bin/env python3
+
+# Helper script used by the NestedVM cmake build script.
+#
+# Usage:   glob-symlinks.py <srcdir> <wildcard> [<srcdir> <wildcard> ...]
+#
+# Each pair of command-line arguments is treated as a source
+# directory, followed by either a single filename or a wildcard.
+#
+# The result is to create symlinks in the program's working directory
+# mirroring all the files matched by the filenames/wildcards, each
+# pointing at the appropriate source directory.
+#
+# For example, this command
+#   glob-symlinks.py /foo \*.txt /bar wibble.blah
+# might create symlinks as follows:
+#   this.txt -> /foo/this.txt
+#   that.txt -> /foo/that.txt
+#   wibble.blah -> /bar/wibble.blah
+#
+# CMake could mostly do this itself, except that some of the files
+# that need symlinking during the NestedVM build (to make a tree that
+# we archive up into a .jar file) are Java class files with some
+# '$suffix' in the name, and CMake doesn't escape the $ signs, so that
+# the suffix vanishes during shell expansion.
+
+import sys
+import os
+import glob
+
+def get_arg_pairs():
+    args = iter(sys.argv)
+    next(args) # skip program name
+    while True:
+        try:
+            yield next(args), next(args)
+        except StopIteration:
+            break
+
+def get_globbed_pairs():
+    for srcdir, pattern in get_arg_pairs():
+        if glob.escape(pattern) == pattern:
+            # Assume that unglobbed filenames exist
+            #print("non-glob:", srcdir, pattern)
+            yield srcdir, pattern
+        else:
+            #print("globbing:", srcdir, pattern)
+            prefix = srcdir + "/"
+            for filename in glob.iglob(prefix + pattern):
+                assert filename.startswith(prefix)
+                filename = filename[len(prefix):]
+                #print("  ->", srcdir, filename)
+                yield srcdir, filename
+
+for srcdir, filename in get_globbed_pairs():
+    dirname = os.path.dirname(filename)
+    if len(dirname) > 0:
+        try:
+            os.makedirs(dirname)
+        except FileExistsError:
+            pass
+    try:
+        os.symlink(os.path.join(srcdir, filename), filename)
+    except FileExistsError:
+        pass
--- /dev/null
+++ b/cmake/nestedvm-toolchain.cmake
@@ -1,0 +1,10 @@
+SET(CMAKE_SYSTEM_NAME NestedVM)
+SET(CMAKE_SYSTEM_PROCESSOR mips)
+
+SET(CMAKE_C_COMPILER ${NESTEDVM}/upstream/install/bin/mips-unknown-elf-gcc)
+
+SET(CMAKE_FIND_ROOT_PATH ${NESTEDVM}/upstream/install)
+SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
--- /dev/null
+++ b/cmake/platforms/emscripten.cmake
@@ -1,0 +1,47 @@
+set(platform_common_sources emcc.c)
+set(platform_gui_libs)
+set(platform_libs)
+set(CMAKE_EXECUTABLE_SUFFIX ".js")
+
+set(emcc_export_list
+  # Event handlers for mouse and keyboard input
+  _mouseup
+  _mousedown
+  _mousemove
+  _key
+  # Callback when the program activates timing
+  _timer_callback
+  # Callback from button presses in the UI outside the canvas
+  _command
+  # Game-saving and game-loading functions
+  _get_save_file
+  _free_save_file
+  _load_game
+  # Callbacks to return values from dialog boxes
+  _dlg_return_sval
+  _dlg_return_ival
+  # Callbacks when the resizing controls are used
+  _resize_puzzle
+  _restore_puzzle_size
+  # Main program, run at initialisation time
+  _main)
+
+list(TRANSFORM emcc_export_list PREPEND \")
+list(TRANSFORM emcc_export_list APPEND \")
+string(JOIN "," emcc_export_string ${emcc_export_list})
+set(CMAKE_C_LINK_FLAGS "-s ASM_JS=1 -s EXPORTED_FUNCTIONS='[${emcc_export_string}]'")
+message("link=${CMAKE_C_LINK_EXECUTABLE}")
+
+set(build_cli_programs FALSE)
+
+function(get_platform_puzzle_extra_source_files OUTVAR NAME)
+  set(${OUTVAR} PARENT_SCOPE)
+endfunction()
+
+function(set_platform_puzzle_target_properties NAME TARGET)
+  em_link_pre_js(${TARGET} ${CMAKE_SOURCE_DIR}/emccpre.js)
+  em_link_js_library(${TARGET} ${CMAKE_SOURCE_DIR}/emcclib.js)
+endfunction()
+
+function(build_platform_extras)
+endfunction()
--- /dev/null
+++ b/cmake/platforms/nestedvm.cmake
@@ -1,0 +1,60 @@
+set(platform_common_sources nestedvm.c printing.c)
+set(platform_libs -lm)
+
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/applet.manifest
+  "Main-Class: PuzzleApplet\n")
+
+include(FindJava)
+add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/PuzzleApplet.class
+  COMMAND ${Java_JAVAC_EXECUTABLE}
+    -source 1.7 -target 1.7 -d . -cp ${NESTEDVM}/build
+    ${CMAKE_SOURCE_DIR}/PuzzleApplet.java
+  DEPENDS ${CMAKE_SOURCE_DIR}/PuzzleApplet.java)
+
+function(get_platform_puzzle_extra_source_files OUTVAR NAME)
+  set(${OUTVAR} PARENT_SCOPE)
+endfunction()
+
+function(set_platform_puzzle_target_properties NAME TARGET)
+  set(build_subdir ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-tmp)
+
+  add_custom_command(OUTPUT ${build_subdir}
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${build_subdir})
+  add_custom_command(OUTPUT ${build_subdir}/PuzzleApplet.class
+    COMMAND ${CMAKE_SOURCE_DIR}/cmake/glob-symlinks.py
+      ${CMAKE_BINARY_DIR} applet.manifest
+      ${CMAKE_BINARY_DIR} PuzzleApplet\\*.class
+      ${NESTEDVM}/build org/ibex/nestedvm/Registers.class
+      ${NESTEDVM}/build org/ibex/nestedvm/UsermodeConstants.class
+      ${NESTEDVM}/build org/ibex/nestedvm/Runtime*.class
+      ${NESTEDVM}/build org/ibex/nestedvm/util/Platform\\*.class
+      ${NESTEDVM}/build org/ibex/nestedvm/util/Seekable\\*.class
+    WORKING_DIRECTORY ${build_subdir}
+    DEPENDS
+      ${build_subdir}
+      ${CMAKE_BINARY_DIR}/PuzzleApplet.class
+      ${CMAKE_SOURCE_DIR}/cmake/glob-symlinks.py)
+
+  add_custom_command(OUTPUT ${build_subdir}/PuzzleEngine.class
+    COMMAND ${Java_JAVA_EXECUTABLE}
+      -cp ${NESTEDVM}/build:${NESTEDVM}/upstream/build/classgen/build
+      org.ibex.nestedvm.Compiler -outformat class -d .
+      PuzzleEngine ${CMAKE_CURRENT_BINARY_DIR}/${EXENAME}
+    DEPENDS
+      ${build_subdir}
+      ${CMAKE_CURRENT_BINARY_DIR}/${EXENAME}
+    WORKING_DIRECTORY ${build_subdir})
+
+  add_custom_target(${TARGET}-jar ALL
+    COMMAND ${Java_JAR_EXECUTABLE}
+      cfm ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.jar
+      applet.manifest PuzzleEngine.class PuzzleApplet*.class org
+      WORKING_DIRECTORY ${build_subdir}
+    DEPENDS
+      ${CMAKE_BINARY_DIR}/PuzzleApplet.class
+      ${build_subdir}/PuzzleApplet.class
+      ${build_subdir}/PuzzleEngine.class)
+endfunction()
+
+function(build_platform_extras)
+endfunction()
--- /dev/null
+++ b/cmake/platforms/osx.cmake
@@ -1,0 +1,58 @@
+set(CMAKE_OSX_DEPLOYMENT_TARGET 10.6)
+find_program(HALIBUT halibut REQUIRED)
+set(CPACK_GENERATOR DragNDrop)
+set(CPACK_PACKAGE_FILE_NAME Puzzles)
+set(CPACK_DMG_VOLUME_NAME "Simon Tatham's Puzzle Collection")
+include(CPack)
+set(build_individual_puzzles FALSE)
+
+function(get_platform_puzzle_extra_source_files OUTVAR NAME)
+  set(${OUTVAR} PARENT_SCOPE)
+endfunction()
+
+function(set_platform_puzzle_target_properties NAME TARGET)
+endfunction()
+
+function(build_platform_extras)
+  write_generated_games_header()
+
+  set(resources
+    ${CMAKE_CURRENT_SOURCE_DIR}/osx/Puzzles.icns)
+  set_source_files_properties(${resources} PROPERTIES
+    MACOSX_PACKAGE_LOCATION Resources)
+
+  add_executable(puzzles MACOSX_BUNDLE
+    osx.m list.c ${puzzle_sources}
+    ${resources})
+
+  set_target_properties(puzzles PROPERTIES
+    OUTPUT_NAME Puzzles
+    MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/osx/Info.plist)
+
+  target_compile_definitions(puzzles PRIVATE COMBINED)
+  target_include_directories(puzzles PRIVATE ${generated_include_dir})
+  target_link_libraries(puzzles common ${platform_gui_libs} ${platform_libs}
+    "-framework Cocoa")
+
+  get_property(bundle_basename TARGET puzzles PROPERTY OUTPUT_NAME)
+  set(help_dir ${CMAKE_CURRENT_BINARY_DIR}/${bundle_basename}.app/Contents/Resources/Help)
+  message(${help_dir})
+  add_custom_command(OUTPUT ${help_dir}
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${help_dir}
+    DEPENDS puzzles)
+  add_custom_command(OUTPUT ${help_dir}/index.html
+    COMMAND ${HALIBUT} --html
+    ${CMAKE_CURRENT_SOURCE_DIR}/osx-help.but
+    ${CMAKE_CURRENT_SOURCE_DIR}/puzzles.but
+    DEPENDS
+    ${help_dir}
+    ${CMAKE_CURRENT_SOURCE_DIR}/osx-help.but
+    ${CMAKE_CURRENT_SOURCE_DIR}/puzzles.but
+    WORKING_DIRECTORY ${help_dir})
+  add_custom_target(osx_help ALL
+    DEPENDS ${help_dir}/index.html)
+
+  install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Puzzles.app
+    USE_SOURCE_PERMISSIONS
+    DESTINATION .)
+endfunction()
--- /dev/null
+++ b/cmake/platforms/unix.cmake
@@ -1,0 +1,68 @@
+find_package(PkgConfig REQUIRED)
+
+set(PUZZLES_GTK_FOUND FALSE)
+macro(try_gtk_package VER PACKAGENAME)
+  if(NOT PUZZLES_GTK_FOUND AND
+      (NOT DEFINED PUZZLES_GTK_VERSION OR
+        PUZZLES_GTK_VERSION STREQUAL ${VER}))
+    pkg_check_modules(GTK ${PACKAGENAME})
+    if(GTK_FOUND)
+      set(PUZZLES_GTK_FOUND TRUE)
+    endif()
+  endif()
+endmacro()
+
+try_gtk_package(3 gtk+-3.0)
+try_gtk_package(2 gtk+-2.0)
+
+if(NOT PUZZLES_GTK_FOUND)
+  message(FATAL_ERROR "Unable to find any usable version of GTK.")
+endif()
+
+include_directories(${GTK_INCLUDE_DIRS})
+link_directories(${GTK_LIBRARY_DIRS})
+
+set(platform_common_sources gtk.c printing.c)
+set(platform_gui_libs ${GTK_LIBRARIES})
+
+set(platform_libs -lm)
+
+set(build_icons TRUE)
+
+function(try_append_cflag flag)
+  set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}")
+  try_compile(compile_passed ${CMAKE_BINARY_DIR}
+    SOURCES ${CMAKE_SOURCE_DIR}/cmake/testbuild.c
+    OUTPUT_VARIABLE test_compile_output
+    CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${GTK_INCLUDE_DIRS}")
+  if(compile_passed)
+    set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} PARENT_SCOPE)
+  endif()
+endfunction()
+if (CMAKE_C_COMPILER_ID MATCHES "GNU" OR
+    CMAKE_C_COMPILER_ID MATCHES "Clang")
+  try_append_cflag(-Wall)
+  try_append_cflag(-Werror)
+  try_append_cflag(-std=c89)
+  try_append_cflag(-pedantic)
+  try_append_cflag(-Wwrite-strings)
+endif()
+
+function(get_platform_puzzle_extra_source_files OUTVAR NAME)
+  if(build_icons AND EXISTS ${CMAKE_SOURCE_DIR}/icons/${NAME}.sav)
+    build_icon(${NAME})
+    set(c_icon_file ${CMAKE_BINARY_DIR}/icons/${NAME}-icon.c)
+  else()
+    set(c_icon_file ${CMAKE_SOURCE_DIR}/no-icon.c)
+  endif()
+
+  set(${OUTVAR} ${c_icon_file} PARENT_SCOPE)
+endfunction()
+
+function(set_platform_puzzle_target_properties NAME TARGET)
+  install(TARGETS ${TARGET})
+endfunction()
+
+function(build_platform_extras)
+endfunction()
--- /dev/null
+++ b/cmake/platforms/windows.cmake
@@ -1,0 +1,40 @@
+set(platform_common_sources windows.c printing.c)
+
+set(platform_gui_libs
+  user32.lib gdi32.lib comctl32.lib comdlg32.lib winspool.lib)
+
+set(platform_libs)
+
+add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
+
+function(get_platform_puzzle_extra_source_files OUTVAR NAME)
+  set(${OUTVAR} ${CMAKE_SOURCE_DIR}/puzzles.rc PARENT_SCOPE)
+endfunction()
+
+function(set_platform_puzzle_target_properties NAME TARGET)
+  if(DEFINED ICO_DIR AND EXISTS ${ICO_DIR}/${NAME}.ico)
+    target_compile_definitions(${TARGET} PRIVATE ICON_FILE=\"${ICO_DIR}/${NAME}.ico\")
+  endif()
+  set_target_properties(${TARGET} PROPERTIES WIN32_EXECUTABLE ON)
+endfunction()
+
+function(build_platform_extras)
+  write_generated_games_header()
+
+  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/gamedesc.txt "")
+  list(SORT puzzle_names)
+  foreach(name ${puzzle_names})
+    file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/gamedesc.txt "\
+${name}:\
+${exename_${name}}.exe:\
+${displayname_${name}}:\
+${description_${name}}:\
+${objective_${name}}\n")
+  endforeach()
+
+  add_executable(puzzles windows.c list.c ${puzzle_sources})
+  target_compile_definitions(puzzles PRIVATE COMBINED)
+  target_include_directories(puzzles PRIVATE ${generated_include_dir})
+  target_link_libraries(puzzles common ${platform_gui_libs} ${platform_libs})
+  set_target_properties(puzzles PROPERTIES WIN32_EXECUTABLE ON)
+endfunction()
--- /dev/null
+++ b/cmake/setup.cmake
@@ -1,0 +1,133 @@
+set(build_individual_puzzles TRUE)
+set(build_cli_programs TRUE)
+set(build_icons FALSE)
+set(need_c_icons FALSE)
+
+# Include one of platforms/*.cmake to define platform-specific stuff.
+# Each of these is expected to:
+#  - define get_platform_puzzle_extra_source_files(), used below
+#  - define set_platform_puzzle_target_properties(), used below
+#  - define build_platform_extras(), called from the top-level CMakeLists.txt
+#  - override the above build_* settings, if necessary
+if(CMAKE_SYSTEM_NAME MATCHES "Windows")
+  include(cmake/platforms/windows.cmake)
+elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
+  include(cmake/platforms/osx.cmake)
+elseif(CMAKE_SYSTEM_NAME MATCHES "NestedVM")
+  include(cmake/platforms/nestedvm.cmake)
+elseif(CMAKE_C_COMPILER MATCHES "emcc")
+  include(cmake/platforms/emscripten.cmake)
+else() # assume Unix
+  include(cmake/platforms/unix.cmake)
+endif()
+
+# Accumulate lists of the puzzles' bare names and source file
+# locations, for use in build_platform_extras() implementations when
+# they want to build things based on all the puzzles at once.
+set(puzzle_names)
+set(puzzle_sources)
+
+include(icons/icons.cmake)
+
+# The main function called from the top-level CMakeLists.txt to define
+# each puzzle.
+function(puzzle NAME)
+  cmake_parse_arguments(OPT
+    "" "DISPLAYNAME;DESCRIPTION;OBJECTIVE;WINDOWS_EXE_NAME" "" ${ARGN})
+
+  if(NOT DEFINED OPT_WINDOWS_EXE_NAME)
+    set(OPT_WINDOWS_EXE_NAME ${NAME})
+  endif()
+
+  if (CMAKE_SYSTEM_NAME MATCHES "Windows")
+    set(EXENAME ${OPT_WINDOWS_EXE_NAME})
+  else()
+    set(EXENAME ${NAME})
+  endif()
+
+  set(exename_${NAME} ${EXENAME} PARENT_SCOPE)
+  set(displayname_${NAME} ${OPT_DISPLAYNAME} PARENT_SCOPE)
+  set(description_${NAME} ${OPT_DESCRIPTION} PARENT_SCOPE)
+  set(objective_${NAME} ${OPT_OBJECTIVE} PARENT_SCOPE)
+
+  set(official TRUE)
+  if(NAME STREQUAL nullgame)
+    # nullgame is not a playable puzzle; it has to be built (to prove
+    # it still can build), but not installed, or included in the main
+    # list of puzzles, or compiled into all-in-one binaries, etc. In
+    # other words, it's not "officially" part of the puzzle
+    # collection.
+    set(official FALSE)
+  endif()
+  if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_SOURCE_DIR}/unfinished)
+    # The same goes for puzzles in the 'unfinished' subdirectory,
+    # although we make an exception if configured to on the command
+    # line.
+    list(FIND PUZZLES_ENABLE_UNFINISHED ${NAME} enable_this_one)
+    if(enable_this_one EQUAL -1)
+      set(official FALSE)
+    endif()
+  endif()
+
+  if (official)
+    set(puzzle_names ${puzzle_names} ${NAME} PARENT_SCOPE)
+    set(puzzle_sources ${puzzle_sources} ${CMAKE_CURRENT_SOURCE_DIR}/${NAME}.c PARENT_SCOPE)
+  endif()
+
+  get_platform_puzzle_extra_source_files(extra_files ${NAME})
+
+  if (build_individual_puzzles)
+    add_executable(${EXENAME} ${NAME}.c ${extra_files})
+    target_link_libraries(${EXENAME}
+      common ${platform_gui_libs} ${platform_libs})
+    set_platform_puzzle_target_properties(${NAME} ${EXENAME})
+  endif()
+endfunction()
+
+# The main function called from the top-level CMakeLists.txt to define
+# a command-line helper tool.
+function(cliprogram NAME)
+  cmake_parse_arguments(OPT
+    "" "COMPILE_DEFINITIONS" "" ${ARGN})
+
+  if(build_cli_programs)
+    add_executable(${NAME} ${CMAKE_SOURCE_DIR}/nullfe.c
+      ${OPT_UNPARSED_ARGUMENTS})
+    target_link_libraries(${NAME} common ${platform_libs})
+    if(OPT_COMPILE_DEFINITIONS)
+      target_compile_definitions(${NAME} PRIVATE ${OPT_COMPILE_DEFINITIONS})
+    endif()
+  endif()
+endfunction()
+
+# A small wrapper around cliprogram, taking advantage of the common
+# formula that puzzle 'foo' often comes with 'foosolver'.
+function(solver NAME)
+  cliprogram(${NAME}solver ${puzzle_src_prefix}${NAME}.c ${ARGN}
+    COMPILE_DEFINITIONS STANDALONE_SOLVER)
+endfunction()
+
+function(write_generated_games_header)
+  set(generated_include_dir ${CMAKE_CURRENT_BINARY_DIR}/include)
+  set(generated_include_dir ${generated_include_dir} PARENT_SCOPE)
+
+  file(MAKE_DIRECTORY ${generated_include_dir})
+  file(WRITE ${generated_include_dir}/generated-games.h "")
+  list(SORT puzzle_names)
+  foreach(name ${puzzle_names})
+    file(APPEND ${generated_include_dir}/generated-games.h "GAME(${name})\n")
+  endforeach()
+endfunction()
+
+# This has to be run from the unfinished subdirectory, so that the
+# updates to puzzle_names etc will be propagated to the top-level scope.
+macro(export_variables_to_parent_scope)
+  set(puzzle_names ${puzzle_names} PARENT_SCOPE)
+  set(puzzle_sources ${puzzle_sources} PARENT_SCOPE)
+  foreach(name ${puzzle_names})
+    set(exename_${name} ${exename_${name}} PARENT_SCOPE)
+    set(displayname_${name} ${displayname_${name}} PARENT_SCOPE)
+    set(description_${name} ${description_${name}} PARENT_SCOPE)
+    set(objective_${name} ${objective_${name}} PARENT_SCOPE)
+  endforeach()
+endmacro()
--- /dev/null
+++ b/cmake/testbuild.c
@@ -1,0 +1,23 @@
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int dummy;
--- /dev/null
+++ b/cmake/windows-dummy-toolchain.cmake
@@ -1,0 +1,14 @@
+# Fake CMake toolchain file, good enough to make cmake's initial
+# configuration think it's going to build for Windows, but not good
+# enough to actually do any building. The purpose is so that I can run
+# Puzzles's CMakeLists.txt in Windows mode as far as making
+# gamedesc.txt.
+
+set(CMAKE_SYSTEM_NAME Windows)
+
+set(CMAKE_C_COMPILER /bin/false)
+set(CMAKE_LINKER /bin/false)
+set(CMAKE_MT /bin/false)
+set(CMAKE_RC_COMPILER /bin/false)
+
+set(CMAKE_C_COMPILER_WORKS ON)
--- a/configure.ac
+++ /dev/null
@@ -1,85 +1,0 @@
-dnl Configure script for the Unix GTK build of puzzles.
-
-AC_INIT([puzzles], [6.66], [anakin@pobox.com])
-AC_CONFIG_SRCDIR([midend.c])
-AM_INIT_AUTOMAKE([foreign])
-AC_PROG_CC
-
-AC_ARG_WITH([gtk],
-  [AS_HELP_STRING([--with-gtk=VER],
-                  [specify GTK version to use (`2' or `3')])],
-  [gtk_version_desired="$withval"],
-  [gtk_version_desired="any"])
-
-case "$gtk_version_desired" in
-  2 | 3 | any) ;;
-  yes) gtk_version_desired="any" ;;
-  *) AC_ERROR([Invalid GTK version specified])
-esac
-
-gtk=none
-
-case "$gtk_version_desired:$gtk" in
-  3:none | any:none)
-    ifdef([AM_PATH_GTK_3_0],[
-    AM_PATH_GTK_3_0([3.0.0], [gtk=3], [])
-    ],[AC_WARNING([generating configure script without GTK 3 autodetection])])
-    ;;
-esac
-
-case "$gtk_version_desired:$gtk" in
-  2:none | any:none)
-    ifdef([AM_PATH_GTK_2_0],[
-    AM_PATH_GTK_2_0([2.0.0], [gtk=2], [])
-    ],[AC_WARNING([generating configure script without GTK 2 autodetection])])
-    ;;
-esac
-
-if test "$gtk" = "none"; then
-   AC_MSG_ERROR([cannot build without GTK 2 or GTK 3])
-fi
-
-if test "x$GCC" = "xyes"; then
-  AC_MSG_CHECKING([for usable gcc warning flags])
-  gccwarningflags=
-  for flag in -Wall -Werror -std=c89 -pedantic; do
-    ac_save_CFLAGS="$CFLAGS"
-    ac_save_LIBS="$LIBS"
-    CFLAGS="$CFLAGS$gccwarningflags $flag $GTK_CFLAGS"
-    LIBS="$GTK_LIBS $LIBS"
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
-        #include <stdio.h>
-        #include <assert.h>
-        #include <stdlib.h>
-        #include <time.h>
-        #include <stdarg.h>
-        #include <string.h>
-        #include <errno.h>
-        #include <math.h>
-
-        #include <sys/time.h>
-        #include <sys/resource.h>
-
-        #include <gtk/gtk.h>
-        #include <gdk/gdkkeysyms.h>
-
-        #include <gdk-pixbuf/gdk-pixbuf.h>
-
-        #include <gdk/gdkx.h>
-        #include <X11/Xlib.h>
-        #include <X11/Xutil.h>
-        #include <X11/Xatom.h>
-    ],[
-        return 0;
-    ])], [gccwarningflags="$gccwarningflags $flag"], [])
-    CFLAGS="$ac_save_CFLAGS"
-    LIBS="$ac_save_LIBS"
-  done
-  AC_MSG_RESULT($gccwarningflags)
-  CFLAGS="$CFLAGS$gccwarningflags"
-fi
-
-AC_PROG_RANLIB
-AC_PROG_INSTALL
-AC_CONFIG_FILES([Makefile])
-AC_OUTPUT
--- a/cube.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-cube     : [X] GTK COMMON cube cube-icon|no-icon
-
-cube     : [G] WINDOWS COMMON cube cube.res|noicon.res
-
-ALL += cube[COMBINED]
-
-!begin am gtk
-GAMES += cube
-!end
-
-!begin >list.c
-    A(cube) \
-!end
-
-!begin >gamedesc.txt
-cube:cube.exe:Cube:Rolling cube puzzle:Pick up all the blue squares by rolling the cube over them.
-!end
--- a/devel.but
+++ b/devel.but
@@ -4441,15 +4441,11 @@
 you'll gradually add functionality until the very boring Null Game
 turns into your real game.
 
-Next you'll need to add your puzzle to the Makefiles, in order to
-compile it conveniently. \e{Do not edit the Makefiles}: they are
-created automatically by the script \c{mkfiles.pl}, from the file
-called \c{Recipe}. Edit \c{Recipe}, and then re-run \c{mkfiles.pl}.
-
-Also, don't forget to add your puzzle to \c{list.c}: if you don't,
-then it will still run fine on platforms which build each puzzle
-separately, but Mac OS X and other monolithic platforms will not
-include your new puzzle in their single binary.
+Next you'll need to add your puzzle to the build scripts, in order to
+compile it conveniently. Puzzles is a CMake project, so you do this by
+adding a \cw{puzzle()} statement to CMakeLists.txt. Look at the
+existing ones to see what those look like, and add one that looks
+similar.
 
 Once your source file is building, you can move on to the fun bit.
 
--- a/dominosa.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-DOMINOSA_EXTRA = laydomino dsf sort findloop
-
-dominosa : [X] GTK COMMON dominosa DOMINOSA_EXTRA dominosa-icon|no-icon
-
-dominosa : [G] WINDOWS COMMON dominosa DOMINOSA_EXTRA dominosa.res|noicon.res
-
-ALL += dominosa[COMBINED] DOMINOSA_EXTRA
-
-dominosasolver :   [U] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
-dominosasolver :   [C] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
-
-!begin am gtk
-GAMES += dominosa
-!end
-
-!begin >list.c
-    A(dominosa) \
-!end
-
-!begin >gamedesc.txt
-dominosa:dominosa.exe:Dominosa:Domino tiling puzzle:Tile the rectangle with a full set of dominoes.
-!end
--- a/emccx.json
+++ /dev/null
@@ -1,33 +1,0 @@
-// -*- js -*-
-//
-// List of entry points exported by the C side of the Emscripten
-// puzzle builds. Passed in to emcc via the option '-s
-// EXPORTED_FUNCTIONS=[list]'.
-//
-// This file isn't actually a valid list in its current state, since
-// emcc doesn't like comments or newlines. However, it's a nicer
-// source form to keep the comments and newlines in, so we sed them
-// away at compile time.
-[
-    // Event handlers for mouse and keyboard input
-    '_mouseup',
-    '_mousedown',
-    '_mousemove',
-    '_key',
-    // Callback when the program activates timing
-    '_timer_callback',
-    // Callback from button presses in the UI outside the canvas
-    '_command',
-    // Game-saving and game-loading functions
-    '_get_save_file',
-    '_free_save_file',
-    '_load_game',
-    // Callbacks to return values from dialog boxes
-    '_dlg_return_sval',
-    '_dlg_return_ival',
-    // Callbacks when the resizing controls are used
-    '_resize_puzzle',
-    '_restore_puzzle_size',
-    // Main program, run at initialisation time
-    '_main'
-]
--- a/fifteen.R
+++ /dev/null
@@ -1,22 +1,0 @@
-# -*- makefile -*-
-
-fifteen  : [X] GTK COMMON fifteen fifteen-icon|no-icon
-
-fifteen  : [G] WINDOWS COMMON fifteen fifteen.res|noicon.res
-
-fifteensolver :    [U] fifteen[STANDALONE_SOLVER] STANDALONE
-fifteensolver :    [C] fifteen[STANDALONE_SOLVER] STANDALONE
-
-ALL += fifteen[COMBINED]
-
-!begin am gtk
-GAMES += fifteen
-!end
-
-!begin >list.c
-    A(fifteen) \
-!end
-
-!begin >gamedesc.txt
-fifteen:fifteen.exe:Fifteen:Sliding block puzzle:Slide the tiles around to arrange them into order.
-!end
--- a/filling.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-FILLING_EXTRA = dsf
-
-fillingsolver :	[U] filling[STANDALONE_SOLVER] FILLING_EXTRA STANDALONE
-fillingsolver :	[C] filling[STANDALONE_SOLVER] FILLING_EXTRA STANDALONE
-
-filling : [X] GTK COMMON filling FILLING_EXTRA filling-icon|no-icon
-
-filling : [G] WINDOWS COMMON filling FILLING_EXTRA filling.res|noicon.res
-
-ALL += filling[COMBINED] FILLING_EXTRA
-
-!begin am gtk
-GAMES += filling
-!end
-
-!begin >list.c
-    A(filling) \
-!end
-
-!begin >gamedesc.txt
-filling:filling.exe:Filling:Polyomino puzzle:Mark every square with the area of its containing region.
-!end
--- a/flip.R
+++ /dev/null
@@ -1,21 +1,0 @@
-# -*- makefile -*-
-
-FLIP_EXTRA = tree234
-
-flip     : [X] GTK COMMON flip FLIP_EXTRA flip-icon|no-icon
-
-flip     : [G] WINDOWS COMMON flip FLIP_EXTRA flip.res|noicon.res
-
-ALL += flip[COMBINED] FLIP_EXTRA
-
-!begin am gtk
-GAMES += flip
-!end
-
-!begin >list.c
-    A(flip) \
-!end
-
-!begin >gamedesc.txt
-flip:flip.exe:Flip:Tile inversion puzzle:Flip groups of squares to light them all up at once.
-!end
--- a/flood.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-flood     : [X] GTK COMMON flood flood-icon|no-icon
-
-flood     : [G] WINDOWS COMMON flood flood.res|noicon.res
-
-ALL += flood[COMBINED]
-
-!begin am gtk
-GAMES += flood
-!end
-
-!begin >list.c
-    A(flood) \
-!end
-
-!begin >gamedesc.txt
-flood:flood.exe:Flood:Flood-filling puzzle:Turn the grid the same colour in as few flood fills as possible.
-!end
--- a/galaxies.R
+++ /dev/null
@@ -1,28 +1,0 @@
-# -*- makefile -*-
-
-GALAXIES_EXTRA = dsf
-
-galaxies : [X] GTK COMMON galaxies GALAXIES_EXTRA galaxies-icon|no-icon
-
-galaxies : [G] WINDOWS COMMON galaxies GALAXIES_EXTRA galaxies.res|noicon.res
-
-galaxiessolver : [U] galaxies[STANDALONE_SOLVER] GALAXIES_EXTRA STANDALONE m.lib
-galaxiessolver : [C] galaxies[STANDALONE_SOLVER] GALAXIES_EXTRA STANDALONE
-
-galaxiespicture : [U] galaxies[STANDALONE_PICTURE_GENERATOR] GALAXIES_EXTRA STANDALONE
-                + m.lib
-galaxiespicture : [C] galaxies[STANDALONE_PICTURE_GENERATOR] GALAXIES_EXTRA STANDALONE
-
-ALL += galaxies[COMBINED] GALAXIES_EXTRA
-
-!begin am gtk
-GAMES += galaxies
-!end
-
-!begin >list.c
-    A(galaxies) \
-!end
-
-!begin >gamedesc.txt
-galaxies:galaxies.exe:Galaxies:Symmetric polyomino puzzle:Divide the grid into rotationally symmetric regions each centred on a dot.
-!end
--- a/guess.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-guess    : [X] GTK COMMON guess guess-icon|no-icon
-
-guess    : [G] WINDOWS COMMON guess guess.res|noicon.res
-
-ALL += guess[COMBINED]
-
-!begin am gtk
-GAMES += guess
-!end
-
-!begin >list.c
-    A(guess) \
-!end
-
-!begin >gamedesc.txt
-guess:guess.exe:Guess:Combination-guessing puzzle:Guess the hidden combination of colours.
-!end
--- a/icons/Makefile
+++ /dev/null
@@ -1,162 +1,0 @@
-# Makefile for Puzzles icons.
-
-PUZZLES = blackbox bridges cube dominosa fifteen filling flip flood	\
-	  galaxies guess inertia keen lightup loopy magnets map mines	\
-	  net netslide palisade pattern pearl pegs range rect		\
-	  samegame signpost singles sixteen slant solo tents towers	\
-	  twiddle tracks undead unequal unruly untangle
-
-BASE = $(patsubst %,%-base.png,$(PUZZLES))
-WEB = $(patsubst %,%-web.png,$(PUZZLES))
-
-IBASE = $(patsubst %,%-ibase.png,$(PUZZLES))
-IBASE4 = $(patsubst %,%-ibase4.png,$(PUZZLES))
-P96D24 = $(patsubst %,%-96d24.png,$(PUZZLES))
-P96D8 = $(patsubst %,%-96d8.png,$(PUZZLES))
-P96D4 = $(patsubst %,%-96d4.png,$(PUZZLES))
-P48D24 = $(patsubst %,%-48d24.png,$(PUZZLES))
-P48D8 = $(patsubst %,%-48d8.png,$(PUZZLES))
-P48D4 = $(patsubst %,%-48d4.png,$(PUZZLES))
-P32D24 = $(patsubst %,%-32d24.png,$(PUZZLES))
-P32D8 = $(patsubst %,%-32d8.png,$(PUZZLES))
-P32D4 = $(patsubst %,%-32d4.png,$(PUZZLES))
-P16D24 = $(patsubst %,%-16d24.png,$(PUZZLES))
-P16D8 = $(patsubst %,%-16d8.png,$(PUZZLES))
-P16D4 = $(patsubst %,%-16d4.png,$(PUZZLES))
-ICONS = $(patsubst %,%.ico,$(PUZZLES))
-CICONS = $(patsubst %,%-icon.c,$(PUZZLES))
-RC = $(patsubst %,%.rc,$(PUZZLES))
-
-BIN = ../
-PIC = ./
-
-# Work around newer ImageMagick unilaterally distorting colours when
-# converting to PNG.
-CSP = -set colorspace RGB
-
-base: $(BASE)
-web: $(WEB)
-pngicons: $(P96D24) $(P48D24) $(P32D24) $(P16D24)
-winicons: $(ICONS) $(RC)
-gtkicons: $(CICONS)
-all: base web pngicons winicons gtkicons
-
-# Build the base puzzle screenshots from which all the other images
-# are derived. Some of them involve showing a move animation
-# part-way through.
-fifteen-base.png : override REDO=0.3
-flip-base.png : override REDO=0.3
-netslide-base.png : override REDO=0.3
-sixteen-base.png : override REDO=0.3
-twiddle-base.png : override REDO=0.3
-$(BASE): %-base.png: $(BIN)% $(PIC)%.sav
-	$(PIC)screenshot.sh $(BIN)$* $(PIC)$*.sav $@ $(REDO)
-
-# Build the screenshots for the web, by scaling the original base
-# images to a uniform size.
-$(WEB): %-web.png: %-base.png
-	$(PIC)square.pl 150 5 $^ $@
-
-# Build the base _icon_ images, by careful cropping of the base
-# images: icons are very small so it's often necessary to zoom in
-# on a smaller portion of the screenshot.
-blackbox-ibase.png : override CROP=352x352 144x144+0+208
-bridges-ibase.png : override CROP=264x264 107x107+157+157
-dominosa-ibase.png : override CROP=304x272 152x152+152+0
-fifteen-ibase.png : override CROP=240x240 120x120+0+120
-filling-ibase.png : override CROP=256x256 133x133+14+78
-flip-ibase.png : override CROP=288x288 145x145+120+72
-galaxies-ibase.png : override CROP=288x288 165x165+0+0
-guess-ibase.png : override CROP=263x420 178x178+75+17
-inertia-ibase.png : override CROP=321x321 128x128+193+0
-keen-ibase.png : override CROP=288x288 96x96+24+120
-lightup-ibase.png : override CROP=256x256 112x112+144+0
-loopy-ibase.png : override CROP=257x257 113x113+0+0
-magnets-ibase.png : override CROP=264x232 96x96+36+100
-mines-ibase.png : override CROP=240x240 110x110+130+130
-net-ibase.png : override CROP=193x193 113x113+0+80
-netslide-ibase.png : override CROP=289x289 144x144+0+0
-palisade-ibase.png : override CROP=288x288 192x192+0+0
-pattern-ibase.png : override CROP=384x384 223x223+0+0
-pearl-ibase.png : override CROP=216x216 94x94+108+15
-pegs-ibase.png : override CROP=263x263 147x147+116+0
-range-ibase.png : override CROP=256x256 98x98+111+15
-rect-ibase.png : override CROP=205x205 115x115+90+0
-signpost-ibase.png : override CROP=240x240 98x98+23+23
-singles-ibase.png : override CROP=224x224 98x98+15+15
-sixteen-ibase.png : override CROP=288x288 144x144+144+144
-slant-ibase.png : override CROP=321x321 160x160+160+160
-solo-ibase.png : override CROP=481x481 145x145+24+24
-tents-ibase.png : override CROP=320x320 165x165+142+0
-towers-ibase.png : override CROP=300x300 102x102+151+6
-tracks-ibase.png : override CROP=246x246 118x118+6+6
-twiddle-ibase.png : override CROP=192x192 102x102+69+21
-undead-ibase.png : override CROP=416x480 192x192+16+80
-unequal-ibase.png : override CROP=208x208 104x104+104+104
-untangle-ibase.png : override CROP=320x320 164x164+3+116
-$(IBASE): %-ibase.png: %-base.png
-	$(PIC)crop.sh $^ $@ $(CROP)
-
-# Convert the full-size icon images to 4-bit colour, because that
-# seems to work better than reducing it in 24 bits and then
-# dithering.
-$(IBASE4): %-ibase4.png: %-ibase.png
-	convert -colors 16 +dither $(CSP) -map $(PIC)win16pal.xpm $^ $@
-
-# Build the 24-bit PNGs for the icons, at four sizes.
-$(P96D24): %-96d24.png: %-ibase.png
-	$(PIC)square.pl 96 4 $^ $@
-$(P48D24): %-48d24.png: %-ibase.png
-	$(PIC)square.pl 48 4 $^ $@
-$(P32D24): %-32d24.png: %-ibase.png
-	$(PIC)square.pl 32 2 $^ $@
-$(P16D24): %-16d24.png: %-ibase.png
-	$(PIC)square.pl 16 1 $^ $@
-
-# The 8-bit icon PNGs are just custom-paletted quantisations of the
-# 24-bit ones.
-$(P96D8) $(P48D8) $(P32D8) $(P16D8): %d8.png: %d24.png
-	convert -colors 256 $^ $@
-
-# But the depth-4 images work better if we re-shrink from the
-# ibase4 versions of the images, and then normalise the colours
-# again afterwards. (They're still not very good, but my hope is
-# that on most modern Windows machines this won't matter too
-# much...)
-$(P96D4): %-96d4.png: %-ibase4.png
-	$(PIC)square.pl 96 1 $^ $@-tmp2.png
-	convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@
-	rm -f $@-tmp2.png
-$(P48D4): %-48d4.png: %-ibase4.png
-	$(PIC)square.pl 48 1 $^ $@-tmp2.png
-	convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@
-	rm -f $@-tmp2.png
-$(P32D4): %-32d4.png: %-ibase.png
-	$(PIC)square.pl 32 1 $^ $@-tmp2.png
-	convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@
-	rm -f $@-tmp2.png
-$(P16D4): %-16d4.png: %-ibase.png
-	$(PIC)square.pl 16 1 $^ $@-tmp2.png
-	convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@
-	rm -f $@-tmp2.png
-
-# Build the actual Windows icons themselves, by feeding all those
-# PNGs to my icon builder script.
-$(ICONS): %.ico: %-48d24.png %-48d8.png %-48d4.png \
-                 %-32d24.png %-32d8.png %-32d4.png \
-                 %-16d24.png %-16d8.png %-16d4.png
-	$(PIC)icon.pl -24 $*-48d24.png $*-32d24.png $*-16d24.png \
-		      -8  $*-48d8.png  $*-32d8.png  $*-16d8.png  \
-		      -4  $*-48d4.png  $*-32d4.png  $*-16d4.png  > $@
-
-# Build the .RC files which bind the icons into the applications.
-$(RC): %.rc:
-	echo '#include "puzzles.rc2"' > $@
-	echo '200 ICON "$*.ico"' >> $@
-
-# Build the GTK icon source files.
-$(CICONS): %-icon.c: %-16d24.png %-32d24.png %-48d24.png %-96d24.png
-	$(PIC)cicon.pl $^ > $@	
-
-clean:
-	rm -f *.png *.ico *.rc *-icon.c
--- a/icons/cicon.pl
+++ b/icons/cicon.pl
@@ -5,12 +5,13 @@
 
 $k = 0;
 @xpms = ();
+$convert = shift @ARGV;
 foreach $f (@ARGV) {
   # XPM format is generated directly by ImageMagick, so that's easy
   # enough. We just have to adjust the declaration line so that it
   # has the right name, linkage and storage class.
   @lines = ();
-  open XPM, "convert $f xpm:- |";
+  open XPM, "-|", $convert, $f, "xpm:-";
   push @lines, $_ while <XPM>;
   close XPM;
   die "XPM from $f in unexpected format\n" unless $lines[1] =~ /^static.*\{$/;
--- a/icons/crop.sh
+++ b/icons/crop.sh
@@ -13,21 +13,23 @@
 # this script will give a build error and alert me to the fact that
 # I need to fiddle with the icon makefile.
 
-infile="$1"
-outfile="$2"
-insize="$3"
-crop="$4"
+identify="$1"
+convert="$2"
+infile="$3"
+outfile="$4"
+insize="$5"
+crop="$6"
 
 # Special case: if no input size or crop parameter was specified at
 # all, we just copy the input to the output file.
 
-if test $# -lt 3; then
+if test -z "$insize"; then
   cp "$infile" "$outfile"
   exit 0
 fi
 
 # Check the input image size.
-realsize=`identify -format %wx%h "$infile"`
+realsize=$("$identify" -format %wx%h "$infile")
 if test "x$insize" != "x$realsize"; then
   echo "crop.sh: '$infile' has wrong initial size: $realsize != $insize" >&2
   exit 1
@@ -34,4 +36,4 @@
 fi
 
 # And crop.
-convert -crop "$crop" "$infile" "$outfile"
+"$convert" -crop "$crop" "$infile" "$outfile"
--- a/icons/icon.pl
+++ b/icons/icon.pl
@@ -85,9 +85,12 @@
 @dat = ();
 
 $depth = undef;
+$convert = "convert";
 foreach $_ (@ARGV) {
     if (/^-(24|8|4|1)$/) {
 	$depth = $1;
+    } elsif (/^--convert=(.*)$/) {
+        $convert = $1;
     } elsif (defined $depth) {
 	&readicon($_, $depth);
     } else {
@@ -127,7 +130,7 @@
     # point, to avoid having to do it ourselves (.BMP and hence
     # .ICO are bottom-up).
     my $data = [];
-    open IDATA, "convert -set colorspace sRGB -flip -depth 8 $filename rgba:- |";
+    open IDATA, "-|", $convert, "-set", "colorspace", "sRGB", "-flip", "-depth", "8", $filename, "rgba:-";
     push @$data, $rgb while (read IDATA,$rgb,4,0) == 4;
     close IDATA;
     # Check we have the right amount of data.
--- /dev/null
+++ b/icons/icons.cmake
@@ -1,0 +1,237 @@
+if(NOT build_icons)
+  # This entire subdirectory does nothing on platforms where we can't
+  # build the icons in any case.
+  return()
+endif()
+
+find_program(CONVERT convert REQUIRED)
+find_program(IDENTIFY identify REQUIRED)
+
+# For puzzles which have animated moves, it's nice to show the sample
+# image part way through the animation of a move. This setting will
+# cause a 'redo' action immediately after loading the save file,
+# causing the first undone move in the undo chain to be redone, and
+# then it will stop this far through the move animation to take the
+# screenshot.
+set(fifteen_redo 0.3)
+set(flip_redo 0.3)
+set(netslide_redo 0.3)
+set(sixteen_redo 0.3)
+set(twiddle_redo 0.3)
+
+# For many puzzles, we'd prefer that the icon zooms in on a couple of
+# squares of the playing area rather than trying to show the whole of
+# a game. These settings configure that. Each one indicates the
+# expected full size of the screenshot image, followed by the area we
+# want to crop to.
+#
+# (The expected full size is a safety precaution: if a puzzle changes
+# its default display size, then that won't match, and we'll get a
+# build error here rather than silently continuing to take the wrong
+# subrectangle of the resized puzzle display.)
+set(blackbox_crop 352x352 144x144+0+208)
+set(bridges_crop 264x264 107x107+157+157)
+set(dominosa_crop 304x272 152x152+152+0)
+set(fifteen_crop 240x240 120x120+0+120)
+set(filling_crop 256x256 133x133+14+78)
+set(flip_crop 288x288 145x145+120+72)
+set(galaxies_crop 288x288 165x165+0+0)
+set(guess_crop 263x420 178x178+75+17)
+set(inertia_crop 321x321 128x128+193+0)
+set(keen_crop 288x288 96x96+24+120)
+set(lightup_crop 256x256 112x112+144+0)
+set(loopy_crop 257x257 113x113+0+0)
+set(magnets_crop 264x232 96x96+36+100)
+set(mines_crop 240x240 110x110+130+130)
+set(net_crop 193x193 113x113+0+80)
+set(netslide_crop 289x289 144x144+0+0)
+set(palisade_crop 288x288 192x192+0+0)
+set(pattern_crop 384x384 223x223+0+0)
+set(pearl_crop 216x216 94x94+108+15)
+set(pegs_crop 263x263 147x147+116+0)
+set(range_crop 256x256 98x98+111+15)
+set(rect_crop 205x205 115x115+90+0)
+set(signpost_crop 240x240 98x98+23+23)
+set(singles_crop 224x224 98x98+15+15)
+set(sixteen_crop 288x288 144x144+144+144)
+set(slant_crop 321x321 160x160+160+160)
+set(solo_crop 481x481 145x145+24+24)
+set(tents_crop 320x320 165x165+142+0)
+set(towers_crop 300x300 102x102+151+6)
+set(tracks_crop 246x246 118x118+6+6)
+set(twiddle_crop 192x192 102x102+69+21)
+set(undead_crop 416x480 192x192+16+80)
+set(unequal_crop 208x208 104x104+104+104)
+set(untangle_crop 320x320 164x164+3+116)
+
+add_custom_target(icons)
+
+# All sizes of icon we make for any purpose.
+set(all_icon_sizes 96 48 32 16)
+
+# Sizes of icon we put into the Windows .ico files.
+set(win_icon_sizes 48 32 16)
+
+# Border thickness for each icon size.
+set(border_96 4)
+set(border_48 4)
+set(border_32 2)
+set(border_16 1)
+
+set(icon_srcdir ${CMAKE_SOURCE_DIR}/icons)
+set(icon_bindir ${CMAKE_BINARY_DIR}/icons)
+
+function(build_icon name)
+  set(output_icon_files)
+
+  # Compile the GTK puzzle binary without an icon, so that we can run
+  # it to generate a screenshot to make the icon out of.
+  add_executable(${NAME}-icon-maker ${NAME}.c
+    ${CMAKE_SOURCE_DIR}/no-icon.c)
+  target_link_libraries(${NAME}-icon-maker
+    common ${platform_gui_libs} ${platform_libs})
+  set_target_properties(${NAME}-icon-maker PROPERTIES
+    RUNTIME_OUTPUT_DIRECTORY ${icon_bindir})
+
+  # Now run that binary to generate a screenshot of the puzzle in
+  # play, which will be the base image we make everything else out
+  # out.
+  if(DEFINED ${name}_redo)
+    set(redo_arg --redo ${name}_redo)
+  else()
+    set(redo_arg)
+  endif()
+  add_custom_command(OUTPUT ${icon_bindir}/${name}-base.png
+    COMMAND ${icon_bindir}/${name}-icon-maker
+      ${redo_arg}
+      --screenshot ${icon_bindir}/${name}-base.png
+      --load ${icon_srcdir}/${name}.sav
+    DEPENDS
+      ${name}-icon-maker)
+
+  # Shrink it to a fixed-size square image for the web page,
+  # trimming boring border parts of the original image in the
+  # process. Done by square.pl.
+  add_custom_command(OUTPUT ${icon_bindir}/${name}-web.png
+    COMMAND ${icon_srcdir}/square.pl
+      ${CONVERT} 150 5
+      ${icon_bindir}/${name}-base.png
+      ${icon_bindir}/${name}-web.png
+    DEPENDS
+      ${icon_srcdir}/square.pl
+      ${icon_bindir}/${name}-base.png)
+  list(APPEND output_icon_files ${icon_bindir}/${name}-web.png)
+
+  # Make the base image for all the icons, by cropping out the most
+  # interesting part of the whole screenshot.
+  add_custom_command(OUTPUT ${icon_bindir}/${name}-ibase.png
+    COMMAND ${icon_srcdir}/crop.sh
+      ${IDENTIFY} ${CONVERT}
+      ${icon_bindir}/${name}-base.png
+      ${icon_bindir}/${name}-ibase.png
+      ${${name}_crop}
+    DEPENDS
+      ${icon_srcdir}/crop.sh
+      ${icon_bindir}/${name}-base.png)
+
+  # Coerce that base image down to colour depth of 4 bits, using the
+  # fixed 16-colour Windows palette. We do this before shrinking the
+  # image, because I've found that gives better results than just
+  # doing it after.
+  add_custom_command(OUTPUT ${icon_bindir}/${name}-ibase4.png
+    COMMAND ${CONVERT}
+      -colors 16
+      +dither
+      -set colorspace RGB
+      -map ${icon_srcdir}/win16pal.xpm
+      ${icon_bindir}/${name}-ibase.png
+      ${icon_bindir}/${name}-ibase4.png
+    DEPENDS
+      ${icon_srcdir}/win16pal.xpm
+      ${icon_bindir}/${name}-ibase.png)
+
+  foreach(size ${all_icon_sizes})
+    # Make a 24-bit icon image at each size, by shrinking the base
+    # icon image.
+    add_custom_command(OUTPUT ${icon_bindir}/${name}-${size}d24.png
+      COMMAND ${icon_srcdir}/square.pl
+        ${CONVERT} ${size} ${border_${size}}
+        ${icon_bindir}/${name}-ibase.png
+        ${icon_bindir}/${name}-${size}d24.png
+      DEPENDS
+        ${icon_srcdir}/square.pl
+        ${icon_bindir}/${name}-ibase.png)
+    list(APPEND output_icon_files ${icon_bindir}/${name}-${size}d24.png)
+
+    # And reduce the colour depth of that one to make an 8-bit
+    # version.
+    add_custom_command(OUTPUT ${icon_bindir}/${name}-${size}d8.png
+      COMMAND ${CONVERT}
+        -colors 256
+        ${icon_bindir}/${name}-${size}d24.png
+        ${icon_bindir}/${name}-${size}d8.png
+      DEPENDS ${icon_bindir}/${name}-${size}d24.png)
+    list(APPEND output_icon_files ${icon_bindir}/${name}-${size}d8.png)
+  endforeach()
+
+  foreach(size ${win_icon_sizes})
+    # 4-bit icons are only needed for Windows. We make each one by
+    # first shrinking the large 4-bit image we made above ...
+    add_custom_command(OUTPUT ${icon_bindir}/${name}-${size}d4pre.png
+      COMMAND ${icon_srcdir}/square.pl
+        ${CONVERT} ${size} ${border_${size}}
+        ${icon_bindir}/${name}-ibase4.png
+        ${icon_bindir}/${name}-${size}d4pre.png
+      DEPENDS
+        ${icon_srcdir}/square.pl
+        ${icon_bindir}/${name}-ibase4.png)
+
+    # ... and then re-coercing the output back to 16 colours, since
+    # that shrink operation will have introduced intermediate colour
+    # values again.
+    add_custom_command(OUTPUT ${icon_bindir}/${name}-${size}d4.png
+      COMMAND ${CONVERT}
+        -colors 16
+        +dither
+        -set colorspace RGB
+        -map ${icon_srcdir}/win16pal.xpm
+        ${icon_bindir}/${name}-${size}d4pre.png
+        ${icon_bindir}/${name}-${size}d4.png
+      DEPENDS ${icon_bindir}/${name}-${size}d4pre.png)
+    list(APPEND output_icon_files ${icon_bindir}/${name}-${size}d4.png)
+  endforeach()
+
+  # Make the Windows icon.
+  set(icon_pl_args)
+  set(icon_pl_deps)
+  foreach(depth 24 8 4)
+    list(APPEND icon_pl_args -${depth})
+    foreach(size ${win_icon_sizes})
+      list(APPEND icon_pl_args ${icon_bindir}/${name}-${size}d${depth}.png)
+      list(APPEND icon_pl_deps ${icon_bindir}/${name}-${size}d${depth}.png)
+    endforeach()
+  endforeach()
+  add_custom_command(OUTPUT ${icon_bindir}/${name}.ico
+    COMMAND ${icon_srcdir}/icon.pl
+      --convert=${CONVERT}
+      ${icon_pl_args} > ${icon_bindir}/${name}.ico
+    DEPENDS
+      ${icon_srcdir}/icon.pl
+      ${icon_pl_deps})
+  list(APPEND output_icon_files ${icon_bindir}/${name}.ico)
+
+  # Make a C source file containing XPMs of all the 24-bit images.
+  set(cicon_pl_infiles)
+  foreach(size ${all_icon_sizes})
+    list(APPEND cicon_pl_infiles ${icon_bindir}/${name}-${size}d24.png)
+  endforeach()
+  add_custom_command(OUTPUT ${icon_bindir}/${name}-icon.c
+    COMMAND ${icon_srcdir}/cicon.pl
+      ${CONVERT} ${cicon_pl_infiles} > ${icon_bindir}/${name}-icon.c
+    DEPENDS
+      ${icon_srcdir}/cicon.pl
+      ${cicon_pl_infiles})
+
+  add_custom_target(${name}-icons DEPENDS ${output_icon_files})
+  add_dependencies(icons ${name}-icons)
+endfunction()
--- a/icons/screenshot.sh
+++ /dev/null
@@ -1,25 +1,0 @@
-#!/bin/sh 
-
-# Generate a screenshot from a puzzle save file. Takes the
-# following arguments, in order:
-#
-#  - the name of the puzzle binary
-#  - the name of the save file
-#  - the name of the output image file
-#  - (optionally) the proportion of the next move to redo before
-#    taking the screenshot.
-#
-# This script requires access to an X server in order to run, but
-# seems to work fine under xvfb-run if you haven't got a real one
-# available (or if you don't want to use it for some reason).
-
-binary="$1"
-save="$2"
-image="$3"
-if test "x$4" != "x"; then
-  redo="--redo $4"
-else
-  redo=
-fi
-
-"$binary" $redo --screenshot "$image" --load "$save"
--- a/icons/square.pl
+++ b/icons/square.pl
@@ -8,7 +8,7 @@
 #  - the input image file name
 #  - the output image file name.
 
-($osize, $oborder, $infile, $outfile) = @ARGV;
+($convert, $osize, $oborder, $infile, $outfile) = @ARGV;
 
 # Determine the input image's size.
 $ident = `identify -format "%w %h" $infile`;
@@ -17,7 +17,7 @@
 
 # Read the input image data.
 $data = [];
-open IDATA, "convert -depth 8 $infile rgb:- |";
+open IDATA, "-|", $convert, "-depth", "8", $infile, "rgb:-";
 push @$data, $rgb while (read IDATA,$rgb,3,0) == 3;
 close IDATA;
 # Check we have the right amount of data.
@@ -82,7 +82,7 @@
 die "internal computation problem" if $ow != $oh; # should be square
 
 # Now write out the resulting image, and resize it appropriately.
-open IDATA, "| convert -size ${ow}x${oh} -depth 8 -resize ${osize}x${osize}! rgb:- $outfile";
+open IDATA, "|-", $convert, "-size", "${ow}x${oh}", "-depth", "8", "-resize", "${osize}x${osize}!", "rgb:-", $outfile;
 for ($y = $ystart; $y <= $yend; $y++) {
     for ($x = $xstart; $x <= $xend; $x++) {
 	if ($x >= 0 && $x < $w && $y >= 0 && $y < $h) {
--- a/inertia.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-inertia  : [X] GTK COMMON inertia inertia-icon|no-icon
-
-inertia  : [G] WINDOWS COMMON inertia inertia.res|noicon.res
-
-ALL += inertia[COMBINED]
-
-!begin am gtk
-GAMES += inertia
-!end
-
-!begin >list.c
-    A(inertia) \
-!end
-
-!begin >gamedesc.txt
-inertia:inertia.exe:Inertia:Gem-collecting puzzle:Collect all the gems without running into any of the mines.
-!end
--- a/keen.R
+++ /dev/null
@@ -1,25 +1,0 @@
-# -*- makefile -*-
-
-KEEN_EXTRA        = dsf LATIN
-KEEN_EXTRA_SOLVER = dsf LATIN_SOLVER
-
-keen    : [X] GTK COMMON keen KEEN_EXTRA keen-icon|no-icon
-
-keen    : [G] WINDOWS COMMON keen KEEN_EXTRA keen.res|noicon.res
-
-keensolver : [U] keen[STANDALONE_SOLVER] KEEN_EXTRA_SOLVER STANDALONE
-keensolver : [C] keen[STANDALONE_SOLVER] KEEN_EXTRA_SOLVER STANDALONE
-
-ALL += keen[COMBINED] KEEN_EXTRA
-
-!begin am gtk
-GAMES += keen
-!end
-
-!begin >list.c
-    A(keen) \
-!end
-
-!begin >gamedesc.txt
-keen:keen.exe:Keen:Arithmetic Latin square puzzle:Complete the latin square in accordance with the arithmetic clues.
-!end
--- a/lightup.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-LIGHTUP_EXTRA = combi
-
-lightup  : [X] GTK COMMON lightup LIGHTUP_EXTRA lightup-icon|no-icon
-
-lightup  : [G] WINDOWS COMMON lightup LIGHTUP_EXTRA lightup.res|noicon.res
-
-lightupsolver : [U] lightup[STANDALONE_SOLVER] LIGHTUP_EXTRA STANDALONE
-lightupsolver : [C] lightup[STANDALONE_SOLVER] LIGHTUP_EXTRA STANDALONE
-
-ALL += lightup[COMBINED] LIGHTUP_EXTRA
-
-!begin am gtk
-GAMES += lightup
-!end
-
-!begin >list.c
-    A(lightup) \
-!end
-
-!begin >gamedesc.txt
-lightup:lightup.exe:Light Up:Light-bulb placing puzzle:Place bulbs to light up all the squares.
-!end
--- /dev/null
+++ b/list.c
@@ -1,0 +1,21 @@
+/*
+ * list.c: List of pointers to puzzle structures, for monolithic
+ * platforms.
+ *
+ * This file depends on the header "generated-games.h", which is
+ * constructed by CMakeLists.txt.
+ */
+
+#include "puzzles.h"
+
+#define GAME(x) extern const game x;
+#include "generated-games.h"
+#undef GAME
+
+#define GAME(x) &x,
+const game *gamelist[] = {
+#include "generated-games.h"
+};
+#undef GAME
+
+const int gamecount = lenof(gamelist);
--- a/loopy.R
+++ /dev/null
@@ -1,31 +1,0 @@
-# -*- makefile -*-
-
-LOOPY_EXTRA = tree234 dsf grid penrose loopgen
-
-loopy     : [X] GTK COMMON loopy LOOPY_EXTRA loopy-icon|no-icon
-
-loopy     : [G] WINDOWS COMMON loopy LOOPY_EXTRA loopy.res|noicon.res
-
-loopysolver :   [U] loopy[STANDALONE_SOLVER] LOOPY_EXTRA STANDALONE m.lib
-loopysolver :   [C] loopy[STANDALONE_SOLVER] LOOPY_EXTRA STANDALONE
-
-#penrose :    [U] penrose[TEST_PENROSE] STANDALONE m.lib
-#penrose :    [C] penrose[TEST_PENROSE] STANDALONE
-
-#test-basis : [U] penrose[TEST_VECTORS] tree234 STANDALONE m.lib
-#test-basis : [C] penrose[TEST_VECTORS] tree234 STANDALONE
-
-
-ALL += loopy[COMBINED] LOOPY_EXTRA
-
-!begin am gtk
-GAMES += loopy
-!end
-
-!begin >list.c
-    A(loopy) \
-!end
-
-!begin >gamedesc.txt
-loopy:loopy.exe:Loopy:Loop-drawing puzzle:Draw a single closed loop, given clues about number of adjacent edges.
-!end
--- a/magnets.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-MAGNETS_EXTRA = laydomino
-
-magnets      : [X] GTK COMMON magnets MAGNETS_EXTRA magnets-icon|no-icon
-
-magnets      : [G] WINDOWS COMMON magnets MAGNETS_EXTRA magnets.res|noicon.res
-
-magnetssolver :     [U] magnets[STANDALONE_SOLVER] MAGNETS_EXTRA STANDALONE m.lib
-magnetssolver :     [C] magnets[STANDALONE_SOLVER] MAGNETS_EXTRA STANDALONE
-
-ALL += magnets[COMBINED] MAGNETS_EXTRA
-
-!begin am gtk
-GAMES += magnets
-!end
-
-!begin >list.c
-    A(magnets) \
-!end
-
-!begin >gamedesc.txt
-magnets:magnets.exe:Magnets:Magnet-placing puzzle:Place magnets to satisfy the clues and avoid like poles touching.
-!end
--- a/makedist.sh
+++ /dev/null
@@ -1,47 +1,0 @@
-#!/bin/sh 
-
-# Build a Unix source distribution from the Puzzles SVN area.
-#
-# Pass a version number argument to have the archive tagged with that
-# version number. Otherwise, the script will not version-tag the
-# archive at all.
-
-version="$1"
-
-if test "x$version" != "x"; then
-  arcsuffix="-$version"
-  ver="-DVER=$version"
-else
-  arcsuffix=
-  ver=
-fi
-
-perl mkfiles.pl
-./mkauto.sh
-
-mkdir tmp.$$
-mkdir tmp.$$/puzzles$arcsuffix
-mkdir tmp.$$/puzzles$arcsuffix/icons
-
-# Build Windows Help and text versions of the manual for convenience.
-halibut --winhelp=puzzles.hlp --text=puzzles.txt puzzles.but
-
-# Build a text version of the HACKING document.
-halibut --text=HACKING devel.but
-
-for i in *.c *.m *.h *.R *.rc *.but *.plist *.icns LICENCE README Recipe \
-  *.rc2 mkfiles.pl Makefile Makefile.* \
-  HACKING puzzles.txt puzzles.hlp puzzles.cnt puzzles.chm \
-  icons/Makefile icons/*.sav icons/*.pl icons/*.sh icons/win16pal.xpm \
-  icons/*.png icons/*.ico icons/*.rc icons/*.c \
-  configure.ac mkauto.sh aclocal.m4 \
-  configure depcomp install-sh missing compile; do
-  case $i in
-    */*) ln -s ../../../$i tmp.$$/puzzles$arcsuffix/$i;;
-    *)   ln -s ../../$i tmp.$$/puzzles$arcsuffix/$i;;
-  esac
-done
-
-tar -C tmp.$$ -chzf - puzzles$arcsuffix > ../puzzles$arcsuffix.tar.gz
-
-rm -rf tmp.$$
--- a/map.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-MAP_EXTRA = dsf
-
-map      : [X] GTK COMMON map MAP_EXTRA map-icon|no-icon
-
-map      : [G] WINDOWS COMMON map MAP_EXTRA map.res|noicon.res
-
-mapsolver :     [U] map[STANDALONE_SOLVER] MAP_EXTRA STANDALONE m.lib
-mapsolver :     [C] map[STANDALONE_SOLVER] MAP_EXTRA STANDALONE
-
-ALL += map[COMBINED] MAP_EXTRA
-
-!begin am gtk
-GAMES += map
-!end
-
-!begin >list.c
-    A(map) \
-!end
-
-!begin >gamedesc.txt
-map:map.exe:Map:Map-colouring puzzle:Colour the map so that adjacent regions are never the same colour.
-!end
--- a/mines.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-MINES_EXTRA = tree234
-
-mines    : [X] GTK COMMON mines MINES_EXTRA mines-icon|no-icon
-
-mines    : [G] WINDOWS COMMON mines MINES_EXTRA mines.res|noicon.res
-
-mineobfusc :    [U] mines[STANDALONE_OBFUSCATOR] MINES_EXTRA STANDALONE
-mineobfusc :    [C] mines[STANDALONE_OBFUSCATOR] MINES_EXTRA STANDALONE
-
-ALL += mines[COMBINED] MINES_EXTRA
-
-!begin am gtk
-GAMES += mines
-!end
-
-!begin >list.c
-    A(mines) \
-!end
-
-!begin >gamedesc.txt
-mines:mines.exe:Mines:Mine-finding puzzle:Find all the mines without treading on any of them.
-!end
--- a/mkauto.sh
+++ /dev/null
@@ -1,2 +1,0 @@
-#! /bin/sh
-autoreconf -i && rm -rf autom4te.cache
--- a/mkfiles.pl
+++ /dev/null
@@ -1,1953 +1,0 @@
-#!/usr/bin/env perl
-#
-# Cross-platform Makefile generator.
-#
-# Reads the file `Recipe' to determine the list of generated
-# executables and their component objects. Then reads the source
-# files to compute #include dependencies. Finally, writes out the
-# various target Makefiles.
-
-# PuTTY specifics which could still do with removing:
-#  - Mac makefile is not portabilised at all. Include directories
-#    are hardwired, and also the libraries are fixed. This is
-#    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 warnings;
-use IO::Handle;
-use Cwd;
-use File::Basename;
-
-while ($#ARGV >= 0) {
-    if ($ARGV[0] eq "-U") {
-        # Convenience for Unix users: -U means that after we finish what
-        # we're doing here, we also run mkauto.sh and then 'configure'. So
-        # it's a one-stop shop for regenerating the actual end-product
-        # Unix makefile.
-        #
-        # Arguments supplied after -U go to configure.
-        $do_unix = 1;
-        shift @ARGV;
-        @confargs = @ARGV;
-        @ARGV = ();
-    } else {
-        die "unrecognised command-line argument '$ARGV[0]'\n";
-    }
-}
-
-@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";
-};
-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
-# dependency analysis.
-eval 'chdir "charset"; require "sbcsgen.pl"; chdir ".."';
-
-@srcdirs = ("./");
-
-$divert = undef; # ref to array of refs of scalars in which text is
-                 # currently being put
-$help = ""; # list of newline-free lines of help text
-$project_name = "project"; # this is a good enough default
-%makefiles = (); # maps makefile types to output makefile pathnames
-%makefile_extra = (); # maps makefile types to extra Makefile text
-%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
-
-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;
-
-  # If we're gathering help text, keep doing so.
-  if (defined $divert) {
-      if ((defined $_[0]) && $_[0] eq "!end") {
-	  $divert = undef;
-      } else {
-          for my $ref (@$divert) {
-              ${$ref} .= "$_\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 "!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 "!cflags" and &mfval($_[1])) {
-      ($rest = $_) =~ s/^\s*\S+\s+\S+\s+\S+\s*//; # find rest of input line
-      $rest = 1 if $rest eq "";
-      $cflags{$_[1]}->{$_[2]} = $rest;
-      next;
-  }
-  if ($_[0] eq "!begin") {
-      my @args = @_;
-      shift @args;
-      $divert = [];
-      for my $component (@args) {
-          if ($component =~ /^>(.*)/) {
-              push @$divert, \$auxfiles{$1};
-          } elsif ($component =~ /^([^_]*)(_.*)?$/ and &mfval($1)) {
-              push @$divert, \$makefile_extra{$component};
-          }
-      }
-      next;
-  }
-  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.
-  @objs = @_;
-
-  if ($_[0] eq "+") {
-    $listref = $lastlistref;
-    $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;
-    shift @objs; # eat the group name
-  } elsif ($_[1] eq ":") {
-    $listref = [];
-    $prog = $_[0];
-    shift @objs; # eat the program name
-  } else {
-    die "$.: unrecognised line type: '$_'\n";
-  }
-  shift @objs; # eat the +, the = or the :
-
-  while (scalar @objs > 0) {
-    $i = shift @objs;
-    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]" or $i eq "[MX]") and defined $prog) {
-      $type = substr($i,1,(length $i)-2);
-    } else {
-      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) {
-    die "multiple program entries for $prog [$type]\n"
-        if defined $programs{$prog . "," . $type};
-    $programs{$prog . "," . $type} = $listref;
-  }
-  $lastlistref = $listref;
-}
-
-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
-# object list for each program, and complain if its type isn't set.
-@prognames = sort keys %programs;
-%depends = ();
-@scanlist = ();
-foreach $i (@prognames) {
-  ($prog, $type) = split ",", $i;
-  # Strip duplicate object names.
-  $prev = '';
-  @list = grep { $status = ($prev ne $_); $prev=$_; $status }
-          sort @{$programs{$i}};
-  $programs{$i} = [@list];
-  foreach $jj (@list) {
-    $j = $srcname{$jj};
-    $file = &finddep($j);
-    if (defined $file) {
-      $depends{$jj} = [$file];
-      push @scanlist, $file;
-    }
-  }
-}
-
-# Scan each file on @scanlist and find further inclusions.
-# Inclusions are given by lines of the form `#include "otherfile"'
-# (system headers are automatically ignored by this because they'll
-# be given in angle brackets). Files included by this method are
-# added back on to @scanlist to be scanned in turn (if not already
-# done).
-#
-# Resource scripts (.rc) can also include a file by means of a line
-# ending `ICON "filename"'. Files included by this method are not
-# added to @scanlist because they can never include further files.
-#
-# In this pass we write out a hash %further which maps a source
-# file name into a listref containing further source file names.
-
-%further = ();
-while (scalar @scanlist > 0) {
-  $file = shift @scanlist;
-  next if defined $further{$file}; # skip if we've already done it
-  $further{$file} = [];
-  $dirfile = &findfile($file);
-  open IN, "$dirfile" or die "unable to open source file $file\n";
-  while (<IN>) {
-    chomp;
-    /^\s*#include\s+\"([^\"]+)\"/ and do {
-      push @{$further{$file}}, $1;
-      push @scanlist, $1;
-      next;
-    };
-    /ICON\s+\"([^\"]+)\"\s*$/ and do {
-      push @{$further{$file}}, $1;
-      next;
-    }
-  }
-  close IN;
-}
-
-# Now we're ready to generate the final dependencies section. For
-# each key in %depends, we must expand the dependencies list by
-# iteratively adding entries from %further.
-foreach $i (keys %depends) {
-  %dep = ();
-  @scanlist = @{$depends{$i}};
-  foreach $i (@scanlist) { $dep{$i} = 1; }
-  while (scalar @scanlist > 0) {
-    $file = shift @scanlist;
-    foreach $j (@{$further{$file}}) {
-      if (!$dep{$j}) {
-        $dep{$j} = 1;
-        push @{$depends{$i}}, $j;
-        push @scanlist, $j;
-      }
-    }
-  }
-#  printf "%s: %s\n", $i, join ' ',@{$depends{$i}};
-}
-
-# Validation of input.
-
-sub mfval($) {
-    my ($type) = @_;
-    # 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","am","mpw","nestedvm","osx","wce","gnustep","emcc","clangcl")) {
-	    return 1;
-	}
-    warn "$.:unknown makefile type '$type'\n";
-    return 0;
-}
-
-# Utility routines while writing out the Makefiles.
-
-sub dirpfx {
-    my ($path) = shift @_;
-    my ($sep) = shift @_;
-    my $ret = "";
-    my $i;
-    while (($i = index $path, $sep) >= 0) {
-	$path = substr $path, ($i + length $sep);
-	$ret .= "..$sep";
-    }
-    return $ret;
-}
-
-sub findfile {
-  my ($name) = @_;
-  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} = (defined $outdir ? $outdir . $name : undef);
-  }
-  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 $ii (@{$programs{$prog}}) {
-    $i = $objname{$ii};
-    $x = "";
-    if ($i =~ /^(.*)\.(res|rsrc)/) {
-      $y = $1;
-      ($x = $rtmpl) =~ s/X/$y/;
-    } elsif ($i =~ /^(.*)\.lib/) {
-      $y = $1;
-      ($x = $ltmpl) =~ s/X/$y/;
-    } elsif ($i !~ /\./) {
-      ($x = $otmpl) =~ s/X/$i/;
-    }
-    push @ret, $x if $x ne "";
-  }
-  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 = "";
-  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;
-    $result .= " ${splitchar}\n\t\t" if $2 ne '';
-    $line = $2;
-    $len = 60;
-  }
-  return $result . $line;
-}
-
-sub deps {
-  my ($otmpl, $rtmpl, $prefix, $dirsep, $depchar, $splitchar) = @_;
-  my ($i, $x, $y);
-  my @deps;
-  my @ret;
-  @ret = ();
-  $depchar ||= ':';
-  foreach $ii (sort keys %depends) {
-    $i = $objname{$ii};
-    next if $specialobj{$mftyp}->{$i};
-    if ($i =~ /^(.*)\.(res|rsrc)/) {
-      next if !defined $rtmpl;
-      $y = $1;
-      ($x = $rtmpl) =~ s/X/$y/;
-    } else {
-      ($x = $otmpl) =~ s/X/$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], defs => $defs{$ii}};
-  }
-  return @ret;
-}
-
-sub prognames {
-  my ($types) = @_;
-  my ($n, $prog, $type);
-  my @ret;
-  @ret = ();
-  foreach $n (@prognames) {
-    ($prog, $type) = split ",", $n;
-    push @ret, $n if index(":$types:", ":$type:") >= 0;
-  }
-  return @ret;
-}
-
-sub progrealnames {
-  my ($types) = @_;
-  my ($n, $prog, $type);
-  my @ret;
-  @ret = ();
-  foreach $n (@prognames) {
-    ($prog, $type) = split ",", $n;
-    push @ret, $prog if index(":$types:", ":$type:") >= 0;
-  }
-  return @ret;
-}
-
-sub manpages {
-  my ($types,$suffix) = @_;
-
-  # assume that all UNIX programs have a man page
-  if($suffix eq "1" && $types =~ /:X:/) {
-    return map("$_.1", &progrealnames($types));
-  }
-  return ();
-}
-
-$orig_dir = cwd;
-
-# Now we're ready to output the actual Makefiles.
-
-if (defined $makefiles{'clangcl'}) {
-    $mftyp = 'clangcl';
-    $dirpfx = &dirpfx($makefiles{'clangcl'}, "/");
-
-    ##-- Makefile for cross-compiling using clang-cl, lld-link, and
-    ##   MinGW's windres for resource compilation.
-    #
-    # This makefile allows a complete Linux-based cross-compile, but
-    # using the real Visual Studio header files and libraries. In
-    # order to run it, you will need:
-    #
-    #  - MinGW windres on your PATH.
-    #     * On Ubuntu as of 16.04, you can apt-get install
-    #       binutils-mingw-w64-x86-64 and binutils-mingw-w64-i686
-    #       which will provide (respectively) 64- and 32-bit versions,
-    #       under the names to which RCCMD is defined below.
-    #  - clang-cl and lld-link on your PATH.
-    #     * I built these from the up-to-date LLVM project trunk git
-    #       repositories, as of 2017-02-05.
-    #  - case-mashed copies of the Visual Studio include directories.
-    #     * On a real VS installation, run vcvars32.bat and look at
-    #       the resulting value of %INCLUDE%. Take a full copy of each
-    #       of those directories, and inside the copy, for each
-    #       include file that has an uppercase letter in its name,
-    #       make a lowercased symlink to it. Additionally, one of the
-    #       directories will contain files called driverspecs.h and
-    #       specstrings.h, and those will need symlinks called
-    #       DriverSpecs.h and SpecStrings.h.
-    #     * Now, on Linux, define the environment variable INCLUDE to
-    #       be a list, separated by *semicolons* (in the Windows
-    #       style), of those directories, but before all of them you
-    #       must also include lib/clang/5.0.0/include from the clang
-    #       installation area (which contains in particular a
-    #       clang-compatible stdarg.h overriding the Visual Studio
-    #       one).
-    #  - similarly case-mashed copies of the library directories.
-    #     * Again, on a real VS installation, run vcvars32 or
-    #       vcvarsx86_amd64 (as appropriate), look at %LIB%, make a
-    #       copy of each directory, and provide symlinks within that
-    #       directory so that all the files can be opened as
-    #       lowercase.
-    #     * Then set LIB to be a semicolon-separated list of those
-    #       directories (but you'll need to change which set of
-    #       directories depending on whether you want to do a 32-bit
-    #       or 64-bit build).
-    #  - for a 64-bit build, set 'Platform=x64' in the environment as
-    #    well, or else on the make command line.
-    #     * This is a variable understood only by this makefile - none
-    #       of the tools we invoke will know it - but it's consistent
-    #       with the way the VS scripts like vcvarsx86_amd64.bat set
-    #       things up, and since the environment has to change
-    #       _anyway_ between 32- and 64-bit builds (different set of
-    #       paths in $LIB) it's reasonable to have the choice of
-    #       compilation target driven by another environment variable
-    #       set in parallel with that one.
-    #  - for older versions of the VS libraries you may also have to
-    #    set EXTRA_console and/or EXTRA_windows to the name of an
-    #    object file manually extracted from one of those libraries.
-    #     * This is because old VS seems to manage its startup code by
-    #       having libcmt.lib contain lots of *crt0.obj objects, one
-    #       for each possible user entry point (main, WinMain and the
-    #       wide-char versions of both), of which the linker arranges
-    #       to include the right one by special-case code. But lld
-    #       only seems to mimic half of that code - it does include
-    #       the right crt0 object, but it doesn't also deliberately
-    #       _avoid_ including the _wrong_ ones, and since all those
-    #       objects define a common set of global symbols for other
-    #       parts of the library to use, lld may well select an
-    #       arbitrary one of them the first time it sees a reference
-    #       to one of those global symbols, and then later also select
-    #       the _right_ one for the application's entry point, causing
-    #       a multiple-definitions crash.
-    #     * So the workaround is to explicitly include the right
-    #       *crt0.obj file on the linker command line before lld even
-    #       begins searching libraries. Hence, for a console
-    #       application, you might extract crt0.obj from the library
-    #       in question and set EXTRA_console=crt0.obj, and for a GUI
-    #       application, do the same with wincrt0.obj. Then this
-    #       makefile will include the right one of those objects
-    #       alongside the matching /subsystem linker option.
-
-    open OUT, ">$makefiles{'clangcl'}"; select OUT;
-    print
-    "# Makefile for cross-compiling $project_name using clang-cl, lld-link,\n".
-    "# and MinGW's windres, using GNU make on Linux.\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".
-    "CCCMD = clang-cl\n".
-    "ifeq (\$(Platform),x64)\n".
-    "CCTARGET = x86_64-pc-windows-msvc18.0.0\n".
-    "RCCMD = x86_64-w64-mingw32-windres\n".
-    "else\n".
-    "CCTARGET = i386-pc-windows-msvc18.0.0\n".
-    "RCCMD = i686-w64-mingw32-windres\n".
-    "endif\n".
-    "CC = \$(CCCMD) --target=\$(CCTARGET)\n".
-    &splitline("RC = \$(RCCMD) --preprocessor=\$(CCCMD) ".
-               "--preprocessor-arg=/TC --preprocessor-arg=/E")."\n".
-    "LD = lld-link\n".
-    "\n".
-    "# C compilation flags\n".
-    &splitline("CFLAGS = /nologo /W3 /O1 " .
-               (join " ", map {"-I$dirpfx$_"} @srcdirs) .
-               " /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401 ".
-               "/D_CRT_SECURE_NO_WARNINGS")."\n".
-    "LFLAGS = /incremental:no /dynamicbase /nxcompat\n".
-    &splitline("RCFLAGS = ".(join " ", map {"-I$dirpfx$_"} @srcdirs).
-               " -DWIN32 -D_WIN32 -DWINVER=0x0400 --define MINGW32_FIX=1")."\n".
-    "\n".
-    "\n";
-    print &splitline("all:" . join "", map { " \$(BUILDDIR)$_.exe" } &progrealnames("G:C"));
-    print "\n\n";
-    foreach $p (&prognames("G:C")) {
-	($prog, $type) = split ",", $p;
-	$objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", undef);
-	print &splitline("\$(BUILDDIR)$prog.exe: " . $objstr), "\n";
-
-	$objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", "X.lib");
-	$subsys = ($type eq "G") ? "windows" : "console";
-	print &splitline("\t\$(LD) \$(LFLAGS) \$(XLFLAGS) ".
-                         "/out:\$(BUILDDIR)$prog.exe ".
-                         "/lldmap:\$(BUILDDIR)$prog.map ".
-                         "/subsystem:$subsys\$(SUBSYSVER) ".
-                         "\$(EXTRA_$subsys) $objstr")."\n\n";
-    }
-    foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", $dirpfx, "/", "vc")) {
-        print &splitline(sprintf("%s: %s", $d->{obj},
-                                 join " ", @{$d->{deps}})), "\n";
-        if ($d->{obj} =~ /\.res$/) {
-            print "\t\$(RC) \$(RCFLAGS) ".$d->{deps}->[0]." -o ".$d->{obj}."\n\n";
-	} else {
-	    $deflist = join "", map { " /D$_" } @{$d->{defs}};
-            print "\t\$(CC) /Fo".$d->{obj}." \$(COMPAT) \$(CFLAGS) \$(XFLAGS)$deflist /c \$<\n\n";
-        }
-    }
-    print "\nclean:\n".
-        &splitline("\trm -f \$(BUILDDIR)*.obj \$(BUILDDIR)*.exe ".
-                   "\$(BUILDDIR)*.res \$(BUILDDIR)*.map ".
-                   "\$(BUILDDIR)*.exe.manifest")."\n";
-    select STDOUT; close OUT;
-}
-
-if (defined $makefiles{'cygwin'}) {
-    $mftyp = 'cygwin';
-    $dirpfx = &dirpfx($makefiles{'cygwin'}, "/");
-
-    ##-- CygWin makefile
-    open OUT, ">$makefiles{'cygwin'}"; select OUT;
-    print
-    "# Makefile for $project_name under cygwin.\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".
-    "# You can define this path to point at your tools if you need to\n".
-    "# TOOLPATH = c:\\cygwin\\bin\\ # or similar, if you're running Windows\n".
-    "# TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/\n".
-    "CC = \$(TOOLPATH)gcc\n".
-    "RC = \$(TOOLPATH)windres\n".
-    "# Uncomment the following two lines to compile under Winelib\n".
-    "# CC = winegcc\n".
-    "# RC = wrc\n".
-    "# You may also need to tell windres where to find include files:\n".
-    "# RCINC = --include-dir c:\\cygwin\\include\\\n".
-    "\n".
-    &splitline("CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT".
-      " -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 " .
-	(join " ", map {"--include $dirpfx$_"} @srcdirs) )."\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.o", "X.res.o", undef);
-      print &splitline($prog . ".exe: " . $objstr), "\n";
-      my $mw = $type eq "G" ? " -mwindows" : "";
-      $libstr = &objects($p, undef, undef, "-lX");
-      print &splitline("\t\$(CC)" . $mw . " \$(LDFLAGS) -o \$@ " .
-                       "-Wl,-Map,$prog.map " .
-                       $objstr . " $libstr", 69), "\n\n";
-    }
-    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 "\nclean:\n".
-    "\trm -f *.o *.exe *.res.o *.map\n".
-    "\n";
-    select STDOUT; close OUT;
-
-}
-
-##-- Borland makefile
-if (defined $makefiles{'borland'}) {
-    $mftyp = 'borland';
-    $dirpfx = &dirpfx($makefiles{'borland'}, "\\");
-
-    %stdlibs = (  # Borland provides many Win32 API libraries intrinsically
-      "advapi32" => 1,
-      "comctl32" => 1,
-      "comdlg32" => 1,
-      "gdi32" => 1,
-      "imm32" => 1,
-      "shell32" => 1,
-      "user32" => 1,
-      "winmm" => 1,
-      "winspool" => 1,
-      "wsock32" => 1,
-    );
-    open OUT, ">$makefiles{'borland'}"; select OUT;
-    print
-    "# Makefile for $project_name under Borland 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";
-    # bcc32 command line option is -D not /D
-    ($_ = $help) =~ s/=\/D/=-D/gs;
-    print $_;
-    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.bor\n".
-    "\n".
-    "# C compilation flags\n".
-    "CFLAGS = -D_WINDOWS -DWINVER=0x0401\n".
-    "\n".
-    "# Get include directory for resource compiler\n".
-    "!if !\$d(BCB)\n".
-    "BCB = \$(MAKEDIR)\\..\n".
-    "!endif\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);
-      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("G:C")) {
-      ($prog, $type) = split ",", $p;
-      print $prog, ".rsp: \$(MAKEFILE)\n";
-      $objstr = &objects($p, "X.obj", undef, undef);
-      @objlist = split " ", $objstr;
-      @objlines = ("");
-      foreach $i (@objlist) {
-        if (length($objlines[$#objlines] . " $i") > 50) {
-          push @objlines, "";
-        }
-        $objlines[$#objlines] .= " $i";
-      }
-      $c0w = ($type eq "G") ? "c0w32" : "c0x32";
-      print "\techo $c0w + > $prog.rsp\n";
-      for ($i=0; $i<=$#objlines; $i++) {
-        $plus = ($i < $#objlines ? " +" : "");
-        print "\techo$objlines[$i]$plus >> $prog.rsp\n";
-      }
-      print "\techo $prog.exe >> $prog.rsp\n";
-      $objstr = &objects($p, "X.obj", "X.res", undef);
-      @libs = split " ", &objects($p, undef, undef, "X");
-      @libs = grep { !$stdlibs{$_} } @libs;
-      unshift @libs, "cw32", "import32";
-      $libstr = join ' ', @libs;
-      print "\techo nul,$libstr, >> $prog.rsp\n";
-      print "\techo " . &objects($p, undef, "X.res", undef) . " >> $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 &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 "\nclean:\n".
-    "\t-del *.obj\n".
-    "\t-del *.exe\n".
-    "\t-del *.res\n".
-    "\t-del *.pch\n".
-    "\t-del *.aps\n".
-    "\t-del *.il*\n".
-    "\t-del *.pdb\n".
-    "\t-del *.rsp\n".
-    "\t-del *.tds\n".
-    "\t-del *.\$\$\$\$\$\$\n";
-    select STDOUT; close OUT;
-}
-
-if (defined $makefiles{'vc'}) {
-    $mftyp = 'vc';
-    $dirpfx = &dirpfx($makefiles{'vc'}, "\\");
-
-    ##-- Visual C++ makefile
-    open OUT, ">$makefiles{'vc'}"; select OUT;
-    print
-      "# Makefile for $project_name under 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.vc\n".
-      "\n".
-      "# C compilation flags\n".
-      "CFLAGS = /nologo /W3 /O1 /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401 /I.\n".
-      "LFLAGS = /incremental:no /fixed\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);
-	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:C")) {
-	($prog, $type) = split ",", $p;
-	print $prog, ".rsp: \$(MAKEFILE)\n";
-	$objstr = &objects($p, "X.obj", "X.res", "X.lib");
-	@objlist = split " ", $objstr;
-	@objlines = ("");
-	foreach $i (@objlist) {
-	    if (length($objlines[$#objlines] . " $i") > 50) {
-		push @objlines, "";
-	    }
-	    $objlines[$#objlines] .= " $i";
-	}
-	$subsys = ($type eq "G") ? "windows" : "console";
-	print "\techo /nologo /subsystem:$subsys > $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 -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{'wce'} || "";
-    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{'vcproj'}) {
-    $mftyp = 'vcproj';
-
-    ##-- MSVC 6 Workspace and projects
-    #
-    # Note: All files created in this section are written in binary
-    # mode, because although MSVC's command-line make can deal with
-    # LF-only line endings, MSVC project files really _need_ to be
-    # CRLF. Hence, in order for mkfiles.pl to generate usable project
-    # files even when run from Unix, I make sure all files are binary
-    # and explicitly write the CRLFs.
-    #
-    # Create directories if necessary
-    mkdir $makefiles{'vcproj'}
-        if(! -d $makefiles{'vcproj'});
-    chdir $makefiles{'vcproj'};
-    @deps = &deps("X.obj", "X.res", "", "\\");
-    %all_object_deps = map {$_->{obj} => $_->{deps}} @deps;
-    # Create the project files
-    # Get names of all Windows projects (GUI and console)
-    my @prognames = &prognames("G:C");
-    foreach $progname (@prognames) {
-    	create_project(\%all_object_deps, $progname);
-    }
-    # Create the workspace file
-    open OUT, ">$project_name.dsw"; binmode OUT; select OUT;
-    print
-    "Microsoft Developer Studio Workspace File, Format Version 6.00\r\n".
-    "# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r\n".
-    "\r\n".
-    "###############################################################################\r\n".
-    "\r\n";
-    # List projects
-    foreach $progname (@prognames) {
-      ($windows_project, $type) = split ",", $progname;
-    	print "Project: \"$windows_project\"=\".\\$windows_project\\$windows_project.dsp\" - Package Owner=<4>\r\n";
-    }
-    print
-    "\r\n".
-    "Package=<5>\r\n".
-    "{{{\r\n".
-    "}}}\r\n".
-    "\r\n".
-    "Package=<4>\r\n".
-    "{{{\r\n".
-    "}}}\r\n".
-    "\r\n".
-    "###############################################################################\r\n".
-    "\r\n".
-    "Global:\r\n".
-    "\r\n".
-    "Package=<5>\r\n".
-    "{{{\r\n".
-    "}}}\r\n".
-    "\r\n".
-    "Package=<3>\r\n".
-    "{{{\r\n".
-    "}}}\r\n".
-    "\r\n".
-    "###############################################################################\r\n".
-    "\r\n";
-    select STDOUT; close OUT;
-    chdir $orig_dir;
-
-    sub create_project {
-    	my ($all_object_deps, $progname) = @_;
-    	# Construct program's dependency info
-    	%seen_objects = ();
-    	%lib_files = ();
-    	%source_files = ();
-    	%header_files = ();
-    	%resource_files = ();
-    	@object_files = split " ", &objects($progname, "X.obj", "X.res", "X.lib");
-    	foreach $object_file (@object_files) {
-	    next if defined $seen_objects{$object_file};
-	    $seen_objects{$object_file} = 1;
-	    if($object_file =~ /\.lib$/io) {
-		$lib_files{$object_file} = 1;
-		next;
-	    }
-	    $object_deps = $all_object_deps{$object_file};
-	    foreach $object_dep (@$object_deps) {
-		if($object_dep =~ /\.c$/io) {
-		    $source_files{$object_dep} = 1;
-		    next;
-		}
-		if($object_dep =~ /\.h$/io) {
-		    $header_files{$object_dep} = 1;
-		    next;
-		}
-		if($object_dep =~ /\.(rc|ico)$/io) {
-		    $resource_files{$object_dep} = 1;
-		    next;
-		}
-	    }
-    	}
-    	$libs = join " ", sort keys %lib_files;
-    	@source_files = sort keys %source_files;
-    	@header_files = sort keys %header_files;
-    	@resources = sort keys %resource_files;
-	($windows_project, $type) = split ",", $progname;
-    	mkdir $windows_project
-	    if(! -d $windows_project);
-    	chdir $windows_project;
-	$subsys = ($type eq "G") ? "windows" : "console";
-    	open OUT, ">$windows_project.dsp"; binmode OUT; select OUT;
-    	print
-    	"# Microsoft Developer Studio Project File - Name=\"$windows_project\" - Package Owner=<4>\r\n".
-    	"# Microsoft Developer Studio Generated Build File, Format Version 6.00\r\n".
-    	"# ** DO NOT EDIT **\r\n".
-    	"\r\n".
-    	"# TARGTYPE \"Win32 (x86) Application\" 0x0101\r\n".
-    	"\r\n".
-    	"CFG=$windows_project - Win32 Debug\r\n".
-    	"!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r\n".
-    	"!MESSAGE use the Export Makefile command and run\r\n".
-    	"!MESSAGE \r\n".
-    	"!MESSAGE NMAKE /f \"$windows_project.mak\".\r\n".
-    	"!MESSAGE \r\n".
-    	"!MESSAGE You can specify a configuration when running NMAKE\r\n".
-    	"!MESSAGE by defining the macro CFG on the command line. For example:\r\n".
-    	"!MESSAGE \r\n".
-    	"!MESSAGE NMAKE /f \"$windows_project.mak\" CFG=\"$windows_project - Win32 Debug\"\r\n".
-    	"!MESSAGE \r\n".
-    	"!MESSAGE Possible choices for configuration are:\r\n".
-    	"!MESSAGE \r\n".
-    	"!MESSAGE \"$windows_project - Win32 Release\" (based on \"Win32 (x86) Application\")\r\n".
-    	"!MESSAGE \"$windows_project - Win32 Debug\" (based on \"Win32 (x86) Application\")\r\n".
-    	"!MESSAGE \r\n".
-    	"\r\n".
-    	"# Begin Project\r\n".
-    	"# PROP AllowPerConfigDependencies 0\r\n".
-    	"# PROP Scc_ProjName \"\"\r\n".
-    	"# PROP Scc_LocalPath \"\"\r\n".
-    	"CPP=cl.exe\r\n".
-    	"MTL=midl.exe\r\n".
-    	"RSC=rc.exe\r\n".
-    	"\r\n".
-    	"!IF  \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n".
-    	"\r\n".
-    	"# PROP BASE Use_MFC 0\r\n".
-    	"# PROP BASE Use_Debug_Libraries 0\r\n".
-    	"# PROP BASE Output_Dir \"Release\"\r\n".
-    	"# PROP BASE Intermediate_Dir \"Release\"\r\n".
-    	"# PROP BASE Target_Dir \"\"\r\n".
-    	"# PROP Use_MFC 0\r\n".
-    	"# PROP Use_Debug_Libraries 0\r\n".
-    	"# PROP Output_Dir \"Release\"\r\n".
-    	"# PROP Intermediate_Dir \"Release\"\r\n".
-    	"# PROP Ignore_Export_Lib 0\r\n".
-    	"# PROP Target_Dir \"\"\r\n".
-    	"# ADD BASE CPP /nologo /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n".
-    	"# ADD CPP /nologo /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n".
-    	"# ADD BASE MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n".
-    	"# ADD MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n".
-    	"# ADD BASE RSC /l 0x809 /d \"NDEBUG\"\r\n".
-    	"# ADD RSC /l 0x809 /d \"NDEBUG\"\r\n".
-    	"BSC32=bscmake.exe\r\n".
-    	"# ADD BASE BSC32 /nologo\r\n".
-    	"# ADD BSC32 /nologo\r\n".
-    	"LINK32=link.exe\r\n".
-    	"# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /machine:I386\r\n".
-    	"# ADD LINK32 $libs /nologo /subsystem:$subsys /machine:I386\r\n".
-    	"# SUBTRACT LINK32 /pdb:none\r\n".
-    	"\r\n".
-    	"!ELSEIF  \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n".
-    	"\r\n".
-    	"# PROP BASE Use_MFC 0\r\n".
-    	"# PROP BASE Use_Debug_Libraries 1\r\n".
-    	"# PROP BASE Output_Dir \"Debug\"\r\n".
-    	"# PROP BASE Intermediate_Dir \"Debug\"\r\n".
-    	"# PROP BASE Target_Dir \"\"\r\n".
-    	"# PROP Use_MFC 0\r\n".
-    	"# PROP Use_Debug_Libraries 1\r\n".
-    	"# PROP Output_Dir \"Debug\"\r\n".
-    	"# PROP Intermediate_Dir \"Debug\"\r\n".
-    	"# PROP Ignore_Export_Lib 0\r\n".
-    	"# PROP Target_Dir \"\"\r\n".
-    	"# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n".
-    	"# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n".
-    	"# ADD BASE MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n".
-    	"# ADD MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n".
-    	"# ADD BASE RSC /l 0x809 /d \"_DEBUG\"\r\n".
-    	"# ADD RSC /l 0x809 /d \"_DEBUG\"\r\n".
-    	"BSC32=bscmake.exe\r\n".
-    	"# ADD BASE BSC32 /nologo\r\n".
-    	"# ADD BSC32 /nologo\r\n".
-    	"LINK32=link.exe\r\n".
-    	"# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n".
-    	"# ADD LINK32 $libs /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n".
-    	"# SUBTRACT LINK32 /pdb:none\r\n".
-    	"\r\n".
-    	"!ENDIF \r\n".
-    	"\r\n".
-    	"# Begin Target\r\n".
-    	"\r\n".
-    	"# Name \"$windows_project - Win32 Release\"\r\n".
-    	"# Name \"$windows_project - Win32 Debug\"\r\n".
-    	"# Begin Group \"Source Files\"\r\n".
-    	"\r\n".
-    	"# PROP Default_Filter \"cpp;c;cxx;rc;def;r;odl;idl;hpj;bat\"\r\n";
-    	foreach $source_file (@source_files) {
-	    print
-	      "# Begin Source File\r\n".
-	      "\r\n".
-	      "SOURCE=..\\..\\$source_file\r\n";
-	    if($source_file =~ /ssh\.c/io) {
-		# Disable 'Edit and continue' as Visual Studio can't handle the macros
-		print
-		  "\r\n".
-		  "!IF  \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n".
-		  "\r\n".
-		  "!ELSEIF  \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n".
-		  "\r\n".
-		  "# ADD CPP /Zi\r\n".
-		  "\r\n".
-		  "!ENDIF \r\n".
-		  "\r\n";
-	    }
-	    print "# End Source File\r\n";
-    	}
-    	print
-    	"# End Group\r\n".
-    	"# Begin Group \"Header Files\"\r\n".
-    	"\r\n".
-    	"# PROP Default_Filter \"h;hpp;hxx;hm;inl\"\r\n";
-    	foreach $header_file (@header_files) {
-	    print
-	      "# Begin Source File\r\n".
-	      "\r\n".
-	      "SOURCE=..\\..\\$header_file\r\n".
-	      "# End Source File\r\n";
-	}
-    	print
-    	"# End Group\r\n".
-    	"# Begin Group \"Resource Files\"\r\n".
-    	"\r\n".
-    	"# PROP Default_Filter \"ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe\"\r\n";
-    	foreach $resource_file (@resources) {
-	    print
-	      "# Begin Source File\r\n".
-	      "\r\n".
-	      "SOURCE=..\\..\\$resource_file\r\n".
-	      "# End Source File\r\n";
-	}
-    	print
-    	"# End Group\r\n".
-    	"# End Target\r\n".
-    	"# End Project\r\n";
-    	select STDOUT; close OUT;
-    	chdir "..";
-    }
-}
-
-if (defined $makefiles{'gtk'}) {
-    $mftyp = 'gtk';
-    $dirpfx = &dirpfx($makefiles{'gtk'}, "/");
-
-    ##-- X/GTK/Unix makefile
-    open OUT, ">$makefiles{'gtk'}"; select OUT;
-    print
-    "# Makefile for $project_name under X/GTK and Unix.\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".
-    "# 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 -ansi -pedantic -g " .
-	       (join " ", map {"-I$dirpfx$_"} @srcdirs) .
-	       " `\$(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";
-    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("\$(BINPREFIX)" . $prog . ": " . $objstr), "\n";
-      $libstr = &objects($p, undef, undef, "-lX");
-      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 "\nclean:\n".
-    "\trm -f *.o". (join "", map { " \$(BINPREFIX)$_" } &progrealnames("X:U")) . "\n";
-    select STDOUT; close OUT;
-}
-
-if (defined $makefiles{'am'}) {
-    $mftyp = 'am';
-    die "Makefile.am in a subdirectory is not supported\n"
-        if &dirpfx($makefiles{'am'}, "/") ne "";
-
-    ##-- Unix/autoconf Makefile.am
-    open OUT, ">$makefiles{'am'}"; select OUT;
-    print
-    "# Makefile.am for $project_name under Unix with Autoconf/Automake.\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\n";
-
-    print $makefile_extra{'am_begin'} || "";
-
-    # All programs go in noinstprogs by default. If you want them
-    # installed anywhere else, you have to also add them to
-    # bin_PROGRAMS using '!begin am'. (Automake doesn't seem to mind
-    # having a program name in _both_ of bin_PROGRAMS and
-    # noinst_PROGRAMS.)
-    @noinstprogs = ();
-    foreach $p (&prognames("X:U")) {
-        ($prog, $type) = split ",", $p;
-        push @noinstprogs, $prog;
-    }
-    print &splitline(join " ", "noinst_PROGRAMS", "=", @noinstprogs), "\n";
-
-    %objtosrc = ();
-    %amspeciallibs = ();
-    %amlibobjname = ();
-    %allsources = ();
-    foreach $d (&deps("X", undef, "", "/", "am")) {
-        my $obj = $d->{obj};
-        my $use_archive = 0;
-        
-        if (defined $d->{defs}) {
-            # This file needs to go in an archive, so that we can
-            # change the preprocess flags to include some -Ds
-            $use_archive = 1;
-            $archivecppflags{$obj} = [map { " -D$_" } @{$d->{defs}}];
-        }
-        if (defined $cflags{'am'} && $cflags{'am'}->{$obj}) {
-            # This file needs to go in an archive, so that we can
-            # change the compile flags as specified in Recipe
-            $use_archive = 1;
-            $archivecflags{$obj} = [$cflags{'am'}->{$obj}];
-        }
-        if ($use_archive) {
-            $amspeciallibs{$obj} = "lib${obj}.a";
-            $amlibobjname{$obj} = "lib${obj}_a-" .
-                basename($d->{deps}->[0], ".c", ".m") .
-                ".\$(OBJEXT)";
-        }
-        $objtosrc{$obj} = $d->{deps};
-        map { $allsources{$_} = 1 } @{$d->{deps}};
-    }
-
-    # 2014-02-22: as of automake-1.14 we begin to get complained at if
-    # we don't use this option
-    print "AUTOMAKE_OPTIONS = subdir-objects\n\n";
-
-    # Complete list of source and header files. Not used by the
-    # auto-generated parts of this makefile, but Recipe might like to
-    # have it available as a variable so that mandatory-rebuild things
-    # (version.o) can conveniently be made to depend on it.
-    print &splitline(join " ", "allsources", "=",
-                     sort {$a cmp $b} keys %allsources), "\n\n";
-
-    @amcppflags = map {"-I\$(srcdir)/$_"} @srcdirs;
-    print &splitline(join " ", "AM_CPPFLAGS", "=", @amcppflags, "\n");
-
-    @amcflags = ("\$(GTK_CFLAGS)", "\$(WARNINGOPTS)");
-    print &splitline(join " ", "AM_CFLAGS", "=", @amcflags), "\n";
-
-    %amlibsused = ();
-    foreach $p (&prognames("X:U")) {
-        ($prog, $type) = split ",", $p;
-        @progsources = ("${prog}_SOURCES", "=");
-        %sourcefiles = ();
-        @ldadd = ();
-        $objstr = &objects($p, "X", undef, undef);
-        foreach $obj (split / /,$objstr) {
-            if ($amspeciallibs{$obj}) {
-                $amlibsused{$obj} = 1;
-                push @ldadd, $amlibobjname{$obj};
-            } else {
-                map { $sourcefiles{$_} = 1 } @{$objtosrc{$obj}};
-            }
-        }
-        push @progsources, sort { $a cmp $b } keys %sourcefiles;
-        print &splitline(join " ", @progsources), "\n";
-        if ($type eq "X") {
-            push @ldadd, "\$(GTK_LIBS)";
-        }
-        push @ldadd, "-lm";
-        print &splitline(join " ", "${prog}_LDADD", "=", @ldadd), "\n";
-        print "\n";
-    }
-
-    foreach $obj (sort { $a cmp $b } keys %amlibsused) {
-        print &splitline(join " ", "lib${obj}_a_SOURCES", "=",
-                         @{$objtosrc{$obj}}), "\n";
-        print &splitline(join " ", "lib${obj}_a_CPPFLAGS", "=",
-                         @amcflags, @{$archivecppflags{$obj}}), "\n"
-                             if $archivecppflags{$obj};
-        print &splitline(join " ", "lib${obj}_a_CFLAGS", "=",
-                         @amcflags, @{$archivecflags{$obj}}), "\n"
-                             if $archivecflags{$obj};
-    }
-    print &splitline(join " ", "noinst_LIBRARIES", "=",
-                     sort { $a cmp $b }
-                      map { $amspeciallibs{$_} }
-                     keys %amlibsused),
-                     "\n\n";
-
-    print $makefile_extra{'am'} || "";
-    select STDOUT; close OUT;
-}
-
-if (defined $makefiles{'mpw'}) {
-    $mftyp = 'mpw';
-    ##-- MPW Makefile
-    open OUT, ">$makefiles{'mpw'}"; select OUT;
-    print
-    "# Makefile for $project_name under MPW.\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";
-    # MPW command line option is -d not /D
-    ($_ = $help) =~ s/=\/D/=-d /gs;
-    print $_;
-    print "\n\n".
-    "ROptions     = `Echo \"{VER}\" | StreamEdit -e \"1,\$ replace /=(\xc5)\xa81\xb0/ 'STR=\xb6\xb6\xb6\xb6\xb6\"' \xa81 '\xb6\xb6\xb6\xb6\xb6\"'\"`".
-    "\n".
-    "C_68K = {C}\n".
-    "C_CFM68K = {C}\n".
-    "C_PPC = {PPCC}\n".
-    "C_Carbon = {PPCC}\n".
-    "\n".
-    "# -w 35 disables \"unused parameter\" warnings\n".
-    "COptions     = -i : -i :: -i ::charset -w 35 -w err -proto strict -ansi on \xb6\n".
-    "	       -notOnce\n".
-    "COptions_68K = {COptions} -model far -opt time\n".
-    "# Enabling \"-opt space\" for CFM-68K gives me undefined references to\n".
-    "# _\$LDIVT and _\$LMODT.\n".
-    "COptions_CFM68K = {COptions} -model cfmSeg -opt time\n".
-    "COptions_PPC = {COptions} -opt size -traceback\n".
-    "COptions_Carbon = {COptions} -opt size -traceback -d TARGET_API_MAC_CARBON\n".
-    "\n".
-    "Link_68K = ILink\n".
-    "Link_CFM68K = ILink\n".
-    "Link_PPC = PPCLink\n".
-    "Link_Carbon = PPCLink\n".
-    "\n".
-    "LinkOptions = -c 'pTTY'\n".
-    "LinkOptions_68K = {LinkOptions} -br 68k -model far -compact\n".
-    "LinkOptions_CFM68K = {LinkOptions} -br 020 -model cfmseg -compact\n".
-    "LinkOptions_PPC = {LinkOptions}\n".
-    "LinkOptions_Carbon = -m __appstart -w {LinkOptions}\n".
-    "\n".
-    "Libs_68K =	\"{CLibraries}StdCLib.far.o\" \xb6\n".
-    "		\"{Libraries}MacRuntime.o\" \xb6\n".
-    "		\"{Libraries}MathLib.far.o\" \xb6\n".
-    "		\"{Libraries}IntEnv.far.o\" \xb6\n".
-    "		\"{Libraries}Interface.o\" \xb6\n".
-    "		\"{Libraries}Navigation.far.o\" \xb6\n".
-    "		\"{Libraries}OpenTransport.o\" \xb6\n".
-    "		\"{Libraries}OpenTransportApp.o\" \xb6\n".
-    "		\"{Libraries}OpenTptInet.o\" \xb6\n".
-    "		\"{Libraries}UnicodeConverterLib.far.o\"\n".
-    "\n".
-    "Libs_CFM =	\"{SharedLibraries}InterfaceLib\" \xb6\n".
-    "		\"{SharedLibraries}StdCLib\" \xb6\n".
-    "		\"{SharedLibraries}AppearanceLib\" \xb6\n".
-    "			-weaklib AppearanceLib \xb6\n".
-    "		\"{SharedLibraries}NavigationLib\" \xb6\n".
-    "			-weaklib NavigationLib \xb6\n".
-    "		\"{SharedLibraries}TextCommon\" \xb6\n".
-    "			-weaklib TextCommon \xb6\n".
-    "		\"{SharedLibraries}UnicodeConverter\" \xb6\n".
-    "			-weaklib UnicodeConverter\n".
-    "\n".
-    "Libs_CFM68K =	{Libs_CFM} \xb6\n".
-    "		\"{CFM68KLibraries}NuMacRuntime.o\"\n".
-    "\n".
-    "Libs_PPC =	{Libs_CFM} \xb6\n".
-    "		\"{SharedLibraries}ControlsLib\" \xb6\n".
-    "			-weaklib ControlsLib \xb6\n".
-    "		\"{SharedLibraries}WindowsLib\" \xb6\n".
-    "			-weaklib WindowsLib \xb6\n".
-    "		\"{SharedLibraries}OpenTransportLib\" \xb6\n".
-    "			-weaklib OTClientLib \xb6\n".
-    "			-weaklib OTClientUtilLib \xb6\n".
-    "		\"{SharedLibraries}OpenTptInternetLib\" \xb6\n".
-    "			-weaklib OTInetClientLib \xb6\n".
-    "		\"{PPCLibraries}StdCRuntime.o\" \xb6\n".
-    "		\"{PPCLibraries}PPCCRuntime.o\" \xb6\n".
-    "		\"{PPCLibraries}CarbonAccessors.o\" \xb6\n".
-    "		\"{PPCLibraries}OpenTransportAppPPC.o\" \xb6\n".
-    "		\"{PPCLibraries}OpenTptInetPPC.o\"\n".
-    "\n".
-    "Libs_Carbon =	\"{PPCLibraries}CarbonStdCLib.o\" \xb6\n".
-    "		\"{PPCLibraries}StdCRuntime.o\" \xb6\n".
-    "		\"{PPCLibraries}PPCCRuntime.o\" \xb6\n".
-    "		\"{SharedLibraries}CarbonLib\" \xb6\n".
-    "		\"{SharedLibraries}StdCLib\"\n".
-    "\n";
-    print &splitline("all \xc4 " . join(" ", &progrealnames("M")), undef, "\xb6");
-    print "\n\n";
-    foreach $p (&prognames("M")) {
-      ($prog, $type) = split ",", $p;
-
-      print &splitline("$prog \xc4 $prog.68k $prog.ppc $prog.carbon",
-    		   undef, "\xb6"), "\n\n";
-
-      $rsrc = &objects($p, "", "X.rsrc", undef);
-
-      foreach $arch (qw(68K CFM68K PPC Carbon)) {
-          $objstr = &objects($p, "X.\L$arch\E.o", "", undef);
-          print &splitline("$prog.\L$arch\E \xc4 $objstr $rsrc", undef, "\xb6");
-          print "\n";
-          print &splitline("\tDuplicate -y $rsrc {Targ}", 69, "\xb6"), "\n";
-          print &splitline("\t{Link_$arch} -o {Targ} -fragname $prog " .
-    		       "{LinkOptions_$arch} " .
-    		       $objstr . " {Libs_$arch}", 69, "\xb6"), "\n";
-          print &splitline("\tSetFile -a BMi {Targ}", 69, "\xb6"), "\n\n";
-      }
-
-    }
-    foreach $d (&deps("", "X.rsrc", "::", ":")) {
-      next unless $d->{obj};
-      print &splitline(sprintf("%s \xc4 %s", $d->{obj}, join " ", @{$d->{deps}}),
-    		   undef, "\xb6"), "\n";
-      print "\tRez ", $d->{deps}->[0], " -o {Targ} {ROptions}\n\n";
-    }
-    foreach $arch (qw(68K CFM68K)) {
-        foreach $d (&deps("X.\L$arch\E.o", "", "::", ":")) {
-    	 next unless $d->{obj};
-    	print &splitline(sprintf("%s \xc4 %s", $d->{obj},
-    				 join " ", @{$d->{deps}}),
-    			 undef, "\xb6"), "\n";
-    	 print "\t{C_$arch} ", $d->{deps}->[0],
-    	       " -o {Targ} {COptions_$arch}\n\n";
-         }
-    }
-    foreach $arch (qw(PPC Carbon)) {
-        foreach $d (&deps("X.\L$arch\E.o", "", "::", ":")) {
-    	 next unless $d->{obj};
-    	print &splitline(sprintf("%s \xc4 %s", $d->{obj},
-    				 join " ", @{$d->{deps}}),
-    			 undef, "\xb6"), "\n";
-    	 # The odd stuff here seems to stop afpd getting confused.
-    	 print "\techo -n > {Targ}\n";
-    	 print "\tsetfile -t XCOF {Targ}\n";
-    	 print "\t{C_$arch} ", $d->{deps}->[0],
-    	       " -o {Targ} {COptions_$arch}\n\n";
-         }
-    }
-    select STDOUT; close OUT;
-}
-
-if (defined $makefiles{'lcc'}) {
-    $mftyp = 'lcc';
-    $dirpfx = &dirpfx($makefiles{'lcc'}, "\\");
-
-    ##-- lcc makefile
-    open OUT, ">$makefiles{'lcc'}"; select OUT;
-    print
-    "# Makefile for $project_name under lcc.\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";
-    # lcc command line option is -D not /D
-    ($_ = $help) =~ s/=\/D/=-D/gs;
-    print $_;
-    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.lcc\n".
-    "\n".
-    "# C compilation flags\n".
-    "CFLAGS = -D_WINDOWS " .
-      (join " ", map {"-I$dirpfx$_"} @srcdirs) .
-      "\n".
-    "\n".
-    "# Get include directory for resource compiler\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);
-      print &splitline("$prog.exe: " . $objstr ), "\n";
-      $subsystemtype = undef;
-      if ($type eq "G") { $subsystemtype = "-subsystem  windows"; }
-      my $libss = "shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib";
-      print &splitline("\tlcclnk $subsystemtype -o $prog.exe $objstr $libss");
-      print "\n\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 &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 "\nclean:\n".
-    "\t-del *.obj\n".
-    "\t-del *.exe\n".
-    "\t-del *.res\n";
-
-    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 = ('x86_64');
-    my $osxminver = "10.6";
-
-    ##-- 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".
-    "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:U")) .
-    "\n";
-    print $makefile_extra{'osx'} || "";
-    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";
-      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";
-      $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=${osxminver} \$(LDFLAGS) -o \$@ " .
-                       $objstr . " $libstr", 69), "\n\n";
-	$archbins .= " ${prog}.${arch}.bin";
-      }
-      print &splitline("${prog}.app/Contents/MacOS/$prog: ".
-	               "${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");
-      $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=${osxminver} \$(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=${osxminver} -x objective-c \$(COMPAT) \$(FWHACK) \$(CFLAGS)".
-	      " \$(XFLAGS)$deflist -c \$< -o \$\@\n";
-        } else {
-	  print "\t\$(CC) -arch $arch -mmacosx-version-min=${osxminver} \$(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";
-      $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 ". (join " ", &progrealnames("U")) . "\n".
-    "\trm -rf *.app\n";
-    select STDOUT; close OUT;
-}
-
-if (defined $makefiles{'emcc'}) {
-    $mftyp = 'emcc';
-    $dirpfx = &dirpfx($makefiles{'emcc'}, "/");
-
-    ##-- Makefile for building Javascript puzzles via Emscripten
-
-    open OUT, ">$makefiles{'emcc'}"; select OUT;
-    print
-    "# Makefile for $project_name using Emscripten. Requires GNU make.\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";
-    # emcc command line option is -D not /D
-    ($_ = $help) =~ s/=\/D/=-D/gs;
-    print $_;
-    print
-    "\n".
-    "# This can be set on the command line to point at the emcc command,\n".
-    "# if it is not on your PATH.\n".
-    "EMCC = emcc\n".
-    "\n".
-    &splitline("CFLAGS = -DSLOW_SYSTEM " .
-	       (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
-    "\n";
-    $output_js_files = join "", map { " \$(OUTPREFIX)$_.js" } &progrealnames("X");
-    print &splitline("all:" . $output_js_files);
-    print "\n\n";
-    foreach $p (&prognames("X")) {
-      ($prog, $type) = split ",", $p;
-      $objstr = &objects($p, "X.o", undef, undef);
-      $objstr =~ s/gtk\.o/emcc\.o/g;
-      print &splitline("\$(OUTPREFIX)" . $prog . ".js: " . $objstr . " emccpre.js emcclib.js emccx.json"), "\n";
-      print "\t\$(EMCC) -o \$(OUTPREFIX)".$prog.".js ".
-          "-O2 ".
-          "-s ASM_JS=1 ".
-          "--pre-js emccpre.js ".
-          "--js-library emcclib.js ".
-          "-s EXPORTED_FUNCTIONS=\"`sed 's://.*::' emccx.json | tr -d ' \\n'`\" " . $objstr . "\n\n";
-    }
-    foreach $d (&deps("X.o", undef, $dirpfx, "/")) {
-      $oobjs = $d->{obj};
-      $ddeps= join " ", @{$d->{deps}};
-      $oobjs =~ s/gtk/emcc/g;
-      $ddeps =~ s/gtk/emcc/g;
-      print &splitline(sprintf("%s: %s", $oobjs, $ddeps)),
-          "\n";
-      $deflist = join "", map { " -D$_" } @{$d->{defs}};
-      print "\t\$(EMCC) \$(CFLAGS) \$(XFLAGS)$deflist" .
-	  " -c \$< -o \$\@\n";
-    }
-    print "\n";
-    print $makefile_extra{'emcc'} || "";
-    print "\nclean:\n".
-    "\trm -rf *.o $output_js_files\n";
-    select STDOUT; close OUT;
-}
-
-# All done, so do the Unix postprocessing if asked to.
-
-if ($do_unix) {
-    chdir $orig_dir;
-    system "./mkauto.sh";
-    die "mkfiles.pl: mkauto.sh returned $?\n" if $? > 0;
-    system "./configure", @confargs;
-    die "mkfiles.pl: configure returned $?\n" if $? > 0;
-}
--- a/net.R
+++ /dev/null
@@ -1,23 +1,0 @@
-# -*- makefile -*-
-
-NET_EXTRA = tree234 dsf findloop
-
-net      : [X] GTK COMMON net NET_EXTRA net-icon|no-icon
-
-# The Windows Net shouldn't be called `net.exe' since Windows
-# already has a reasonably important utility program by that name!
-netgame  : [G] WINDOWS COMMON net NET_EXTRA net.res|noicon.res
-
-ALL += net[COMBINED] NET_EXTRA
-
-!begin am gtk
-GAMES += net
-!end
-
-!begin >list.c
-    A(net) \
-!end
-
-!begin >gamedesc.txt
-net:netgame.exe:Net:Network jigsaw puzzle:Rotate each tile to reassemble the network.
-!end
--- a/netslide.R
+++ /dev/null
@@ -1,21 +1,0 @@
-# -*- makefile -*-
-
-NETSLIDE_EXTRA = tree234
-
-netslide : [X] GTK COMMON netslide NETSLIDE_EXTRA netslide-icon|no-icon
-
-netslide : [G] WINDOWS COMMON netslide NETSLIDE_EXTRA netslide.res|noicon.res
-
-ALL += netslide[COMBINED] NETSLIDE_EXTRA
-
-!begin am gtk
-GAMES += netslide
-!end
-
-!begin >list.c
-    A(netslide) \
-!end
-
-!begin >gamedesc.txt
-netslide:netslide.exe:Netslide:Toroidal sliding network puzzle:Slide a row at a time to reassemble the network.
-!end
--- a/noicon.rc
+++ /dev/null
@@ -1,11 +1,0 @@
-/* Puzzle resource file without an icon, used in the absence of icons/foo.rc */
-
-#include "puzzles.rc2"
-
-/* XXX this probably isn't the right test, but it'll do. */
-#ifdef MINGW32_FIX
-/* XXX The MinGW toolchain (specifically, windres) doesn't like a resource
- * file with no resources. Give it a dummy one.
- * This can go if/when VERSIONINFO resources are added. */
-200 RCDATA { 0 }
-#endif
--- a/nullgame.R
+++ /dev/null
@@ -1,12 +1,0 @@
-# -*- makefile -*-
-
-# The `nullgame' source file is a largely blank one, which contains
-# all the correct function definitions to compile and link, but
-# which defines the null game in which nothing is ever drawn and
-# there are no valid moves. Its main purpose is to act as a
-# template for writing new game definition source files. I include
-# it in the Makefile because it will be worse than useless if it
-# ever fails to compile, so it's important that it should actually
-# be built on a regular basis.
-nullgame : [X] GTK COMMON nullgame nullgame-icon|no-icon
-nullgame : [G] WINDOWS COMMON nullgame nullgame.res|noicon.res
--- a/osx-info.plist
+++ /dev/null
@@ -1,36 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleIconFile</key>
-	<string>Puzzles.icns</string>
-	<key>CFBundleHelpBookFolder</key>
-	<string>Help</string>
-	<key>CFBundleHelpBookName</key>
-	<string>Puzzles Help</string>
-	<key>CFBundleName</key>
-	<string>Puzzles</string>
-	<key>CFBundleDisplayName</key>
-	<string>Puzzles</string>
-	<key>CFBundleExecutable</key>
-	<string>Puzzles</string>
-	<key>CFBundleVersion</key>
-	<string>Unidentified build</string>
-	<key>CFBundleShortVersionString</key>
-	<string>Unidentified build</string>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>en</string>
-	<key>CFBundleIdentifier</key>
-	<string>uk.org.greenend.chiark.sgtatham.puzzles</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundlePackageType</key>
-	<string>APPL</string>
-	<key>CFBundleSignature</key>
-	<string>????</string>
-	<key>NSHumanReadableCopyright</key>
-	<string>This software is copyright (c) 2004-2014 Simon Tatham</string>
-	<key>NSHighResolutionCapable</key>
-	<true/>
-</dict>
-</plist>
binary files a/osx.icns /dev/null differ
--- a/osx.m
+++ b/osx.m
@@ -77,7 +77,9 @@
  *    recreate it.
  */
 
-#define COMBINED /* we put all the puzzles in one binary in this port */
+#ifndef COMBINED
+#error Expected -DCOMBINED to come from the makefile
+#endif
 
 #include <ctype.h>
 #include <time.h>
--- /dev/null
+++ b/osx/Info.plist
@@ -1,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleIconFile</key>
+	<string>Puzzles.icns</string>
+	<key>CFBundleHelpBookFolder</key>
+	<string>Help</string>
+	<key>CFBundleHelpBookName</key>
+	<string>Puzzles Help</string>
+	<key>CFBundleName</key>
+	<string>Puzzles</string>
+	<key>CFBundleDisplayName</key>
+	<string>Puzzles</string>
+	<key>CFBundleExecutable</key>
+	<string>Puzzles</string>
+	<key>CFBundleVersion</key>
+	<string>Unidentified build</string>
+	<key>CFBundleShortVersionString</key>
+	<string>Unidentified build</string>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleIdentifier</key>
+	<string>uk.org.greenend.chiark.sgtatham.puzzles</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>This software is copyright (c) 2004-2014 Simon Tatham</string>
+	<key>NSHighResolutionCapable</key>
+	<true/>
+</dict>
+</plist>
binary files /dev/null b/osx/Puzzles.icns differ
--- a/palisade.R
+++ /dev/null
@@ -1,21 +1,0 @@
-# -*- makefile -*-
-
-PALISADE_EXTRA = divvy dsf
-
-palisade    : [X] GTK COMMON palisade PALISADE_EXTRA palisade-icon|no-icon
-
-palisade    : [G] WINDOWS COMMON palisade PALISADE_EXTRA palisade.res|noicon.res
-
-ALL += palisade[COMBINED] PALISADE_EXTRA
-
-!begin am gtk
-GAMES += palisade
-!end
-
-!begin >list.c
-    A(palisade) \
-!end
-
-!begin >gamedesc.txt
-palisade:palisade.exe:Palisade:Grid-division puzzle:Divide the grid into equal-sized areas in accordance with the clues.
-!end
--- a/pattern.R
+++ /dev/null
@@ -1,25 +1,0 @@
-# -*- makefile -*-
-
-pattern  : [X] GTK COMMON pattern pattern-icon|no-icon
-
-pattern  : [G] WINDOWS COMMON pattern pattern.res|noicon.res
-
-patternsolver : [U] pattern[STANDALONE_SOLVER] STANDALONE
-patternsolver : [C] pattern[STANDALONE_SOLVER] STANDALONE
-
-patternpicture : [U] pattern[STANDALONE_PICTURE_GENERATOR] STANDALONE
-patternpicture : [C] pattern[STANDALONE_PICTURE_GENERATOR] STANDALONE
-
-ALL += pattern[COMBINED]
-
-!begin am gtk
-GAMES += pattern
-!end
-
-!begin >list.c
-    A(pattern) \
-!end
-
-!begin >gamedesc.txt
-pattern:pattern.exe:Pattern:Pattern puzzle:Fill in the pattern in the grid, given only the lengths of runs of black squares.
-!end
--- a/pearl.R
+++ /dev/null
@@ -1,23 +1,0 @@
-# -*- makefile -*-
-
-PEARL_EXTRA    = dsf tree234 grid penrose loopgen tdq
-
-pearl          : [X] GTK COMMON pearl PEARL_EXTRA pearl-icon|no-icon
-pearl          : [G] WINDOWS COMMON pearl PEARL_EXTRA pearl.res?
-
-pearlbench     : [U] pearl[STANDALONE_SOLVER] PEARL_EXTRA STANDALONE m.lib
-pearlbench     : [C] pearl[STANDALONE_SOLVER] PEARL_EXTRA STANDALONE
-
-ALL += pearl[COMBINED] PEARL_EXTRA
-
-!begin am gtk
-GAMES += pearl
-!end
-
-!begin >list.c
-    A(pearl) \
-!end
-
-!begin >gamedesc.txt
-pearl:pearl.exe:Pearl:Loop-drawing puzzle:Draw a single closed loop, given clues about corner and straight squares.
-!end
--- a/pegs.R
+++ /dev/null
@@ -1,21 +1,0 @@
-# -*- makefile -*-
-
-PEGS_EXTRA = tree234
-
-pegs     : [X] GTK COMMON pegs PEGS_EXTRA pegs-icon|no-icon
-
-pegs     : [G] WINDOWS COMMON pegs PEGS_EXTRA pegs.res|noicon.res
-
-ALL += pegs[COMBINED] PEGS_EXTRA
-
-!begin am gtk
-GAMES += pegs
-!end
-
-!begin >list.c
-    A(pegs) \
-!end
-
-!begin >gamedesc.txt
-pegs:pegs.exe:Pegs:Peg solitaire puzzle:Jump pegs over each other to remove all but one.
-!end
--- /dev/null
+++ b/puzzles.rc
@@ -1,0 +1,76 @@
+/* Windows resource file for all puzzles. */
+
+#if defined ICON_FILE
+200 ICON ICON_FILE
+#else
+#ifdef MINGW32_FIX
+/* XXX The MinGW toolchain (specifically, windres) doesn't like a resource
+ * file with no resources. Give it a dummy one.
+ * This can go if/when VERSIONINFO resources are added. */
+200 RCDATA { 0 }
+#endif
+#endif
+
+#ifdef _WIN32_WCE
+
+#include <winuser.h>
+#include <commctrl.h>
+
+#define SHMENUBAR       RCDATA
+#define I_IMAGENONE     (-2)
+#define IDC_STATIC      (-1)
+
+#include "resource.h"
+
+IDR_MENUBAR1 MENU DISCARDABLE 
+BEGIN
+    POPUP "Game"
+    BEGIN
+        MENUITEM "Dummy", 0
+    END
+    POPUP "Type"
+    BEGIN
+        MENUITEM "Dummy", 0
+    END
+END
+
+IDR_MENUBAR1 SHMENUBAR DISCARDABLE 
+BEGIN
+    IDR_MENUBAR1, 2,
+    I_IMAGENONE, ID_GAME, TBSTATE_ENABLED, 
+    TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_GAME, 0, 0,
+    I_IMAGENONE, ID_TYPE, TBSTATE_ENABLED, 
+    TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_TYPE, 0, 1,
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_CAP_GAME            "Game"
+    IDS_CAP_TYPE            "Type"
+END
+
+IDD_ABOUT DIALOG DISCARDABLE  0, 0, 0, 0
+STYLE WS_POPUP
+FONT 8, "Tahoma"
+BEGIN
+    LTEXT "", IDC_ABOUT_CAPTION,        4,  4, 150,  8
+    LTEXT "", IDC_ABOUT_LINE,           4, 16, 150,  1, WS_BORDER
+    LTEXT "", IDC_ABOUT_GAME,           4, 22, 150,  8
+    LTEXT "from Simon Tatham's Portable Puzzle Collection",
+	      IDC_STATIC,               4, 36, 150,  8, SS_LEFTNOWORDWRAP
+    LTEXT "Pocket PC port by Darek Olszewski",
+	      IDC_STATIC,               4, 46, 150,  8
+    LTEXT "", IDC_ABOUT_VERSION,        4, 60, 150,  8
+END
+
+IDD_CONFIG DIALOG DISCARDABLE  0, 0, 0, 0
+STYLE WS_POPUP
+FONT 8, "Tahoma"
+BEGIN
+    LTEXT "", IDC_CONFIG_CAPTION,       4,  4, 150,  8
+    LTEXT "", IDC_CONFIG_LINE,          4, 16, 150,  1, WS_BORDER
+END
+
+IDR_PADTOOLBAR BITMAP DISCARDABLE "padtoolbar.bmp"
+
+#endif /* _WIN32_WCE */
--- a/puzzles.rc2
+++ /dev/null
@@ -1,65 +1,0 @@
-/* Standard stuff that goes into the Windows resources for all puzzles. */
-
-#ifdef _WIN32_WCE
-
-#include <winuser.h>
-#include <commctrl.h>
-
-#define SHMENUBAR       RCDATA
-#define I_IMAGENONE     (-2)
-#define IDC_STATIC      (-1)
-
-#include "resource.h"
-
-IDR_MENUBAR1 MENU DISCARDABLE 
-BEGIN
-    POPUP "Game"
-    BEGIN
-        MENUITEM "Dummy", 0
-    END
-    POPUP "Type"
-    BEGIN
-        MENUITEM "Dummy", 0
-    END
-END
-
-IDR_MENUBAR1 SHMENUBAR DISCARDABLE 
-BEGIN
-    IDR_MENUBAR1, 2,
-    I_IMAGENONE, ID_GAME, TBSTATE_ENABLED, 
-    TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_GAME, 0, 0,
-    I_IMAGENONE, ID_TYPE, TBSTATE_ENABLED, 
-    TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_TYPE, 0, 1,
-END
-
-STRINGTABLE DISCARDABLE 
-BEGIN
-    IDS_CAP_GAME            "Game"
-    IDS_CAP_TYPE            "Type"
-END
-
-IDD_ABOUT DIALOG DISCARDABLE  0, 0, 0, 0
-STYLE WS_POPUP
-FONT 8, "Tahoma"
-BEGIN
-    LTEXT "", IDC_ABOUT_CAPTION,        4,  4, 150,  8
-    LTEXT "", IDC_ABOUT_LINE,           4, 16, 150,  1, WS_BORDER
-    LTEXT "", IDC_ABOUT_GAME,           4, 22, 150,  8
-    LTEXT "from Simon Tatham's Portable Puzzle Collection",
-	      IDC_STATIC,               4, 36, 150,  8, SS_LEFTNOWORDWRAP
-    LTEXT "Pocket PC port by Darek Olszewski",
-	      IDC_STATIC,               4, 46, 150,  8
-    LTEXT "", IDC_ABOUT_VERSION,        4, 60, 150,  8
-END
-
-IDD_CONFIG DIALOG DISCARDABLE  0, 0, 0, 0
-STYLE WS_POPUP
-FONT 8, "Tahoma"
-BEGIN
-    LTEXT "", IDC_CONFIG_CAPTION,       4,  4, 150,  8
-    LTEXT "", IDC_CONFIG_LINE,          4, 16, 150,  1, WS_BORDER
-END
-
-IDR_PADTOOLBAR BITMAP DISCARDABLE "padtoolbar.bmp"
-
-#endif /* _WIN32_WCE */
--- a/range.R
+++ /dev/null
@@ -1,21 +1,0 @@
-# -*- makefile -*-
-
-RANGE_EXTRA = dsf
-
-range    : [X] GTK COMMON range RANGE_EXTRA range-icon|no-icon
-
-range    : [G] WINDOWS COMMON range RANGE_EXTRA range.res|noicon.res
-
-ALL += range[COMBINED] RANGE_EXTRA
-
-!begin am gtk
-GAMES += range
-!end
-
-!begin >list.c
-    A(range) \
-!end
-
-!begin >gamedesc.txt
-range:range.exe:Range:Visible-distance puzzle:Place black squares to limit the visible distance from each numbered cell.
-!end
--- a/rect.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-rect     : [X] GTK COMMON rect rect-icon|no-icon
-
-rect     : [G] WINDOWS COMMON rect rect.res|noicon.res
-
-ALL += rect[COMBINED]
-
-!begin am gtk
-GAMES += rect
-!end
-
-!begin >list.c
-    A(rect) \
-!end
-
-!begin >gamedesc.txt
-rect:rect.exe:Rectangles:Rectangles puzzle:Divide the grid into rectangles with areas equal to the numbers.
-!end
--- a/samegame.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-samegame : [X] GTK COMMON samegame samegame-icon|no-icon
-
-samegame : [G] WINDOWS COMMON samegame samegame.res|noicon.res
-
-ALL += samegame[COMBINED]
-
-!begin am gtk
-GAMES += samegame
-!end
-
-!begin >list.c
-    A(samegame) \
-!end
-
-!begin >gamedesc.txt
-samegame:samegame.exe:Same Game:Block-clearing puzzle:Clear the grid by removing touching groups of the same colour squares.
-!end
--- a/signpost.R
+++ /dev/null
@@ -1,23 +1,0 @@
-# -*- makefile -*-
-
-SIGNPOST_EXTRA = dsf
-
-signpost : [X] GTK COMMON signpost SIGNPOST_EXTRA signpost-icon|no-icon
-signpost : [G] WINDOWS COMMON signpost SIGNPOST_EXTRA signpost.res|noicon.res
-
-signpostsolver : [U] signpost[STANDALONE_SOLVER] SIGNPOST_EXTRA STANDALONE m.lib
-signpostsolver : [C] signpost[STANDALONE_SOLVER] SIGNPOST_EXTRA STANDALONE
-
-ALL += signpost[COMBINED] SIGNPOST_EXTRA
-
-!begin am gtk
-GAMES += signpost
-!end
-
-!begin >list.c
-    A(signpost) \
-!end
-
-!begin >gamedesc.txt
-signpost:signpost.exe:Signpost:Square-connecting puzzle:Connect the squares into a path following the arrows.
-!end
--- a/singles.R
+++ /dev/null
@@ -1,23 +1,0 @@
-# -*- makefile -*-
-
-SINGLES_EXTRA = dsf LATIN
-
-singles : [X] GTK COMMON singles SINGLES_EXTRA singles-icon|no-icon
-singles : [G] WINDOWS COMMON singles SINGLES_EXTRA singles.res|noicon.res
-
-ALL += singles[COMBINED] SINGLES_EXTRA
-
-singlessolver : [U] singles[STANDALONE_SOLVER] SINGLES_EXTRA STANDALONE
-singlessolver : [C] singles[STANDALONE_SOLVER] SINGLES_EXTRA STANDALONE
-
-!begin am gtk
-GAMES += singles
-!end
-
-!begin >list.c
-    A(singles) \
-!end
-
-!begin >gamedesc.txt
-singles:singles.exe:Singles:Number-removing puzzle:Black out the right set of duplicate numbers.
-!end
--- a/sixteen.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-sixteen  : [X] GTK COMMON sixteen sixteen-icon|no-icon
-
-sixteen  : [G] WINDOWS COMMON sixteen sixteen.res|noicon.res
-
-ALL += sixteen[COMBINED]
-
-!begin am gtk
-GAMES += sixteen
-!end
-
-!begin >list.c
-    A(sixteen) \
-!end
-
-!begin >gamedesc.txt
-sixteen:sixteen.exe:Sixteen:Toroidal sliding block puzzle:Slide a row at a time to arrange the tiles into order.
-!end
--- a/slant.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-SLANT_EXTRA = dsf findloop
-
-slant    : [X] GTK COMMON slant SLANT_EXTRA slant-icon|no-icon
-
-slant    : [G] WINDOWS COMMON slant SLANT_EXTRA slant.res|noicon.res
-
-slantsolver :   [U] slant[STANDALONE_SOLVER] SLANT_EXTRA STANDALONE
-slantsolver :   [C] slant[STANDALONE_SOLVER] SLANT_EXTRA STANDALONE
-
-ALL += slant[COMBINED] SLANT_EXTRA
-
-!begin am gtk
-GAMES += slant
-!end
-
-!begin >list.c
-    A(slant) \
-!end
-
-!begin >gamedesc.txt
-slant:slant.exe:Slant:Maze-drawing puzzle:Draw a maze of slanting lines that matches the clues.
-!end
--- a/solo.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-SOLO_EXTRA = divvy dsf
-
-solo     : [X] GTK COMMON solo SOLO_EXTRA solo-icon|no-icon
-
-solo     : [G] WINDOWS COMMON solo SOLO_EXTRA solo.res|noicon.res
-
-solosolver :    [U] solo[STANDALONE_SOLVER] SOLO_EXTRA STANDALONE
-solosolver :    [C] solo[STANDALONE_SOLVER] SOLO_EXTRA STANDALONE
-
-ALL += solo[COMBINED] SOLO_EXTRA
-
-!begin am gtk
-GAMES += solo
-!end
-
-!begin >list.c
-    A(solo) \
-!end
-
-!begin >gamedesc.txt
-solo:solo.exe:Solo:Number placement puzzle:Fill in the grid so that each row, column and square block contains one of every digit.
-!end
--- a/tents.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-TENTS_EXTRA = matching dsf
-
-tents    : [X] GTK COMMON tents TENTS_EXTRA tents-icon|no-icon
-
-tents    : [G] WINDOWS COMMON tents TENTS_EXTRA tents.res|noicon.res
-
-ALL += tents[COMBINED] TENTS_EXTRA
-
-tentssolver :   [U] tents[STANDALONE_SOLVER] TENTS_EXTRA STANDALONE
-tentssolver :   [C] tents[STANDALONE_SOLVER] TENTS_EXTRA STANDALONE
-
-!begin am gtk
-GAMES += tents
-!end
-
-!begin >list.c
-    A(tents) \
-!end
-
-!begin >gamedesc.txt
-tents:tents.exe:Tents:Tent-placing puzzle:Place a tent next to each tree.
-!end
--- a/towers.R
+++ /dev/null
@@ -1,25 +1,0 @@
-# -*- makefile -*-
-
-TOWERS_EXTRA        = LATIN
-TOWERS_EXTRA_SOLVER = LATIN_SOLVER
-
-towers    : [X] GTK COMMON towers TOWERS_EXTRA towers-icon|no-icon
-
-towers    : [G] WINDOWS COMMON towers TOWERS_EXTRA towers.res|noicon.res
-
-towerssolver : [U] towers[STANDALONE_SOLVER] TOWERS_EXTRA_SOLVER STANDALONE
-towerssolver : [C] towers[STANDALONE_SOLVER] TOWERS_EXTRA_SOLVER STANDALONE
-
-ALL += towers[COMBINED] TOWERS_EXTRA
-
-!begin am gtk
-GAMES += towers
-!end
-
-!begin >list.c
-    A(towers) \
-!end
-
-!begin >gamedesc.txt
-towers:towers.exe:Towers:Tower-placing Latin square puzzle:Complete the latin square of towers in accordance with the clues.
-!end
--- a/tracks.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-TRACKS_EXTRA = dsf findloop
-
-tracks  : [X] GTK COMMON tracks TRACKS_EXTRA tracks-icon|no-icon
-
-tracks  : [G] WINDOWS COMMON tracks TRACKS_EXTRA tracks.res|noicon.res
-
-ALL += tracks[COMBINED] TRACKS_EXTRA
-
-trackssolver : [U] tracks[STANDALONE_SOLVER] TRACKS_EXTRA STANDALONE
-trackssolver : [C] tracks[STANDALONE_SOLVER] TRACKS_EXTRA STANDALONE
-
-!begin am gtk
-GAMES += tracks
-!end
-
-!begin >list.c
-    A(tracks) \
-!end
-
-!begin >gamedesc.txt
-tracks:tracks.exe:Tracks:Path-finding railway track puzzle:Fill in the railway track according to the clues.
-!end
--- a/twiddle.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-twiddle  : [X] GTK COMMON twiddle twiddle-icon|no-icon
-
-twiddle  : [G] WINDOWS COMMON twiddle twiddle.res|noicon.res
-
-ALL += twiddle[COMBINED]
-
-!begin am gtk
-GAMES += twiddle
-!end
-
-!begin >list.c
-    A(twiddle) \
-!end
-
-!begin >gamedesc.txt
-twiddle:twiddle.exe:Twiddle:Rotational sliding block puzzle:Rotate the tiles around themselves to arrange them into order.
-!end
--- a/undead.R
+++ /dev/null
@@ -1,18 +1,0 @@
-# -*- makefile -*-
-
-undead : [X] GTK COMMON undead undead-icon|no-icon
-undead : [G] WINDOWS COMMON undead undead.res|noicon.res
-
-ALL += undead[COMBINED]
-
-!begin am gtk
-GAMES += undead
-!end
-
-!begin >list.c
-    A(undead) \
-!end
-
-!begin >gamedesc.txt
-undead:undead.exe:Undead:Monster-placing puzzle:Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors.
-!end
--- a/unequal.R
+++ /dev/null
@@ -1,25 +1,0 @@
-# -*- makefile -*-
-
-UNEQUAL_EXTRA        = LATIN
-UNEQUAL_EXTRA_SOLVER = LATIN_SOLVER
-
-unequal  : [X] GTK COMMON unequal UNEQUAL_EXTRA unequal-icon|no-icon
-
-unequal  : [G] WINDOWS COMMON unequal UNEQUAL_EXTRA unequal.res|noicon.res
-
-unequalsolver : [U] unequal[STANDALONE_SOLVER] UNEQUAL_EXTRA_SOLVER STANDALONE
-unequalsolver : [C] unequal[STANDALONE_SOLVER] UNEQUAL_EXTRA_SOLVER STANDALONE
-
-ALL += unequal[COMBINED] UNEQUAL_EXTRA
-
-!begin am gtk
-GAMES += unequal
-!end
-
-!begin >list.c
-    A(unequal) \
-!end
-
-!begin >gamedesc.txt
-unequal:unequal.exe:Unequal:Latin square puzzle:Complete the latin square in accordance with the > signs.
-!end
--- /dev/null
+++ b/unfinished/CMakeLists.txt
@@ -1,0 +1,31 @@
+puzzle(group
+  DISPLAYNAME "Group"
+  DESCRIPTION "Group theory puzzle"
+  OBJECTIVE "Complete the unfinished Cayley table of a group.")
+solver(group ${CMAKE_SOURCE_DIR}/latin.c)
+
+puzzle(separate
+  DISPLAYNAME "Separate"
+  DESCRIPTION "Rectangle-dividing puzzle"
+  OBJECTIVE "Partition the grid into regions containing one of each letter.")
+
+puzzle(slide
+  DISPLAYNAME "Slide"
+  DESCRIPTION "Sliding block puzzle"
+  OBJECTIVE "Slide the blocks to let the key block out.")
+solver(slide)
+
+puzzle(sokoban
+  DISPLAYNAME "Sokoban"
+  DESCRIPTION "Barrel-pushing puzzle"
+  OBJECTIVE "Push all the barrels into the target squares.")
+
+# These unfinished programs don't even have the structure of a puzzle
+# game yet; they're just command-line programs containing test
+# implementations of some of the needed functionality.
+
+cliprogram(numgame numgame.c)
+
+cliprogram(path path.c COMPILE_DEFINITIONS TEST_GEN)
+
+export_variables_to_parent_scope()
--- a/unfinished/README
+++ b/unfinished/README
@@ -2,8 +2,13 @@
 half-written, fundamentally flawed, or in other ways unready to be
 shipped as part of the polished Puzzles collection.
 
-Those puzzles which have .R files can be built as part of the
-Puzzles collection by symlinking their source files into the parent
-directory and re-running mkfiles.pl. Anything without a .R file
-isn't even finished enough to do that, and you should read the
-source file itself to find out the status.
+The CMake build system will _build_ all of the source in this
+directory (to ensure it hasn't become unbuildable), but they won't be
+included in all-in-one puzzle binaries or installed by 'make install'
+targets. If you want to temporarily change that, you can reconfigure
+your build by defining the CMake variable PUZZLES_ENABLE_UNFINISHED.
+For example,
+
+  cmake . -DPUZZLES_ENABLE_UNFINISHED="group;slide"
+
+will build as if both Group and Slide were fully official puzzles.
--- a/unfinished/group.R
+++ /dev/null
@@ -1,25 +1,0 @@
-# -*- makefile -*-
-
-GROUP_EXTRA        = LATIN
-GROUP_EXTRA_SOLVER = LATIN_SOLVER
-
-group    : [X] GTK COMMON group GROUP_EXTRA group-icon|no-icon
-
-group    : [G] WINDOWS COMMON group GROUP_EXTRA group.res|noicon.res
-
-groupsolver : [U] group[STANDALONE_SOLVER] GROUP_EXTRA_SOLVER STANDALONE
-groupsolver : [C] group[STANDALONE_SOLVER] GROUP_EXTRA_SOLVER STANDALONE
-
-ALL += group[COMBINED] GROUP_EXTRA
-
-!begin am gtk
-GAMES += group
-!end
-
-!begin >list.c
-    A(group) \
-!end
-
-!begin >gamedesc.txt
-group:group.exe:Group:Group theory puzzle:Complete the unfinished Cayley table of a group.
-!end
--- a/unfinished/separate.R
+++ /dev/null
@@ -1,21 +1,0 @@
-# -*- makefile -*-
-
-SEPARATE_EXTRA = divvy dsf
-
-separate       : [X] GTK COMMON separate SEPARATE_EXTRA separate-icon|no-icon
-
-separate       : [G] WINDOWS COMMON separate SEPARATE_EXTRA separate.res|noicon.res
-
-ALL += separate[COMBINED] SEPARATE_EXTRA
-
-!begin am gtk
-GAMES += separate
-!end
-
-!begin >list.c
-    A(separate) \
-!end
-
-!begin >gamedesc.txt
-separate:separate.exe:Separate:Rectangle-dividing puzzle:Partition the grid into regions containing one of each letter.
-!end
--- a/unfinished/slide.R
+++ /dev/null
@@ -1,24 +1,0 @@
-# -*- makefile -*-
-
-SLIDE_EXTRA    = dsf tree234
-
-slide          : [X] GTK COMMON slide SLIDE_EXTRA slide-icon|no-icon
-
-slide          : [G] WINDOWS COMMON slide SLIDE_EXTRA slide.res|noicon.res
-
-slidesolver :   [U] slide[STANDALONE_SOLVER] SLIDE_EXTRA STANDALONE
-slidesolver :   [C] slide[STANDALONE_SOLVER] SLIDE_EXTRA STANDALONE
-
-ALL += slide[COMBINED] SLIDE_EXTRA
-
-!begin am gtk
-GAMES += slide
-!end
-
-!begin >list.c
-    A(slide) \
-!end
-
-!begin >gamedesc.txt
-slide:slide.exe:Slide:Sliding block puzzle:Slide the blocks to let the key block out.
-!end
--- a/unfinished/sokoban.R
+++ /dev/null
@@ -1,19 +1,0 @@
-# -*- makefile -*-
-
-sokoban  : [X] GTK COMMON sokoban sokoban-icon|no-icon
-
-sokoban  : [G] WINDOWS COMMON sokoban sokoban.res?
-
-ALL += sokoban[COMBINED]
-
-!begin am gtk
-GAMES += sokoban
-!end
-
-!begin >list.c
-    A(sokoban) \
-!end
-
-!begin >gamedesc.txt
-sokoban:sokoban.exe:Sokoban:Barrel-pushing puzzle:Push all the barrels into the target squares.
-!end
--- a/unruly.R
+++ /dev/null
@@ -1,21 +1,0 @@
-# -*- makefile -*-
-
-unruly : [X] GTK COMMON unruly unruly-icon|no-icon
-unruly : [G] WINDOWS COMMON unruly unruly.res|noicon.res
-
-unrulysolver : [U] unruly[STANDALONE_SOLVER] STANDALONE
-unrulysolver : [C] unruly[STANDALONE_SOLVER] STANDALONE
-
-ALL += unruly[COMBINED]
-
-!begin am gtk
-GAMES += unruly
-!end
-
-!begin >list.c
-    A(unruly) \
-!end
-
-!begin >gamedesc.txt
-unruly:unruly.exe:Unruly:Black and white grid puzzle:Fill in the black and white grid to avoid runs of three.
-!end
--- a/untangle.R
+++ /dev/null
@@ -1,21 +1,0 @@
-# -*- makefile -*-
-
-UNTANGLE_EXTRA = tree234
-
-untangle : [X] GTK COMMON untangle UNTANGLE_EXTRA untangle-icon|no-icon
-
-untangle : [G] WINDOWS COMMON untangle UNTANGLE_EXTRA untangle.res|noicon.res
-
-ALL += untangle[COMBINED] UNTANGLE_EXTRA
-
-!begin am gtk
-GAMES += untangle
-!end
-
-!begin >list.c
-    A(untangle) \
-!end
-
-!begin >gamedesc.txt
-untangle:untangle.exe:Untangle:Planar graph layout puzzle:Reposition the points so that the lines do not cross.
-!end
--- a/webpage.pl
+++ b/webpage.pl
@@ -9,8 +9,10 @@
 use warnings;
 use HTML::Entities;
 
-open my $desc, "<", "gamedesc.txt"
-    or die "gamedesc.txt: open: $!\n";
+my $gamedesc = shift @ARGV;
+
+open my $desc, "<", $gamedesc
+    or die "$gamedesc: open: $!\n";
 
 open my $spans, ">", "wwwspans.html"
     or die "wwwspans.html: open: $!\n";