Subject: bug#12715: [PATCH] build: do not require help2man at build-from-tarball time

From: Stefano Lattarini <[email-masked]>
Date: Wed, 24 Oct 2012 12:46:30 +0200

On 10/24/2012 12:38 PM, Stefano Lattarini wrote:
> On 10/24/2012 11:31 AM, Stefano Lattarini wrote:
>> On 10/24/2012 10:54 AM, Jim Meyering wrote:
>>> Pádraig Brady wrote:
>>>
>>>> `echo "warning"; touch $manpage`.
>>>
>>> Sounds good.  I.e., it sounds like we want Stefano's patch, after all.
>>>
>>> As you recall, a nice side effect is that we will
>>> no longer distribute the generated man/*.1 files.
>>>
>>> Stefano, would you care to rebase your patch?
>>>
>> Will do soon.  Just give me some time to also re-test it ...
>>
> Done.  The patch is inlined below.
> 
Yikes, some blunders slipped in.  We need at least the following diffs
squashed in:

-*-*-*-

diff --git a/man/dummy-man b/man/dummy-man
index cba661c..2e32320 100755
--- a/man/dummy-man
+++ b/man/dummy-man
@@ -1,5 +1,5 @@
 #!/bin/sh
-# Poor place holder for help2man invocation on systems lacking perl;
+# Poor placeholder for help2man invocation on systems lacking perl;
 # it generates a dummy manpage stating that as proper manpage could
 # not be generated, and redirecting the user back to either the info
 # documentation or the '--help' output.
@@ -21,7 +21,7 @@ output=
 source="GNU coreutils"
 while test $# -gt 0; do
   case $1 in
-    # Help2man options we recognize handle.
+    # Help2man options we recognize and handle.
     --output=*) output=`expr x"$1" : x'--output=\(.*\)'`;;
     --output) shift; output=$1;;
     --source=*) source=`expr x"$1" : x'--source=\(.*\)'`;;
@@ -40,7 +40,7 @@ done
 test $# -gt 0 || fatal_ "missing argument"
 test $# -le 1 || fatal_ "too many non-option arguments"

-baseout=`basename "$output"`
+baseout=`basename_ "$output"`
 sed 's/^/WARNING: /' >&2 <<END
 Cannot create proper man page '$baseout' since perl is missing or
 inadequate on this system.  Will create a stub man page instead.

-*-*-*-

Here is the updated patch.  Sorry for the noise,
  Stefano

----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----

>From f61dcb763975c1aab299fa9678ea180d70db6acf Mon Sep 17 00:00:00 2001
Message-Id: <f61dcb763[email-masked]>
From: Stefano Lattarini <[email-masked]>
Date: Tue, 11 Sep 2012 20:54:30 +0200
Subject: [PATCH] build: graceful degradation in man pages generation if perl
 is lacking

Fixes coreutils bug#12715.

Since commit v8.19-118-g57da212, our 'dist-hook' rule tweaks the
distributed Makefile.in so that each man page 'man/foo.1' depends
on the corresponding source 'man/foo.c' rather than the corresponding
program 'man/foo'.  That is done to accommodate inferior systems that,
lacking perl, cannot run 'help2man' to regenerate the manpage after
its corresponding program has been built.

This seems a right and proper graceful degradation, in that the
man pages dependencies are still 100% correct in a git checkout,
while being laxer but "more portable" in a distribution tarball.

Alas, that is not the case in practice, as it turns out the tweaked
Makefile makes the building of man pages unreliable and potentially
incorrect!

In fact, assume that instead of the correct a dependency:

    man/ls.1: src/ls

we have the laxer one:

    man/ls.1: src/ls.c

and think of what happens if a user modifies, say, 'src/ls.c', and then
runs "make -j4" to rebuild everything.  The make process will see that
it has to rebuild the man page 'man/ls.1' (because its prerequisite
'src/ls.c' has changed), but won't see that it has to rebuild 'src/ls'
*before* re-running 'help2man' to generate that man page; so, if
'man/ls.1' is rebuilt before 'src/ls' (which can happen with concurrent
make), our user will get either a build error (if 'src/ls' did non
exist) or, worse, a man page with an up-to-date timestamp but an
out-of-date content.  And what's even worse in all of this is that
this problem will be present also for users who have perl installed:
this is not a "graceful degradation" at all!

In our situation, the best and simplest way to implement a graceful
degradation it to keep the correct dependencies for man pages (that
is, "man/ls.1: src/ls"), and if perl is not present, just generate
dummy man pages reporting that built-time issue and redirecting the
user back to either the info documentation or the '--help' output.

As a consequence of this change, we also stop distributing man pages,
since they would be anyway unconditionally rebuilt

* Makefile.am (do-not-require-help2man): Remove.
(dist-hook): Don't depend on it.
* man/local.mk: Remove an obsolete comment.
(EXTRA_DIST): Stop distributing generated man pages.
($(EXTRA_MANS)): This no longer needs to depend on $(all_programs).
(MAINTAINERCLEANFILES): $(ALL_MANS) should not be listed here ...
(CLEANFILES): ... but here.
(.x.1): Instead of warning if perl is missing, but then trying to run
'help2man' unconditionally, simply run ...
(run_help2man): ... the command referenced by this new variable, that
expands to a proper invocation of 'help2man' if perl is present, and
to an invocation of a shell script generating a dummy manpage if it's
not.
(EXTRA_DIST): Distribute that shell script.
* man/dummy-man: Be that shell script
---
 Makefile.am   | 20 +----------------
 man/dummy-man | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 man/local.mk  | 25 +++++++++++----------
 3 files changed, 86 insertions(+), 31 deletions(-)
 create mode 100755 man/dummy-man

diff --git a/Makefile.am b/Makefile.am
index 0232090..5eaa45b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -93,29 +93,11 @@ BUILT_SOURCES = .version
 .version:
 	$(AM_V_GEN)echo $(VERSION) > $@-t && mv $@-t $@

-# In general, we run help2man to build a man page from the binary's --help
-# output, but when building from a just-unpacked distribution tarball, we
-# must not do that, since help2man uses perl.  We don't want to depend on
-# perl in that case.  In general, the .1 file does indeed depend on the
-# binary.  I.e., for cat, we have this Makefile dependency:
-#   man/cat.1: src/cat
-# That means that once we build src/cat, we would trigger the .x.1
-# rule which runs help2man.  The trick is simply to change the RHS to
-# "src/cat.c" in the $(distdir) that we're about to tar and compress.
-# Also handle the three exceptions corresponding to the three binaries
-# for which there is no like-named .c file: dir, vdir, ginstall.
-.PHONY: do-not-require-help2man
-do-not-require-help2man:
-	perl -pi -e 's,^(man/.+?\.1:\s*src/.+?)$$,$$1.c,;'		\
-	    -e  's,^(man/.+?\.1:\s*src)/ginstall\.c$$,$$1/install.c,;'	\
-	    -e  's,^(man/.+?\.1:\s*src)/v?dir\.c$$,$$1/ls.c,;'		\
-          $(distdir)/Makefile.in
-
 # Arrange so that .tarball-version appears only in the distribution
 # tarball, and never in a checked-out repository.
 # The perl substitution is to change some key uses of "rm" to "/bin/rm".
 # See the rm_subst comment for details.
-dist-hook: gen-ChangeLog do-not-require-help2man
+dist-hook: gen-ChangeLog
 	$(AM_V_GEN)echo $(VERSION) > $(distdir)/.tarball-version
 	$(AM_V_at)perl -pi -e '$(rm_subst)' $(distdir)/Makefile.in

diff --git a/man/dummy-man b/man/dummy-man
new file mode 100755
index 0000000..2e32320
--- /dev/null
+++ b/man/dummy-man
@@ -0,0 +1,72 @@
+#!/bin/sh
+# Poor placeholder for help2man invocation on systems lacking perl;
+# it generates a dummy manpage stating that as proper manpage could
+# not be generated, and redirecting the user back to either the info
+# documentation or the '--help' output.
+
+set -e; set -u
+
+fatal_ ()
+{
+  printf '%s: %s\n' "$0" "$*" >&2
+  exit 1
+}
+
+basename_ ()
+{
+  printf '%s\n' "$1" | sed 's,.*/,,'
+}
+
+output=
+source="GNU coreutils"
+while test $# -gt 0; do
+  case $1 in
+    # Help2man options we recognize and handle.
+    --output=*) output=`expr x"$1" : x'--output=\(.*\)'`;;
+    --output) shift; output=$1;;
+    --source=*) source=`expr x"$1" : x'--source=\(.*\)'`;;
+    --source) shift; source=$1;;
+    # Recognize (as no-op) other help2man options that might be used
+    # in the makefile.
+    --include=*);;
+    --include) shift;;
+    -*) fatal_ "invalid or unrecognized help2man option '$1'";;
+    --) shift; break;;
+     *) break;;
+  esac
+  shift
+done
+
+test $# -gt 0 || fatal_ "missing argument"
+test $# -le 1 || fatal_ "too many non-option arguments"
+
+baseout=`basename_ "$output"`
+sed 's/^/WARNING: /' >&2 <<END
+Cannot create proper man page '$baseout' since perl is missing or
+inadequate on this system.  Will create a stub man page instead.
+END
+
+progname=`basename_ "$1"`
+year=`LC_ALL=C date +%Y`
+bs='\'
+
+cat >"$output" <<END
+.TH "$progname" 1 "$year" "$source" "User Commands"
+.SH NAME
+$progname $bs- a GNU coreutils program
+.SH DESCRIPTION
+.B OOOPS!
+Due to lack of perl on your system, the GNU coreutils build system
+hasn't been able to create the manual page for
+.B $progname.
+For a quick help about that program, try running
+.IP
+.B env $progname --help
+.PP
+The full documentation for
+.B $progname
+is maintained as a Texinfo manual, which you might be accessible
+on your system with the command
+.IP
+.B info coreutils $bs(aq$progname invocation$bs(aq
+END
diff --git a/man/local.mk b/man/local.mk
index ebc111d..2c05bb6 100644
--- a/man/local.mk
+++ b/man/local.mk
@@ -16,17 +16,24 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

-EXTRA_DIST += man/help2man
+EXTRA_DIST += man/help2man man/dummy-man
+
+## Graceful degradation for systems lacking perl.
+if HAVE_PERL
+run_help2man = $(PERL) -- $(srcdir)/man/help2man
+else
+run_help2man = $(SHELL) $(srcdir)/man/dummy-man
+endif

 man1_MANS = @man1_MANS@
-EXTRA_DIST += $(man1_MANS) $(man1_MANS:.1=.x)
+EXTRA_DIST += $(man1_MANS:.1=.x)

 EXTRA_MANS = @EXTRA_MANS@
-EXTRA_DIST += $(EXTRA_MANS) $(EXTRA_MANS:.1=.x)
+EXTRA_DIST += $(EXTRA_MANS:.1=.x)

 ALL_MANS = $(man1_MANS) $(EXTRA_MANS)

-MAINTAINERCLEANFILES += $(ALL_MANS)
+CLEANFILES += $(ALL_MANS)

 # This is required because we have subtle inter-directory dependencies:
 # in order to generate all man pages, even those for which we don't
@@ -161,13 +168,7 @@ man/whoami.1:    src/whoami
 man/yes.1:       src/yes

 .x.1:
-	$(AM_V_GEN)case '$(PERL)' in				\
-	  *"/missing "*)					\
-	    echo 'WARNING: cannot update man page $@ since perl is missing' \
-	      'or inadequate' 1>&2				\
-	    exit 0;;						\
-	esac; \
-	name=`echo $@ | sed -e 's|.*/||' -e 's|\.1$$||'` || exit 1;	\
+	$(AM_V_GEN)name=`echo $@ | sed 's|.*/||; s|\.1$$||'` || exit 1;	\
 ## Ensure that help2man runs the 'src/ginstall' binary as 'install' when
 ## creating 'install.1'.  Similarly, ensure that it uses the 'src/[' binary
 ## to create 'test.1'.
@@ -184,7 +185,7 @@ man/yes.1:       src/yes
 	  && rm -rf $$t							\
 	  && $(MKDIR_P) $$t						\
 	  && (cd $$t && $(LN_S) '$(abs_top_builddir)/src/'$$prog $$name) \
-	  && $(PERL) -- $(srcdir)/man/help2man				\
+	  && $(run_help2man)						\
 		     --source='$(PACKAGE_STRING)'			\
 		     --include=$(srcdir)/man/$$name.x			\
 		     --output=$$t/$$name.1 $$t/$$name			\
-- 
1.8.0.rc2.11.gd25c58c