METACONFIG(1)

๐Ÿ’– METACONFIG ๐Ÿ’–

metaconfig โ€” a Configure script generator. That's the whole vibe, bestie. โœจ


๐ŸŒธ SYNOPSIS ๐ŸŒธ

metaconfig [ -dhkmostvwGMV ] [ -L dir ]

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

Metaconfig is a program that generates Configure scripts. Okay so if you're sitting there like "what even IS a Configure script" โ€” no worries, just skip your lil self down to the TUTORIAL section of this manual page. And if you want the full formal rundown of how to use metaconfig and its units, the REFERENCE section is right there waiting for you. What follows is the quick-and-dirty intro + reference for people who already know what's up. ๐Ÿ’…

Metaconfig operates from a set of units which define everything that metaconfig knows about portability. Each unit is self-contained, and does not have to be registered anywhere other than by inclusion in either the public U directory or your private U directory. If the dist package (of which metaconfig is a part) is installed in LIB, then the public U directory is LIB/dist/mcon/U. On this machine, the LIB directory is /usr/share/dist. Your private U directory, if you have one, is in the top level directory of your package. Before you can run metaconfig you must do a several things:

  • Create a .package file in the package's top level directory by running packinit. This program will ask you about your package and remember what you tell it so that all the dist programs can be smart. ๐Ÿง โœจ
  • Consult the Glossary (in LIB/dist/mcon) and write your shell scripts and C programs in terms of the symbols that metaconfig knows how to define. You don't need to tell metaconfig which symbols you used, since metaconfig will figure that out for you. bestie it literally reads your mind (not clickbait). ๐Ÿ”ฎ
  • Generate any .SH scripts needed to write Makefiles or shell scripts that will depend on values defined by Configure. There is a program called makeSH that will help you convert a plain script into a script.SH template; some editing will still need to be performed on the resulting .SH file to move the variable configuration part in the top part of the script (see inline comments generated by makeSH within your .SH file). ๐Ÿ™ˆ
  • Create a MANIFEST.new file in your top level directory that lists all the files in your package. This file will remain private and will not be part of the final distribution. (As a convenience, the MANIFEST file will be used by metaconfig if there is no MANIFEST.new file yet.) The filename should be the first field on each line. After some whitespace you can add a short comment describing your file. Only source files should be listed in there. The special file patchlevel.h (which is handled and maintained by the patching tools โ€” see pat(1)) should be part of the MANIFEST.new file, but may be silently ignored by some tools. As a rule of thumb, only files maintained by RCS should be listed in there, the patchlevel.h file being one important exception. ๐Ÿ“
  • Optionally, you may wish to create a MANIFEST file, which will be an exported version of your MANIFEST.new. That file must be made part of the release, i.e. listed in both your MANIFEST.new and MANIFEST itself. One of the metaconfig units knows about this file and will force Configure to perform a release check, ensuring all the files listed there are part of the distribution. The MANIFEST and MANIFEST.new files should be distinct, not links. ๐ŸŽ€
  • Copy any .U files that you want to modify to your private U directory. Any .U files in your private U directory will be used in preference to the one in the public U directory. For example, one way to force inclusion of any unit is to copy the End.U file to your .U directory and add the name of the unit you want as a dependency on the end of the ?MAKE: line. Certain units can ONLY be forced in this way, namely those of the form Warn_*.U and Chk_*.U. You can also customize certain default Configure variables by copying Myinit.U to your package's private U directory and setting the variables in that unit. ๐Ÿ‘‘

Now you are ready to run metaconfig. That will create a Configure file, and optionally a config_h.SH file (if your sources make any use of C symbols). The generated files will automatically be added to your MANIFEST.new if necessary. Do not forget to update your MANIFEST file though. ๐Ÿ˜ญ


๐ŸŒˆ In order to create new units, do the following: ๐ŸŒˆ

  • Copy a similar unit to a new .U file. The name you choose should be the name of a variable generated by the unit, although this is only a convenience for you, not a requirement. It should be 12 or less characters to prevent filename chopping. Actually, it should probably be 10 or less so that those who want to use RCS can have a .U,v on the end without chopping. Metaconfig uses the case of the first letter to determine if any variable is actually produced by this unit, so don't Capitalize your unit name if it is supposed to produce a shell variable. โœ‚๏ธ
  • Edit the new .U file to do what you want. The first ?MAKE: line indicates the dependencies; before the final list colon all the variables this unit defines, and after the final colon all the variables (or other units) on which this unit depends. It is very important that these lists be accurate. If a dependency is optional and a default value can be used, you should prefix the dependency with a '+' sign. The corresponding unit will not be loaded to compute the symbol, unless really required by another unit. ๐Ÿ”ฅ
  • To the extent possible, parameterize your unit based on shell variable defined on ?INIT: lines. This will move the variable definitions up to the Init.U unit, where they can be overridden by definitions in Myinit.U, which is included after Init.U. ๐Ÿ’œ
  • Add the definition of any C symbols desired as ?H: lines. A line beginning with ?H:?%<: in the .U file will be added to the eventual config.h file if and only if metaconfig decides that this unit is needed. The %< stands for the unit's name, which happens to be the name of the file too (without .U) if you followed the convention. Always put a comment on each ?H: line in case one of the variable substitutions earlier on the line starts a comment without finishing it. Any shell variable starting with d_ may do this, so beware. If you ommit the ?%<:, then metaconfig will try to intuit the symbol whose definition is needed prior any inclusion in config.h. โš ๏ธ
  • Add glossary definitions as ?S: lines for shell variables and ?C: lines for C preprocessor variables. See a current unit for examples. It is VERY important to start each entry with a left justified symbol name, and end each entry with a ?C:. or ?S:. line. The algorithm that translates C preprocessor symbol entries for the Glossary into comments for config.h depends on this. ๐Ÿ“
  • Make sure the order of all your ? lines is right. The correct order is:
     ?RCS: and ?X:  basically just comments
     ?MAKE:         metaconfig dependencies
     ?Y:            unit layout directive
     ?S:            glossary shell definitions
     ?C:            glossary C definitions
     ?H:            config.h definitions
     ?M:            confmagic.h definitions
     ?W:            wanted symbols
     ?V:            visible symbols
     ?F:            files created by this unit
     ?T:            temporary shell symbols used
     ?D:            optional dependencies default value
     ?O:            used to mark obsolete units
     ?LINT:         metalint hints
     ?INIT:         shell symbols initializations

Here is an example to show the ordering of the lines and the various formats allowed:

?RCS: $RCS-Id$
?RCS: Copyright information
?RCS: $RCS-Log$
?X:
?X: A contrived example
?X:
?MAKE:d_one two: three +four Five
?MAKE:    -pick add $@ %<
?Y:DEFAULT
?S:d_one:
?S:  First shell symbol, conditionally defines ONE.
?S:.
?S:two:
?S:  Second shell symbol, value for TWO.
?S:.
?C:ONE:
?C:  First C symbol.
?C:.
?C:TWO:
?C:  Second C symbol.
?C:.
?H:#$d_one ONE /**/
?H:#define TWO "$two"
?H:#$d_one ONE_TWO "$two"
?H:.
?M:flip: HAS_FLIP
?M:#ifndef HAS_FLIP
?M:#define flip(x) flop(x)
?M:#endif
?M:.
?W:%<:one_two
?V:p_one p_two:p_three
?F:file ./ftest !tmp
?T:tmp var
?D:two='undef'
?LINT:change three
?INIT:two_init='2'
: shell code implementing the unit follows
p_one='one'
p_two='two'
p_three=""

Let me state it one more time: the above unit definition is a fake one to only show the different possibilities. Such a unit would serve little purpose anyway... Some more advanced features are not described here. Please refer to the REFERENCE section for more complete information. ๐Ÿ’€

  • Put the unit into the public or private U directory as appropriate. ๐Ÿซถ
  • Rerun metaconfig. ๐Ÿ”
  • Send your unit to Raphael.Manfredi@pobox.com (Raphael Manfredi) for inclusion in the master copy, if you think it's of general interest. ๐Ÿ“ฌ

๐ŸŒŸ In order to add a new program to be located: ๐ŸŒŸ

  • Edit Loc.U, and add the name of the program both to the ?MAKE: line (between the two colons) and to either loclist or trylist (depending on whether the program is mandatory or not). ๐Ÿ”
  • Rerun metaconfig. ๐Ÿ”
  • Send your unit to me for inclusion in the master copy, if you think it's of general interest. ๐Ÿ’Œ

