As I mentioned in an earlier post, I feel like some kind of support for
autoconf is essential in any C/C++ build tool that is going to be used
for portable code. I'm going to describe abuild's support for
autoconf. I'm not sure whether this is the right model for gradle or
not. I think it works well in a large system with lots of components,
but it's pretty different from how autoconf is used in most open source
projects. It may be that gradle will want some kind of support, like
abuild has, for running autoconf, autoheader, and configure, or it may
just be that gradle needs to be such that it plays nice with autoconf
used in a more conventional fashion. Certainly tools like automake have
no place with gradle since they are completely make-based, but autoconf
stands by itself and is useful without the rest of the autotools.
In particular, if autoconf is used as part of the overall autotools, the
assumption is generally that there is a single configure.ac for each
thing that builds independently, that the developer has run autoconf to
generate a configure script in advance, and that the configure script
has been distributed with the source distribution. The end user (person
who is compiling the code) is not required to have autoconf installed.
If you're using all the autotools, then the developer makes configure.ac
and Makefile.am (for automake) and then tools like autoconf and automake
are used to general configure and Makefile.in. Then the final user who
downloads and builds the source runs ./configure, which generates
various header files and makefiles. This works okay for open source
distributions, but I think it's a lousy model for an enterprise build.
Reasonable people may differ here, but I personally despise
automatically generated makefiles since they create their own sets of
problems with dependency management. I've seen several systems that use
automatically generated makefiles, and most have not been particularly
friendly to the problems of ongoing maintenance. (Anyone remember
imake?) A notable exception is cmake, which seems to handle this pretty
well, though I haven't used cmake extensively and can't really comment
on it too much. All I'll say further about that debate is that abuild
does not use automatically generated build files and uses autoconf in a
somewhat different way from how it was intended to be used.
To recap in a sentence, autoconf's intended use is that the developer
runs autoconf to generate configure, and the end user runs configure
explicitly as part of the build prior to running make. The end user
does not need to have autoconf installed to run configure. In contrast,
with abuild, autoconf is run behind the scenes. autoconf must be
present on the build system used by the end user, but the end user (and
by this, for this whole discussion, I mean the person building the code)
doesn't have to know autoconf is there or ever run a configure script.
There are a few anti-patterns that can occur with autoconf. One that
I've seen often is installing header files that are generated by
configure. I consider this to be an antipattern because it ends up
having the opposite effect of what autoconf is intended for. In
particular the main reason to use autoconf is to make your source code
portable. This means your source code should use flags that are
automatically defined by autoconf (based on the rules you provide) to
make decisions about platforms, and that your interfaces should be
platform-independent. If you install autoconf-generated header files as
part of your distribution, then your distribution's header files become
platform-dependent because anything that is an OUTPUT of configure (not
of autoconf itself) is platform-dependent. In other words, it's okay to
ship autoconf's output, but not the output of anything autoconf
generated, such as anything generated by configure. For example, if you
use autoconf to determine what size the "long" type is and you install
the header file that defines this constant, that copy of the header file
can no longer be used on any system whose long isn't the same size as
the one on which autoconf was run. What's even worse is if the
autoconf-generated header (or, more accurately, the configure-generated
header) contains information about which features were or were not
present on the system. Put more succinctly, files generated by autoconf
can be part of your implementation, but they should never be part of
your interface. There is nothing the tool can do to prevent people from
making this mistake (well, there probably is if you know exactly which
files are part of the interface), but the tool should certainly
discourage this in any way it can. This is pretty hard for developers
to get right. I've seen high-profile packages get this wrong. For a
good example of getting this wrong, take a look at
/usr/include/tiffconf.h on any Linux system that has libtiff's
development package installed. This is clearly not a
platform-independent header, but it has to be there because it is
include by other header files instead of being hidden behind the
scenes. The maintainers of libtiff know that this is a mistake as you
can tell from comments at the top of the header. In their case, the
mistake was made a long time ago, and fixing it now would likely break
lots of things, so they're pretty much stuck with it.
Another pitfall with autoconf is that it is not friendly to parallel
builds. This is because autoconf creates lots of temporary files when
it runs, and it keeps reusing file names like conftest.c. While two
runs of autoconf can run together, it's not a good idea to run autoconf
or its output (like configure) in a directory in which anything else is
going on at the same time. If you run ./configure in advance of running
the build (like the autoconf developers intend), this is a non-issue.
I don't know whether the kind of style abuild uses to support autoconf
would make sense in gradle, or whether it's instead reasonable to have
projects that build with gradle run autoconf in advance and run
./configure before running gradle. For what it's worth, I'll describe
the pattern I use for autoconf with abuild. Take it or leave it.
Abuild has the concept of private interfaces. The abuild interface is
an interface at BUILD time, not comparable to something like a Java
interface or a C++ header file. The abuild interface is associated with
a build item (think "project" or "component") and tells things that want
to use this component as part of their build what they need to do to see
it. For C/C++, this typically means adding directories to the include
and library path and adding libraries to the link command. If abuild
declares something in an interface to be private, then it is only
available to things that specifically request it through a direct
dependency with a flag. This in turn can be restricted to only certain
other items based on namespace rules. When designing an API, I would
make sure the API is platform-independent. If I needed to use autoconf,
I would use it as part of the implementation only. In abuild, I would
do this by creating a build item whose sole job is to run autoconf and
configure and to generate any products, such as automatically generated
header files, that are needed by the build, and then I would put those
products in a private interface. I would also mark that build item as
"serial", which prevents abuild from running a parallel build inside
that build item (but doesn't and doesn't have to prevent that build from
running in parallel with builds of other items/projects that are
unrelated to it). Then other build items that are part of the
implementation can depend privately on the autoconf build item in a
manner such that its build interface doesn't leak out and pollute the
rest of the build.
For example, suppose you are writing code that wraps around some choice
of external libraries depending on what implementations are available,
and that the artifact you build will be a shared library that will be
installed with its associated headers. (To be concrete, say you are
writing an imaging library whose backend capabilities can vary based on
which backend imaging library is used but whose programmer interface
remains the same either way.) You could use autoconf at the time of
your build to figure out which external libraries are available, but
that knowledge should be kept private inside your implementation and
should not affect your header files at all. Then when someone else
builds against your library, what they do is independent of whether your
library has one backend or another. This means that if you rebuild your
shared library to include an alternative back end, you can swap out the
result of your build with the new one and not require downstream
programs even to relink.
I'm not sure how this really affects gradle. Maybe it's something that
can go in gradle's documentation, or maybe it's just something people
need to understand in general about using autoconf. Abuild's autoconf
support basically just lets the user define the name of some
automatically generated files and the location of the configure.ac file,
and then abuild runs autoconf, autoheader, and configure behind the
scenes at the right time to make sure any generated files exist before
they are referenced. There's not much more to it than that. The rules
themselves also deal with not updating modification times on files that
are regenerated but didn't change, but if gradle can be smarter about
fine-grained dependencies (more like scons and less like make), maybe
that part's not necessary.
Okay, rereading this post, I think this ends up looking more like my
rant about proper ways to use autoconf than anything else. Hopefully it
can still be useful.
--Jay
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email