Generate a "package options" part of a GNU Autoconf input recipe file ("configure.ac").


Node "Changelog"

Reason for writing it:

The GNU autoconf tool, part of the Autotools family of "package development helpers", creates the familiar configure file that comes with most Open-source software source kits seen today. The writing of the document that autoconf uses as input (the configure.ac file, a.k.a [but deprecated as] the configure.in), is a chore that only some developers have learned how to do painlessly. Such an input file is written in a combination of *NIX 'shell' scripting language and the m4-derived autoconf 'language', which consists of references to m4-ish macros defined in other places, along with parameters passed to the autoconf macro processor system.

For some software packaging chores, the only macros present in the configure.ac file are those dealing with platform variations like system headers and C-compiler syntax. However other packages have some number of build-time user-configurable options, referred to as 'package options' in this documentation. The file which the developer must write then must also contain properly-written blocks which, when interpreted by autoconf into the output configure script, test for flag arguments in the ./configure invocation, that pertain to such package options.

I found that the writing of these package option components of the configure.ac file was tedious and error-prone in the extreme, and furthermore was not undertaken frequently enough to allow the memories of past experiences to ease the current effort very much. So, this tool was written to automate the creation of these 'blocks' of autoconf statements, one for each option needed by the package. The decision to write such a tool was not made lightly. To do so introduces Yet Another Level of Indirection (abstraction) into a workflow-process which is already well-nigh buried in indirection, and which many developers, justly or not, abhor and detest.

Regardless of the feeling of some coders about the GNU Autotools, and about the important autoconf tool specifically, these tools have practically no rival in accomplishing what they were meant to do: to enable the maintainable systematic portability of Open-source software packages to a wide variety of *NIXand *NIX-like systems. They are not going to go away tomorrow, or the day after. In fact the usability of many Open-source projects undertaken in the past would have been quite considerably enhanced had their authors really understood the GNU autoconf system and had they been able to write a correct configure.ac file as part of their build support infrastructure for their package. It is my hope that this tool will make a small contribution to the Open-source world by enabling authors to more easily write their all-important configure.ac file and thus ship valid and robust configure scripts with their software.

Attention Please: the code shown here is stripped of all POD for better display on Perlmonks. This code is nonetheless Copyrighted:

Copyright (c)2003,2004 Soren Andersen, U.S.A. This program is Free software; you may redistribute it and/or modify it under the same terms as Perl itself (See the F<LICENSE> file in your Perl source kit). Part of that license stipulates that you, the user, agrees that this Free software is accepted AS-IS and comes with absolutely NO WARRANTY, not even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Links provided for reader convenience:

This script has an external Web space. Please check there for the very latest versions (although I will keep updating this node)

  • This script's documentation as POD: mk-ac-pkgopts.pod
  • This script as syntax-hilighted stand-alone code, no POD: mk-ac-pkgopts.html
  • This script's "production copy" with inline POD and cryptographically signed: mk-ac-pkgopts
  • links last checked: Thu Feb 5 23:50:52 UTC 2004

