Why Switch a Big Software Project to autoconf?
woggo queries: "I'm a CS grad student working on a large research project (over 1 million lines of code, supported on many platforms). The project has been under development for several years, and the build system is nontrivial for end-users. We'd like to make it easier to build our software, and I'm investigating the feasibility of migrating to GNU autoconf. I need to demonstrate that the benefits of autoconf outweigh the costs of migrating a large system of makefiles with a lot of ad-hoc kludge-enabling logic. Has anyone made a similar case to their advisor/manager? Does anyone have some good 'autoconfiscation' war stories to share? (I've already seen the Berkeley amd story and the obvious links from a google search....)" Depending on the intricacies of the build process, such a conversion might take an awful lot of work. It might be easier to put a nicer face on the "nontrivial build process", although there is something to be said for the ease of "./configure; make; make install"
There's even more to be said for running short test programs which discern the same things that configure does, and then writing the correct .h file to disk. That way, you can write a makefile which ALWAYS works. The trouble that I have with configure is that it creates a makefile which differs from machine to machine, so that debugging it is quite difficult. Plus, if configure gets something wrong, you can make and make and make all day long, and you'll never fix it. So even though configure, in theory, saves you from having to maintain a makefile, in reality it means that everyone who runs configure may have to debug the makefile.
In other words, it's better to have one makefile, and fix the problems in the makefile, than to have to fix problems in configure or your configure specification.
If you want an example of how this can work, look at Dan Bernstein's djbdns, or qmail. On both of these, "make" Just Works(tm).
-russ
Don't piss off The Angry Economist
Here's an article on the subject, written by Uwe Ohse (you can read the original article here. Many of the problems were fixed in the mean time, but it makes an interesting read nevertheless.
autoconf config.guess, config.sub There was no source from which one could get up-to-date version of these scripts, which are used to determine the operating system type. This often caused pain: asked the openbsd port maintainers about it. (btw: there is now a canonical source for them, "ftp.gnu.org/gnu/config") autoconf takes the wrong approach The autoconf approach is, in short:
- check for headers and define some preprocessor variables based
on the result.
- check for functions and define some preprocessor variables.
- replace functions not already found in the installed libraries.
Yes, it works, albeit some details are discouraging, to but it mildly.No, it doesn't work good enough. This approach has lead to an incredible amount of badly code around the world.
Studying the autoconf documentation one learns what kind of incompatibilities exists. Using autoconf one can work around them. But autoconf doesn't provide a known working solution of such problems. Examples:
- AC_REPLACE_FUNCS, a macro to replace a function not in the
systems libraries, leads to the inclusion of rarely used
code in the package - which is a recipe for disaster.
On the developers system the function unportable() is available,
on another system it isn't? Oh well, just compile unportable.c
there and link it into the programs
...
- The same is true for AC_CHECK_FUNC(S). In this case there isn't
a replacement source file, but even worse, there's an #ifdef
inside the main sources, unless the programmers are careful
to use wrappers, which they often aren't, because compatibility
problems are often discovered very late in the testing process
(or even after release) and people are usually trying to make
the smallest possible changes.
In both cases you end up with rarely, if ever, used code in your programs. It's not dead code, it's zombie code - one day, somewhere, it will get alive again.Yes, this solves a problem. But it's overused, it's dangerous. In many cases unportable.c doesn't work on the developers system, so she can't test it. On other cases unportable.c only works correctly on _one_ kind of system, but will be used on others, too.
Yes, the often used packages _have_ been tested almost everywhere. But what about the lesser often used? ...
Keep in mind that there is no central repository of replacement functions anywhere
This is surely nothing which can be avoided completely, but it's something which has to be avoided whereever possible.
There's a solution to this problem, but it is completely different from what's used now: Instead of providing bare workaround autoconf (or a wrapper around it) ought to provide an abstraction layer above it, and a central repository for such things.
That way a programmer wouldn't use opendir, readdir, closedir directly but call they through the wrap_opendir, wrap_readdir and wrap_closedir functions (i'm aware of the fact that the GNU C library is this kind of wrapper, but it hasn't been ported to lots of systems, and you can't rip only a few functions out of it).
autoconf macros are inconsistent. For example: AC_FUNC_FNMATCH checks whether fnmatch is available and usable, and defines HAVE_FNMATCH in this case. AC_FUNC_MEMCMP checks for availability and usability of memcmp, and adds memcmp.o to LIBOBJS if that's not the case. Other examples exist. autoconf is not namespace-clean. autoconf doesn't stick to a small set of prefixes for macro names. For example it defines CLOSEDIR_VOID, STDC_HEADERS, MAJOR_IN_MKDEV, WORDS_BIGENDIAN, in addition to a number of HAVE_somethings. I really dislike that, and it seems to get worse with every new release.
My absolutely best-loved macro in this regard is AC_FUNC_GETLOADAVG, which might define the following symbols: SVR4, DGUX, UMAX, UMAX4_3, NLIST_STRUCT, NLIST_NAME_UNION, GETLOADAVG_PRIVILEGED. autoconf is large I'm feeling uneasy about the sheer size of autoconf. I'm not impressed: autoconf-2.13.tar.gz has a size of 440 KB. Add automake to that (350 KB for version 1.4). Does it _really_ have to be that large? I don't think so.
The size has a meaning - for me it means autoconf is very complicated. It didn't use to be so, back in the "good old days". And it accomplished it's task. I really don't see that it can do so much more today (i don't mean "so much more for me"). configure is large Even trivial configure scripts amount to 70 KB of size. Not much?
Compressed with gzip it's still 16 KB. Multiply that by millions of copies and millions of downloads.
No, i don't object to the size. It's perfectly ok if you get something for it. But you don't, about one half or more of each configure script can be thrown away without any lossage.
- Large parts of it just deal
with caching, which wouldn't be needed if configure wasn't so slow.
- Other parts of it are the --help output, which looks so good
...
but doesn't help usually (try it and find out what to do, without
reading README or INSTALL).
- Then there is the most bloated
command line argument parser i've ever seen in any shell script.
- Then there are many, many comments, but they aren't meant to
help you seeing what's going on inside configure, they are the
documentation for the macro maintainers (some might actually
prove to be useful, but the vast majority doesn't).
The configure scripts are the utter horror to read. There's a reason for this: configure doesn't use any "advanced" feature of the shell. But i wonder - are shell functions really unportable? And if the answer is yes: Do you really expect anything to work on that system? The problem is that a shell that old is unlikely to handle almost anything, for example large here documents.The configure scripts are the utter horror to debug. Please just try _once_ to debug 4000 lines of automatically generated shell scripts.
Note the autoconf maintainers: The road you're on is going to end soon. autoconf is badly maintained Let me clarify this first: I don't think bad about the developement. I'm missing maintainance of the already released versions. Now, at the end of 2000, almost two years have passed without a maintainance release of autoconf. 9 months have passed since a security problem has been found (in the handling of temporary files). There have more bugs been found, of course. ...
I know that nobody likes to dig in old code, but 2 years are a little bit much. automake My primary objection to automake is that automake forces me to use the same version of autoconf everywhere. Autoconf has a large number of inter-version conflicts anyway, but automake makes that situation worse, much worse.
I'd need the same version of both tools on all machines i happen to touch the Makefile.am or configure.in or any other autoconf input file on. There are a number of reasons for that, one of them is that automake used to provide some autoconf-macros during the years autoconf wasn't developed at all, and these macros are now moved to autoconf, where they belong to. But if you happen to use, say, AM_PROG_INSTALL, and later versions declare that macro obsolete
That doesn't sound too bad? Ok, but suppose
- update all those machines regulary (i'm not going to really do
that, i'd rather stick to what's installed, but anyway)
- i didn't touch a project for, say, 2 years, and then i need to
change something and release a new version. This involves changing
the version number in configure.in.
In more cases than not this will need considerable changes to configure.in. Some major, most minor - but even the minor ones need attention.I found that hard to deal with. Things were even worse since every CVS checkout tends to change time stamps, which can mean that autoreconf is run even if there's no chance been done to any autconf input file.
Don't misunderstand me: i don't attribute that to automake. I attribute it to the internal instability of autoconf. Unfortunately you can't have automake without autoconf. libtool Libtool adds an additional layer of complexity. I never had any reason to look at the insides of libtool (which proves that it worked for me). But having one project which used autoconf, automake and libtool together was enough - never again. I got exactly the same problems as i got with automake, described above, but they were worse and happened more often.
One problem with libtool is that releases don't happen very often. Libtool rarely is up to date with regards to changes on some operating systems. Which makes it difficult to use in packages meant to be really portable (to put it mildly).
A libtool script and a number of support files are distributed with every package making use of libtool, which ensures that any user can compile the package without having to install libtool before. Sounds good? But it isn't.
Another problem is the size of the libtool script. 4200 lines ...
summary
Autoconf is the weak link of the three tools, without any doubt.
Version 1 wasn't really designed, version 2 made a half-hearted
attempt of dealing with design problems. I'm not sure about the
next version.
Petru
I am an engineering grad student working on a similarly sized project. Our project is compiled on a variety of Unix platforms using automake, autoconf and libtool. As you are already compiling for multiple platforms you are 90% of the way there in determining the different needs for each compile. If you haven't already organized your build process, now would be a good time before it becomes 10M lines of code.
Autoconf and friends make it infinitely easier to compile our code. However you will have to put in a fair bit of work determining all variety of tests required to determine the idiosyncracies of each build. You are probably already doing something similar if you can build on multiple platforms.
Autoconf has been well worth the initial effort. Occasionally new compile problems crop up, but they are usually solved by the addition of another 1 line check in configure.in.
Selling autoconf should be easy. Wrestle with compile problems once getting autoconf working, or have users repeatedly wrestle with the problems without autoconf.
I have found that autoconf has problems when you are cross-compiling. If you are or plan to cross-compile your project, stay far away from autoconf.
/usr/share/autoconf/acspecific.m4 or not do the test!
A few macros (particularly AC_TRY_RUN, and anything that calls it) compile and run a program on the host to test for functionality. Unfortunately, there is no way to tell autoconf that you aren't going to be able to run the compiled program, because it's for your target platform.
Many of the AC_FUNC_ macros simply DIE when running in a cross-compiling environment. For example, let's cross-compile the ncftp package. The configure.in uses the AC_FUNC_SETPGRP macro. Running autoconf results in:
configure.in:146: warning: AC_TRY_RUN called without default to allow cross compiling
That's right. This macro (defined elsewhere in the autoconf package) uses AC_TRY_RUN without a default clause. So instead of recovering nicely, the configure script dies when it tries to do this test. The only way to get around this is to modify
Don't forget the Autoconf macro library and also the fact that there are thousands of free packages out there which will have configure scripts from which you can borrow - try to find packages in a similar domain to your own.
The difficulty (complexity, time taken) in maintaining a package which works on N platforms is usually proportional to N - or if you are unlucky, some small power of N like 2. What your code is really doing is trying to understand the properties of the target system and so the #ifdef __hpux__ for example in line 1249 of blurfl.c is actually trying to determine if the quux is use like this or like that. Autoconf on the other hand will produce a single preprocessor macro for driving the quux. This means that you don't have an extra 15 lines of #ifs to handle quuxes in other operating systems. Hence you may not have less #ifdeffed bits, but each of the #ifdeffed bits will be shorter.
Autoconf works differently to thge standard approach - it allows your code to work with each feature independently, and so while the normal approach is O(N) or O(N^2) in the number of suppoered OSes, the complexity of maintaining an autoconfiscated program is O(N) in the number of different features supported by the operating systems between them. The great thing about this is that Autoconf keeps these orthogonal and prevents these things from interacting too heavily, and it turns out to the the case that the total number of different features basically flattens out to some constant number even when you continue to add more supported OSes (i.e. there are only a certain total number of different ways of doing things even though each of the N operating systems can choose among X ways of doing Y things).
So, what this means is that you shold do the feasibility study as discussed above, but naturally autoconfiscating the whole system will take a while. The ideal time to do this is either
Another option to explore in combination with Autoconf is the Apache Portable Runtime. There is also Autoproject but I suspect that is a little lightweight for your needs.
Taking all the above together I suggest that you
Autoconf is a tool that in the end can only make portability choices for you. In order for those choices to mean anything you have to have a need for your software to be portable (to a wide number of platforms, really), and you need to understand the real issues with writing portable software.
If you're writing for FreeBSD, Solaris, and Linux 98% of the time for application software you can write it so there are no portability issues. Why have the autoconf step when you can "make;make install"? Modern systems are not all that different for high level stuff, and are converging for medium level stuff. It's really only the low level details keeping them apart.
If on the other hand running on an old Ultrix box, and on your SCO Unix box, or on that PDP-11 in your garage is important autoconf can give you the mechanisms to make all that work but only if you know that differences between the platforms, and what changes need to happen to your code to make it work . It does no good to have autoconf check to see if bzero exists if you don't know to use memcpy as an alternative, or vice versa. A check without an alternative is just a way of bombing a little sooner than the compile stage.
The other thing autoconf can help with is optional packages. These are not portability issues per se, but rather choices that need to be made, but often aren't worth bothering a user about. Consider the application that's all command line based except for a single X app that's not really needed, just nifty. Well, if the system doesn't have X, you don't build it, and if there's no X it's unlikely the user wanted to run X apps anyway.
As far as the mechanics go, autoconf is fairly easy. Once you understand the changes that need to happen making autoconf make them for you is trivial.
i may well be in the minority among this crowd, but i think you should avoid autoconf like the plauge. people's most common reason for using autoconf/configure is portability, but that's a cop-out.
basically, the autoconf/configure school of portability says "forget about actually writing portable code, just write it for each variant and let the build process pick". that's whacked. you'll continually be running accross new variants, new ways in which systems are different, or just new systems. for example, i use Plan 9, which has a very good ANSI/POSIX compatability system (better than many *nix systems). despite being near-fully ANSI compliant, pretty much every autoconf fails because it doesn't recognize the output of uname or something stupid like that (of course, that says noting about when programs that claim to be ANSI or POSIX arn't, like including non-ANSI/POSIX headers, typically BSD stuff).
this school of portability also typically makes your code far less readable - littering it with ifdef's every third line - and much larger. it ends up taking much more time than just slowing down and disiplining yourself to write real portable code.
the argument 'configure ; make ; make install' is easy is stupid, as well. y'know why? 'cause 'make install' is easier. build your makefiles well. the 'install' target should have prerequisits, obviously. make will build them for install. and 'configure' is slower and more prone to failure than 'cp makefile.linux.386 makefile'. and light years more reliable. and editing an example makefile is way easier than putzing with autoconf/configure if something doesn't work. not to mention easier to debug (uh, 'echo' anyone?).
on a personal note, having done portability work a good bunch, i'd offer just a little extra advice: do not require GNU-specific stuff. don't use any of the gmake-specific features or gcc language extentions. GNU propaganda, that'll just kill your portability. and if you need something both simpler and more powerful than make, check out mk from the Plan 9 and Inferno distrubutions. Vita Nuova's got a version that runs on various unixes and win32 systems. it's very similar to make, but a bit more general and flexible. but, given that you've already got all the makefiles, i'd suggest your best bet is just sticking with plain makefiles and cleaning them up.
i speak for myself and those who like what i say.