Quick cut at an autoconf tutorial - Mailing list pgsql-hackers

From Tom Lane
Subject Quick cut at an autoconf tutorial
Date
Msg-id 25253.903310508@sss.pgh.pa.us
Whole thread Raw
In response to Re: [HACKERS] Autoconf'd test for int64  ("Thomas G. Lockhart" <lockhart@alumni.caltech.edu>)
List pgsql-hackers
"Thomas G. Lockhart" <lockhart@alumni.caltech.edu> writes:
> Using autoconf for things sounds great. I've been relying on scrappy for
> that stuff, and find it a mystery myself. Marc or someone, would you be
> willing to write a few sentences on how to make incremental changes to
> the Postgres autoconfig system?

Well, there's really no substitute for reading the autoconf manual ;-)

But basically the idea is that you don't want to have your code
#ifdef'ing on system ID symbols except in very specific circumstances.
Usually what you really want to know is "does feature X exist here?",
and the right way to handle that is to have a direct test for feature X.
If you take that approach your code is a lot more likely to work
out-of-the-box on a new system.  Not to mention the fact that some systems
like HPUX and Linux come in a lot of flavors --- testing the system ID
symbols isn't good enough there anyway.

What autoconf does is provide a system whereby you can make exactly the
tests you need during configuration, and have the results available when
your code is being compiled.  The results are usually expressed in the
form of configuration symbols that get defined (or not) in config.h.
You can also have autoconf apply system-specific edits to other places
like makefiles, but config.h is most often the file to tweak.

To take the particular example at hand, I had to go through the
following steps:

1. Ask myself what we really wanted to know.  What we wanted to know,
it seemed like, was whether either "long int" or "long long int" could
be used as a 64-bit integer type.  So I invented some configuration
symbol names that would carry this info from autoconf to the
compile-time tests needed in int8.h:

/* Set to 1 if type "long int" works and is 64 bits */
#undef HAVE_LONG_INT_64

/* Set to 1 if type "long long int" works and is 64 bits */
#undef HAVE_LONG_LONG_INT_64

2. I put the above lines into src/include/config.h.in, which is the
master file that autoconf will edit during configuration to create the
localized version src/include/config.h (.in is the usual convention for
indicating an autoconf master file).  Autoconf provides automatic
support for changing "#undef X" into "#define X something" when
appropriate, so we start with the "#undef" format.

3. Then I needed to figure out how to test these conditions.  Basically
what autoconf can do is to (try to) compile and run little test
programs; if it succeeds then it figures that particular feature is
there.  So I wrote a little program that checks sizeof(long long int)
(to make sure it's 8) and also tries to do some simple arithmetic to
make sure the answers come out right.  You can see the details in the
patch, but the important point is that if long long isn't OK, the
program will either fail to compile at all, or will exit with a nonzero
return status (which is program failure by Unix convention).  Autoconf
will only believe the test succeeded if the program compiles and exits
with zero status when run.

4. After I'd tested the test program to my satisfaction, I wrapped it
into this autoconf code:

AC_MSG_CHECKING(whether 'long long int' is 64 bits)
AC_TRY_RUN([
... test program code here ...
],
    [AC_MSG_RESULT(yes) AC_DEFINE(HAVE_LONG_LONG_INT_64)],
    AC_MSG_RESULT(no),
    AC_MSG_RESULT(assuming not on target machine))

AC_TRY_RUN is a macro that's basically like an if-then-else structure.
If the test program (whose text is the first argument) succeeds, the
second argument is executed; if the test fails, the third argument
is executed; and if autoconf knows it is cross-compiling for another
machine and therefore can't run a test program at all, it doesn't try
but just executes the fourth argument.  Usually that last alternative
should be the most conservative assumption.

The AC_MSG macros are just user interface aids (they generate all that
junk that the configure script prints as it runs).  The important aspect
is the AC_DEFINE() macro --- if that gets executed, it cues Autoconf
to convert "#undef HAVE_LONG_LONG_INT_64" to
"#define HAVE_LONG_LONG_INT_64 1" later on when it's generating config.h
from config.h.in.  (I could give AC_DEFINE a second argument if I wanted
to define the symbol as some particular string instead of "1".)  In the
other cases, AC_DEFINE is not executed so that line of config.h.in will
be left unmodified.

The other thing to know to read this is that square brackets are m4's
quoting convention.

I made another copy that was exactly the same but it works on "long int"
and defines HAVE_LONG_INT_64 if it wins.

5. Then I ran autoconf to generate a configure shell script from the
configure.in source code, and I was done.

6. AC_TRY_RUN is the sledgehammer of Autoconf programming --- you only
need it if you need to verify some unusual detail of a system's run-time
behavior.  Most of the standard sorts of problems can be dealt with
using simpler Autoconf macros.  For example, if I had been satisfied
just to test whether the declaration "long long int" would compile, I
could've just done
     AC_TRY_COMPILE(, [ long long int x; ],
            [success action], [fail action])
which would compile the code but not try to run it.  There are even
simpler macros that handle most of the really common cases.  For
example, PostgreSQL's configure.in includes
    AC_CHECK_HEADERS(limits.h)
which tries to compile a program that says "#include <limits.h>",
and if it succeeds then it automatically defines HAVE_LIMITS_H.
(We still have to remember to put #undef HAVE_LIMITS_H into
config.h.in, however, or the AC_DEFINE would have no visible effect.)

BTW this last example should cue you that there are conventions for
choosing configuration symbol names in Autoconf --- a symbol that
indicates that <stdlib.h> exists should be called HAVE_STDLIB_H,
and not anything else; if you violate these conventions you risk
confusing autoconf experts, or even breaking some other part of
configuration.  (A while back we had a configuration bug because
HAVE_HISTORY was getting used for two different purposes: both to
indicate whether <history.h> exists and whether libhistory exists
to be linked with.  Following the convention prevents this error,
since HAVE_HISTORY_H refers to the include file and HAVE_LIBHISTORY
to the library.)

7. The configure script generated by Autoconf is a self-contained
shell script that will run on darn near any Unix platform.  But to generate
configure from configure.in, you need to have Autoconf itself installed,
and you also need GNU m4 because Autoconf is really just a script in
the m4 macro processing language.  You can find these at any GNU archive
site if you don't have 'em already.  Both are painless to install.

8. There's really no substitute for reading the autoconf manual ;-).
But maybe this will motivate you to go do that.

            regards, tom lane

pgsql-hackers by date:

Previous
From: "Thomas G. Lockhart"
Date:
Subject: Re: [HACKERS] Autoconf'd test for int64
Next
From: Tom Lane
Date:
Subject: Re: [HACKERS] Autoconf'd test for int64