#!/usr/bin/env perl eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if 0; # not running under some shell ## # $Author: somian $ Last modified: 05 Feb 2004 18:15:52 # $Source: /usr/src/repositories/cvs/perl-proj/devtools/mk-ac-pkgopts, +v $ # $Date: 2004/02/05 22:22:55 $ ## use strict; use warnings; use vars ('$VERSION'); my $script_revision = q$Revision: 1.5 $ ; use Safe; use Tie::IxHash; use Data::Dumper; use Pod::Usage; $VERSION = 0.05 ; # above must be *all one line* for MakeMaker.pm use vars qw(@Config_Entries $Descfile $cACen $PkgName $Wet_already); my $mode; my $BeDugg = 0x0; # make true if want to debug # Simplified optionflag handling. my( $argC,$argN ) = ( 0x0+@ARGV , $ARGV[$#ARGV] ); if ( $argC and grep { $_ eq lc($argN) or $_ eq substr lc($argN)=>1 } qw/ -h -u -man -help -usage / ) { pod2usage(-verbose => 2) + } elsif ( $argN && $argN =~m'-version' ) { print "$VERSION\n" and exit 0 + } elsif ( not $argC==0 || $argC==2 and $argN && $argN ne '-demo' ) { pod2usage(1) + } sub just_add_water # implements our "demo-template" mode. { my @Demo; my $clo_Data = sub { my @array; @array = @Demo; }; if ( ref($Wet_already) && wantarray ) { return @{ [&$Wet_already] } } else { my $dfh = *DEMO::DATA::AConfHelper::DATA; local $/ = undef; @Demo = ( eval <$dfh> ); $Wet_already = $clo_Data; my %Demo = @Demo; return \%Demo; } } if (@ARGV) { unless ($ARGV[0] =~ m%^\w*[[:upper:]]\w+$%) { die "First arg must be a string containing PKGNAME!"; } else { $PkgName = shift; die "Not enough args!" if not @ARGV; $Descfile = shift; if (! -f $Descfile) { die "File not found!" } else { my $jail = Safe->new("ACoptCO"); $jail->permit_only( qw/ aassign anonhash anonlist const return enter gv leave nextstate pushmark rv2av srefgen / ); if ( $jail->rdo($Descfile) ) { print STDERR 'size of array: ' . @ACoptCO::Config_Entries if $BeDugg; @Config_Entries = @ACoptCO::Config_Entries; unless (not @Config_Entries & 1) { warn 'Input cannot be right, odd number of elements ' .'in @Config_Entries:' . scalar(@Config_Entries) ."!"; my $ddo = Data::Dumper->new([@Config_Entries]); print STDERR $ddo->Dump; die "terminating"; } $cACen = Tie::IxHash->new; for ( my $i=0; $i<=$#Config_Entries; $i++ ) { $cACen->Push(@Config_Entries[$i , 1+$i]); $i++; } } else { if ($@) { print STDERR "Compilation error in desc-file:\n", $@; } die "Description file did not open, parse or compile\n", "Please check path and data and try again."; } } } } else ### demo / skeleton-template gen mode. ### { $cACen = just_add_water; $PkgName = 'FAUX_DEMO_TEMPLATE'; # output an example of what an input file might look like, to stdou +t: $Data::Dumper::Purity = 1; print STDERR Data::Dumper->Dump([ $cACen ]=>[ 'Config_Entries' ]); # then send it through after tie'ing it to preserve ordering: $cACen = Tie::IxHash->new( @{ [just_add_water()] } ); } if ($BeDugg) { my $ddo = Data::Dumper->new([@Config_Entries]); print STDERR $ddo->Dump; } for my $ent ($cACen->Keys) { my $ent_v = $cACen->FETCH($ent); my $optstem = $ent; $optstem =~ s% - %_%gx ; my $optdecl = q[$]. $PkgName .q[_feat_]. $optstem; my $enORdis = $ent_v->{defstat} || 'enable'; my $opthelp = $ent_v->{helpSTR}; my $optverb = $ent_v->{msg_PTS}->[0] || 'create'; my $optsubj = $ent_v->{msg_PTS}->[1] || $ent; my $defaultstatus = $enORdis .'d'; my $optinvert = q[--]. ($enORdis eq 'enable' ? 'disable':'enable') +; my $acsubst_shlvar = q[$]. $PkgName .q[_]. $optstem; my $optfeatvar_internal = substr($optdecl,1); my $acsubst_macvar = $ent_v->{ac_SUBT}->[0]; my $acsubst_usrval = $ent_v->{ac_SUBT}->[1]; my $acsubst_defval = q[]; my $optdeclinit_L = $optfeatvar_internal .q[=]. ($enORdis eq 'enable' ? 'ENABLE_Y' : 'ENABLE_N' ); my $acsubstitute_assignment_L = sub { no strict 'refs'; return q[] if ! $acsubst_macvar; $mode = shift(); my $context_val = $mode ? $acsubst_usrval : $acsubst_defval ; return substr($acsubst_shlvar,1) .qq/="/. $context_val .qq/"\n/; } ; #" my $acsubstitute_L = $acsubst_macvar ? qq/\nAC_SUBST(/. $acsubst_macvar .qq/, [$acsubst_shlvar])\n/ : q[]; my $acdefine_L = $ent_v->{ac_DEFI}->[0] ? qq/\n AC_DEFINE(/. $ent_v->{ac_DEFI}->[0] .q/, / . $ent_v->{ac_DEFI}->[1] .q/, [/. $ent_v->{ac_DEFI}->[2] .qq/])\n/ : q[]; # Lets rock and roll! print <<"MACRO!!BLOCK!!"; # Starting With User-Build-Option "$ent": make an init declaration to +set default dnl $optdeclinit_L dnl AC_MSG_CHECKING([whether to $optverb $optsubj]) AC_ARG_ENABLE($ent, [AC_HELP_STRING([$optinvert-$ent], [$optverb $optsubj (default=$defaultstatus)])], [if test "unset\$enable_$optstem" != unset then enableval="\$enable_$optstem" case \$enableval in n|N|no|NO|disable ) \\ ${PkgName}_pkgopt_$optstem="disable caught (\$enablev +al) ENABLE_N" ;; * ) ${PkgName}_pkgopt_$optstem="enable caught (\$enableval) E +NABLE_Y" ;; esac else if test \${enableval+setwithnoval} == setwithnoval then ${PkgName}_pkgopt_$optstem="enable caught (\$enableval) E +NABLE_Y" fi fi], [if test "\${disable_${optstem}+defined}" == defined then ${PkgName}_pkgopt_$optstem="disable caught ENABLE_N" else ${PkgName}_pkgopt_$optstem="$optdecl" fi]) # Results post-processing for "$ent" case \$${PkgName}_pkgopt_$optstem in *ENABLE_Y ) AC_MSG_RESULT(will $optverb ${optsubj}.)$acdefine_L @{ [&$acsubstitute_assignment_L(0x1)] } ## OTHER ASSIGNMENTS, SHELL EXPRESSIONS GO HERE!! ## ;; *ENABLE_N ) AC_MSG_RESULT(will NOT $optverb ${optsubj}.) @{ [&$acsubstitute_assignment_L(0x0)] } ## OTHER ASSIGNMENTS, SHELL EXPRESSIONS GO HERE!! ## ;; * ) AC_MSG_ERROR([fatal -- bad value for internal configure variable \\\$${PkgName}_pkgopt +_$optstem "\$${PkgName}_pkgopt_$optstem" -- cannot continue. Pls notify p +ackage maintainer.], 1) ;; esac$acsubstitute_L # Done With User-Build-Option "$ent". # -------------------------------------------------------------------- +------- MACRO!!BLOCK!! } package DEMO::DATA::AConfHelper; __DATA__ @Config_Entries = ( 'DEMO-OPT-1' =>{ defstat => q[enable], ac_SUBT => [qw/DEMO_OPT1 demoopt.cpp/], ac_DEFI => [], msg_PTS => [qw/build demo-opt/], helpSTR => q[build a demo-opt component], }, 'DEMO-OPT-2' =>{ defstat => q[disable], ac_SUBT => [], ac_DEFI => [q/DEMO_OPT2/, 1, q/include DEMO_OPT2 code +/], msg_PTS => [qw/configure another-demo-opt/], helpSTR => q[build the another-demo-opt variation], }, );

