autotools transition report: autotools+dejagnu vs phil in a steel cage match

Phil Edwards phil@jaj.com
Fri Jul 25 00:22:00 GMT 2003


This email is for the archives, and for those others who are working on
autoconf conversion.  The next time somebody feels like making software
"smarter," find this message, print it out, and beat yourself over the
head with it until the feeling goes away.  This is the story of how three
separate packages, each trying to be smart in its own smart way, combined
together to drive me nuts.

Some history.  With the latest autotools (autoconf 2.57 and automake 1.7),
running the testsuite fails spectacularly.  Namely, a critical Dejagnu
variable is set wrongly, which causes our own DG framework to conclude
we're testing the install tree instead of the build tree:

    http://gcc.gnu.org/ml/libstdc++/2003-07/msg00412.html
    http://gcc.gnu.org/ml/libstdc++/2003-07/msg00414.html


Get something to drink.  Settle back.  Read along for the explanation
that follows.  Feel free to laugh aloud.

The variable ("blddir") is set using libgloss routines, which try to find
the path to the build tree's target libraries using a wide assortment
of tricks.  They test multiple situations, and bail out as soon as a test
seems to have succeeded.  A reasonable strategy, except that...

    # search for the top level multilib directory
    set multitop [lookfor_file "${comp_base_dir}" "${target_alias}"]
    if { $multitop == "" } {
        # .... other attempts here ....
    }

    ....
    return "$multitop/$gccpath";

the very first attempt "succeeds", i.e., multitop is not an empty string.
(And gccpath is ".", which is actually correct.  Think --print-multi-*.)

See, comp_base_dir is the top of the build directory, and lookfor_file
searches in <first-arg> for something named <second-arg>.  The result
happened to be equal to <first-arg>, because $target_alias was an empty
string.  And of course empty strings match anything, and nothing in
libgloss/DG ever tests for target_alias being empty.

Why?  'Cause libgloss uses the toplevel's sense of "target_alias", but
we're not running from the top level.  We're running from a target lib,
where We Don't Build For The Target, We Build For The Host, and nothing
is called "target_alias".

So, DG concludes that the target libraries are built one directory higher
than they actually are, and from there everything goes straight to hell.


Why is target_alias empty?  Ah, now we leave Dejagnu and go on -- or
backwards, really, in cause-and-effect -- to the next piece of smartness.

The host/build/target variables, and their _alias counterparts, are fed
to DG from testsuite/site.exp, which is built from testsuite/Makefile.in
as a result of

    AUTOMAKE_OPTIONS = dejagnu

in testsuite/Makefile.am.  In an old-tools tree, Makefile.in's site.exp
target contained commands like

    @echo 'set build_alias "$(build_alias)"' >>site.tmp
    @echo 'set build_triplet $(build_triplet)' >>site.tmp
    @echo 'set host_alias "$(host_alias)"' >>site.tmp
    @echo 'set host_triplet $(host_triplet)' >>site.tmp
    @echo 'set target_alias "$(target_alias)"' >>site.tmp
    @echo 'set target_triplet $(target_triplet)' >>site.tmp

In a new-tools tree, only

    @echo 'set host_alias "$(host_alias)"' >>site.tmp
    @echo 'set host_triplet $(host_triplet)' >>site.tmp

Thus, no target_alias ever gets set, and all of dejagnu's assumptions go
out the window.

Why isn't automake emitting the other variables?  Because it's /smart/,
see, and in dejagnu mode only emits the variables it thinks you need.
The template that it examines when you write "dejagnu" in AUTOMAKE_OPTIONS
looks like this:

    ?BUILD?     @echo 'set build_alias "$(build_alias)"' >>site.tmp
    ?BUILD?     @echo 'set build_triplet $(build_triplet)' >>site.tmp
    ?HOST?      @echo 'set host_alias "$(host_alias)"' >>site.tmp
    ?HOST?      @echo 'set host_triplet $(host_triplet)' >>site.tmp
    ?TARGET?    @echo 'set target_alias "$(target_alias)"' >>site.tmp
    ?TARGET?    @echo 'set target_triplet $(target_triplet)' >>site.tmp

What sets the ?foo? variables?  This fragment of the automake script,
which is in perl:

    'BUILD'    => $seen_canonical == AC_CANONICAL_SYSTEM,
    'HOST'     => $seen_canonical,
    'TARGET'   => $seen_canonical == AC_CANONICAL_SYSTEM,

It gives you ?HOST? if you've called any one of the AC_CANONICAL_*
macros (which are what you call to get the variables like host_os,
target_vendor, etc).  But it only gives you ?TARGET? if you happen to have
called AC_CANONICAL_SYSTEM.


Now we move to the final piece of smartness.  In autoconf 2.13, there
was only the one macro to set all the host/build/target variables,
called AC_CANONICAL_SYSTEM.  In 2.5x, there are three separate macros,
AC_CANONICAL_HOST, _BUILD, and _TARGET, and this note in the manual:

    15.4 Obsolete Macros
    [...] They are still supported, but deprecated: their use should be avoided.

    Macro: AC_CANONICAL_SYSTEM
        Determine the system type and set output variables to the names of
        the canonical system types. See section 11.2 Getting the Canonical
        System Type, for details about the variables this macro sets.

        The user is encouraged to use either AC_CANONICAL_BUILD, or
        AC_CANONICAL_HOST, or AC_CANONICAL_TARGET, depending on the
        needs. Using AC_CANONICAL_TARGET is enough to run the two other
        macros.

So in the new-tools tree, I replaced AC_CANONICAL_SYSTEM with
AC_CANONICAL_TARGET, even though all we really needed was AC_CANONICAL_HOST.
(At the time I wasn't certain, so I used _TARGET just to be sure and get
them all, according to the last sentance in the manual fragment above.)
Thanks to a change that Benjamin had already checked in, nowhere in the v3
configury nor the v3 source did we ever refer to $target or $target_alias,
and that was -- and is -- the correct thing to do, because this is a target
library, which means We Don't Build For The Target, We Build For The Host.

Except that by following the smart recommendations of the autoconf manual
and not using the Obsolete Macro, we tripped over a smart determination by
automake, and no longer emitted the "clearly unneeded for a target library"
variable assignments that dejagnu smartly assumed would be there.



Short version:  follow the documened autoconf upgrade recommendation,
and dejagnu breaks with no indication of what went wrong.  And there's no
trail of evidence leading from

    "change AC_CANONICAL_SYSTEM to AC_CANONICAL_TARGET"

to

    "dejagnu can't find anything located in the build tree"

because Autoconf knows nothing about Dejagnu.

Whose fault is it?  Nobody's.  Each software package made the design
decisions it did for good reasons.  Each one trying to be just a little
smart, to be helpful to the user.  Put all the helpfulness together,
and wow, look at that bullet hole in my foot.


So, in the v3 configury, there will be a call to AC_CANONICAL_SYSTEM,
even though it's obsolete.  Since $target_alias isn't always set, there
will be a line to ensure that it's set, even though we don't ever use it
anywhere because We Don't Build For The Target, We Build For The Host.
And for documentation in the configury, there will be a comment pointing
to this message in the archives, so that in the future, when someone
(possibly me) looks at the "obviously" wrong code, that someone will know
to be smart and leave it the hell alone.  :-)


Phil

-- 
Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it.
    - Brian W. Kernighan (the K in K&R)



More information about the Libstdc++ mailing list