automake-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Reliable multi-file install for man pages.


From: Ralf Wildenhues
Subject: Reliable multi-file install for man pages.
Date: Sun, 7 Sep 2008 10:34:43 +0200
User-agent: Mutt/1.5.18 (2008-05-17)

Man pages is where it gets more interesting (and less readable).
The ugly things (from an rule writer's POV) about MANS are:

- they can appear in the source or in the build tree,
- their extension may need mangling,
- their base names may need mangling with $(transform), or not
  (notrans_).

The only nice thing about them is that there is no nobase_ prefix.

This patch builds upon an earlier one posted by Jim.  This one has been
squeezed some more, to the point that it is possible to have some files
that need renaming and some that don't, and still get fast install for
the latter part of the crowd.  Specifically, with
  man_MANS = a.1 b.1
  man1_MANS = c.1 d.man

and transform='s/^b$/bb/', the rule will still install a.1 and c.1 in
one go.  :-)

(It could probably be squeezed even more, in order to unify the bits
that deal with notrans_ and those without notrans_.  Not doing that
now though.)

The patch has a slight nit, in that there are a couple of loops
containing
   list="$$list $$file"

which will scale quadratically for shells other than recent bash.
I didn't see an easy way to avoid this, while still ensuring that
the per-file install rule will reliably signal failure.  Since the
file lists should hopefully be limited by line length considerations
anyway though, I figured this to be an acceptable compromise.

The uninstall rule is fixed to be fast here as well.  It's simpler
than the install rule, stealing from it some needed bits.
And considerably shorter than its previous version.  :-)

Cheers,
Ralf

2008-09-07  Ralf Wildenhues  <address@hidden>
            Jim Meyering <address@hidden>

        Reliable multi-file install for man pages.
        * lib/am/mans.am: Include inst-vars.am.
        (install-man%SECTION%): Gather lists of man pages to install,
        and install up to 40 of those that do not have to be renamed
        at once.  Fail if installation of any file fails.
        (uninstall-man%SECTION%): Rewrite to use fewer sed invocations.
        * doc/automake.texi (Man pages): Hint the user to the fast path.
        Indentation fix.
        * tests/instmany-mans.test: New test.
        * tests/Makefile.am: Update.

diff --git a/doc/automake.texi b/doc/automake.texi
index 0ea5780..263d1b6 100644
--- a/doc/automake.texi
+++ b/doc/automake.texi
@@ -7644,6 +7644,7 @@ For instance, consider this example:
 man1_MANS = rename.man thesame.1 alsothesame.1c
 @end example
 
address@hidden
 In this case, @file{rename.man} will be renamed to @file{rename.1} when
 installed, but the other files will keep their names.
 
@@ -7661,6 +7662,10 @@ not expend effort to keep the man pages up to date.  In 
these cases, the
 installed by default.  The user can still explicitly install them via
 @samp{make install-man}.
 
+For fast installation, with many files it is preferable to use
address@hidden@var{SECTION}_MANS} over @samp{man_MANS} as well as files that
+do not need to be renamed.
+
 Man pages are not currently considered to be source, because it is not
 uncommon for man pages to be automatically generated.  Therefore they
 are not automatically included in the distribution.  However, this can
diff --git a/lib/am/mans.am b/lib/am/mans.am
index 338b43c..803a892 100644
--- a/lib/am/mans.am
+++ b/lib/am/mans.am
@@ -15,6 +15,7 @@
 ## You should have received a copy of the GNU General Public License
 ## along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+include inst-vars.am
 man%SECTION%dir = $(mandir)/man%SECTION%
 
 ## ------------ ##
@@ -34,62 +35,69 @@ install-man%SECTION%: %DEPS%
 if %?NOTRANS_MANS%
 ## Handle MANS with notrans_ prefix
        @list='%NOTRANS_SECT_LIST%'; \
+       { for i in $$list; do echo "$$i"; done;  \
 ## Extract all items from notrans_man_MANS that should go in this section.
 ## This must be done dynamically to support conditionals.
-?HAVE_NOTRANS? l2='%NOTRANS_LIST%'; \
-?HAVE_NOTRANS? for i in $$l2; do \
-?HAVE_NOTRANS?   case "$$i" in \
-## Have to accept files like `foo.1c'.
-?HAVE_NOTRANS?     *.%SECTION%*) list="$$list $$i" ;; \
-?HAVE_NOTRANS?   esac; \
+?HAVE_NOTRANS? l2='%NOTRANS_LIST%'; for i in $$l2; do \
+## Accept files like `foo.1c'.
+?HAVE_NOTRANS?   case $$i in *.%SECTION%*) echo "$$i";; esac; \
 ?HAVE_NOTRANS? done; \
