[Top][All Lists]

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

lots of fatal build system bugs

From: Ingo Schwarze
Subject: lots of fatal build system bugs
Date: Tue, 22 Mar 2022 00:54:05 +0100

Hi Branden,

after a (too) long time, i once more attempted to update my private
version of the OpenBSD groff port to the current git head, to avoid
a disaster when the next release finally appears - and got
completely stuck for several hours because there are large
numbers of portability issues that prevent building.

The current state is that i have a patch that barely makes it build.
I don't think the build is clean yet even with the patch, i have
seen indications of non-fatal issues at a few places, but did not yet
investigate these, let alone scrutinize the build logs as i usually do.
Also, i did not start any run time testing yet.

There are so many issues already now that it makes sense to get
these fixed before even starting a review for cleanliness and
before starting run time testing.

I'm sending this to you directly because you are a by far the
most active groff developer right now, so i hope we can agree
how to fix all this and then i can then push one or more patches
doing it.  This really doesn't feel like the stuff to shove under
the bugtracker carpet and let it rot there.

Please be assured that i'm *not* claiming you are responsible for
any of these regressions; that's *not* the reason why i'm sending
this to you.  Quite to the contrary, the reason is that i appreciate
your industriousness working on groff.

So, let's go through the patch appended at the end.
It's a minimal patch to make the software build.

 1. File, .version.tree mechanism
    There are several independent problems with this.

   1.1. .version.tree target line:
        automake parses this as a suffix rule and consequently
        adds .version and .tree to the .SUFFIXES pseudo-target.
        With the resulting ".SUFFIXES: .version .tree ..."  in the
        generated master Makefile, BSD make(1) no longer knows how to
        build the file ".version.tree" because the rule intended as
        a target rule is actually a suffix rule.  So that rule does
        explain how to build foo.tree from foo.version (for example,
        .version.tree from .version.version), but since there is no
        .version.version anywhere, BSD make dies complaining that it
        doesn't know how to build .version.tree.  GNU make somehow
        copes, but i have no idea why.
        So at the very least, the file .version.tree needs to be
        renamed such that its target rule does not look like a
        suffix rule, for example to ".version_tree".

   1.2. src/libs/libgroff/version.cpp: $(top_srcdir)/.version
        dependency in src/libs/libgroff/
           In this respect, BSD make make does the following.
        When considering whether version.cpp needs to be rebuilt,
        it first considers whether $(top_srcdir)/.version is
        up to date.  Obviously, it is not, because it depends
        on the .PHONY target .version_tree, so every make runs the
        shell script snippet to rebuild $(top_srcdir)/.version.
        I find it quite logical that BSD make then goes ahead
        and also rebuilds version.cpp.  After all, that one had
        a dependency that was found to be out of date and a script
        was run to recreate it.  The end result is that during
        "make install", large swaths of the tree are rebuilt
        from scratch because lots of stuff depends on version.cpp.

        GNU make copes by being somewhat schizophrenic.
        First, it does believe that inspecting
        whether $(top_srcdir)/.version is up to date matters
        for deciding whether to rebuild version.cpp,
        finds it is out of date and runs the script to rebuild it.
        But then, it seems to forget all that it did so far,
        and adopts a new personality now assuming
        that $(top_srcdir)/.version is *not* out of date (even
        though a script was just run because it is).  Perfect
        double-think: GNU make firmly believes .version is out
        of date and is up to date at the same time.
        In any case, relying on that split personality is not

   1.3. In any case, automatically regenerating .version
        from the Makefile defeats the whole purpose of the file,
        which is documented in build-aux/git-version-gen
        (not build-aux/git-gen-version) as follows:

# .version - present in a checked-out repository and in a distribution
#   tarball.  Usable in dependencies, particularly for files that don't
#   want to depend on config.h but do want to track version changes.
#   Delete this file prior to any autoconf run where you want to rebuild
#   files to pick up a version string change; and leave it stale to
#   minimize rebuild time after unrelated changes to configure sources.

        It says "and leave it stale" otherwise, so it is
        intentional and documented that it should *not* be
        automatically changed.  Also, build-aux/git-version-gen
        explicitly documents how the file is intended to be used:

