|
CIRR.COMCompile TimeProgramming Portably: Feature Definition Generation |
In September, we discussed the significant advantage of re-implementing desired, but less common functions: if you use a feature of your local operating system, but discover it doesn't exist on other platforms, write your own implementation and make that code a part of your distribution. In that column we also discussed the benefit of testing for features in your code: feature test macros make code easier to port, and far easier to read.
This month, we'll discuss four ways to generate feature definitions for your project for any Unix-like platform. The four techniques, in order of current popularity are: by hand, OS definition based, using metaconfig, and using autoconf.
One of the oldest mechanisms for configuring software is doing it I
We'll use Listing 0 as the basis of all future examples.
Generating feature definitions by hand may be easy for you, the
developer of the software, but it's terribly inconvenient for the
end-user (since you're distributing code, the end-user could be
another programmer, a system administrator, or a true end-user).The
end-user is expected, nay, required to know if a given feature is
available on their system and how it acts. Fortunately, the system
manual pages usually provide the needed information.
To allow the end-user to generate the feature definitions,
you should provide an include file with all of the features required
or desired. The file should include a description of the features
used, and what to do if the feature doesn't exist on the end users
platform.This documentation should include where to enable any
needed re-implementation sources and objects in the projects
Makefiles.
It's also good practice for all projects to write their
configuration include file in the manner above. It provides
excellent documentation of the features required/expected by
your software. It also greatly eases the porting effort should
your software be moved to some platform where your automated
configuration tools don't work. Imagine trying to port to Windows
without cygwin, or any other operating system that doesn't look
at lot like UNIX, but having a C language development environment
(assuming project development in C.) Having all of the features
clearly documented in the configuration file may greatly ease the
porting effort to such platforms.
By the by, you use a variation of configuring by hand every time you type
An example of configuring software entirely by hand
is the 5.X and early 6.X versions of tcsh.
The advantages of hand configuration fall on the developer. They
include not having to write a script (or generate one) to find the
feature definitions and the ease of tweaking/modifying the
configuration. Fundamentally, it's simple for the developer.
The disadvantages of hand configuration fall on the user. The
largest disadvantage is having to delve deeply into the system
documentation to determine if required features are available.
On the other hand, tweaking/modifying the configuration is easy, and the
process allows the user to more easily debug compilation and runtime
problems, mostly due to the familiarity learned during the configuration
process.
The second oldest method of generating feature definitions is attempting
to do so based on OS definition. The early versions of this showed up as
lots of
To use OS definitions to configure your software package, an include file should
be created containing only the feature definitions for the supported
operating systems. For each supported operating system, create the
feature definition block protected by a preprocessor macro that
identifies a the operating system. For many platforms, the C compiler
provides a nice single definition to uniquely identify the system
without outside intervention. However, on some systems (such as the
many System V, release 4 systems for Intel released in the early '90's),
a new macro may need to be defined to differentiate between
variations (this may be needed to support various Linux
distributions.)
Inside the OS specific preprocessor block, all of the supported features
are turned on (or off, as the case may be.)
Listing 1 provides an example of such a configuration.
To reliably set such a mechanism up, you need either access to a
large number of systems, or you need a user community that is
willing to send back patches/updates with feature definitions for
previously unknown
An example of a project that uses this method is sendmail.
The largest advantage of using the OS based feature definition is it
comes the closest to providing a true ``compile and go'' software
distribution to the end user.
Among the disadvantages of using the OS based feature definitions is the
complexity of maintaining the per OS feature definition lists for the
maintainers, and the relative complexity of supporting previously
unknown systems to the end users (if an OS isn't known, the defaults
_will_ cause something to break without adequate explanation.
metaconfig is the automatic configuration file generator developed
by Larry Wall (and a cast of others.) An early use of metaconfig
was with Wall's news reader, rn, and later with trn. It's
also used with Perl. metaconfig was one of the earliest (perhaps
the earliest) singing and dancing scripts to determine features
available on an OS. It's been in regular use since 1987 (at least.)
Strangely, enough, metaconfig's package is known as dist.
Probably because it provides a strong set of distribution tools in
addition to generating a Configure script.
metaconfig-generated Configure scripts are interactive scripts
that provide directions to the end-user, and ask for confirmation
of some potentially weak information it determines. For the average
user, on a relatively normal system, running Configure scripts
is mostly an exercise in typing
metaconfig provides a rich, standard set of feature definitions to
the developer. At the time of this writing, there are 465 feature
macros for use in C language modules and 468 feature
macros for use in shell scripts.
To use metaconfig, the following tasks are done during the initial
configuration of the project for metaconfig:
The directory you run packinit will be considered the top level
directory of the project.
Listing 3 is the
Listing 4 is the generated config_h.SH, which when run through the
shell, will become config.h for all the sources to include. This
config_h.SH doesn't (yet) contain the path information for the default
log file or console (mostly because the author hasn't figured out how to
get
Once the software tests out correctly, you can use makedist to generate
distribution sets. makedist will gladly try to generate shell
archives suitable for posting to the USENET comp.sources.* groups.
The entire sample project using metaconfig can be found at
ftp://ftp.cirr.com/pub/compiletime/2002-Dec/MetaConfig-example.tar.gz.
Example packages that use metaconfig are trn and Perl.
Probably the largest advantage to using metaconfig is the
interactivity. It allows you, as a software developer, to ask
policy type questions of the installer on the installation system.
trn uses this flexibility to ask for USENET news distributions
to support, the preferred news server, organization name, etc.
The next largest advantage is that metaconfig automatically
generates the Configure script directly from the sources,
and provides the tools to greatly ease packaging and distribution.
The biggest disadvantage is listed above as the greatest advantage.
metaconfig generated Configure scripts are extremely
interactive. This annoys many people.
A second disadvantage is that the metaconfig distributions
appear to have forked. This is probably due more to the extremely
small number of packages using it more than anything else. At the
moment, there does not seem to be a single metaconfig
maintainer, so each project using it is maintaining their own
distribution, usually based off of the current perl distribution.
The final feature definition generation system we'll look at is
the Free Software Foundations autoconf utility. autoconf
is frequently used with automake and libtool, although
we won't delve deeply into either of those here. automake
and libtool are topics suitable for columns of there own.
autoconf is newest of the feature definition generation
tools we're going to discuss. It's has been in around in its
current form for about 10 years now.
autoconf-generated configure scripts attempt to
determine everything needed in a batch fashion. Any information
that cannot be determined by poking and prodding the system under
investigation must be provided by command line switches to the
generated configure script.
To use autoconf to generate a configure script, you
create a configure.in file using various autoconf
macros to describe the features to be tested. autoconf has
no intrinsic knowledge of the files in the product distribution.
This means that autoconf doesn't provide a mechanism to
automatically search source files for feature definitions.
Setting up
For the logger project, autoscan generated listing 5:
The 26 lines of Listing file are converted by
For the sample project, we need to update
Conditional replacement for syslog.c, snprintf.c and strftime.c
needs to be added.
Note that
The above
Listing 6 provides the source of
In Listing 6, we define the program to be built as
These six lines of source will expand into about 175 lines of
resulting
As with all the feature definition generation techniques we've
discussed,
The advantages include automated generation of feature testing
scripts for a software package, allowing the end user to get by
with knowing less about their system and an easy, non-interactive,
two step process getting from raw source to compiled executable.
Two of the largest disadvantages are the steep learning curve to
generate the
We've looked at four different methods of determining the feature
definitions to define for a given platform. They are by hand; based
on OS definitions; by using a metaconfig generated
Configure script; and by using an autoconf generated
configure script.
The current favorite mechanism for generating feature definitions
definitions is autoconf generated configure scripts.
However, an extremely popular package, namely perl uses a
metaconfig generated Configure script. So, evaluate
the different tools, and pick the one most appropriate to your
package.
Is there some topic you'd like to see discussed in Compile
Time?
The only restrictions are that the topic be somewhat UNIX/Linux-centric,
and that some documentation on the topic exist (pointers are always
helpful too.)
Metaconfig manual page:
http://www.vmlinux.org/cgi-bin/man2html?metaconfig+1
Metaconfig tarball:
http://www.netsw.org/softeng/configuration/dist/dist-3.0@70.tar.gz
(For trn)
http://trn.sourceforge.net/dist-3.0-70-wd.tar.gz
(For Perl)
http://www.cpan.org/authors/id/JHI/metaconfig-for-Perl-5.8.0.tar.gz
Autoconf Info documentation:
http://www.gnu.org/manual/autoconf-2.53/html_node/index.html
GNU Autoconfig, Automake, and Libtool
The metaconfig sample project can be found at
ftp://ftp.cirr.com/pub/compiletime/2002-Dec/MetaConfig-example.tar.gz.
The autoconf sample project can be found at
ftp://ftp.cirr.com/pub/compiletime/2002-Dec/AutoConf-example.tar.gz.
/*
* features.h -- features used by logger(1l) from the local system
*/
/*
* HAS_SYSLOG -- does the system have a working syslog implementation?
*/
#define HAS_SYSLOG 0
/*
* HAS_SNPRINTF -- does the system have a working snprintf() function?
* If not, we'll use our local version. snprintf() is a bounds
* checking version of sprintf(3s).
*/
#define HAS_SNPRINTF 0
/*
* HAS_STRFTIME -- does the system have an strftime() implementation
* we can use to generate time stamps? If not, we'll fall back
* on a version pulled from the *BSD source trees.
*/
#define HAS_STRFTIME 0
/*
* Policy level decisions.
*/
/*
* PATH_LOGFILE -- where to drop the logfile.
*/
#define PATH_LOGFILE "/var/log/syslog"
/*
* PATH_CONSOLE -- path to the console device
* on any UNIX-like system, the default should be
* reasonable enough.
*/
#define PATH_CONSOLE "/dev/console"
LISTING 0
make config in /usr/src/linux to configure your Linux system.
Features by OS Definition
#ifdef OSNAME littered throughout the code. But we all
know that is bad style. So it has mutated into a single source file
that converts OS names into feature definition lists.
/*
* configure.h -- determine what features are available by
* looking at compiler definitions
*/
#include "features.h"
/*
* Linux, in its various incarnations
*/
#if defined(__linux__)
/* some reasonable defaults for any late model LINUX */
# define HAS_SYSLOG 1
# define HAS_STRFTIME 1
# if defined(__GLIBC__)
# if __GLIBC__ > 1 && __GLIBC_MINOR__ > 1
/* we're on a linux system with glibc > 2.1 -- yeah! */
# define HAS_SNPRINTF 1
# endif
# endif
#endif /* defined (__linux__) */
/*
* NetBSD -- tested on NetBSD 1.5.x, 1.6
*/
#if defined(__NetBSD_Version__)
/* For All versions of NetBSD *
# define HAS_SYSLOG 1
# define HAS_STRFTIME 1
# if __NetBSD_Version__ > 104000000
/* only releases after 1.4 */
# define HAS_SNPRINTF 1
# endif
#endif /* defined (__NetBSD_Version__) */
/*
* EmbededOS -- an OS that supports ANSI C, but only barely looks like
* UNIX (hmm, maybe more like VMS)
*/
#if defined(__EmbeddedOS__)
# define HAS_SYSLOG 0
# define HAS_STRFTIME 1
# define HAS_SNSPRINTF 0
# define PATH_CONSOLE "cons:"
# define PATH_LOGFILE "DUA0:[0,0]LOG.TXT"
#endif /* defined (__EmbeddedOS__) */
LISTING 1
OSTYPEs. And changes between OS releases may
not be properly handled.
Auto-configuration with metaconfig
RETURN. It does allow the software
developer to ask the person doing the software configuration/installation
some questions that might be site policy specific. Examples of such
questions are the organization name and the email address of the
site administrator.
Listing 2 is an initial MANIFEST.new file in the top level directory.
The format for MANIFEST.new is ``<filename>
<description>''. List only source files in the
MANIFEST.new. metaconfig will use the files
listed in MANIFEST.new to determine what feature tests
to include in the final Configure program.
MANIFEST.new will not be distributed with your final
package. You can provide a MANIFEST file (which should
be listed in both itself and MANIFEST.new) listing
all the files that should be delivered. The resulting Configure
program knows how to use MANIFEST to verify that the
package is complete. .U directory
to pull in any locally defined modules needed to generate the
Configure program. config_h.SH, Makefile.SH, and any
other .SH files you defined in your
MANIFEST.new. metaconfig will update
MANIFEST.new to include any generated programs.
Remember to update MANIFEST in a similar fashion.
MANIFEST.new for a project
including features.h defined in Listing 0
MANIFEST This file, a listing of all included sources
Makefile.SH Generates into a Makefile to build logger(1l)
features.h Feature definitions
logger.1 Manual page, in *BSD mdoc format
logger.c logger(1m) main module
snprintf.c re-implementation of snprintf(), from sendmail (from UCB)
strftime.c re-implementation of strftime(), from UCB
syslog.c file based re-implementation of syslog(3)
syslog.h supporting include file for re-implementation of syslog(3)
LISTING 2
MANIFEST.new after running metaconfig.
MANIFEST This file, a listing of all included sources
Makefile.SH Generates into a Makefile to build logger(1l)
features.h Feature definitions
logger.1 Manual page, in *BSD mdoc format
logger.c logger(1m) main module
snprintf.c re-implementation of snprintf(), from sendmail (from UCB)
strftime.c re-implementation of strftime(), from sendmail (from UCB)
syslog.c file based re-implementation of syslog(3)
syslog.h supporting include file for re-implementation of syslog(3)
Configure Portability tool
config_h.SH Produces config.h
LISTING 3
Configure to prompt for it.. Bugger.)
case $CONFIG in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
elif test -f ../../config.sh; then TOP=../..;
elif test -f ../../../config.sh; then TOP=../../..;
elif test -f ../../../../config.sh; then TOP=../../../..;
else
echo "Can't find config.sh."; exit 1
fi
. $TOP/config.sh
;;
esac
case "$0" in
*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
esac
echo "Extracting config.h (with variable substitutions)"
sed <<!GROK!THIS! >config.h -e 's!^#undef\(.*/\)\*!/\*#define\1 \*!' -e 's!^#un-def!#undef!'
/*
* This file was produced by running the config_h.SH script, which
* gets its values from config.sh, which is generally produced by
* running Configure.
*
* Feel free to modify any of this as the need arises. Note, however,
* that running config_h.SH again will wipe out any changes you've made.
* For a more permanent change edit config.sh and rerun config_h.SH.
*
* \$Id: 2002-Dec.html,v 1.2 2002/11/27 03:25:46 eric Exp $
*/
/*
* Package name : $package
* Source directory : $src
* Configuration time: $cf_time
* Configured by : $cf_by
* Target system : $myuname
*/
#ifndef _config_h_
#define _config_h_
/* BSD:
* This symbol, if defined, indicates that the program is running under
* a BSD system.
*/
#$d_bsd BSD /**/
/* HASCONST:
* This symbol, if defined, indicates that this C compiler knows about
* the const type. There is no need to actually test for that symbol
* within your programs. The mere use of the "const" keyword will
* trigger the necessary tests.
*/
#$d_const HASCONST /**/
#ifndef HASCONST
#define const
#endif
/* HAS_STRFTIME:
* This symbol, if defined, indicates that the strftime routine is
* available to format locale-specific times.
*/
#$d_strftime HAS_STRFTIME /**/
/* HAS_SYSLOG:
* This symbol, if defined, indicates that the program can rely on the
* system providing syslog(). Otherwise, the syslog code provided by
* the package should be used.
*/
#$d_syslog HAS_SYSLOG /**/
/* CAN_PROTOTYPE:
* If defined, this macro indicates that the C compiler can handle
* function prototypes.
*/
/* _:
* This macro is used to declare function parameters for folks who want
* to make declarations with prototypes using a different style than
* the above macros. Use double parentheses. For example:
*
* int main _((int argc, char *argv[]));
*/
#$prototype CAN_PROTOTYPE /**/
#ifdef CAN_PROTOTYPE
#define _(args) args
#else
#define _(args) ()
#endif
/* VOIDFLAGS:
* This symbol indicates how much support of the void type is given by this
* compiler. What various bits mean:
*
* 1 = supports declaration of void
* 2 = supports arrays of pointers to functions returning void
* 4 = supports comparisons between pointers to void functions and
* addresses of void functions
* 8 = suports declaration of generic void pointers
*
* The package designer should define VOIDUSED to indicate the requirements
* of the package. This can be done either by #defining VOIDUSED before
* including config.h, or by defining defvoidused in Myinit.U. If the
* latter approach is taken, only those flags will be tested. If the
* level of void support necessary is not present, defines void to int.
*/
#ifndef VOIDUSED
#define VOIDUSED $defvoidused
#endif
#define VOIDFLAGS $voidflags
#if (VOIDFLAGS & VOIDUSED) != VOIDUSED
#define void int /* is void to be avoided? */
#define M_VOID /* Xenix strikes again */
#endif
#endif
!GROK!THIS!
LISTING 4
GNU autoconf
logger(1m) to make use of autoconf
would follow the following steps:
The entire sample project using autoconf can be found at
ftp://ftp.cirr.com/pub/compiletime/2002-Dec/AutoConf-example.tar.gz.
configure.scan file.
configure.scan can be used as a starting point
for your configure.in
# Process this file with autoconf to produce a configure script.
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AC_CONFIG_SRCDIR([syslog.c])
AC_CONFIG_HEADER([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([fcntl.h netdb.h paths.h stdlib.h string.h sys/socket.h syslog.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_SIZE_T
AC_STRUCT_TM
# Checks for library functions.
AC_FUNC_MKTIME
AC_FUNC_STRFTIME
AC_CHECK_FUNCS([localtime_r memmove strcasecmp strchr strerror tzset])
AC_CONFIG_FILES([])
AC_OUTPUT
LISTING 5
autoconf
into over 4000 lines of shell script. configure.in is effectively an Bourne shell script that is
preprocessed via m4 to expand the autoconf provided macros.
Since it is a Bourne shell script, any Bourne shell constructs
may be used in testing the system for feature availability. In
general, you want to limit yourself to the autoconf provided
macros as much as possible, but the ability to include Bourne
shell code does allow easy extension. If you find yourself using
the same Bourne shell code repeatedly, you might consider writing
a macro to implement the code.
configure.scan as a starting point, write
configure.in in terms of autoconf m4
macros and in line shell scripting. AC_INIT
to have the correct arguments. Then we need to remove some of
the header checks that shouldn't be there (and the corresponding
code needs to be updated as well.) syslog.[ch] is needed if the
OS doesn't have a syslog(); snprintf()
and strftime() are needed by the syslog(),
if the OS doesn't provide those supporting functions. autoscan guessed that the primary source
file for the project was syslog.c, when in reality,
it is logger.c. configure.in still needs to be taught
how to gather and propagate the paths for the console and the
log file. Given autoconf's batch orientation, this
would be gathered by a switch to the resulting configure
program. Makefile.am in terms of automake
m4 macros. Makefile.am which
will be processed into Makefile.in. automake can
support multiple executables per directory, by using the
executable name as part of a later variable name. logger.
We define the sources to build logger as
logger.c We further define that snprintf.c
strftime.c syslog.c may be needed to build.
Finally we define the manual pages to be installed.
bin_PROGRAMS = logger
logger_SOURCES = logger.c
EXTRA_logger_SOURCES = snprintf.c strftime.c syslog.c
man_MANS = logger.1
LISTING 6
Makefile.in. The resulting
Makefile.in will conform rather closely to the
GNU Coding Conventions. Makefile.in,
config.h.in, and configure autoconf and automake have
their advantages and disadvantages. configuration script, and the resulting
scripts batch nature (which was also called a big advantage above.)
Final configuration
Future Compile Time Columns
References
2001 New Riders; ISBN 1-57870-190-2
Gary V. Vaughan, Ben Elliston, Tom Tromey, and Ian Lance Taylor
If you have any questions about our site, please
send us mail.
Copyright
2000,2001
Central Iowa (Model) Railroad
Contact Us
Referral
Program
Support
$Id: 2002-Dec.html,v 1.2 2002/11/27 03:25:46 eric Exp $
Terms of Service
Privacy Information