-       for i in $$list; do \
+## Extract basename of manpage, change the extension if needed.
+       } | while read p; do \
 ## Find the file.
-         if test -f $$i; then file=$$i; \
-         else file=$(srcdir)/$$i; fi; \
-## Change the extension if needed.
-         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-         case "$$ext" in \
-           %SECTION%*) ;; \
-           *) ext='%SECTION%' ;; \
-         esac; \
-## Extract basename of man page and append extension.
-         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-         inst=`echo $$inst | sed -e 's/^.*\///'`.$$ext; \
-         echo " $(INSTALL_DATA) '$$file' 
'$(DESTDIR)$(man%SECTION%dir)/$$inst'"; \
-         $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man%SECTION%dir)/$$inst"; \
-       done
+         if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; echo "$$p"; \
+       done | \
+## Extract the basename of the man page and change the extension if needed.
+       sed 'n;s,.*/,,;p;s,\.[^%SECTION%][0-9a-z]*$$,.%SECTION%,' | \
+       sed 'N;N;s,\n, ,g' | { \
+## We now have a list "sourcefile basename installed-name".
+       list=; while read file base inst; do \
+         if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+           echo " $(INSTALL_DATA) '$$file' 
'$(DESTDIR)$(man%SECTION%dir)/$$inst'"; \
+           $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man%SECTION%dir)/$$inst" || 
exit $$?; \
+         fi; \
+       done; \
+       for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+       while read files; do \
+         test -z "$$files" || { \
+           echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man%SECTION%dir)'"; \
+           $(INSTALL_DATA) $$files "$(DESTDIR)$(man%SECTION%dir)" || exit $$?; 
}; \
+       done; }
 endif %?NOTRANS_MANS%
 if %?TRANS_MANS%
 ## Handle MANS without notrans_ prefix
        @list='%TRANS_SECT_LIST%'; \
-## Extract all items from man_MANS that should go in this section.
+       { for i in $$list; do echo "$$i"; done; \
+## Extract all items from notrans_man_MANS that should go in this section.
 ## This must be done dynamically to support conditionals.
-?HAVE_TRANS?   l2='%TRANS_LIST%'; \
-?HAVE_TRANS?   for i in $$l2; do \
-?HAVE_TRANS?     case "$$i" in \
-## Have to accept files like `foo.1c'.
-?HAVE_TRANS?       *.%SECTION%*) list="$$list $$i" ;; \
-?HAVE_TRANS?     esac; \
+?HAVE_TRANS?   l2='%TRANS_LIST%'; for i in $$l2; do \
+## Accept files like `foo.1c'.
+?HAVE_TRANS?     case $$i in *.%SECTION%*) echo "$$i";; esac; \
 ?HAVE_TRANS?   done; \
-       for i in $$list; do \
+## Extract basename of manpage, change the extension if needed.
+       } | while read p; do \
 ## Find the file.
-         if test -f $$i; then file=$$i; \
-         else file=$(srcdir)/$$i; fi; \
-## Change the extension if needed.
-         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-         case "$$ext" in \
-           %SECTION%*) ;; \
-           *) ext='%SECTION%' ;; \
-         esac; \
-## Extract basename of man page and run it through the program rename
-## transform.
-         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-         inst=`echo $$inst | sed -e 's/^.*\///'`; \
-         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-         echo " $(INSTALL_DATA) '$$file' 
'$(DESTDIR)$(man%SECTION%dir)/$$inst'"; \
-         $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man%SECTION%dir)/$$inst"; \
-       done
+         if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; echo "$$p"; \
+       done | \
+## Extract the basename of the man page and change the extension if needed.
+       sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^%SECTION%][0-9a-z]*$$,%SECTION%,;x' 
\
+             -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+       sed 'N;N;s,\n, ,g' | { \
+## We now have a list "sourcefile basename installed-name".
+       list=; while read file base inst; do \
+         if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+           echo " $(INSTALL_DATA) '$$file' 
'$(DESTDIR)$(man%SECTION%dir)/$$inst'"; \
+           $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man%SECTION%dir)/$$inst" || 
exit $$?; \
+         fi; \
+       done; \
+       for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+       while read files; do \
+         test -z "$$files" || { \
+           echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man%SECTION%dir)'"; \
+           $(INSTALL_DATA) $$files "$(DESTDIR)$(man%SECTION%dir)" || exit $$?; 
}; \
+       done; }
 endif %?TRANS_MANS%
 
 