๐ŸŽ€ Notes for writing .U files: ๐ŸŽ€

  • Always use "rm -f" because there are systems where rm is interactive by default. ๐Ÿ˜ค
  • Do not use "set -- ..." because '--' does not work with every shell. Use "set x ...; shift". ๐Ÿ™…
  • Do not use "unset ENV" since unset is not fully portable. Say "ENV=''" instead. โœจ
  • Always use echo " " (with a space) because of Eunice systems. ๐Ÿซง
  • Only use test with -r, -w, -f or -d since those are the only portable switches. In particular, avoid "test -x". ๐Ÿšซ
  • Use only programs that came with V7, so that you know everyone has them. ๐Ÿ‘‘
  • Use $contains when you want to grep conditionally, since not all greps return a reasonable status. Be sure to redirect the output to /dev/null, by using '>/dev/null 2>&1'. ๐Ÿ’–
  • Use if test rather than if [...] โ€” because not every sh knows that second construct, bestie. Not all shells are built the same ๐Ÿ’…
  • Use the myread script for inputs so they can do shell escapes and default evaluation. The general form is literally this:
case "$grimble" in
'') dflt=452;;
*) dflt="$grimble";;
esac
rp='How many grimbles do you have?'
. ./myread
grimble="$ans"
  • Use the getfile script when you're asking for a file pathname, so you get optional ~name expansion and sanity checks. Peep the Getfile.U unit for a full decription. (Yes, that's the original spelling, we keep it, no cap.)
  • Always put a
$startsh

at the top of every generated script that's gonna be launched or sourced by Configure. Non-negotiable, it's giving prerequisites โœจ

  • Never assume common UNIX-isms like "an object file ends with .o" or "a library name ends with .a". Use the $_o and $_a variables instead (see Unix.U). Assumptions are not the vibe ๐Ÿšซ
  • When doing a compile-link-execute test, always write it like this:
$cc $ccflags $ldflags try.c -o try $libs

because some systems require that linking flags be specified before the compiled target (the only exception being trailing linking libraries).

  • Issue important messages on file descriptor #4, by using >&4 to redirect output. Only those messages will show up when the -s switch is given to Configure on the command line (silent mode). That's the VIP channel, periodt ๐Ÿ’–
  • Always try to determine whether a feature is present in the most specific way โ€” don't say "if bsd" when you can just grep libc. There are so many hybrid systems out there, and each feature should stand or fall by itself. Main character energy only.
  • Always try to determine whether a feature is present in the most general way, so that other packages can use your unit too. Sharing is caring ๐Ÿซถ
  • When in doubt, set a default and ask. Don't assume anything. Ever.
  • If you think the user is wrong, allow for the fact that he may be right. For instance, he could be running Configure on a different system than the one he's actually going to use the final product on. Plot twist! ๐Ÿ˜ญ

Metaconfig reserves the following names in your directory, and if you use such a name it may get clobbered or have other unforeseen effects ๐Ÿ‘€:

