I'm sick of repeatedly adding and removing local changes to Recipe

when testing a new game, so here's a new architecture for the Recipe
file. mkfiles.pl now supports several new features:

 - an `!include' directive, which accepts wildcards
 - += to append to an existing object group definition
 - the ability to divert output to an arbitrary file.

So now each puzzle has a `.R' file containing a fragment of Recipe
code describing that puzzle, and the central Recipe does `!include
*.R' to construct the Makefiles. That way, I can keep as many
experimental half-finished puzzles lying around my working directory
as I like, and I won't have to keep reverting Recipe when I check in
any other changes.

As part of this change, list.c is no longer a version-controlled
file; it's now constructed by mkfiles.pl, so that it too can take
advantage of this mechanism.

[originally from svn r6781]
This commit is contained in:
Simon Tatham
2006-08-05 17:20:29 +00:00
parent f05c25347d
commit cf880225ed
30 changed files with 512 additions and 195 deletions

View File

@ -6,20 +6,25 @@ Things to remember when adding a new puzzle
Write the source file for the new puzzle (duhh).
Add it to Recipe in _four_ places:
- the `ALL' definition, to ensure it is compiled into the OS X binary
- as a GTK build target
- as a Windows build target
- in the Unix `make install' section at the bottom.
Create a .R file for it which:
- defines a Recipe symbol for it if it requires auxiliary object
files
- adds it to the `ALL' definition, to ensure it is compiled into
the OS X binary
- adds it as a GTK build target
- adds it as a Windows build target
- adds auxiliary solver binaries if any
- adds it to $(GAMES) in the GTK makefile, for `make install'
- adds it to list.c for the OS X binary.
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
below under website changes.)
Add it to list.c so that the OS X binary will be able to select it
from the menus. (Also, double-check that the game structure name in
the source file has been renamed from `nullgame'. Actually compiling
it on OS X would be a good way to check this, if convenient.)
Double-check that the game structure name in the source file has
been renamed from `nullgame', so that it'll work on OS X. Actually
compiling it on OS X would be a good way to check this, if
convenient.
Add a documentation section in puzzles.but.

127
Recipe
View File

@ -16,95 +16,38 @@
WINDOWS = windows printing
+ user32.lib gdi32.lib comctl32.lib comdlg32.lib winspool.lib
COMMON = midend drawing misc malloc random version
NET = net tree234 dsf
NETSLIDE = netslide tree234
MINES = mines tree234
FLIP = flip tree234
PEGS = pegs tree234
UNTANGLE = untangle tree234
SLANT = slant dsf
MAP = map dsf
LOOPY = loopy tree234 dsf
LIGHTUP = lightup combi
TENTS = tents maxflow
BRIDGES = bridges dsf
ALL = list NET NETSLIDE cube fifteen sixteen rect pattern solo twiddle
+ MINES samegame FLIP guess PEGS dominosa UNTANGLE blackbox SLANT
+ LIGHTUP MAP LOOPY inertia TENTS BRIDGES
GTK = gtk printing ps
net : [X] GTK COMMON NET
netslide : [X] GTK COMMON NETSLIDE
cube : [X] GTK COMMON cube
fifteen : [X] GTK COMMON fifteen
sixteen : [X] GTK COMMON sixteen
rect : [X] GTK COMMON rect
pattern : [X] GTK COMMON pattern
solo : [X] GTK COMMON solo
twiddle : [X] GTK COMMON twiddle
mines : [X] GTK COMMON MINES
samegame : [X] GTK COMMON samegame
flip : [X] GTK COMMON FLIP
guess : [X] GTK COMMON guess
pegs : [X] GTK COMMON PEGS
dominosa : [X] GTK COMMON dominosa
untangle : [X] GTK COMMON UNTANGLE
blackbox : [X] GTK COMMON blackbox
slant : [X] GTK COMMON SLANT
lightup : [X] GTK COMMON LIGHTUP
map : [X] GTK COMMON MAP
loopy : [X] GTK COMMON LOOPY
inertia : [X] GTK COMMON inertia
tents : [X] GTK COMMON TENTS
bridges : [X] GTK COMMON BRIDGES
# Auxiliary command-line programs.
# Objects needed for auxiliary command-line programs.
STANDALONE = nullfe random misc malloc
solosolver : [U] solo[STANDALONE_SOLVER] STANDALONE
patternsolver : [U] pattern[STANDALONE_SOLVER] STANDALONE
mineobfusc : [U] mines[STANDALONE_OBFUSCATOR] tree234 STANDALONE
slantsolver : [U] slant[STANDALONE_SOLVER] dsf STANDALONE
mapsolver : [U] map[STANDALONE_SOLVER] dsf STANDALONE m.lib
lightupsolver : [U] lightup[STANDALONE_SOLVER] combi STANDALONE
tentssolver : [U] tents[STANDALONE_SOLVER] maxflow STANDALONE
ALL = list
solosolver : [C] solo[STANDALONE_SOLVER] STANDALONE
patternsolver : [C] pattern[STANDALONE_SOLVER] STANDALONE
mineobfusc : [C] mines[STANDALONE_OBFUSCATOR] tree234 STANDALONE
slantsolver : [C] slant[STANDALONE_SOLVER] dsf STANDALONE
mapsolver : [C] map[STANDALONE_SOLVER] dsf STANDALONE
lightupsolver : [C] lightup[STANDALONE_SOLVER] combi STANDALONE
tentssolver : [C] tents[STANDALONE_SOLVER] maxflow STANDALONE
# 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
# 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
netslide : [G] WINDOWS COMMON NETSLIDE
cube : [G] WINDOWS COMMON cube
fifteen : [G] WINDOWS COMMON fifteen
sixteen : [G] WINDOWS COMMON sixteen
rect : [G] WINDOWS COMMON rect
pattern : [G] WINDOWS COMMON pattern
solo : [G] WINDOWS COMMON solo
twiddle : [G] WINDOWS COMMON twiddle
mines : [G] WINDOWS COMMON MINES
samegame : [G] WINDOWS COMMON samegame
flip : [G] WINDOWS COMMON FLIP
guess : [G] WINDOWS COMMON guess
pegs : [G] WINDOWS COMMON PEGS
dominosa : [G] WINDOWS COMMON dominosa
untangle : [G] WINDOWS COMMON UNTANGLE
blackbox : [G] WINDOWS COMMON blackbox
slant : [G] WINDOWS COMMON SLANT
lightup : [G] WINDOWS COMMON LIGHTUP
map : [G] WINDOWS COMMON MAP
loopy : [G] WINDOWS COMMON LOOPY
inertia : [G] WINDOWS COMMON inertia
tents : [G] WINDOWS COMMON TENTS
bridges : [G] WINDOWS COMMON BRIDGES
# 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
# Mac OS X unified application containing all the puzzles.
Puzzles : [MX] osx osx.icns osx-info.plist COMMON ALL
@ -138,17 +81,6 @@ Puzzles.dmg: Puzzles
rm -f raw.dmg devicename
!end
# 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 : [G] WINDOWS COMMON nullgame
# Version management.
!begin vc
version.obj: *.c *.h
@ -194,10 +126,7 @@ FORCE:
# make install for Unix.
!begin gtk
install:
for i in cube net netslide fifteen sixteen twiddle \
pattern rect solo mines samegame flip guess \
pegs dominosa untangle blackbox slant lightup \
map loopy inertia tents bridges; do \
for i in $(GAMES); do \
$(INSTALL_PROGRAM) -m 755 $$i $(DESTDIR)$(gamesdir)/$$i \
|| exit 1; \
done

15
blackbox.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
blackbox : [X] GTK COMMON blackbox
blackbox : [G] WINDOWS COMMON blackbox
ALL += blackbox
!begin gtk
GAMES += blackbox
!end
!begin >list.c
A(blackbox) \
!end

17
bridges.R Normal file
View File

@ -0,0 +1,17 @@
# -*- makefile -*-
BRIDGES = bridges dsf
bridges : [X] GTK COMMON BRIDGES
bridges : [G] WINDOWS COMMON BRIDGES
ALL += BRIDGES
!begin gtk
GAMES += bridges
!end
!begin >list.c
A(bridges) \
!end

15
cube.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
cube : [X] GTK COMMON cube
cube : [G] WINDOWS COMMON cube
ALL += cube
!begin gtk
GAMES += cube
!end
!begin >list.c
A(cube) \
!end

View File

@ -193,7 +193,9 @@ end module builds a different puzzle.
\b On platforms such as MacOS X and PalmOS, which build all the
puzzles into a single monolithic binary, the game structure in each
back end must have a different name, and there's a helper module
\c{list.c} which contains a complete list of those game structures.
\c{list.c} (constructed automatically by the same Perl script that
builds the \cw{Makefile}s) which contains a complete list of those
game structures.
On the latter type of platform, source files may assume that the
preprocessor symbol \c{COMBINED} has been defined. Thus, the usual
@ -2916,9 +2918,10 @@ base), then there will be two global variables defined:
\c extern const int gamecount;
\c{gamelist} will be an array of \c{gamecount} game structures,
declared in the source module \c{list.c}. The application should
search that array for the game it wants, probably by reaching into
each game structure and looking at its \c{name} field.
declared in the automatically constructed source module \c{list.c}.
The application should search that array for the game it wants,
probably by reaching into each game structure and looking at its
\c{name} field.
}

15
dominosa.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
dominosa : [X] GTK COMMON dominosa
dominosa : [G] WINDOWS COMMON dominosa
ALL += dominosa
!begin gtk
GAMES += dominosa
!end
!begin >list.c
A(dominosa) \
!end

15
fifteen.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
fifteen : [X] GTK COMMON fifteen
fifteen : [G] WINDOWS COMMON fifteen
ALL += fifteen
!begin gtk
GAMES += fifteen
!end
!begin >list.c
A(fifteen) \
!end

17
flip.R Normal file
View File

@ -0,0 +1,17 @@
# -*- makefile -*-
FLIP = flip tree234
flip : [X] GTK COMMON FLIP
flip : [G] WINDOWS COMMON FLIP
ALL += FLIP
!begin gtk
GAMES += flip
!end
!begin >list.c
A(flip) \
!end

15
guess.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
guess : [X] GTK COMMON guess
guess : [G] WINDOWS COMMON guess
ALL += guess
!begin gtk
GAMES += guess
!end
!begin >list.c
A(guess) \
!end

15
inertia.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
inertia : [X] GTK COMMON inertia
inertia : [G] WINDOWS COMMON inertia
ALL += inertia
!begin gtk
GAMES += inertia
!end
!begin >list.c
A(inertia) \
!end

20
lightup.R Normal file
View File

@ -0,0 +1,20 @@
# -*- makefile -*-
LIGHTUP = lightup combi
lightup : [X] GTK COMMON LIGHTUP
lightup : [G] WINDOWS COMMON LIGHTUP
lightupsolver : [U] lightup[STANDALONE_SOLVER] combi STANDALONE
lightupsolver : [C] lightup[STANDALONE_SOLVER] combi STANDALONE
ALL += LIGHTUP
!begin gtk
GAMES += lightup
!end
!begin >list.c
A(lightup) \
!end

72
list.c
View File

@ -1,72 +0,0 @@
/*
* list.c: List of puzzles.
*/
#include "puzzles.h"
/*
* The available games can be most easily enumerated by searching
* for the line in each game source file saying "#define thegame
* <gamename>". Hence, the following piece of shell/Perl should
* regenerate this list automatically:
perl -ne '/^#define thegame (\S+)/ and $1 ne "nullgame" and print "extern const game $1;\n"' *.c
echo -e '\nconst game *gamelist[] = {'
perl -ne '/^#define thegame (\S+)/ and $1 ne "nullgame" and print " &$1,\n"' *.c
echo -e '};\n\nconst int gamecount = lenof(gamelist);'
*/
extern const game blackbox;
extern const game bridges;
extern const game cube;
extern const game dominosa;
extern const game fifteen;
extern const game flip;
extern const game guess;
extern const game inertia;
extern const game lightup;
extern const game loopy;
extern const game map;
extern const game mines;
extern const game net;
extern const game netslide;
extern const game pattern;
extern const game pegs;
extern const game rect;
extern const game samegame;
extern const game sixteen;
extern const game slant;
extern const game solo;
extern const game tents;
extern const game twiddle;
extern const game untangle;
const game *gamelist[] = {
&blackbox,
&bridges,
&cube,
&dominosa,
&fifteen,
&flip,
&guess,
&inertia,
&lightup,
&loopy,
&map,
&mines,
&net,
&netslide,
&pattern,
&pegs,
&rect,
&samegame,
&sixteen,
&slant,
&solo,
&tents,
&twiddle,
&untangle,
};
const int gamecount = lenof(gamelist);

17
loopy.R Normal file
View File

@ -0,0 +1,17 @@
# -*- makefile -*-
LOOPY = loopy tree234 dsf
loopy : [X] GTK COMMON LOOPY
loopy : [G] WINDOWS COMMON LOOPY
ALL += LOOPY
!begin gtk
GAMES += loopy
!end
!begin >list.c
A(loopy) \
!end

20
map.R Normal file
View File

@ -0,0 +1,20 @@
# -*- makefile -*-
MAP = map dsf
map : [X] GTK COMMON MAP
map : [G] WINDOWS COMMON MAP
mapsolver : [U] map[STANDALONE_SOLVER] dsf STANDALONE m.lib
mapsolver : [C] map[STANDALONE_SOLVER] dsf STANDALONE
ALL += MAP
!begin gtk
GAMES += map
!end
!begin >list.c
A(map) \
!end

20
mines.R Normal file
View File

@ -0,0 +1,20 @@
# -*- makefile -*-
MINES = mines tree234
mines : [X] GTK COMMON MINES
mines : [G] WINDOWS COMMON MINES
mineobfusc : [U] mines[STANDALONE_OBFUSCATOR] tree234 STANDALONE
mineobfusc : [C] mines[STANDALONE_OBFUSCATOR] tree234 STANDALONE
ALL += MINES
!begin gtk
GAMES += mines
!end
!begin >list.c
A(mines) \
!end

View File

@ -17,16 +17,19 @@
# - special-define objects (foo.o[PREPROCSYMBOL]) are not
# supported in the mac or vcproj makefiles.
use FileHandle;
use IO::Handle;
use Cwd;
open IN, "Recipe" or do {
@filestack = ();
$in = new IO::Handle;
open $in, "Recipe" or do {
# We want to deal correctly with being run from one of the
# subdirs in the source tree. So if we can't find Recipe here,
# try one level up.
chdir "..";
open IN, "Recipe" or die "unable to open Recipe file\n";
open $in, "Recipe" or die "unable to open Recipe file\n";
};
push @filestack, $in;
# HACK: One of the source files in `charset' is auto-generated by
# sbcsgen.pl. We need to generate that _now_, before attempting
@ -45,13 +48,20 @@ $project_name = "project"; # this is a good enough default
@allobjs = (); # all object file names
while (<IN>) {
readinput: while (1) {
while (not defined ($_ = <$in>)) {
close $in;
last readinput if 0 == scalar @filestack;
$in = pop @filestack;
}
chomp;
split;
# Skip comments (unless the comments belong, for example because
# they're part of a diversion).
next if /^\s*#/ and !defined $divert;
chomp;
split;
if ($_[0] eq "!begin" and $_[1] eq "help") { $divert = \$help; next; }
if ($_[0] eq "!end") { $divert = undef; next; }
if ($_[0] eq "!name") { $project_name = $_[1]; next; }
@ -59,13 +69,27 @@ while (<IN>) {
if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;}
if ($_[0] eq "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;}
if ($_[0] eq "!begin") {
if (&mfval($_[1])) {
if ($_[1] =~ /^>(.*)/) {
$divert = \$auxfiles{$1};
} elsif (&mfval($_[1])) {
$divert = \$makefile_extra{$_[1]};
} else {
$divert = \$dummy;
}
next;
}
if ($_[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;
}
$in = $filestack[$#filestack];
next;
}
# If we're gathering help text, keep doing so.
if (defined $divert) { ${$divert} .= "$_\n"; next; }
# Ignore blank lines.
@ -80,6 +104,11 @@ while (<IN>) {
$prog = undef;
die "$.: unexpected + line\n" if !defined $lastlistref;
} elsif ($_[1] eq "=") {
$groups{$_[0]} = [];
$listref = $groups{$_[0]};
$prog = undef;
shift @objs; # eat the group name
} elsif ($_[1] eq "+=") {
$groups{$_[0]} = [] if !defined $groups{$_[0]};
$listref = $groups{$_[0]};
$prog = undef;
@ -89,7 +118,7 @@ while (<IN>) {
$prog = $_[0];
shift @objs; # eat the program name
} else {
die "$.: unrecognised line type\n";
die "$.: unrecognised line type: '$_'\n";
}
shift @objs; # eat the +, the = or the :
@ -113,7 +142,11 @@ while (<IN>) {
$lastlistref = $listref;
}
close IN;
foreach $aux (sort keys %auxfiles) {
open AUX, ">$aux";
print AUX $auxfiles{$aux};
close AUX;
}
# Find object file names with predefines (in square brackets after
# the module name), and decide on actual object names for them.

19
net.R Normal file
View File

@ -0,0 +1,19 @@
# -*- makefile -*-
NET = net tree234 dsf
net : [X] GTK COMMON NET
# 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
ALL += NET
!begin gtk
GAMES += net
!end
!begin >list.c
A(net) \
!end

17
netslide.R Normal file
View File

@ -0,0 +1,17 @@
# -*- makefile -*-
NETSLIDE = netslide tree234
netslide : [X] GTK COMMON NETSLIDE
netslide : [G] WINDOWS COMMON NETSLIDE
ALL += NETSLIDE
!begin gtk
GAMES += netslide
!end
!begin >list.c
A(netslide) \
!end

12
nullgame.R Normal file
View File

@ -0,0 +1,12 @@
# -*- 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 : [G] WINDOWS COMMON nullgame

18
pattern.R Normal file
View File

@ -0,0 +1,18 @@
# -*- makefile -*-
pattern : [X] GTK COMMON pattern
pattern : [G] WINDOWS COMMON pattern
patternsolver : [U] pattern[STANDALONE_SOLVER] STANDALONE
patternsolver : [C] pattern[STANDALONE_SOLVER] STANDALONE
ALL += pattern
!begin gtk
GAMES += pattern
!end
!begin >list.c
A(pattern) \
!end

17
pegs.R Normal file
View File

@ -0,0 +1,17 @@
# -*- makefile -*-
PEGS = pegs tree234
pegs : [X] GTK COMMON PEGS
pegs : [G] WINDOWS COMMON PEGS
ALL += PEGS
!begin gtk
GAMES += pegs
!end
!begin >list.c
A(pegs) \
!end

15
rect.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
rect : [X] GTK COMMON rect
rect : [G] WINDOWS COMMON rect
ALL += rect
!begin gtk
GAMES += rect
!end
!begin >list.c
A(rect) \
!end

15
samegame.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
samegame : [X] GTK COMMON samegame
samegame : [G] WINDOWS COMMON samegame
ALL += samegame
!begin gtk
GAMES += samegame
!end
!begin >list.c
A(samegame) \
!end

15
sixteen.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
sixteen : [X] GTK COMMON sixteen
sixteen : [G] WINDOWS COMMON sixteen
ALL += sixteen
!begin gtk
GAMES += sixteen
!end
!begin >list.c
A(sixteen) \
!end

20
slant.R Normal file
View File

@ -0,0 +1,20 @@
# -*- makefile -*-
SLANT = slant dsf
slant : [X] GTK COMMON SLANT
slant : [G] WINDOWS COMMON SLANT
slantsolver : [U] slant[STANDALONE_SOLVER] dsf STANDALONE
slantsolver : [C] slant[STANDALONE_SOLVER] dsf STANDALONE
ALL += SLANT
!begin gtk
GAMES += slant
!end
!begin >list.c
A(slant) \
!end

18
solo.R Normal file
View File

@ -0,0 +1,18 @@
# -*- makefile -*-
solo : [X] GTK COMMON solo
solo : [G] WINDOWS COMMON solo
solosolver : [U] solo[STANDALONE_SOLVER] STANDALONE
solosolver : [C] solo[STANDALONE_SOLVER] STANDALONE
ALL += solo
!begin gtk
GAMES += solo
!end
!begin >list.c
A(solo) \
!end

20
tents.R Normal file
View File

@ -0,0 +1,20 @@
# -*- makefile -*-
TENTS = tents maxflow
tents : [X] GTK COMMON TENTS
tents : [G] WINDOWS COMMON TENTS
ALL += TENTS
tentssolver : [U] tents[STANDALONE_SOLVER] maxflow STANDALONE
tentssolver : [C] tents[STANDALONE_SOLVER] maxflow STANDALONE
!begin gtk
GAMES += tents
!end
!begin >list.c
A(tents) \
!end

15
twiddle.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
twiddle : [X] GTK COMMON twiddle
twiddle : [G] WINDOWS COMMON twiddle
ALL += twiddle
!begin gtk
GAMES += twiddle
!end
!begin >list.c
A(twiddle) \
!end

17
untangle.R Normal file
View File

@ -0,0 +1,17 @@
# -*- makefile -*-
UNTANGLE = untangle tree234
untangle : [X] GTK COMMON UNTANGLE
untangle : [G] WINDOWS COMMON UNTANGLE
ALL += UNTANGLE
!begin gtk
GAMES += untangle
!end
!begin >list.c
A(untangle) \
!end