shithub: puzzles

Download patch

ref: b375232d7dc8357e820ec00808749d077c8b06b9
parent: 7ae22afaf365d7012ebca82b069cf9b3a145b302
author: Simon Tatham <anakin@pobox.com>
date: Sun Jun 30 04:58:45 EDT 2013

Support building via autoconf and automake. mkfiles.pl now outputs a
Makefile.am, and there's a new mkauto.sh which builds a corresponding
configure script.

The old makefile has been renamed from 'Makefile' to 'Makefile.gtk',
indicating that the intended new _default_ approach is to use the
autoconf world. Makefile.gtk is provided as an emergency fallback in
case anything fails with the new stuff that used to work with it.

The new configure script does not support the same $(BINPREFIX) system
as the old Makefile did. However, as I understand it, it should be
possible to configure using --program-prefix="sgt-" (for example) and
then the binaries should all be renamed appropriately at install time.

The Makefile.am is quite painful. The Puzzles codebase relies heavily
on compiling individual object files multiple times with different the
cpp flags per build deliverable (program or library) and not per
source file. Solution: anything built with non-default compile options
has to go in its own little library. But that doesn't work either in
the general case, because as soon as you have more than one such
library linked into an application, Unix ld semantics bite you if the
objects in the libraries both refer to each other. So I ended up
building all those little libraries but not _using_ them - instead the
link commands for the programs needing those objects refer to the
objects directly, under the silly names that automake gives them.
(That's less fragile than it sounds, because it does _document_ the
names of the intermediate object files. But still, yuck.)

[originally from svn r9886]

--- a/Buildscr
+++ b/Buildscr
@@ -3,19 +3,27 @@
 
 module puzzles
 
+# Start by substituting the right version number in configure.ac.
+in puzzles do perl -i~ -pe 's/rNNNN/r$(revision)/' configure.ac
+in puzzles do rm configure.ac~
+
 # First build some local binaries, to run the icon build.
-in puzzles do perl mkfiles.pl
+in puzzles do perl mkfiles.pl -U
 in puzzles do make
 
 # Now build the screenshots and icons.
 in puzzles/icons do xvfb-run -s "-screen 0 1024x768x24" make web winicons gtkicons
 
+# 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
+
 # Re-run mkfiles.pl now that it knows the icons are there.
 in puzzles do perl mkfiles.pl
 
-# Destroy the local binaries, mostly to avoid wasting network
-# bandwidth by transferring them to the delegate servers.
-in puzzles do make clean
+# Rebuild the configure script.
+in puzzles do ./mkauto.sh
 
 # Build the OS X .dmg archive.
 # 2012-04-03: commented out because my Mac is dead.
--- a/Recipe
+++ b/Recipe
@@ -8,7 +8,8 @@
 
 !name puzzles
 
-!makefile gtk Makefile
+!makefile gtk Makefile.gtk
+!makefile am Makefile.am
 !makefile vc Makefile.vc
 !makefile wce Makefile.wce
 !makefile cygwin Makefile.cyg
@@ -130,6 +131,22 @@
 .PHONY: FORCE
 !end
 !specialobj gtk version