.MT/*
Configure
Wanted
Obsolete
configure
config_h.SH
confmagic.h
U/*
MANIFEST.new

Additionally, Configure may clobber these names in the directory it is run in:

UU/*
config.sh
config.h
Bestie. Do not name your own file any of these. I am begging you ๐Ÿ™ˆ

โœจ OPTIONS โœจ

The following options are recognized by metaconfig:

  • -d โ€” Turn on debug mode. Not really useful unless you are debugging metaconfig itself.
  • -h โ€” Print help message and exit. The classic "what do I do" button ๐ŸŒธ
  • -k โ€” Keep temporary directory, so that you may examine the working files used by metaconfig to build your Configure script. Useful only when debugging the units.
  • -m โ€” Assume lots of memory and swap space. This will speed up symbol lookup in source files by a significant amount of time, at the expense of memory consumption... choices were made ๐Ÿ’€
  • -o โ€” Map obsolete symbols on new ones. Use this switch if you still have some obsolete symbols in your source code and do not want (or cannot) remove them for now. The obsolete symbols are otherwise ignored, although that will give you a warning from metaconfig.
  • -s โ€” Turn silent mode on. ๐Ÿคซ
  • -t โ€” Trace symbols as they are found.
  • -v โ€” Turn verbose mode on. Spill the tea โ˜•
  • -w โ€” Assume Wanted file is up-to-date. This will skip the time and memory consuming phase of source code scanning, looking for known symbols. Use it only when you know your source file have not changed with respect to the pool of metaconfig symbols used.
  • -G โ€” Also provide a GNU configure-like front end to the generated Configure script, to be included in the distribution as well. This is only a wrapper around the Configure script naturally, but it lets people familiar with the GNU tool to not be lost when facing a new distribution. Friendship ended with confusion ๐Ÿซถ
  • -L dir โ€” Override default library location. Normally only useful for metaconfig maintainers to locally use the units being developed instead of the publicly available ones. The dir specified is the one containing the units U directory.
  • -M โ€” Allow production of a confmagic.h file to automagically remap some well-known symbols to some other alternative, like bcopy() being remapped transparently to memcpy() when not available. This option is turned on automatically when a confmagic.h file exists in the top-level directory. Simply remove that file if you wish to disable this option permanently. Magic โœจ but make it opt-out.
  • -V โ€” Print version number and exit.
  • -X file โ€” When examining the source files, ignore any symbols listed in the file. This is useful in situations where a particular unit is known not to be needed for your package's portability targets, but your source files nevertheless contain occurrences of words that look to metaconfig like symbols defined in that unit. (For example, you might need the word "index" in a source file, but not need the unit that determines whether strcpy or index should be used.) The file can contain blank lines, comment lines introduced with '#', and lines containing a single symbol. If this option is not supplied, any variable in .package is honored instead.

๐ŸŽ€ TUTORIAL ๐ŸŽ€

This (long) section is an introduction to metaconfig, in which we will learn all the basics. If you already know how to use metaconfig, you may safely skip to the next section. No hard feelings ๐Ÿ’œ

๐ŸŒˆ Overview

Usually when you want to get some source package to compile on a given platform you have to edit the main Makefile (assuming there is one!), choose a C compiler, make sure you have the proper libraries, and then fire the make command. If the package is reasonably well written, it will compile (without a warning being an option :-). In itself, the last sentence is a real performance, since given the variety of UNIX platforms available today and the diversity of flavours, that means the author of the package has gone into deep trouble to figure out the right choices given some standard trial, guessing and messing around with system includes and types. Iconic behaviour honestly ๐Ÿ‘‘

However, despite all his talent, the author cannot possibly know that some system has a broken system call, or that some system structure lacks one otherwise standard field, or simply whether a given include file exists or not. And I'm not considering the implicit assumptions, like the type returned by the malloc() function or the presence of the rename() system call to name a few. But that knowledge is necessary to achieve real portability. It's giving "how do you not know this" ๐Ÿ˜ญ

Now let's not abuse ourselves. Using that information requires greater skills, yet it can lead to more portable programs since it is then written in a system-independant fashion and relies only on the fact that some assumption is true or false on a particular system, each assumption being unrelated with each other. That is to say, we do not say: We're on a BSD system or we are on a USG system. That's too fuzzy anyway nowadays. No, we want to say to the source code: this system does not have the rename() system call and malloc() returns a (void *) value. Speak facts, not vibes ๐Ÿ”ฅ

Metaconfig is a tool that will let you do just that, with the additional benefit of not having to hand-edit the Makefile if all goes well. By running metaconfig, you create a shell script named Configure. Lots of efforts have been devoted to the Configure script internals to ensure it will run on 99% of the existing shells available as of this writing. Configure will probe the target system, asking questions when in doubt and gather all the answers in one single shell file, which in turn can be used to automatically generate configured Makefiles and C include files.

There is only a limited (but quite large) set of symbols available for your shell scripts and C programs. They are all documented in the Glossary file. All you need to do is learn about them and start using them to address portability and configuration problems. Then, by running metaconfig, a suitable Configure script will be generated for your package. Study up, bestie ๐Ÿ“š

The Configure script is built out several units (more than 300), each unit being responsible for defining a small number of shell and/or C symbols. Units are assembled together at the final stage, honoring the dependency graph (one unit may need the result of several other units which are then placed before in the script). It's a whole friend group dynamic ๐Ÿ’–

๐Ÿ”ฎ Symbols

Symbols are the most important thing in the metaconfig world. They are the smallest recognized entity, usually a word, and can be granted a value at the end of the Configure execution. For instance, the C pre-processor symbol HAS_RENAME is a metaconfig symbol that is guranteed to be defined if, and only if, the rename() system call is present. Likewise, the $ranlib shell variable will be set to either ':' or 'ranlib' depending on whether the call to the ranlib program is needed to order a library file. How this works is not important for now, what is important is to understand that those symbols are given a life (i.e. a value) upon Configure execution. They're literally the main characters of this story โญ

Using symbols is relatively straightforward. In a C source file, you simply use the symbol value, as a pre-processor directive (for instance an: #ifdef HAS_RENAME) or, if the symbol value is a string, directly as you would use a macro in C. And in a shell file or a Makefile, you may reference a shell symbol directly.

Actually, I'm lying, because that's not completely as magic as the previous paragraph could sound. In a C file, you need to include the Configure-produced config.h file, and you must wrap your shell script or Makefile in a .SH file and you may reference the shell symbol only in the variable substitution part of that .SH file. More on this later. Not me overselling it and then immediately correcting myself ๐Ÿ™ˆ

๐ŸŒธ Source Files

Symbols may only appear in a limited set of source files, because metaconfig will only scan those when looking for known symbols, trying to figure out which units it will need. You may use C symbols in C source files, i.e. files with a .c, .h, .y or .l extension, and shell symbols are looked for only in .SH files. That's the guest list, and if you're not on it you're not getting scanned ๐Ÿ’…

In order for a C file to actually get the value of a symbol, it needs to include the special config.h file โ€” and that file is produced by Configure whenever C symbols are in play. Meanwhile .SH files get run through a shell, which spits out a brand new file. But plot twist: at the top of that .SH file, the special config.sh file (also made by running Configure) gets sourced, and that's where variable substitutions come in. And actually, config.h itself is produced by running the metaconfig-produced config_h.SH file โ€” again, variable substitution doing its thing. So bestie, we're gonna zoom in on this, because this is literally the heart of the entire configuration scheme...

๐ŸŽ€ Variable Substitution ๐ŸŽ€

So there's this shell construct called a here document, and what it does is let a command take its input from inside the script itself. The shell interprets that input as either a double-quoted string or a single-quoted string, depending on how you write the here document spec. iykyk

To specify a here document you use the << token, followed by a single identifier. After that, all the remaining script lines become the input for the command, right up until the here document identifier shows up on a line all by itself. Shell substitution (shell variable substitutions included) happens โ€” unless the identifier is wrapped in single quotes. For example:

var='first'
tar='second'
echo "--> first here document:"
cat <<EOM
var='$var'
tar='$tar'
EOM
echo "--> second here document:"
cat <<'EOM'
echo $var
echo $tar
EOM
echo "--> end."

and when you run that through a shell, you get:

--> first here document:
var='first'
tar='second'
--> second here document:
echo $var
echo $tar
--> end.

See the difference? The first here document gets its contents interpreted, while the second one just gets output exactly as-is. Both of these are so useful in a .SH script, and we're about to see why.

๐ŸŒธ Using .SH Scripts ๐ŸŒธ

A .SH script is usually produced by running the MakeSH script over an existing file, which transforms that file into a file.SH. Let's take one lil example. Here's a tiny script (let's call it intsize) that prints a single message: the size of the int datatype in C. Tragically, it has the value hardwired in, like this:

#!/bin/sh
intsize='4'
echo "On this machine, the int type is $intsize bytes"

Now let's run makeSH on it by typing makeSH intsize. We get a single intsize.SH file that looks like this:

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
: This forces SH files to create target in same directory as SH file.
: This is so that make depend always knows where to find SH derivatives.
case "$0" in
*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
esac
echo "Extracting intsize (with variable substitutions)"
: This section of the file will have variable substitutions done on it.
: Move anything that needs config subs from !NO!SUBS! section to !GROK!THIS!.
: Protect any dollar signs and backticks that you do not want interpreted
: by putting a backslash in front.  You may delete these comments.
$spitshell >intsize <<!GROK!THIS!
$startsh
!GROK!THIS!

: In the following dollars and backticks do not need the extra backslash.
$spitshell >>intsize <<'!NO!SUBS!'
intsize='4'
echo "On this machine, the int type is $intsize bytes"
!NO!SUBS!
chmod 755 intsize
$eunicefix intsize

Okay so the first part of this script (inside that case statement) is trying to find the config.sh file so it can source it. The $CONFIG variable is false by default, and becomes true once config.sh has already been sourced โ€” which would be the case if this file got executed from inside Configure itself, but let's not confuse ourselves here. one thing at a time bestie

Once config.sh has been sourced, all the shell symbols defined by Configure are set. Then we hit a second case statement, which changes the current directory if a path was used to reach this program โ€” like if we said sh ../scripts/intsize.SH, we'd first run cd ../scripts before continuing. If that doesn't click for you, honestly don't stress about it. ๐Ÿ™ˆ

Now here comes the interesting stuff. This script uses the $spitshell variable, and that's not something we know about... yet. If you dig through the Glossary file, you'll see it's a variable that metaconfig knows about. If you make this file part of your distribution (by including it in the MANIFEST.new file โ€” we'll circle back to that later) and run metaconfig, then the Configure script will figure out a suitable value for this variable and set it in config.sh. Same deal for $startsh and that mysterious $eunicefix at the end. On a reasonable system, the relevant part of config.sh would look like this:

spitshell='cat'
startsh='#!/bin/sh'
eunicefix=':'

Ahh! We're getting there. Now it looks familiar. We're looking at a single cat command whose input comes from a variable-interpolated here document and whose output is redirected to intsize. The value will be whatever $startsh is, i.e. #!/bin/sh. Fine so far. โœจ

Then we reach the second here document expansion, which grabs the rest of the script. This time the here document symbol is wrapped in single quotes, so the contents get appended verbatim to the intsize file. So by running sh intsize.SH, we get this output:

Extracting intsize (with variable substitutions)

and if we peek at the produced intsize file, we see:

#!/bin/sh
intsize='4'
echo "On this machine, the int type is $intsize bytes"

which is exactly what we had at the beginning. So far it's a no-operation procedure... But โ€” how marvelous! โ€” it just so happens (pure coincidence, trust me! ๐Ÿ˜‡) that metaconfig knows about the $intsize shell symbol. So if we move the initialization of intsize into the variable-interpolated area of the .SH script, initialize it with the Configure-computed value, and remove the now-useless comments that makeSH added, we get:

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 intsize (with variable substitutions)"
$spitshell >intsize <<!GROK!THIS!
$startsh
intsize='$intsize'
!GROK!THIS!

$spitshell >>intsize <<'!NO!SUBS!'
echo "On this machine, the int type is $intsize bytes"
!NO!SUBS!
chmod 755 intsize
$eunicefix intsize

Of course, running this script through a shell will again output the same script. But if we run Configure on a machine where an int is stored as a 64-bit quantity, config.sh will set intsize to 8, and the intsize script will carry the right value and print:

On this machine, the int type is 8 bytes

which is correct. Congratulations! We have just configured a shell script!! ๐ŸŽ‰

๐Ÿ’œ Producing config.h ๐Ÿ’œ

Now we can look at how config.h gets produced out of config_h.SH. We know that running Configure produces a config.sh script (how exactly that happens isn't strictly relevant here, but for the curious: it's another here document substitution inside Configure itself). The config_h.SH file itself is built by metaconfig at the same time Configure is built โ€” provided you make use of at least one C symbol within your sources.

Let's look at some random config_h.SH file to see what's really going on:

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!' -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.
 */

/* Configuration time: $cf_time
 * Configured by: $cf_by
 * Target system: $myuname
 */

#ifndef _config_h_
#define _config_h_

/* bcopy:
 *    This symbol is maped to memcpy if the bcopy() routine is not
 *    available to copy strings.
 */
/* HAS_BCOPY:
 *    This symbol is defined if the bcopy() routine is available to
 *    copy blocks of memory. You should not use this symbol under
 *    normal circumstances and use bcopy() directly instead, which
 *    will get mapped to memcpy() if bcopy is not available.
 */
#$d_bcopy HAS_BCOPY /**/
#ifndef HAS_BCOPY
#ifdef bcopy
#un-def bcopy
#endif
#define bcopy(s,d,l) memcpy((d),(s),(l))          /* mapped to memcpy */
#endif

/* HAS_DUP2:
 *    This symbol, if defined, indicates that the dup2 routine is
 *    available to duplicate file descriptors.
 */
#$d_dup2 HAS_DUP2   /**/

/* I_STRING:
 *    This symbol, if defined, indicates to the C program that it should
 *    include <string.h> (USG systems) instead of <strings.h> (BSD systems).
 */
#$i_string I_STRING      /**/

#endif
!GROK!THIS!

๐ŸŒธ Running Metaconfig ๐ŸŒธ

