Switch to using automake for the Unix autoconfigured build.
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 23 Jul 2011 11:33:29 +0000 (11:33 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 23 Jul 2011 11:33:29 +0000 (11:33 +0000)
mkfiles.pl no longer generates a Makefile.in, but instead generates a
Makefile.am on which mkauto.sh runs automake. This means that the
autoconfigured makefile now does build-time dependency tracking (a
standard feature of automake-generated makefiles), and is generally
more like what Unix people will expect.

Some of the old-style make command-line settings (VER=-DRELEASE=foo,
XFLAGS=-DDEBUG) will still work; the COMPAT settings are better done
by autoconfiguration, and my habitual 'XFLAGS="-g -O0"' for an easily
debuggable build will actually not work any more because CFLAGS is
specified _after_ XFLAGS, so I should instead write 'make CFLAGS=-O0'
(-g is the default in automake, removed at 'make install' time).

The new makefile will automatically degrade into one that builds the
command-line tools only, in the case where GTK could not be found. In
principle, therefore, it should be an adequate replacement for _both_
the static Unix makefiles, Makefile.gtk and Makefile.ux. I haven't
actually retired those in this commit, but I'm pretty tempted.

git-svn-id: svn://svn.tartarus.org/sgt/putty@9239 cda61777-01e9-0310-a592-d414129be87e

README
Recipe
mkauto.sh
mkfiles.pl
mkunxarc.sh
unix/configure.ac
version.c

diff --git a/README b/README
index 24789b3..a215aef 100644 (file)
--- a/README
+++ b/README
@@ -80,22 +80,38 @@ For building on Unix:
    utilities and has no Gtk dependence.
 
  - For the graphical utilities, Gtk+-1.2 and Gtk+-2.0 should both be
-   supported.
-
- - Both Unix Makefiles have an `install' target. Note that by default
-   it tries to install `man' pages, which you may need to have built
-   using Halibut first -- see below.
+   supported. In the absence of either, the configure script will
+   automatically construct a Makefile which builds only the
+   command-line utilities.
+
+ - pterm would like to be setuid or setgid, as appropriate, to permit
+   it to write records of user logins to /var/run/utmp and
+   /var/log/wtmp. (Of course it will not use this privilege for
+   anything else, and in particular it will drop all privileges before
+   starting up complex subsystems like GTK.) By default the makefile
+   will not attempt to add privileges to the pterm executable at 'make
+   install' time, but you can ask it to do so by running configure
+   with the option '--enable-setuid=USER' or '--enable-setgid=GROUP'.
+
+ - The Unix Makefiles have an `install' target. Note that by default
+   it tries to install `man' pages; if you have fetched the source via
+   Subversion then you will need to have built these using Halibut
+   first - see below.
 
 All of the Makefiles are generated automatically from the file
-`Recipe' by the Perl script `mkfiles.pl'. Additions and corrections
-to Recipe and the mkfiles.pl are much more useful than additions and
-corrections to the alternative Makefiles themselves.
+`Recipe' by the Perl script `mkfiles.pl' (except for the Unix one,
+which is generated by the `configure' script; mkfiles.pl only
+generates the input to automake). Additions and corrections to Recipe,
+mkfiles.pl and/or configure.ac are much more useful than additions and
+corrections to the actual Makefiles, Makefile.am or Makefile.in.
 
 The Unix `configure' script and its various requirements are generated
 by the shell script `mkauto.sh', which requires GNU Autoconf, GNU
 Automake, and Gtk; if you've got the source from Subversion rather
 than using one of our source snapshots, you'll need to run this
-yourself.
+yourself. The input file to Automake is generated by mkfiles.pl along
+with all the rest of the makefiles, so you will need to run mkfiles.pl
+and then mkauto.sh.
 
 Documentation (in various formats including Windows Help and Unix
 `man' pages) is built from the Halibut (`.but') files in the `doc'
diff --git a/Recipe b/Recipe
index 698d42d..454fe32 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -19,7 +19,7 @@
 !makefile lcc windows/Makefile.lcc
 !makefile gtk unix/Makefile.gtk
 !makefile unix unix/Makefile.ux
-!makefile ac unix/Makefile.in
+!makefile am unix/Makefile.am
 !makefile osx macosx/Makefile
 !makefile devcppproj windows/DEVCPP
 # Source directories.
@@ -169,6 +169,19 @@ version.o: FORCE
        fi
 !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 '/* nothing to see here */' >$@
+
+!end
 
 # Add VER to Windows resource targets, and force them to be rebuilt every
 # time, on the assumption that they will contain version information.
@@ -228,6 +241,21 @@ install-strip:
 CFLAGS += -DMACOSX
 !end
 
+# List the man pages for the automake makefile.
+!begin am
+man1_MANS = ../doc/plink.1 ../doc/pscp.1 ../doc/psftp.1 ../doc/pterm.1 \
+            ../doc/putty.1 ../doc/puttygen.1 ../doc/puttytel.1
+!end
+
+# In automake, chgrp/chmod pterm after installation, if configured to.
+!begin am
+if HAVE_SETID_CMD
+install-exec-local:
+       @SETID_CMD@ $(bindir)/pterm
+       chmod @SETID_MODE@ $(bindir)/pterm
+endif
+!end
+
 # Random symbols.
 !begin cygwin vars
 # _WIN32_IE is required to expose identifiers that only make sense on
index 0786694..4a467a5 100755 (executable)
--- a/mkauto.sh
+++ b/mkauto.sh
@@ -7,41 +7,5 @@
 # well as from outside.
 test -f unix.h && cd ..
 
-# Persuade automake to give us a copy of its install-sh. This is a
-# pain because I don't actually want to have to _use_ automake.
-# Instead, I construct a trivial unrelated automake project in a
-# temporary subdirectory, run automake so that it'll copy
-# install-sh into that directory, then copy it back out again.
-# Hideous, but it should work.
-
-mkdir automake-grievous-hack
-cat > automake-grievous-hack/hello.c << EOF
-#include <stdio.h>
-int main(int argc, char **argv)
-{
-    printf("hello, world\n");
-    return 0;
-}
-EOF
-cat > automake-grievous-hack/Makefile.am << EOF
-bin_PROGRAMS = hello
-hello_SOURCES = hello.c
-EOF
-cat > automake-grievous-hack/configure.ac << EOF
-AC_INIT
-AM_INIT_AUTOMAKE(hello, 1.0)
-AC_CONFIG_FILES([Makefile])
-AC_PROG_CC
-AC_OUTPUT
-EOF
-echo Some news > automake-grievous-hack/NEWS
-echo Some text > automake-grievous-hack/README
-echo Some people > automake-grievous-hack/AUTHORS
-echo Some changes > automake-grievous-hack/ChangeLog
-rm -f install-sh # this won't work if we accidentally have one _here_
-(cd automake-grievous-hack && autoreconf -i && \
-  cp install-sh ../unix/install-sh)
-rm -rf automake-grievous-hack
-
-# That was the hard bit. Now run autoconf on our real configure.in.
-(cd unix && autoreconf && rm -rf aclocal.m4 autom4te.cache)
+# Run autoconf on our real configure.in.
+(cd unix && autoreconf -i && rm -rf autom4te.cache)
index bc77ec0..8f5aa6c 100755 (executable)
@@ -64,6 +64,12 @@ while (<IN>) {
   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 "!forceobj") { $forceobj{$_[1]} = 1; next; }
   if ($_[0] eq "!begin") {
       if (&mfval($_[1])) {
@@ -177,11 +183,13 @@ foreach $i (@prognames) {
 # file name into a listref containing further source file names.
 
 %further = ();
+%allsourcefiles = (); # this is wanted by some makefiles
 while (scalar @scanlist > 0) {
   $file = shift @scanlist;
   next if defined $further{$file}; # skip if we've already done it
   $further{$file} = [];
   $dirfile = &findfile($file);
+  $allsourcefiles{$dirfile} = 1;
   open IN, "$dirfile" or die "unable to open source file $file\n";
   while (<IN>) {
     chomp;
@@ -226,7 +234,7 @@ sub mfval($) {
     # prints a warning and returns false;
     if (grep { $type eq $_ }
        ("vc","vcproj","cygwin","borland","lcc","devcppproj","gtk","unix",
-        "ac","osx",)) {
+        "am","osx",)) {
            return 1;
        }
     warn "$.:unknown makefile type '$type'\n";
@@ -1084,71 +1092,89 @@ if (defined $makefiles{'unix'}) {
     select STDOUT; close OUT;
 }
 
-if (defined $makefiles{'ac'}) {
-    $dirpfx = &dirpfx($makefiles{'ac'}, "/");
+if (defined $makefiles{'am'}) {
+    $dirpfx = "\$(srcdir)/" . &dirpfx($makefiles{'am'}, "/");
 
-    ##-- Unix/autoconf makefile
-    open OUT, ">$makefiles{'ac'}"; select OUT;
+    ##-- Unix/autoconf Makefile.am
+    open OUT, ">$makefiles{'am'}"; select OUT;
     print
-    "# Makefile.in for $project_name under Unix with Autoconf.\n".
+    "# 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";
-    # gcc command line option is -D not /D
-    ($_ = $help) =~ s/([=" ])\/D/$1-D/gs;
-    print $_;
-    print
-    "\n".
-    "CC = \@CC\@\n".
-    "\n".
-    &splitline("CFLAGS = \@CFLAGS\@ \@PUTTYCFLAGS\@ \@CPPFLAGS\@ " .
-               "\@DEFS\@ \@GTK_CFLAGS\@ " .
-              (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
-    "XLDFLAGS = \@LDFLAGS\@ \@LIBS\@ \@GTK_LIBS\@\n".
-    "ULDFLAGS = \@LDFLAGS\@ \@LIBS\@\n".
-    "INSTALL=\@INSTALL\@\n".
-    "INSTALL_PROGRAM=\$(INSTALL)\n".
-    "INSTALL_DATA=\$(INSTALL)\n".
-    "prefix=\@prefix\@\n".
-    "exec_prefix=\@exec_prefix\@\n".
-    "bindir=\@bindir\@\n".
-    "datarootdir=\@datarootdir\@\n".
-    "mandir=\@mandir\@\n".
-    "man1dir=\$(mandir)/man1\n".
-    "\n".
-    &def($makefile_extra{'gtk'}->{'vars'}) .
-    "\n".
-    ".SUFFIXES:\n".
-    "\n".
-    "\n".
-    "all: \@all_targets\@\n".
-    &splitline("all-cli:" . join "", map { " $_" } &progrealnames("U"))."\n".
-    &splitline("all-gtk:" . join "", map { " $_" } &progrealnames("X"))."\n";
-    print "\n";
-    foreach $p (&prognames("X:U")) {
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\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.
+    @sources = ("allsources", "=",
+                map {"${dirpfx}$_"} sort keys %allsourcefiles);
+    print &splitline(join " ", @sources), "\n\n";
+
+    @cliprogs = ("bin_PROGRAMS", "=");
+    foreach $p (&prognames("U")) {
       ($prog, $type) = split ",", $p;
-      $objstr = &objects($p, "X.o", undef, undef);
-      print &splitline($prog . ": " . $objstr), "\n";
-      $libstr = &objects($p, undef, undef, "-lX");
-      print &splitline("\t\$(CC) -o \$@ " .
-                       $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n";
+      push @cliprogs, $prog;
     }
-    foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) {
-      if ($forceobj{$d->{obj_orig}}) {
-        printf("%s: FORCE\n", $d->{obj});
-      } else {
-        print &splitline(sprintf("%s: %s", $d->{obj},
-                                 join " ", @{$d->{deps}})), "\n";
+    @allprogs = @cliprogs;
+    foreach $p (&prognames("X")) {
+      ($prog, $type) = split ",", $p;
+      push @allprogs, $prog;
+    }
+    print "if HAVE_GTK\n";
+    print &splitline(join " ", @allprogs), "\n";
+    print "else\n";
+    print &splitline(join " ", @cliprogs), "\n";
+    print "endif\n\n";
+
+    %objtosrc = ();
+    foreach $d (&deps("X", undef, $dirpfx, "/", "am")) {
+      $objtosrc{$d->{obj}} = $d->{deps}->[0];
+    }
+
+    @amcflags = ("\$(COMPAT)", "\$(XFLAGS)", map {"-I$dirpfx$_"} @srcdirs);
+    print "if HAVE_GTK\n";
+    print &splitline(join " ", "AM_CFLAGS", "=",
+                     "\$(GTK_CFLAGS)", @amcflags), "\n";
+    print "else\n";
+    print &splitline(join " ", "AM_CFLAGS", "=", @amcflags), "\n";
+    print "endif\n\n";
+
+    %amspeciallibs = ();
+    foreach $obj (sort { $a cmp $b } keys %{$cflags{'am'}}) {
+      print "lib${obj}_a_SOURCES = ", $objtosrc{$obj}, "\n";
+      print &splitline(join " ", "lib${obj}_a_CFLAGS", "=", @amcflags,
+                       $cflags{'am'}->{$obj}), "\n";
+      $amspeciallibs{$obj} = "lib${obj}.a";
+    }
+    print &splitline(join " ", "noinst_LIBRARIES", "=",
+                     sort { $a cmp $b } values %amspeciallibs), "\n\n";
+
+    foreach $p (&prognames("X:U")) {
+      ($prog, $type) = split ",", $p;
+      print "if HAVE_GTK\n" if $type eq "X";
+      @progsources = ("${prog}_SOURCES", "=");
+      %sourcefiles = ();
+      @ldadd = ();
+      $objstr = &objects($p, "X", undef, undef);
+      foreach $obj (split / /,$objstr) {
+        if ($amspeciallibs{$obj}) {
+          push @ldadd, $amspeciallibs{$obj};
+        } else {
+          $sourcefiles{$objtosrc{$obj}} = 1;
+        }
       }
-      print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n");
+      push @progsources, sort { $a cmp $b } keys %sourcefiles;
+      print &splitline(join " ", @progsources), "\n";
+      if ($type eq "X") {
+        push @ldadd, "\$(GTK_LIBS)";
+      }
+      if (@ldadd) {
+        print &splitline(join " ", "${prog}_LDADD", "=", @ldadd), "\n";
+      }
+      print "endif\n" if $type eq "X";
+      print "\n";
     }
-    print "\n";
-    print $makefile_extra{'gtk'}->{'end'};
-    print "\nclean:\n".
-    "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:U")) . "\n";
-    print "\ndistclean: clean\n".
-    "\t". &splitline("rm -f config.status config.cache config.log ".
-                    "configure.lineno config.status.lineno Makefile") . "\n";
-    print "\nFORCE:\n";
+    print $makefile_extra{'am'}->{'end'};
     select STDOUT; close OUT;
 }
 
index 6c3e892..0f9ff31 100755 (executable)
 case "$1" in
   ????-??-??)
     case "$1" in *[!-0-9]*) echo "Malformed snapshot ID '$1'" >&2;exit 1;;esac
-    arcsuffix="-`cat LATEST.VER`-$1"
+    autoconfver="`cat LATEST.VER`-$1"
+    arcsuffix="-$autoconfver"
     ver="-DSNAPSHOT=$1"
     docver=
     ;;
   r*)
-    arcsuffix="-$1"
+    autoconfver="$1"
+    arcsuffix="-$autoconfver"
     ver="-DSVN_REV=${1#r}"
     docver=
     ;;
   '')
+    autoconfver="X.XX" # got to put something in here!
     arcsuffix=
     ver=
     docver=
     ;;
   *)
     case "$1" in *[!.0-9a-z]*) echo "Malformed release ID '$1'">&2;exit 1;;esac
-    arcsuffix="-$1"
+    autoconfver="$1"
+    arcsuffix="-$autoconfver"
     ver="-DRELEASE=$1"
     docver="VERSION=\"PuTTY release $1\""
     ;;
@@ -34,7 +38,6 @@ esac
 
 perl mkfiles.pl
 (cd doc && make -s ${docver:+"$docver"})
-sh mkauto.sh 2>/dev/null
 
 relver=`cat LATEST.VER`
 arcname="putty$arcsuffix"
@@ -49,6 +52,7 @@ find . -name uxarc -prune -o \
        -name CVS -prune -o \
        -name .cvsignore -prune -o \
        -name .svn -prune -o \
+       -name configure.ac -prune -o \
        -name '*.zip' -prune -o \
        -name '*.tar.gz' -prune -o \
        -type f -exec ln -s $PWD/{} uxarc/$arcname/{} \;
@@ -57,5 +61,8 @@ if test "x$ver" != "x"; then
    md5sum `find . -name '*.[ch]' -print` > manifest;
    echo "$ver" > version.def)
 fi
+sed "s/^AC_INIT(putty,.*/AC_INIT(putty, $autoconfver)/" unix/configure.ac > uxarc/$arcname/unix/configure.ac
+(cd uxarc/$arcname && sh mkauto.sh) 2>errors || { cat errors >&2; exit 1; }
+
 tar -C uxarc -chzof $arcname.tar.gz $arcname
 rm -rf uxarc
index bd40373..2115dff 100644 (file)
@@ -4,18 +4,41 @@
 # * Automake (for aclocal)
 # If you've got them, running "autoreconf" should work.
 
-AC_INIT
+# Version number is substituted by Buildscr for releases, snapshots
+# and custom builds out of svn; X.XX shows up in ad-hoc developer
+# builds, which shouldn't matter
+AC_INIT(putty, X.XX)
 AC_CONFIG_FILES([Makefile])
 AC_CONFIG_HEADERS([uxconfig.h:uxconfig.in])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
 
 AC_PROG_INSTALL
-AC_PROG_CC
-if test "X$GCC" = Xyes; then
-    PUTTYCFLAGS="-Wall -Werror"
-else
-    PUTTYCFLAGS=""
-fi
-AC_SUBST(PUTTYCFLAGS)
+AC_PROG_RANLIB
+
+# Mild abuse of the '--enable' option format to allow manual
+# specification of setuid or setgid setup in pterm.
+setidtype=none
+AC_ARG_ENABLE([setuid],
+            [AS_HELP_STRING([--enable-setuid=USER],
+                            [make pterm setuid to a given user])],
+            [case "$enableval" in
+               no) setidtype=none;;
+               *) setidtype=setuid; setidval="$enableval";;
+             esac])
+AC_ARG_ENABLE([setgid],
+            [AS_HELP_STRING([--enable-setgid=GROUP],
+                            [make pterm setgid to a given group])],
+            [case "$enableval" in
+               no) setidtype=none;;
+               *) setidtype=setgid; setidval="$enableval";;
+             esac])
+AM_CONDITIONAL(HAVE_SETID_CMD, [test "$setidtype" != "none"])
+AS_IF([test "x$setidtype" = "xsetuid"],
+      [SETID_CMD="chown $setidval"; SETID_MODE="4755"])
+AS_IF([test "x$setidtype" = "xsetgid"],
+      [SETID_CMD="chgrp $setidval"; SETID_MODE="2755"])
+AC_SUBST(SETID_CMD)
+AC_SUBST(SETID_MODE)
 
 AC_ARG_WITH([gssapi],
   [AS_HELP_STRING([--without-gssapi],
@@ -42,7 +65,8 @@ AC_CHECK_HEADERS([utmpx.h sys/select.h],,,[
 #include <sys/types.h>
 #include <utmp.h>])
 
-# Look for both GTK 1 and GTK 2.
+# Look for both GTK 1 and GTK 2. If we can't find either, have the
+# makefile only build the CLI programs.
 
 gtk=none
 
@@ -62,11 +86,7 @@ case "$gtk_version_desired" in
     ;;
 esac
 
-if test "$gtk" = "none"; then
-  all_targets="all-cli"
-else
-  all_targets="all-cli all-gtk"
-fi
+AM_CONDITIONAL(HAVE_GTK, [test "$gtk" != "none"])
 
 if test "$gtk" = "2"; then
   ac_save_CFLAGS="$CFLAGS"
@@ -77,7 +97,6 @@ if test "$gtk" = "2"; then
   CFLAGS="$ac_save_CFLAGS"
   LIBS="$ac_save_LIBS"
 fi
-AC_SUBST([all_targets])
 
 AC_SEARCH_LIBS([socket], [xnet])
 
@@ -100,6 +119,16 @@ AC_CHECK_FUNCS([getaddrinfo ptsname setresuid strsignal updwtmpx])
 
 AC_OUTPUT
 
+if test "$gtk" = "none"; then cat <<EOF
+
+'configure' was unable to find either the GTK 1 or GTK 2 libraries on
+your system. Therefore, PuTTY itself and the other GUI utilities will
+not be built by the generated Makefile: only the command-line tools
+such as puttygen, plink and psftp will be built.
+
+EOF
+fi
+
 AH_BOTTOM([
 /* Convert autoconf definitions to ones that PuTTY wants. */
 
index 3fa0c0e..7734160 100644 (file)
--- 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 SNAPSHOT
 
 #if defined SVN_REV