@@ -97,7 +105,6 @@ endif %?TRANS_MANS%
 ## Uninstalling.  ##
 ## -------------- ##
 
-## This is just completely gross.
 .PHONY: uninstall-man
 ?INSTALL-MAN?uninstall-am: uninstall-man
 .PHONY uninstall-man: uninstall-man%SECTION%
@@ -106,54 +113,34 @@ uninstall-man%SECTION%:
 if %?NOTRANS_MANS%
 ## Handle MANS with notrans_ prefix
        @list='%NOTRANS_SECT_LIST%'; \
+       files=`{ for i in $$list; do echo "$$i"; done; \
 ## Extract all items from notrans_man_MANS that should go in this section.
 ## This must be done dynamically to support conditionals.
-?HAVE_NOTRANS? l2='%NOTRANS_LIST%'; \
-?HAVE_NOTRANS? for i in $$l2; do \
-?HAVE_NOTRANS?   case "$$i" in \
-## Have to accept files like `foo.1c'.
-?HAVE_NOTRANS?     *.%SECTION%*) list="$$list $$i" ;; \
-?HAVE_NOTRANS?   esac; \
+?HAVE_NOTRANS? l2='%NOTRANS_LIST%'; for i in $$l2; do \
+## Accept files like `foo.1c'.
+?HAVE_NOTRANS?   case $$i in *.%SECTION%*) echo "$$i";; esac; \
 ?HAVE_NOTRANS? done; \
-       for i in $$list; do \
-## Change the extension if needed.
-         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-         case "$$ext" in \
-           %SECTION%*) ;; \
-           *) ext='%SECTION%' ;; \
-         esac; \
-## Extract basename of man page and append extension.
-         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-         inst=`echo $$inst | sed -e 's/^.*\///'`.$$ext; \
-         echo " rm -f '$(DESTDIR)$(man%SECTION%dir)/$$inst'"; \
-         rm -f "$(DESTDIR)$(man%SECTION%dir)/$$inst"; \
-       done
+## Extract basename of manpage, change the extension if needed.
+       } | sed 's,.*/,,;s,\.[^%SECTION%][0-9a-z]*$$,.%SECTION%,'`; \
+       test -n "$$files" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(man%SECTION%dir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(man%SECTION%dir)" && rm -f $$files
 endif %?NOTRANS_MANS%
 if %?TRANS_MANS%
 ## Handle MANS without notrans_ prefix
        @list='%TRANS_SECT_LIST%'; \
+       files=`{ for i in $$list; do echo "$$i"; done; \
 ## Extract all items from man_MANS that should go in this section.
 ## This must be done dynamically to support conditionals.
-?HAVE_TRANS?   l2='%TRANS_LIST%'; \
-?HAVE_TRANS?   for i in $$l2; do \
-?HAVE_TRANS?     case "$$i" in \
-## Have to accept files like `foo.1c'.
-?HAVE_TRANS?       *.%SECTION%*) list="$$list $$i" ;; \
-?HAVE_TRANS?     esac; \
+?HAVE_TRANS?   l2='%TRANS_LIST%'; for i in $$l2; do \
+## Accept files like `foo.1c'.
+?HAVE_TRANS?     case $$i in *.%SECTION%*) echo "$$i";; esac; \
 ?HAVE_TRANS?   done; \