NOTES

This tool produces a fragment of the complete configure.ac data file, not a complete file. The user is expected to RT-Fine/Funky/Fundamental-D[ocumentation] for Autoconf in order to learn what must come before and come after this piece or fragment of Autoconf/shell coding.

How To Run the Tool

When run in normal mode, mk-ac-pkgopts takes two arguments (no more and no less):

I
the identifier by which the package is to be known to autoconf. This is the same as is passed to autoconf at the top of the configure.ac file, in AC_INIT, as the first arg.

II
the filename of an input file to process, which contains a statement declaring a reference to an array of anonymous hashes (an AoH).

Demo Mode

mk-ac-pkgopts is self-documenting: if run without arguments it will output a demo of what a valid input file would look like, to the stderr filehandle. This may be redirected in the shell to a file on disk in order to serve as a ``fill in the blanks'' template for creation of a real working data file.

The Data File Format

Since the self-documenting feature exists, only a brief explanation of the data file will be given. It should be obvious to those familiar with Perl, but may not be, to others.

The data input into the program is a Perl data structure consisting of an array of alternating identifiers and refs to anonymous hashes. The identifiers should be names for options as the software author wants these options to appear in the final configure file, i.e. the use of the 2 identifiers ``foo-ability'' and ``bar-lib'' will give rise to these possible configure options (and negated permutation of):

    --enable-foo-ability --enable-bar-lib
     (or, negated forms:)
    --disable-foo-ability --disable-bar-lib

Please see autoconf(1) for background on how to provide a GNU standards-compliant configure script.

The identifiers become keys to hash elements, whose values are references to anonymous hashes. These hashes need to contain the following keys:

  Name    :   Perl Type   :    Description of purpose (default if not set)
 ------      -----------      ------------------------


PREREQUISITES

The Tie::IxHash module, not in the core Perl distribution, is required for running this program.


I don't know how many Perl monks do build-support authoring for packages that need it, but for those who do, I hope this tool is of interest.

    Soren A / somian / perlspinr / Intrepid

-- 
2003: The 3 least meaningful terms in Online jargon:
  troll   flame   rant
These used to mean something -- but then they were highjacked by
inferior intellects who, when faced with a more erudite opponent
employing superior arguments, abuse them as merely another form of
name-calling.

In reply to mk-ac-pkgopts by Intrepid

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.