# EXTRA_DIST = $(top_srcdir)/.version
# BUILT_SOURCES = $(top_srcdir)/.version
# $(top_srcdir)/.version:
#       echo $(VERSION) > $@-t && mv $@-t $@
# dist-hook:
#       echo $(VERSION) > $(distdir)/.tarball-version

        Second-guessing that and adding complicated, non-portable
        dependencies seems like a bad idea to me.  Certainly,
        just following what build-aux/git-version-gen recommends
        looks like the simplest and cleanest fix to me.

 2. contrib/sboxes/, 1st chunk (SBOXES_PROCESSEDEXAMPLEFILES)
    This one was hellish to figure out.  The problem is that it
    results in an empty rule
      contrib/sboxes/ contrib/sboxes/msboxes.pdf: yada yada
        no shell commands following
    whitout leading "./" whereas the later $(sboxes_builddir)/
    target rule results in a rule
      ./contrib/sboxes/ yada yada
         some shell commands here
    with a leading "./".  BSD make considers "contrib/sboxes/"
    and "./contrib/sboxes/" different targets and does *not*
    combine these rules, so when it comes to building the target,
    it uses the empty shell script and vonsequently the file never
    gets built.

    I believe the best fix is to always use $(sboxes_builddir)
    for the generated files rather than switching back and forth.

 3. contrib/sboxes/, 2nd chunk (msboxes.pdf)
    The DOC_GROFF mechanism is seriously complicated, and one of
    its many features is that it uses the variable $< which POSIX
    says can only be used in suffix rules, so this fails outright
    with POSIX make.
    On top of that, the whole point of DOC_GROFF is to pipe
    standard input into DOC_GROFF_ONLY which then calls groff.
    So it makes no sense whatsoever to add a file name argument
    (in this case, "$(sboxes_builddir)/") after
    DOC_GROFF because that causes standard input to be discarded,
    neutering most of what DOC_GROFF did earlier.
    Fortunately, there is a simple way to fix both issues at the
    same time: change the rule to become a suffix rule
    and remove the bogus argument.
    Alternatively, one could keep the argument, change DOC_GROFF
    to DOC_GROFF_ONLY, and not switch to a suffix rule; i have
    no strong preference.

 4. doc/, doc/groff-man-pages.* rules:
    Again, we have $< in non-suffix rules, this time not only
    with one, but with many file name arguments.  Amazingly,
    GNU make somehow copes.  Not that what GNU make does makes
    any sense - it appears the $< picks the first dependency
    and silently discards all the others, which is obviously
    the perfect way to shove errors under the carpet - but at
    least it doesn't crash like POSIX make does!  ;-)
    Then again, all that resulted from $< is discarded anyway... :-(

    So here, i think the best fix is to just be explicit and
    switch from DOC_GROFF to DOC_GROFF_ONLY.

 5. doc/, doc/meintro.*, doc/ rules:
    More $< GNUisms.
    The best fix here seems to replace $< by the POSIX $?.

 6. doc/, doc/ rule:
    Yet another $< GNUism.
    This is harder to fix because an "" suffix rule
    lacking the -mfr already exists in doc/ and clashes.
    But with the right "SUFFIXES +=" incantation to please automake,
    we can get a "" work in a portable way.
    Alternatively, we could switch to DOC_GROFF_ONLY instead;
    again, no strong preference.

 7. doc/, manual SUFFIXES definition:
    This is the only item on my list that isn't required to make
    it build, but i had to do so much wrestling with the .SUFFIXES
    pseudo-target anyway that i cleaned this up while there.
    The .xhtml is not used anywhere, for nothing whatsoever.
    All the others get automatically generated by automake,
    so there is no need to manually add them.
    In fact, maually adding them is detrimental in two ways:
    The manual list is doomed to get outdated (.xhtml proves
    the point) and it adds noise to the generated Makefile,
    hindering debugging (which is already difficult enough in
    such generated Makefiles without additional, pointless

So, here are my questions:

 I. Do you agree with all my points, or did i miss something,

 II. When you apply my patch, do things still work for you on Linux?

 III. Do you prefer that i commit this in one single commit
      or separately?  If separately, do you want somme issues
      grouped together, for example
       * one commit for issue 1
       * one commit for issue 2
       * one commit for issue 3
       * one commit for issues 4-6
       * one commit for issue 7

 IV. I'm pretty sure these are all recent regressions and none
     of these problems existed in the last release.  Consequently,
     do you agree that ChangeLog entries are not needed?


Right now, i'm quite fed up because i had to do at least one bootstrap -
configure - build cyle for each of the issues, and some of them weren't
even identified until i did a complete bootstrap - configure - build -
make-dist - port-patch - port-configure - port-built - port-fake-install
cycle, and then i had to repeat all these cycles to develop and test
fixes, and in between, some required quite some staring at Makefiles
to understand.  At one point i even patched additional printf(3)
statements into our BSD make(1) implementation for debugging and stepped
through make(1) with gdb because at first i failed to understand from
mere "make -d m" debugging output what was really going on.  Ugh!

But i believe i'll get over it and do a full audit for cleanliness plus
run-time testing on some other day, once these nasty issues are out of
the way.
It would no doubt be better if i tested more often and did not let
so many issues accumulate.  :-/

diff --git a/ b/
index 483efe46f..30180d774 100644
--- a/
+++ b/
@@ -921,20 +921,11 @@ SUFFIXES += .man
             $< \
-# Version files - see script 'build-aux/git-gen-version'
+# Version files - see script 'build-aux/git-version-gen'
 EXTRA_DIST += $(top_srcdir)/.version
 BUILT_SOURCES += $(top_srcdir)/.version
-# Regenerate a temporary version string file on every build, but update
-# the real version file's mtime only if its contents change.
-.PHONY: .version.tree
-       @test -f $(distdir)/.tarball-verion || echo $(VERSION) > $@
-$(top_srcdir)/.version: .version.tree
-       @if ! test -f $(distdir)/.tarball-version; then \
-         test -f $@ || cp .version.tree $@; \
-         cmp -s .version.tree $@ || cp .version.tree $@; \
-       fi
-MOSTLYCLEANFILES += .version.tree
+       echo $(VERSION) > $@-t && mv $@-t $@
        echo $(VERSION) > $(distdir)/.tarball-version
diff --git a/contrib/sboxes/ b/contrib/sboxes/
index 61d814f7b..66afef382 100644
--- a/contrib/sboxes/
+++ b/contrib/sboxes/
@@ -36,8 +36,8 @@ SBOXES_EXAMPLEFILES = $(sboxes_srcdir)/
 sboxesexampledir = $(exampledir)/sboxes
 dist_sboxesexample_DATA = $(SBOXES_EXAMPLEFILES)
-    contrib/sboxes/msboxes.pdf
+    $(sboxes_builddir)/msboxes.pdf
 sboxesprocessedexampledir = $(exampledir)/sboxes
 nodist_sboxesprocessedexample_DATA = $(SBOXES_PROCESSEDEXAMPLEFILES)
@@ -62,9 +62,8 @@ $(sboxes_builddir)/ $(SBOXES_EXAMPLEFILES) 
            $(SBOXES_EXAMPLEFILES) >> $@.tmp
        $(AM_V_GEN)mv $@.tmp $@
-$(sboxes_builddir)/msboxes.pdf: $(sboxes_builddir)/
-       $(GROFF_V)$(DOC_GROFF) -M$(sboxes_srcdir) -ms -msboxes -Tpdf \
-         $(sboxes_builddir)/ > $@
+       $(GROFF_V)$(DOC_GROFF) -M$(sboxes_srcdir) -ms -msboxes -Tpdf > $@
 uninstall_groffdirs: uninstall_sboxes
diff --git a/doc/ b/doc/
index 44f8c73e8..5f0103f3c 100644
--- a/doc/
+++ b/doc/
@@ -229,37 +229,36 @@ man-clean:
        $(RM) $(GROFF_MAN_PAGES_ALL)
 doc/groff-man-pages.pdf: $(GROFF_MAN_PAGES_ALL)
-       $(GROFF_V)$(DOC_GROFF) -Tpdf -P-e -mandoc -rC1 \
+       $(GROFF_V)$(DOC_GROFF_ONLY) -Tpdf -P-e -mandoc -rC1 \
          -rCHECKSTYLE=3 $(GROFF_MAN_PAGES1) \
          $(tmac_srcdir)/sv.tmac $(GROFF_MAN_PAGES2) \
          $(tmac_srcdir)/en.tmac $(GROFF_MAN_PAGES3) > $@
 doc/groff-man-pages.utf8.txt: $(GROFF_MAN_PAGES_ALL)
-       $(GROFF_V)$(DOC_GROFF) -Tutf8 -mandoc \
+       $(GROFF_V)$(DOC_GROFF_ONLY) -Tutf8 -mandoc \
          -rCHECKSTYLE=3 $(GROFF_MAN_PAGES1) \
          $(tmac_srcdir)/sv.tmac $(GROFF_MAN_PAGES2) \
          $(tmac_srcdir)/en.tmac $(GROFF_MAN_PAGES3) > $@
 doc/ $(doc_srcdir)/
        $(GROFF_V)$(MKDIR_P) `dirname $@` \
-       && $(DOC_SED) $< >$@
+       && $(DOC_SED) $? >$@
 doc/ $(doc_srcdir)/
        $(GROFF_V)$(MKDIR_P) `dirname $@` \
-       && $(DOC_SED) $< >$@
+       && $(DOC_SED) $? >$@
 doc/ $(doc_srcdir)/
        $(GROFF_V)$(MKDIR_P) `dirname $@` \
-       && $(DOC_SED) $< >$@
+       && $(DOC_SED) $? >$@
 # The me(7) intro French translation gets its own target rule because it
 # needs the "-mfr" option.
-doc/ doc/
        $(GROFF_V)$(MKDIR_P) `dirname $@` \
        && $(DOC_GROFF) -k -Tps -me -mfr >$@
-SUFFIXES += .me .ms .ps .html .txt .texi .dvi .pdf .xhtml
 # For simplicity, we always call preconv, grn, and eqn.
        $(GROFF_V)$(MKDIR_P) `dirname $@` \

reply via email to

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