-       for i in $$list; do \
-## Change the extension if needed.
-         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-         case "$$ext" in \
-           %SECTION%*) ;; \
-           *) ext='%SECTION%' ;; \
-         esac; \
-## Extract basename of man page and run it through the program rename
-## transform.
-         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-         inst=`echo $$inst | sed -e 's/^.*\///'`; \
-         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-         echo " rm -f '$(DESTDIR)$(man%SECTION%dir)/$$inst'"; \
-         rm -f "$(DESTDIR)$(man%SECTION%dir)/$$inst"; \
-       done
+## Extract basename of manpage, run it through the program rename
+## transform, and change the extension if needed.
+       } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^%SECTION%][0-9a-z]*$$,%SECTION%,;x' 
\
+             -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+       test -n "$$files" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(man%SECTION%dir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(man%SECTION%dir)" && rm -f $$files
 endif %?TRANS_MANS%
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7a6b286..a9e995e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -311,6 +311,7 @@ insthook.test \
 instman.test \
 instman2.test \
 instmany.test \
+instmany-mans.test \
 instmany-python.test \
 instspc.test \
 interp.test \
diff --git a/tests/Makefile.in b/tests/Makefile.in
index 914ebf8..d5559b6 100644
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -462,6 +462,7 @@ insthook.test \
 instman.test \
 instman2.test \
 instmany.test \
+instmany-mans.test \
 instmany-python.test \
 instspc.test \
 interp.test \
diff --git a/tests/instmany-mans.test b/tests/instmany-mans.test
new file mode 100755
index 0000000..3f331a6
--- /dev/null
+++ b/tests/instmany-mans.test
@@ -0,0 +1,147 @@
+#! /bin/sh
+# Copyright (C) 2008  Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Installing many files should not exceed the command line length limit.
+
+# This is the mans sister test of instmany.test, see there for details.
+
+required=non-root    # hope to catch 'chmod a-r'-challenged file systems.
+. ./defs || Exit 1
+
+set -e
+
+# In order to have a useful test on modern systems (which have a high
+# limit, if any), use a fake install program that errors out for more
+# than 2K characters in a command line.  The POSIX limit is 4096, but
+# that may include space taken up by the environment.
+
+limit=2500
+subdir=long_subdir_name_with_many_characters
+nfiles=81
+
+# Let's use `seq' if available, it's faster than the loop.
+list=`(seq 1 $nfiles) 2>/dev/null || {
+  i=1
+  while test $i -le $nfiles; do
+    echo $i
+    i=\`expr $i + 1\`
+  done; }`
+
+sed "s|@limit@|$limit|g" >myinstall.in <<'END'
+#! /bin/sh
+# Fake install script.  This doesn't really install
+# (the INSTALL path below would be wrong outside this directory).
address@hidden@
+INSTALL='@INSTALL@'
+len=`expr "$INSTALL $*" : ".*" 2>/dev/null || echo $limit`
+if test $len -ge $limit; then
+  echo "$0: safe command line limit of $limit characters exceeded" >&2
+  exit 1
+fi
+exit 0
+END
+
+# Creative quoting in the next line to please maintainer-check.
+sed "s|@limit@|$limit|g" >'rm' <<'END'
+#! /bin/sh
address@hidden@
+PATH=$save_PATH
+export PATH
+RM='rm -f'
+len=`expr "$RM $*" : ".*" 2>/dev/null || echo $limit`
+if test $len -ge $limit; then
+  echo "$0: safe command line limit of $limit characters exceeded" >&2
+  exit 1
+fi
+exec $RM "$@"
+exit 1
+END
+
+chmod +x rm
+
+cat >>configure.in <<END
+AC_CONFIG_FILES([myinstall], [chmod +x ./myinstall])
+AC_CONFIG_FILES([$subdir/Makefile])
+AC_OUTPUT
+END
+
+cat >Makefile.am <<END
+SUBDIRS = $subdir
+END
+
+mkdir $subdir
+cd $subdir
+
+cat >Makefile.am <<'END'
+man_MANS =
+man3_MANS =
+notrans_man_MANS =
+notrans_man3_MANS =
+END
+
+for n in $list; do
+  cat >>Makefile.am <<END
+man_MANS += page$n.1
+man3_MANS += page$n.man
+notrans_man_MANS += npage$n.1
+notrans_man3_MANS += npage$n.man
+END
+  echo >page$n.1
+  echo >page$n.man
+  echo >npage$n.1
+  echo >npage$n.man
+done
+
+cd ..
+$ACLOCAL
+$AUTOCONF
+$AUTOMAKE --add-missing
+
+instdir=`pwd`/inst
+mkdir build
+cd build
+../configure --prefix="$instdir"
+$MAKE
+# Try whether native install (or install-sh) works.
+$MAKE install
+$MAKE uninstall
+test `find "$instdir" -type f -print | wc -l` = 0
+
+# Try whether we don't exceed the low limit.
+INSTALL='$(SHELL) $(top_builddir)/myinstall' $MAKE -e install
+env save_PATH="$PATH" PATH="`pwd`/..:$PATH" $MAKE uninstall
+
+cd $subdir
+srcdir=../../$subdir
+
+# Ensure 'make install' fails when 'install' fails.
+
+# We cheat here, for efficiency, knowing the internal rule names.
+# For correctness, one should `$MAKE install' here always, or at
+# least use install-exec or install-data.
+
+for file in page3.1 page$nfiles.1 npage3.1 npage$nfiles.1; do
+  chmod a-r $srcdir/$file
+  $MAKE install-man1 && Exit 1
+  chmod u+r $srcdir/$file
+done
+
+for file in page3.man page$nfiles.man npage3.man npage$nfiles.man; do
+  chmod a-r $srcdir/$file
+  $MAKE install-man3 && Exit 1
+  chmod u+r $srcdir/$file
+done
+:




reply via email to

[Prev in Thread] Current Thread [Next in Thread]