+# In the automake build, we have to do the whole job by supplying
+# extra CFLAGS, so we have to put the if statement inside one big
+# backtick expression. We also force rebuilding via a -D option that
+# makes version.o include empty.h, which we construct ourselves and
+# touch whenever any source file is updated.
+!cflags am version $(VER) -DINCLUDE_EMPTY_H `if test -z "$(VER)" && (cd $(srcdir)/..; md5sum -c manifest >/dev/null 2>&1); then cat $(srcdir)/../version.def; else echo "$(VER)"; fi`
+!begin am
+BUILT_SOURCES = empty.h
+CLEANFILES = empty.h
+empty.h: $(allsources)
+	echo '/* Empty file touched by automake makefile to force rebuild of version.o */' >$@
+
+!end
+!begin >empty.h
+/* Empty file touched by automake makefile to force rebuild of version.o */
+!end
 !begin nestedvm
 version.o: version.c version2.def
 	$(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version2.def` -c version.c
--- /dev/null
+++ b/configure.ac
@@ -1,0 +1,15 @@
+dnl Configure script for the Unix GTK build of puzzles.
+
+AC_INIT([puzzles], rNNNN, [anakin@pobox.com])
+AC_CONFIG_SRCDIR([midend.c])
+AM_INIT_AUTOMAKE([foreign])
+AC_PROG_CC
+if test "x$GCC" = "xyes"; then
+    : # FIXME: do something interesting enabling as many warning
+      # options as possible without breaking system headers.
+fi
+AM_PATH_GTK_2_0([2.0.0])
+AC_PROG_RANLIB
+AC_PROG_INSTALL
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
--- a/makedist.sh
+++ b/makedist.sh
@@ -44,7 +44,8 @@
   *.rc2 mkfiles.pl Makefile Makefile.* \
   HACKING puzzles.txt puzzles.hlp puzzles.cnt puzzles.chm \
   icons/Makefile icons/*.{sav,pl,sh} icons/win16pal.xpm \
-  icons/*.png icons/*.ico icons/*.rc icons/*.c; do
+  icons/*.png icons/*.ico icons/*.rc icons/*.c \
+  configure.ac mkauto.sh aclocal.m4 configure depcomp install-sh missing; do
   case $i in
     */*) ln -s ../../../$i tmp.$$/puzzles$arcsuffix/$i;;
     *)   ln -s ../../$i tmp.$$/puzzles$arcsuffix/$i;;
--- /dev/null
+++ b/mkauto.sh
@@ -1,0 +1,2 @@
+#! /bin/sh
+autoreconf -i && rm -rf autom4te.cache
--- a/mkfiles.pl
+++ b/mkfiles.pl
@@ -20,7 +20,25 @@
 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 {
@@ -77,6 +95,12 @@
   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") {
       if ($_[1] =~ /^>(.*)/) {
 	  $divert = \$auxfiles{$1};
@@ -287,7 +311,7 @@
     # Returns true if the argument is a known makefile type. Otherwise,
     # prints a warning and returns false;
     if (grep { $type eq $_ }
-	("vc","vcproj","cygwin","borland","lcc","gtk","mpw","nestedvm","osx","wce","gnustep","emcc")) {
+	("vc","vcproj","cygwin","borland","lcc","gtk","am","mpw","nestedvm","osx","wce","gnustep","emcc")) {
 	    return 1;
 	}
     warn "$.:unknown makefile type '$type'\n";
@@ -467,6 +491,8 @@
   return ();
 }
 
+$orig_dir = cwd;
+
 # Now we're ready to output the actual Makefiles.
 
 if (defined $makefiles{'cygwin'}) {
@@ -837,8 +863,6 @@
 if (defined $makefiles{'vcproj'}) {
     $mftyp = 'vcproj';
 
-    $orig_dir = cwd;
-
     ##-- MSVC 6 Workspace and projects
     #
     # Note: All files created in this section are written in binary
@@ -1155,6 +1179,119 @@
     select STDOUT; close OUT;
 }
 
+if (defined $makefiles{'am'}) {
+    $mftyp = 'am';
+    $dirpfx = "\$(srcdir)/" . &dirpfx($makefiles{'am'}, "/");
+
+    ##-- 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";
+
+    @binprogs = ();
+    @noinstprogs = ();
+    foreach $p (&prognames("X:U")) {
+        ($prog, $type) = split ",", $p;
+        if ("FIXME") { # decide which programs go where
+            push @binprogs, # FIXME "\$(BINPREFIX)" .
+                $prog;
+        } else {
+            push @noinstprogs, # FIXME "\$(BINPREFIX)" .
+                $prog;
+        }
+    }
+    print &splitline(join " ", "bin_PROGRAMS", "=", @binprogs), "\n";
+    print &splitline(join " ", "noinst_PROGRAMS", "=", @noinstprogs), "\n";
+
+    %objtosrc = ();
+    %amspeciallibs = ();
+    %amlibobjname = ();
+    %allsources = ();
+    foreach $d (&deps("X", undef, $dirpfx, "/", "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
+            $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}};
+    }
+
+    # 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$dirpfx$_"} @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
@@ -1644,4 +1781,14 @@
     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/version.c
+++ b/version.c
@@ -5,6 +5,22 @@
 #define STR1(x) #x
 #define STR(x) STR1(x)
 
+#ifdef INCLUDE_EMPTY_H
+/*
+ * Horrible hack to force version.o to be rebuilt unconditionally in
+ * the automake world: empty.h is an empty header file, created by the
+ * makefile and forcibly updated every time make is run. Including it
+ * here causes automake to track it as a dependency, which will cause
+ * version.o to be rebuilt too.
+ *
+ * The space between # and include causes mkfiles.pl's dependency
+ * scanner (for all other makefile types) to ignore this include,
+ * which is correct because only the automake makefile passes
+ * -DINCLUDE_EMPTY_H to enable it.
+ */
+# include "empty.h"
+#endif
+
 #if defined REVISION
 
 char ver[] = "Revision: r" STR(REVISION);