Subject: bug#7635: AC_LIBSOURCE doesn't fail as it should if AC_OUTPUT is not used

From: Stefano Lattarini <[email-masked]>
Date: Tue, 14 Dec 2010 15:35:33 +0100

Hello automakers.

On Tuesday 14 December 2010, Ralf Wildenhues wrote:
> tags 7635 wontfix
> close 7635
> thanks
Even if the bug is closed, I'm following up this thread anyway, since
I'd like to share (and put on the trial of peer review) my explanation
of why automake currently behaves the way it does w.r.t. AC_LIBSOURCE.

> * Stefano Lattarini wrote on Mon, Dec 13, 2010 at 11:35:32PM CET:
> > If configure.ac does not contain a call to AC_OUTPUT, Automake
> > does not fail as expected when file(s) specified in calls to
> > macros AC_LIBSOURCE and/or AC_LIBSOURCES do not exist. See the
> > attached test script.
> > 
OK, maybe I've understood why this happens.

AC_LIBSOURCE is basically a "dummy" macro, whose only purpose is to
be traced by automake, which can then use the traces to automatically
distribute any source file listed in AC_LIBSOURCE:

  $ cat /usr/share/autoconf/autoconf/general.m4
  ...
  # AC_LIBSOURCE(FILE-NAME)
  # -----------------------
  # Announce we might need the file `FILE-NAME'.
  m4_define([AC_LIBSOURCE], [])
  ... 

On the other hand, AC_LIBOBJ actually calls AC_SUBST on `LIBOBJS':

  $ cat /usr/share/autoconf/autoconf/general.m4
  ...
  # _AC_LIBOBJ(FILE-NAME-NOEXT, ACTION-IF-INDIR)
  # --------------------------------------------
  # We need `FILE-NAME-NOEXT.o', save this into `LIBOBJS'.
  m4_define([_AC_LIBOBJ],
  [case " $LIB@&t@OBJS " in
    *" $1.$ac_objext "* ) ;;
    *) AC_SUBST([LIB@&t@OBJS], ["$LIB@&t@OBJS $1.$ac_objext"]) ;;
  esac
  ])
  
  # AC_LIBOBJ(FILE-NAME-NOEXT)
  # --------------------------
  # We need `FILE-NAME-NOEXT.o', save this into `LIBOBJS'.
  AC_DEFUN([AC_LIBOBJ],
  [_AC_LIBOBJ([$1])]dnl
  [AS_LITERAL_WORD_IF([$1], [AC_LIBSOURCE([$1.c])],
    [AC_DIAGNOSE([syntax], [$0($1): you should use literals])])])
  ...
  
Now, the check that all the files which *might* end up in $(LIBOBJS) truly
exist[1][2] is performed by Automake if *and only if* the `@LIBOBJS@'
token appears (directly or indirectly) in some proper `_LIBADD' or `_LDADD'
variable[3].
 [1] or are listed in the $(BUILT_SOURCES) special variable
 [2] the check is performed in the subroutine `automake:handle_LIBOBJS()'
 [3] see code in subroutine `automake:handle_lib_objects()'

If we use AC_LIBSOURCE in configure.ac without ever calling AC_LIBOBJ (or
at least AC_SUBST([LIBOBJS])), $(LIBOBJS) get not defined to @LIBOBJS@, so
that even a use of:
  foo_LDADD = $(LIBOBJS)
in Makefile.am will not trigger the check described above[1], because
automake doesn't see the token `@LIBOBS@' in the expanded value of
$(foo_LIBADD).
 [1] i.e., the check that all the arguments passed to AC_LIBSOURCE refer
     to existing files or to files listed in BUILT_SOURCES

An example might help to clarify the matter:

 --- Example ---

  $ cat > configure.ac <<'END'
  AC_INIT([foo], [1.0])
  AM_INIT_AUTOMAKE([foreign])
  AC_CONFIG_FILES([Makefile])
  AC_PROG_CC
  AC_LIBSOURCE([foobar.c])
  END

  $ ls -l foobar.c
  ls: cannot access foobar.c: No such file or directory

  $ aclocal

  $ cat > Makefile.am <<'END'
  noinst_PROGRAMS = foo
  foo_SOURCES =
  foo_LDADD = $(LIBOBJS)
  END
  $ automake -a; echo exit_status = $?  # gives no error
  exit_status = 0

  $ cat > Makefile.am <<'END'
  noinst_PROGRAMS = foo
  foo_SOURCES =
  foo_LDADD = @LIBOBJS@
  END

  $ automake -a  # now it will error out, as expected
  Makefile.am:3: required file `./foobar.c' not found
  exit_status = 1

  $ cat > Makefile.am <<'END'
  extra_libs = @LIBOBJS@
  noinst_PROGRAMS = foo
  foo_SOURCES =
  foo_LDADD =  $(extra_libs)
  END
  $ automake -a  # errors out again, as it's smart enough to follow
                 # indirections in variables' definitions
  Makefile.am:1: required file `./foobar.c' not found
  exit_status = 1

  # now try with our original Makefile.am, while having
  # `LIBOBJS' AC_SUBST'd
  $ cat > Makefile.am <<'END'
  noinst_PROGRAMS = foo
  foo_SOURCES =
  foo_LDADD = $(LIBOBJS)
  END
  $ echo 'AC_SUBST([LIBOBJS])' >> configure.ac
  $ rm -rf autom4te*.cache && aclocal # just to be sure
  $ automake
  configure.ac:6: required file `./foobar.c' not found

 --- End Example ---

At this point, the only question remaning is: why does AC_LIBSOURCE
warn about non-existing sources when AC_OUTPUT is used, even if
AC_LIBOBJ and AC_SUBST([LIBOBJS]) are never called directly?  And
now the answer is pretty easy: AC_OUTPUT *does* end up calling
(through some indirections) AC_SUBST on `LIBOBJS'!

Let's see how.

AC_OUTPUT calls the macro AC_OUTPUT_COMMANDS_PRE (see code in
`autoconf/status.m4').  And we have:

 $ cat /usr/share/autoconf/autoconf/status.m4
 ...
 # AC_OUTPUT_COMMANDS_PRE
 # ----------------------
 # A *variable* in which we append all the actions that must be
 # performed before *creating* config.status.  For a start, clean
 # up all the LIBOBJ mess.
 m4_define([AC_OUTPUT_COMMANDS_PRE],
 [_AC_LIBOBJS_NORMALIZE
 ])
 ...

and:

 $ cat /usr/share/autoconf/autoconf/general.m4
 ...
 # _AC_LIBOBJS_NORMALIZE
 # ---------------------
 # Clean up LIBOBJS and LTLIBOBJS so that they work with 1. ac_objext,
 # 2. Automake's ANSI2KNR, 3. Libtool, 4. combination of the three.
 # Used with AC_CONFIG_COMMANDS_PRE.
 AC_DEFUN([_AC_LIBOBJS_NORMALIZE],
 [ac_libobjs=
 ac_ltlibobjs=
 ...
 AC_SUBST([LIB@&t@OBJS], [$ac_libobjs])
 AC_SUBST([LTLIBOBJS], [$ac_ltlibobjs])
 ])
 ...

See?  "AC_SUBST([LIB@&t@OBJS], [$ac_libobjs])".  Bingo!

Regards,
   Stefano