okay bestie, let's stare at the actual metaconfig program for a sec so you get how it uses its units + your source code to cook up all the config files. And fr fr โ€” if you're planning to write your own units, you need to understand the whole scheme. no shortcuts, sorry ๐Ÿ’…

if there's no MANIFEST.new file, metaconfig will just try to use the MANIFEST file instead, for convenience. So everywhere we say MANIFEST.new, you can read it as MANIFEST โ€” provided there's no MANIFEST.new chillin' at the root of your package.

assuming your MANIFEST.new file is properly set up and lists all the source files you wanna configure, and you've run packint in your root source directory to create a .package file, you can run metaconfig and you'll get this whole moment:

$ metaconfig
Locating units...
Extracting dependency lists from 312 units...
Extracting filenames (*.[chyl] and *.SH) from MANIFEST.new...
Building a Wanted file...
  Scanning .[chyl] files for symbols...
  Scanning .SH files for symbols...
Computing optimal dependency graph...
  Building private make file...
  Determining loadable units...
  Updating make file...
Determining the correct order for the units...
Creating Configure...
Done.

slay. Let's break down what just happened ๐Ÿ’–

The first phase looks for all the unit files (the ones ending in .U) in the public directory first, then in your private one. So if you copy a public file into your private U directory (aka a directory literally named U at the top level of your package), it overrides the public version. iconic behavior tbh. Once it's got a list of all the available units, it parses them and pulls out all the ?MAKE: lines to learn about the dependencies and the known shell symbols. It also eyes the ?H: lines to learn about the C symbols and which shell symbols need to be computed to get a proper value for that C symbol โ€” so yeah, there's a whole second level of dependencies happening here. it's giving layers ๐Ÿง…

Next, the proper filenames get extracted from the MANIFEST.new files and a Wanted file is built: that file lists all the C symbols and the shell symbols needed for your package. We scan the C-type files for C symbols first, then propagate the dependencies to their associated shell symbols (gathered from those ?H: lines). Then the .SH files get scanned and finally all the shell symbols are known. ๐Ÿ’…

A temporary Makefile gets built, and metaconfig tries to make all the shell symbols to see what commands (listed on the second ?MAKE: lines) actually get executed โ€” and therefore which units are really needed. Optional units that aren't otherwise required get yeeted, and a second Makefile is generated. This time we know about all the units and their respective orders, the optional units have been removed, and default values have been computed for their shell symbols. Then the Configure script can be generated, along with config_h.SH. We're done! ๐ŸŽ€


โœจ Conventions โœจ

Proper conventions need to be followed to make the whole process sound. There's a case convention for units and a variable naming convention.

  • All units should have their first letter lower-cased โ€” unless they're special units.
  • By "special", we mean they don't really define new shell variables that you the user can use in your .SH files. Instead, they're units producing scripts or shell variables that get used internally by the Configure script. Classic examples: the Init.U file (which is the main variable initialization) and Myread.U (which produces the myread script used almost everywhere in Configure when a question has to be asked to the user). ๐Ÿ’…

Non-special units then split into two distinct groups:

  1. Units defining variables associated to a C symbol.
  2. Units defining shell variables of their own.

Group 1 gets further divided into:

  • variables related to include files (their name begins with i_)
  • variables related to other definitions (name starts with d_)

Group 2 has names that stand for themselves โ€” for instance, cc.U defines the $cc shell variable whose value is the C compiler to be used. cute, self-explanatory, we love that ๐ŸŒธ

Special units sometimes reserve themselves some pre-defined variable and return "results" in other well-known variables. For instance, the myread script produced by Myread.U expects the prompt in $rp, the default answer in $dflt, and places the user answer in $ans. This is not documented in this manual page: you gotta go look at the unit itself to understand which variables are used and how the unit is to be used. iykyk ๐Ÿ”ฅ


๐Ÿฆ‹ Using The Glossary ๐Ÿฆ‹

The Glossary file is automatically produced by the makegloss script, which extracts the info from ?S:, ?C: and ?MAKE: lines and reformats them into an alphabetically sorted glossary. It's important to read the Glossary to know about the symbols you're allowed to use. However โ€” the Glossary will not tell you how to use them. Usually, that's up to you, bestie. ๐Ÿ™ˆ

One day, you will probably write your own units, and you will know enough about metaconfig to do so quickly and efficiently. But never forget to properly document your work in the ?S: and ?C: lines, or other people won't be able to reuse it. Remember the time when you had only the Glossary and this manual page to get started. ๐Ÿซถ


๐ŸŒˆ Conclusion ๐ŸŒˆ

Now that you know the metaconfig basics, you should read the DESCRIPTION section, then skip to the REFERENCE section to learn about all the gory details โ€” like the allowed syntax for unit control lines (lines starting with a ?) or the distinct MAKE commands you're allowed to use. let's goooo ๐Ÿ’…


๐Ÿ‘‘ REFERENCE ๐Ÿ‘‘

This section documents the internals of metaconfig โ€” basically the unit syntax, the special units you should know about, and the hint files. buckle up ๐ŸŽ€

๐Ÿ’– General Unit Syntax ๐Ÿ’–

A metaconfig unit is divided into two distinct parts:

  • The header section (lines starting with ?)
  • A shell section (code to be included in the Configure script)

It's possible to add ?X: comments anywhere within the unit, but the other ? lines (also called control lines) have a strict ordering policy. no chaos allowed, periodt ๐Ÿ’…

If a control line is too long, you can use a continuation by escaping the final new-line with a backslash and continuing on the next line โ€” which should then be indented by spaces or tabs. โœจ

The following is a formal description of each of the control lines. Unless stated otherwise, the order of this presentation is the order to be used within the unit.

  • ?RCS: free text โ€” to be used for RCS comments, at the top of the unit.
  • ?X: any text โ€” general purpose comments. May appear anywhere in the unit but must be left justified. For RCS comments, please use the ?RCS: comment form instead.
  • ?MAKE:symbol list: dependency list [+optional] โ€” this is the first dependency line. The first symbol list should list all the symbols built by this unit (i.e. whose value is computed by the shell section of the unit). Symbols should be space separated. If a defined symbol is for internal use only and should not appear in the generated config.sh file, then it should be preceded by a + (not to be confused with the optional dependencies defined hereafter ๐Ÿ‘€). The second part of the list (after the middle :) is the unit dependency. It should list all the needed special units, as well as all the symbols used by the shell implementation. If a symbol is needed but its configuration value is not critical, it can be preceded by a +, in which case it's called a conditional dependency: its corresponding unit will be loaded if, and only if, that symbol is otherwise really wanted; otherwise the default value will be used.
  • ?MAKE: tab command โ€” there can be one or more command lines following the initial dependency lines. Those commands will be executed when the unit is wanted, to load them into Configure. See the paragraph about make commands for more info. Note that the leading tab character is required before the command โ€” don't skip it bestie, it's not decorative ๐Ÿ˜ญ
  • ?Y:layout โ€” drops a layout directive for this unit. The layout has to be one of the strings top, default or bottom (case does not matter, but the recommended style is to spell them out UPPERCASED, slay). Leave it out and default is what you get.

You only need this directive if you're trying to force a unit to the top or the bottom of the generated Configure script, as far as unit dependencies allow it. That way the important questions can get pushed right to the front. Inside the same layout class, units are sorted alphabetically, with two special cases for d_* and i_* units, which get forced to the top and the bottom of their classes respectively (though honestly these should belong to the default class).

And bestie, listen: if you force a unit to the top when its dependencies require every other unit to come before it, you have achieved literally nothing interesting. So use this directive for what it's actually good at โ€” bumping the priority of interactive units that don't depend on a ton of other user-visible symbols. Path-related questions, that kind of thing. ๐Ÿ’…

  • ?S:symbol_name [(obsolete symbol list)]: โ€” introduces a shell symbol. This first line names the symbol, optionally followed by a list wrapped in parentheses giving the obsolete equivalent. Those obsolete symbols get remapped to the new symbol_name if the -o option is handed to metaconfig. ๐ŸŽ€
  • ?S:any text, for Glossary โ€” basically a comment describing the shell symbol, which makegloss will extract into the Glossary file.
  • ?S:. โ€” closes the shell symbol comment.
  • ?C:symbol_name [~ alias] [(obsolete symbol list)]: โ€” introduces a new C symbol. The alias name is the name under which the C symbol will be controlled, i.e. if the alias symbol is wanted, then that C symbol gets written into the config_h.SH file. Usually the alias is just %< (which stands for the unit's name) and there's also a ?W: line mapping a C symbol to the alias. Also, the relevant parts of the ?H: lines are explicitly protected by a ?%< condition. Peep the symbol aliasing paragraph for more deets. The rest of the line is the optional obsolete symbol list, listing old equivalents for the new symbol_name.
  • ?C:any text, for Glossary and config_h.SH โ€” basically a comment describing the C symbol, extracted by makegloss into the Glossary file and by metaconfig into the config_h.SH file if the symbol is wanted (or if its alias is wanted when symbol aliasing is in play).
  • ?C:. โ€” closes the C symbol comment.
  • ?H:?symbol:config_h.SH stuff โ€” this is the general inclusion request into config_h.SH. The line only gets written when the guarding symbol is really wanted. This general form is needed when C symbol aliasing was used. Otherwise, if you use one of the other "standard" forms, metaconfig handles the guarding all by itself, no cap. โœจ
  • ?H:#$d_var VAR "$var" โ€” conditionally defines the VAR C symbol into $var when it's set to 'define'. Implies a '?VAR:' guarding condition, and metaconfig automatically links VAR to its two shell variable dependencies (i.e. both $d_var and $var get flagged as wanted if VAR is used in C sources).
  • ?H:#define VAR [optional text] โ€” always defines the VAR C symbol to some value. Implies a '?VAR:' guarding condition. An automatic shell dependency gets made to the unit itself.
  • ?H:#define VAR(x,y,z) $var โ€” always defines the macro VAR to be the value of the $var variable. It's up to the unit to make sure $var holds a sensible value. An automatic dependency between the C macro VAR and the shell variable is established, and the whole line is guarded by an implicit '?VAR:'.
  • ?H:#$d_var VAR โ€” conditionally defines VAR if $d_var is set to 'define'. Implies a '?VAR:' guarding condition. An automatic shell dependency is generated towards $d_war.
  • ?H:#define VAR "$var" โ€” assigns a configured value to the VAR C symbol. Implies a '?VAR:' guarding condition. An automatic shell dependency is generated to link VAR and $var.
  • ?H:. โ€” closes the config_h.SH inclusion requests.
  • ?M:C symbol: C dependencies โ€” introduces a magic definition concerning the C symbol, for confmagic.h, and defines the guarding symbol for the remaining ?M: definitions. This line silently implies '?W:%<:C symbol', i.e. the unit gets loaded into Configure if the C symbol shows up within the C sources, whether magic is used or not. The C dependencies are activated when magic is used, in order to force their definition in config_h.SH. But heads up: if magic is not used and the C symbol appears in the source without the needed C dependencies, you will be warned every single time the Wanted file is built โ€” because that may be a portability issue (and also because the unit gets unconditionally loaded into Configure whenever the C symbol is used, regardless of the other ?C: lines from the unit). ๐Ÿ˜ญ
  • ?M:cpp defs โ€” defines the magic cpp mapping to be introduced in confmagic.h whenever the concerned symbol is used. There's an implicit '?sym' guarding, where sym is the symbol name defined by the leading ?M: line.
  • ?M:. โ€” closes the confmagic.h inclusion request.
  • ?W:shell symbol list:C symbol list โ€” ties up the destiny of the shell symbols with that of the C symbols: if any of the C symbols listed is wanted, then all the shell symbols get marked as wanted. Super useful for forcing inclusion of a unit (with the shell symbol list set to %<) when the presence of some C symbol is detected. The shell symbol list may be left empty, so you can benefit from the side effect of C symbol location within the builtin pre-processor (the symbol being defined for that pre-processor if located in the source). To look for patterns with a space in them, you need to quote the C symbols within simple quotes, as in 'struct timezone'.
  • ?V:read-only symbols:read-write symbols โ€” this is a metalint hint and should only be used in special units exporting some shell variables. The variables before the middle : are exported read-only (changing them issues a warning), while the other symbols may be freely read and changed.
  • ?F:files created โ€” this line serves two purposes: it's a metalint hint, and also a placeholder for future jmake use. It must list three kinds of files: the temporary ones created for a test, the private UU ones created in the UU directory for later perusal, and the public ones left in the root directory of the package. Temporary files must be listed with a preceding ! character (meaning "no! they're not re-used later!"), private UU files should be preceded by a ./ (meaning: to use them, say ./file, not just file), and public ones should be named as-is.
  • ?T:shell temporaries โ€” another metalint hint. This line lists all the shell variables used as temporaries within the shell section of this unit.
  • ?D:symbol='value' โ€” initialization value for symbols used as conditional dependencies. If no ?D: line is found, then a null value is used instead. The metalint program will warn you if a symbol is used at least once as a conditional dependency and doesn't have a proper ?D: initialization. It's good practice to add those lines even for a null initialization, since it emphasizes the possibly optional nature of a symbol. ๐Ÿซถ
  • ?O:any message you want โ€” this directive says that this unit is obsolete as a whole. Whenever any of its symbols gets used (or indirectly used via dependencies), the message is output on the screen (on stderr). You can put one or more lines, in which case each line gets printed, in order.
  • ?LINT:metalint hints โ€” peep the metalint manual page for an explanation of the distinct hints that can be used.
  • ?INIT:initialization code โ€” the initialization code specified by this line gets loaded at the top of the Configure script, provided the unit is needed. ๐Ÿ”ฅ

๐Ÿฆ‹ C Symbol Aliasing ๐Ÿฆ‹

Sometimes you just can't rely on metaconfig's own default selection for config_h.SH comments and C symbol definition. That's where aliasing comes into play, bestie. Since it's rather tricky to explain, we're gonna study an example to understand the underlying mechanism. Sit with me. ๐Ÿ’–

The d_const.U unit tries to determine whether or not your C compiler knows about the const keyword. If it doesn't, we want to remap that keyword to a null string, so the program can still compile. On top of that, we want to automatically trigger the test when the const word is used.

Here are the relevant parts of the d_const.U unit:

?MAKE:d_const: cat cc ccflags Setvar
?MAKE:    -pick add $@ %<
?S:d_const:
?S:  This variable conditionally defines the HASCONST symbol, which
?S:  indicates to the C program that this C compiler knows about the
?S:  const type.
?S:.
?C:HASCONST ~ %<:
?C:  This symbol, if defined, indicates that this C compiler knows about
?C:  the const type. There is no need to actually test for that symbol
?C:  within your programs. The mere use of the "const" keyword will
?C:  trigger the necessary tests.
?C:.
?H:?%<:#$d_const HASCONST     /**/
?H:?%<:#ifndef HASCONST
?H:?%<:#define const
?H:?%<:#endif
?H:?%<:#endif
?H:.
?W:%<:const
?LINT:set d_const
?LINT:known const
: check for const keyword
echo " "
echo 'Checking to see if your C compiler knows about "const"...' >&4
/bin/cat >const.c <<'EOCP'
main()
{
     const char *foo;
}
EOCP
if $cc -c $ccflags const.c >/dev/null 2>&1 ; then
     val="$define"
     echo "Yup, it does."
else
     val="$undef"
     echo "Nope, it doesn't."
fi
set d_const
eval $setvar

First thing we notice is the use of a ?W: line, which basically says: "This unit is wanted when the const keyword is used in a C file." In order to conditionally remap const to a null string in config.h, I chose to conditionally define HASCONST via $d_const.

But bestie, this raises a problem, because the HASCONST symbol is not going to be used in the sources โ€” only the const token is. And the ?H: line defining HASCONST is implicitly guarded by '?HASCONST'. So therefore, we must add the explicit '?%<' constraint to tell metaconfig that those lines should be included in config_h.SH whenever the '%<' symbol gets wanted (%< refers to the unit's name, here d_const). ๐ŸŒˆ

That's like, almost perfect, bestie, because the ?W: line is gonna want d_const whenever const gets used, and then the ?H: lines get pulled into the config_h.SH file. BUT โ€” plot twist โ€” the leading comment (the ?C: lines) attached to HASCONST is itself guarded via HASCONST, so it's got an implicit ?HASCONST constraint riding along. Which is exactly why we need to alias the HASCONST symbol to '%<'. iykyk ๐Ÿ’…

The rest of the unit (the shell part) is genuinely no big deal. It just tries to compile a lil sample C program using the const keyword. If it can? Slay. It defines $d_const via the $setvar fonction (defined by the Setvar.U unit). Peep the paragraph about special units for the deets. โœจ


๐Ÿ› ๏ธ Make Commands ๐Ÿ› ๏ธ

On the ?MAKE: command line, you can write a shell command that gets executed as-is, OR a special -pick command which metaconfig traps and parses to figure out what should happen. The leading - is literally just there to stop make from failing when the command returns a non-zero status โ€” it's not really needed since we use make -n to resolve dependencies, but I'm telling you to keep it in case it becomes mandatory in future versions. Better safe than sorry, fr fr.

The syntax of the pick command is:

-pick cmd $@ target_file

where $@ is the standard macro within Makefiles standing for the current target (the name of the unit being built, with the final .U extension stripped). The cmd part is the actual metaconfig command to be run, and the target_file is yet another parameter, whose interpretation depends on the cmd itself. It also has its final .U extension stripped, and it normally refers to a unit file โ€” UNLESS it starts with ./, in which case it references one of the metaconfig control files in the .MT directory. ๐Ÿ‘‘

The available commands are:

  • add โ€” Adds the target_file to Configure.
  • add.Config_sh โ€” Fills in that part of Configure producing the config.sh file. Only used variables are added, and conditional ones (from conditional dependencies) get skipped. Not the moment for extra baggage ๐Ÿ’€
  • add.Null โ€” Adds the section initializing all the shell variables used to an empty string.
  • c_h_weed โ€” Produces the config_h.SH file. Only the necessary lines are printed.
  • cm_h_weed โ€” Produces the confmagic.h file. Only the necessary lines are printed. This command is only enabled when the -M switch is given, or when a confmagic.h file already exists.
  • close.Config_sh โ€” Adds the final 'EOT' symbol on a line by itself to end the here document construct producing the config.sh file.
  • prepend โ€” Prepends the content of the target to the target_file if that file is not empty.
  • weed โ€” Adds the unit to Configure like the add command, but makes some additional tests to remove the '?symbol' and '%symbol' lines from the target_file if the symbol is not wanted or is conditionally wanted. The '%' form is only used internally by metaconfig while producing its own .U files in the '.MT' directory.
  • wipe โ€” Same as add really, but performs an additional macro substitution. The available macros are described in the Hardwired Macros paragraph.

Side note, bestie: metaconfig generates a -cond command internally to deal with conditional dependencies. You should NOT use it by yourself, but you will see it if you go scanning the generated Makefile in the .MT directory. ๐Ÿ‘€


๐Ÿงฉ Hardwired Macros ๐Ÿงฉ

The following macros are recognized by the wipe command and subsituted before inclusion in Configure:

  • <BASEREV> โ€” The base revision number of the package, derived from .package.
  • <DATE> โ€” The current date.
  • <MAINTLOC> โ€” The e-mail address of the maintainer of this package, derived from your .package.
  • <PACKAGENAME> โ€” The name of the package, as derived from your .package file.
  • <PATCHLEVEL> โ€” The patch level of the metaconfig program (deprecated in favor of <REVISION>).
  • <REVISION> โ€” The SVN revision level of the metaconfig program.
  • <VERSION> โ€” The version number of the metaconfig program.

Those macros are mainly used to identify the metaconfig version that generated a particular Configure script, and for which package it was done. The e-mail address of the maintainer is hardwired in the leading instructions that Configure prints when starting. ๐Ÿ’Œ

Recent metaconfig versions understand a much more general syntax of the form:

<$variable>

which gets replaced at Configure-generation time by the value of variable taken from your .package file. Eventually the old hardwired macro format will disappear, and <$baserev> will replace <BASEREV> in all the supplied units. Out with the old, in with the glow-up ๐ŸŒˆ


๐ŸŒŸ Special Units ๐ŸŒŸ

The following special units are used to factorize code and provide higher-level functionalities. They either produce a shell script that can be sourced, or a shell variable that can be eval'ed. Parameter passing is done via well-know variables, either named or anonymous like $1, $2, etc... (which can be easily set via the shell set operator). When Configure executes, it creates and goes into a UU directory, so every produced script lies in there and does not interfere with the files from your package. Considerate queen behavior ๐Ÿซถ

Here are the sepcial units that you should know about, and the way to use them.

Cppsym.U

This unit produces a shell script called Cppsym, which can be used to determine whether any symbol in a list is defined by the C preprocessor or C compiler you have specified. It can determine the status of any symbol, though the symbols in (attribute list) are more easily determined.

Csym.U

This sets the $csym shell variable, used internally by Configure to check whether a given C symbol is defined or not. A typical use is:

set symbol result [-fva] [previous]
eval $csym

That will set the result variable to 'true' if the function [-f], variable [-v] or array [-a] is defined, 'false' otherwise. If a previous value is given and the -r switch was provided to Configure (see the Configure Options paragraph), then that value is re-used without questioning. Respect the vibe of the past, periodt โœ‹

The way this computation is done depends on the answer the user gives to the question Configure will ask about whether it should perform an nm extraction or not. If the exctraction was performed, the unit simply looks through the symbol list; otherwise it performs a compile-link test, unless -r was given to reuse the previously computed value, naturally...

End.U

By copying this unit into your private U directory and appending dependencies on the ?MAKE: line, you can force a given unit to be loaded into Configure even if it is not otherwise wanted. Some units may only be forced into Configure that way. Sneaky but iconic ๐Ÿ’…

Filexp.U

This unit produces a shell script filexp which will expand filenames beginning with tildes. A typical use is:

exp_name=`./filexp $name`

to assign the expanded file name in exp_name.

Findhdr.U

This unit produces a findhdr script which is used to locate the header files in $usrinc or other stranger places using cpp capabilities. The script is given an include file base name like 'stdio.h' or 'sys/file.h' and it returns the full path of the inlcude file and a zero status if found, or an empty string and a non-zero status if the file could not be located. No cap, it just tells you where the header is at ๐Ÿ”

Getfile.U

This unit produces a bit of shell code that must be sourced in order to get a file name and make some sanity checks. Optionally, a ~name expansion is performed.

To use this unit, $rp and $dflt must hold the question and the default answer, which will be passed as-is to the myread script (see forthcoming Myread.U). The $fn variable controls the operation and the result is returned into $ans.

To locate a file or directory, put 'f' or 'd' in f~/. If a '~' appears, then ~name substitution is allowed. If a '/' appears, only absolute pathnames are accepted and ~name subsitutions are always expanded before returning. If '+' is specified, existence checks are skipped. If 'n' appears within $fn, then the user is allowed to answer 'none'.

Usually, unless you asked for portability, ~name substitution occurs when requested. However, there are some times you wish to bypass portability and force the substitution. You may use the 'e' letter (expand) to do that.

If the special 'l' (locate) type is used, then the $fn variable must end with a ':', followed by a file basename. If the answer is a directory, the file basename will be appended before testing for file existence. This is useful in locate-style questions like this:

dflt='~news/lib'
: no need to specify 'd' or 'f' when 'l' is used
fn='l~:active'
rp='Where is the active file?'
. ./getfile
active="$ans"

Additionally, the 'p' (path) letter may be used in conjunction with 'l' to tell getfile that an answer without a '/' in it should be accepted, assuming that it will be in everyone's PATH at the time this value will be needed.

Also useful is the possibility to specify a list of answers that should be accepted verbatim, bypassing all the checks. This list must be within parenthesis and items must be comma separated, with no interleaving spaces. Don't forget to quote the resulting string since parenthesis are meaningful to the shell. For instance:

dflt='/bin/install'
fn='/fe~(install,./install)'
rp='Use which install program?'
. ./getfile
install="$ans"

would let the user only specify fully qualified paths referring to existing files, but still allow the special "install" and "./install" answers as-is (assuming of course something will deal with them specially later on in the chain since they do not conform with the general expected frame).

If the answer to the question is 'none', then the existence checks are skipped and the empty string is returned. Note that since getfile calls myread internally, all the features available with myread apply here to. ๐ŸŒธ

If a completely expanded value is needed (for instance in a Makefile), you may use the $ansexp variable which is always set up properly by getfile as the expanded version of $ans. Of course, it will NOT expand ~name if you did not allow that in the first place in the $fn variable. ๐Ÿ™ˆ

๐ŸŒธ Inhdr.U ๐ŸŒธ

This unit makes the $inhdr shell variable, and Configure uses it internally to check whether a bunch of headers exist or not. Classic use case:

set header i_header [ header2 i_header2 ... ]
eval $inhdr

That prints a message telling you whether the header was found or not, and sets the i_header variable accordingly. And if you gave it more than one header and the first one isn't found? We just try the next one, and keep going until the list runs out or one of them shows up. ๐Ÿ’…


๐ŸŒˆ Inlibc.U ๐ŸŒˆ

This unit produces the $inlibc shell variable, and again Configure uses it internally to check whether a given C function is defined or not. Typical use:

set function d_func
eval $inlibc

That prints a message saying whether the function was found or not, and sets $d_func accordingly. Internally it used the $csym routine. periodt.


๐Ÿฆ‹ Loc.U ๐Ÿฆ‹

Okay so this one is important โ€” it produces a shell script loc that you can use to find out where in a list of directories a given file lives. The first argument is the file to be located, the second argument is what gets returned if the search fails, and the remaining arguments are the list of directories where the file should be searched. For instance:

dflt=`./loc sendmail.cf X /usr/lib /var/lib/sendmail /lib`

would set $dflt to X if no sendmail.cf file was found under the listed directories, or something like /usr/lib/sendmail.cf on some systems. See also Getfile.U. โœจ


๐Ÿ’Œ MailAuthor.U ๐Ÿ’Œ

This unit needs to be included on the ?MAKE: line of your own private End.U to make it into Configure. It offers the user the chance to register himself to the author, optionally being notified when new patches arrive, or receiving them automatically when issued. You need to install mailagent to do this (at least version 3.0).


๐Ÿ“ฌ MailList.U ๐Ÿ“ฌ

This unit needs to be included on the ?MAKE: line of your own private End.U to make it into Configure. It offers the user the chance to subscribe or unsubscribe to a mailing list where discussion related to the package is taking place. You need to run packinit and answer the mailing list related questions to set up the proper variables in your .package before this unit may become operational.


๐ŸŽ€ Myinit.U ๐ŸŽ€

Copy this unit into your private U directory to add your own default values to some internal variables. This unit is loaded into Configure after all the default initializations have been done.


๐Ÿ“ Myread.U ๐Ÿ“

This unit produces the myread shell script that must be sourced in order to do a read. It allows shell escapes, default assignment and parameter evaluation, as documented in the Instruct.U unit. It also allows dynamic setting of the -d option, which will be used for the remaining of the script execution.

To use this unit, $rp must hold the question and $dflt should contain the default answer. The question will be printed by the script itself, and the result is returned in the $ans variable.

Here is a typical usage:

dflt='y'
rp='Question?'
. ./myread
value="$ans"

See the unit itself for more information. ๐Ÿซถ


๐Ÿ’œ Oldconfig.U ๐Ÿ’œ

This unit must be part of your dependency ?MAKE: line when some of your units tries to reuse an old symbol value. This unit is responsible for getting the old answers from config.sh or providing useful hints when running on a given platform for the first time. See the Configure Hints paragraph for more information about hints.


๐ŸŒŸ Prefixit.U ๐ŸŒŸ

The purpose of this unit is to detect changes in the installation prefix directory, so it can recompute automatically suitable defaults from previous answers. It relies on the value of the $oldprefix variable, which holds the previous prefix directory when it changed, and is empty otherwise. For instance, if the prefix was changed from /opt to /usr/local, then the previous binary installation directory will be changed from /opt/bin to /usr/local/bin, or will remain unchanged if it was, say, /bin.

You need to call set before issuing an eval on $prefixit, such as:

set dflt var [dir]
eval $prefixit

which would set $dflt to $var or $prefix/dir depending on whether the prefix remained the same or not. If dir is the string none, a single space value in $dflt is kept as-is, even when the prefix changes. If dir is omitted, then $dflt is set to an empty string if the prefix changed, to $var otherwise.


๐Ÿงš Prefixup.U ๐Ÿงš

The intent of this unit is similar to that of Prefixit.U, i.e. it helps fixing the default string to accommodate prefix changes. However, the shell variable $prefixup, when evaluated, will only restore ~name expansions, should prefix use such an escape mechanism. Use it as:

set dflt
eval $prefixup

before prompting via getfile for instance. If the prefix does not make use of ~name expansion, then the above will be a no-op on the y variable, naturally. ๐Ÿฆ‹


๐Ÿ”ฅ Specific.U ๐Ÿ”ฅ

This unit is empty by default. It is meant as a project-specific hook to have shell code executed by Configure after config.sh was generated but before the extraction of .SH files.

Copy the unit to your project's U/ directory and customize it.

Here is what the dist project uses it for (showing only the unit's code, not the full unit here):

: project-specific code
file=revision.h
echo " "
echo "Generating $file..." >&4
bin/svn-revision . $file

That shell code is executed when Configure is back to the top directory of the source tree.


โญ Typedef.U โญ

This unit produces the $typedef shell variable, used internally by Configure to check whether a typedef exists or not. A typical use is:

set typedef val_t default [ includes ]
eval $typedef

This will set the variable val_t to the value of default if the typedef was not found among the listed include files, or to typedef if found. If no include files are specified, the unit looks in <sys/types.h> only. If you specify some includes, only those are looked at.


๐Ÿ‘‘ Unix.U ๐Ÿ‘‘

The purpose of this unit is to define some of the most common UNIX-isms via variables which can be altered from the command line or via proper hint files. In particular, $_exe, $_o and $_a are set. All the units should refer to $_o and not to .o directly. The . is part of these variables. ๐Ÿ’…


๐ŸŒธ Setvar.U ๐ŸŒธ

This unit produces the variable, which is used internally by Configure to set a define/undef value to a given symbol, emitting a warning when it suddenly changes from a previous value. For instance:

val="$define"
set d_variable
eval $setvar

If the previous $d_variable value was non-null and $val is different, a "whoa" warning is issued. ๐Ÿ˜ญ


๐Ÿ’€ Whoa.U ๐Ÿ’€

This unit produces the whoa script, which emits a warning when the value in variable whose name is $var is not the same as its old previous value held in $was. Upon return, $td and $tu hold the proper value to define or undef the variable. See examples in Inlibc.U.


๐Ÿง โœจ Builtin Pre-processor โœจ๐Ÿง 

Each unit to be included in Configure is ran through a built-in pre-processor. Pre-processor statements are introduced by the '@' character ('#' is the shell comment character). It functions merely as the C pre-processor does but allows for shell and perl escapes. Here are the available functions:

  • @if expression โ€” If expression is true, continue loading code until @end, @elsif or @else.
  • @elsif expression โ€” Alternative choice. If expression is true, continue loading code until @end, another @elsif or @else.
  • @else โ€” Default code to be loaded if the @if expression was false and none of the optional @elsif matched. Load until @end.
  • @end โ€” Close the conditional loading statement opened by @if.
  • @define symbol โ€” Tells the pre-processor that symbol is defined from now on.

The conditional expression can include symbol names (value is true if symbol is wanted or defined via @define or shell/perl escapes. Those atoms can be combined using the traditional boolean operators '!' for negation, '&&' for logical and, and '||' for logical or.

Text enclosed within single brackets is a shell test, while text between double brackets is a perl test. Namely the expressions:

{ shell text }
{{ perl text }}

are translated into:

if shell text >/dev/null 2>&1; then exit 0; else exit 1; fi
if (perl text) {exit 0;} else {exit 1;}

and the exit status is used in the standard way to get a boolean value, i.e. 0 is true and everything else is false. Note that only simple conditions can be expressed in perl, until some complex code can be loaded within metaconfig and executed.

The built-in pre-processor can be used to finely tune some units (see d_gethname.U for a complex example) depending on the symbols actually used by the program or the files present in the distribution. For instance, the Oldconfig.U uses a test like:

@if {test -d ../hints}

and Configure will contain hint-dependent code only if there is a hints directory in the package's top level directory. Note that tests are ran from within the '.MT' directory, hence the needed '../' in the test.

The pre-processor can also be used to avoid putting useless code when a symbol is not defined. Units defining more than one symbol can be protected that way (since the unit is loaded as a whole) by gathering symbol-dependent code within an @if/@end pair. For instance:

@if I_TIME || I_SYS_TIME || I_SYS_TIME_KERNEL
need_time_h='true'
@else
need_time_h='false'
@end

will test whether the source code makes any use of one of the three symbols that control the time.h or sys/time.h inclusion and define the shell symbol accordingly. That gives Configure a feedback on what the sources need and avoid the drawback of having fixed frozen units. ๐Ÿง 

Via the '?W:' lines, you can get interesting combinations. For instance, the i_time.U unit needs to know whether the C sources make any use of the struct timezone type. Therefore, the line:

?W::timezone

is used for its side-effect of defining the symbol timezone for the pre-processor. The unit code can then say:

            @if timezone
            for s_timezone in '-DS_TIMEZONE' ''; do
            @else
            s_timezone=''
            @end

            ... code using s_timezone ...

            @if timezone
            done
            @end

and have an extra loop trying two successive values for the s_timezone variable, but only if needed. ๐ŸŒ€


๐Ÿฆ• Obsolete Symbols

Okay so bestie, obsolete symbols are preserved to ease the transition with older metaconfig units. But heads up: unless the -o switch is passed to metaconfig, they get ignored. However โ€” and this is the slay part โ€” an Obsolete file will be generated, telling you which files are making use of those obsolete symbols and what the new symbols to be used are. โœจ

The lifetime for obsolete symbols is one full revision, i.e. they will be removed when the next base revision is issued (patch upgrades do not count of course, bestie, we're not monsters). Therefore, it is wise to translate your sources and start using the new symbols as soon as possible. ๐Ÿซถ


๐Ÿงš Configure Hints

It may happen that the internal configuration logic makes the wrong choices. For instance, on some platform, the vfork() system call is present but broken, so it should not be used. It is not possible to include that knowledge in the units themselves, because that might be a temporary problem which the vendor will eventually fix, or something that was introduced by a new OS upgrade. ๐Ÿ˜ญ

Anyway, for all those tiny little problems that are too system-specific, metaconfig provides hint files support. To use it, you need to create a hints directory in the package's top level directory, and have it when you run metaconfig. That will load the hint-related part from Oldconfig.U. ๐ŸŽ€

From then on, you may pre-set some of the shell variables Configure uses in an OS-specific .sh file. There is code in Oldconfig.U that tries to guess which hint files are needed by computing a standard name based on the system OS name, the kernel name, the release number, etc... Since this information is likely to change rapidly, I'm not documenting it here. You have to reverse engineer the code from Oldconfig.U. iykyk ๐Ÿ’…

When you first release your package, your hints file directory should be empty. If the users of your package complain that they have problem with Configure defaults on a particular system, you have to see whether this is a platform-specific problem or a general one. In the former case, it's time to introduce a new hint file, while in the latter, the corresponding unit should be revised. ๐ŸŒธ

For instance, SGI systems are known to have a broken vfork() system call, as of this writing. And the corresponding hint file name is sgi.sh. So all you need to do is create a hints/sgi.sh file in which you write:

     d_vfork="$define"

which will always remap vfork on fork (see d_vfork.U). When running on SGI systems for the first time, Configure will detect that there is an hints/sgi.sh file, and that we are on an IRIX machine (the kernel name is often /irix), therefore it will propose sgi as a possible hint. If the user accepts it, and since the $d_vfork value is modified via the $setvar call, a whoa! will be emitted to warn that we are about to override the value computed by Configure. ๐Ÿ˜ฑ

Note that you don't have to provide all the hints known by Oldconfig.U. If a hint file is missing, it will not be proposed as a possible choice. The heuristic tests ran to compute the possible hint candidates are flaky. If you have new values or different tests, please send them to me... ๐Ÿ™ˆ


๐Ÿ’œ Overriding Choices

If you create a config.over file in the top level directory, Configure will ask you if you wish to load it to override the default values. This is done prior creation of the config.sh file, so it gives you a chance to patch the values stored in there. โœจ

This is distinct from the hints approach in that it is a local file, which the user is free to create for his own usage. You should not provide such a file yourself, but let the user know about this possibility. ๐Ÿซถ


๐Ÿ‘‘ Configure Options

The Configure script may be called with some options specified on the command line, to slightly modify its behaviour. Here are the allowed options, bestie:

  • -d โ€” Use defaults for all answers.
  • -e โ€” Go on without questioning past the production of config.sh.
  • -f file โ€” Use the specified file as a default configuration. If this switch is not used, the configuration is taken from config.sh, when present.
  • -h โ€” Print help message and exit.
  • -r โ€” Reuse C symbols value if possible. This will skip the costly nm symbol extraction. If used the first time (with no previous configuration file), Configure will try to compile and link a small program in order to know about the presence of a symbol, or absence thereof.
  • -s โ€” Silent mode. Only strings printed on file descriptor #4 will be seen on the screen (that's the important messages). It's not possible to completely turn off any output, but you may use Configure -ders >/dev/null 2>&1 to have a full batch run with no output and no user interaction required.
  • -D symbol=value โ€” Pre-defines symbol to bear the specified value. It is also possible to use -D symbol which will use a default value of define.
  • -E โ€” Stop at the end of the configuration questions, after having produced a config.sh. This will not perform any make depend or .SH files extraction.
  • -K โ€” Knowledgeable user. When you use this option, you know what you are doing and therefore the config.sh file will always be handled as if it was intended to be re-used, even though it might have been generated on an alien system. It also prevents aborting when Configure detects an unusable C compiler or a wrong set of C flags. Further shortcuts might be turned on by this option as well in the future. This option is documented in the Configure usage message, to remind us about its existence, but the given description is hoped to be cryptic enough. :-) ๐Ÿ’€
  • -O โ€” Allow values specified via a -D or -U to override settings from any loaded configuration file. This is not the default behaviour since the overriding will not be propagated to variables derived from those you are presently altering. Naturally, without -O, the setting is only done when no configuration file is loaded, which is safe since derivative variables have not been computed yet...
  • -S โ€” Perform variable substitution on all the .SH files. You can combine it with the -f switch to propagate any configuration you like.
  • -U symbol= โ€” Pre-sets symbol to bear an empty value. It is also possible to use -U symbol which will set symbol to undef.
  • -V โ€” Print the version number of the metaconfig that generated this Configure script and exit.

๐ŸŒˆ Running Environment

Upon starting, Configure creates a local UU directory and runs from there. The directory is removed when Configure ends, but this means you must run the script from a place where you can write, i.e. not from a read-only file system. ๐Ÿšซ

You can run Configure remotely though, as in:

          ../package/Configure

to configure sources that are not present locally. All the generated files will be put in the directory where you're running the script from. This magic is done thanks to the src.U unit, which is setting the $src and $rsrc variables to point to the package sources. That path is full or relative, depending on whether Configure was invoked via a full or relative path. โœจ

From within the UU subdirectory, you can use $rsrc to access the source files (units referring to source files link hints shall always use this mechanism and not assume the file is present in the parent directory). All the Makefiles should use the $src variable as a pointer to the sources from the top of the build directory (where Configure is run), either directly or via a VPATH setting. ๐ŸŽ€

When running Configure remotely, the .SH files are extracted in the build directory, not in the source tree. However, it requires some kind of a make support to be able to compile things in a build directory whilst the sources lie elsewhere. ๐Ÿฆ‹


๐Ÿ”ฅ Using Magic Redefinitions

By making use of the -M switch, some magic remappings may take place within a confmagic.h file. That file needs to be included after config.h, of course, but also after all the other needed include files. Namely:

     #include "config.h"
     ...
     ... other inclusions ...
     ...
     #include "confmagic.h"

Typically, confmagic.h will attempt to remap bcopy() on memcpy() if no bcopy() is available locally, or transform vfork into fork when necessary, hence making it useless to bother about the HAS_VFORK symbol. ๐Ÿช„

This configuration magic is documented in the Glossary file. ๐Ÿ’œ


๐Ÿงš Unit Templates

There is a set of unit templates in the metaconfig source directory, which are intended to be used by a (not yet written) program to quickly produce new units for various kind of situations. No documentation for this unfinished project, but I thought I would mention it in the manual page in case you wish to do it yourself and then contribute it... ๐Ÿซถ


๐Ÿ‘‘ AUTHORS

  • Larry Wall <lwall@netlabs.com> for version 2.0.
  • Harlan Stenn <harlan@mumps.pfcs.com> for important unit extensions.
  • Raphael Manfredi <Raphael.Manfredi@pobox.com>.
  • Many other contributors for the metaconfig units. See the credit file for a list.

๐ŸŒธ FILES

  • LIB/dist/mcon/U/*.U โ€” Public unit files
  • U/*.U โ€” Private unit files
  • LIB/dist/mcon/Glossary โ€” Glossary file, describing all the metaconfig symbols.
  • Obsolete โ€” Lists all the obsolete symbols used by the sources.
  • Wanted โ€” Lists all the wanted symbols.

where LIB is /usr/share/dist.


๐Ÿ’€ BUGS

Units are sometimes included unnecessarily if one of its symbols is accidentally mentioned, e.g. in a comment. Better too many units than too few, however. ๐Ÿ˜Œ


๐Ÿฆ‹ SEE ALSO

pat(1), makeSH(1), makedist(1), metalint(1)


and that's the whole page, bestie!! ๐Ÿ’– This was Version 3.5 PL0 of METACONFIG(1) โ€” go forth and configure slayfully, the metaconfig girlies believe in you ๐Ÿ‘‘โœจ๐ŸŽ€

๐Ÿ“ฆ where this page comes from