CONFIG USERS MANUAL 0. TABLE OF CONTENTS 0. TABLE OF CONTENTS 1. INTRODUCTION 1.1 Description 1.2 Overview 1.3 Conventions 2. PREPARING A CONFIGURATION 2.1 Syntax 2.2 Options Files 2.3 Configuration Files 3. PREPARING OPTIONS FILES 3.1 Value Options 3.2 Option Options 3.3 Required Options 3.4 Action Options 3.5 Names To Avoid 4. PROGRAMMING FOR CONFIG 4.1 Starting Out 4.2 Required Options 4.3 Option Options 4.4 Value Options 4.5 The "Appropriate" Options File 4.6 Converting a Program for Config 1. INTRODUCTION This section describes config, gives an overview of this manual, and introduces the user to the conventions and terminology of config. 1.1 Description Config is a tool that makes configuring a certain type of large program much easier for anyone not intimately familiar with the details of the program. It also makes keeping multiple different configurations of such programs easier - even though they may run on different machines, or under different operating systems. The programs in question all use numerous symbols to control the conditional compilation of features in the program, or to control which code is compiled for what system. These programs need to run on multiple different machines or operating systems, or run in an environment where having all the features of a program enabled entails an unacceptable performance cost, or makes running the program impossible. The two normal approaches to this type of program both have problems. One approach is to change the compile commands to enable and disable features. The other approach is to keep a large file which defines all the features, and have all other files depend on that. The first can quickly generate unwieldy command lines. Both require recompiling the entire program after a change of one option, unless that option only effects one file, and the automatic building facility used for that program is defeated. Config provides an alternative that avoids both of these problems. 1.2 Overview This config users manual has the following four sections: Section 1 states what config is used for, describes this manual, and explains some conventions and terminology used throughout the manual. It should be read by anyone not already partly familiar with config. Section 2 explains how, if you have a program already prepared for use with config and all the appropriate files set up, you would go about preparing a new configuration of that program. It should be read by everyone who wishes to rebuild a config-supported program. Section 3 explains the meaning of the options files in terms of the program. It should be read by all those who are adding features to a config-supported program. Section 4 describes what form a program must be in before it can be supported by config, and how to set up the initial configuration files for a program. It should be read by anyone who has a program they would like config to support. 1.3 Conventions There are several conventions associated with the use of config that are assumed to be true throughout this manual. The first is that config is being used to configure a program, referred to as "the program." The second is that all the files needed by config are in one directory, near the top of the source tree for the program. The third is that that directory is called "conf". It will hereinafter be referred to as "the conf directory", or just "conf". Config assumes that at the same level as the conf directory there will be one directory for every configuration that can currently be built. These will be called "configuration directories", or "the configuration directory". The names of these directories are usually in upper case. In the conf directory, there are two types of files which config cares about. The first are those with the extension ".options". These are "options files". The second are conventionally in upper case, and represent different configurations of the program. For each of these, there should be a configuration directory with the same name. These are "configuration files" or "config files". Configuration files and options files contain lines, each of which is either a comment, or describes some symbol the programmer uses to control compilation of the program. These symbols are called options. Currently, config only supports the configuration of C programs. It could easily support others, but this document assumes the program is written in C. 2. PREPARING A CONFIGURATION This section describes the steps necessary to prepare a configuration file for use in building a configuration of a config-supported program. 2.1 Syntax In the conf directory, you should find a collection of options files. These list all the options that can be used in all configurations of the program. Some options are required; some can be toggled on or off; others allow you to change values used by the compiler. The form of each line in an option file indicates which it is. The most important rule about options files is that it's line based. The end of a line is the end of an option. The second thing to know is that a "#" character followed by a space starts a comment. Everything after that will be ignored by config, and is for the benefit of the reader. The meaning of the option is given in the comment. The third rule is that the first word on the line is the "name" for the option this line describes, and that everything from the end of the name to the start of any comment or the end of the line, ignoring any leading or trailing whitespace, is the "value" for the option this line describes. There are two special values for options in the options files. The first is "required". This means any config file that uses options in that options file must specify a value for that option (explained in a later section). The second special value is "option". This means that the named option is a toggle, and is normally off. It also changes the format used in the configuration file for that option. Finally, there is one special name for options: "action". You should ignore any option lines with that name. All other options allow you to specify a value for the named option, and list the default value if you don't specify one. A required option could look like this, with comments: system required # which system we're compiling it for # currently supported: amiga, vms, bsd A toggle option might look like this, with comments. no_metakey option # disables the use of "meta" keys as modifiers # instead of prefacing commands sequences with # an ESC character This option, according to the comments, says that "meta" keys are supported for all configurations unless their config file specifies "no_metakey". A non-required value option could look like this: metabit 0x80 # what bit in a char is the metabit which says that metabit is normally "0x80", unless the config file changes it. 2.2 Options Files Now that you know the syntax for an options file, you can start reading them. The first one to read is always "config.options". It describes the options that exist in every configuration of the program. You should read through config.options, reading the comments that describe each option (if there are no such comments, you should find the author of the program or options file and insist that they be added), and considering what values you would like those options to have. First, decide what values the required options should have. These values will be limited to a small set, and should be listed in the comments. For example, according to the comments: system required # which system we're compiling it for # currently supported: amiga, vms, bsd the required option "system" can be set to amiga, vms or bsd. After you've decided what values you want the required options to have, you should look for options files with the first part of the names being the values you've chosen. For example, if you wanted system to be "amiga", you would look for "amiga.options". This file will list more options that are available for the program if it's configured with "system" as "amiga". You should read through that file in the same way you went through config.options. If it has more required options, you should go through the files associated with those, and so on. Should you wish to set a required option to a value for which there is no options file, you should contact the author of the options file that has that required option in it, or the author of the program. 2.3 Configuration Files Having read through all the options files associated with the configuration you are working on, you can start on your configuration file. Most people build it as they go. Decide on a name for your file, and create it in the conf directory - if it isn't already in use. At the same time, create an empty configuration directory with the same name. By convention, both these names should be in upper case. The form of a configuration file is the same as the form of an options file: name as the first token on the line, value as everything from the name to any comment or the end of the line, followed by comments, which start with a "# " and go to the end of the line. For a configuration file, the name of an option must be an option that appeared in one of the options file, or the word "option". The required options must all be listed before any other options. In addition, the required option whose value names an options file must be listed before any required options from the options file it names. So if config.options contained: system required # which system we're compiling it for # currently supported: amiga, vms, bsd and amiga.options contained: compiler required # which C compiler is being used. # currently supports only lattice or manx then your config file might start like: system amiga compiler lattice The remaining options can be listed in any order. Normal value options are listed the same way that required options are listed. Options whose value was "option" can be listed in one of two ways. The first and preferred way is with a line which specifies the name "option", and then the name of the option you wish to turn on. You can also list the name of the option as the name on the line, and give it a value of either "on" or "off", but this form may not be supported in future versions of config. Config will not complain if you vary from these forms, but the compiler might when you attempt to compile the program. The option options default to off, and "name on" is identical to "option name". So, if you encounter the following two options in various options files: no_metakey option # disables the use of "meta" keys as modifiers # instead of prefacing commands sequences with # an ESC character metabit 0x80 # what bit in a char is the metabit then part of your config file might look like: option no_metakey # My terminal doesn't support it anyway metabit 0x100 # For the international character set which is identical to no_metakey on metabit 0x100 Both of these turn on the no_metakey option (thus, according to the comments, disabling support for the meta key), and change metabit from its normal value of 0x80 to 0x100. The final step is to run config on your config file. To do that, make sure you're in the config directory, and issue the command config CONFIGFILE Config will then scan all the appropriate options files and your config file and prepare your configuration directory for building the configuration you've specified. 3. PREPARING OPTIONS FILES This section discusses what config does with the values given to options, and how that can change the program produced. 3.1 Value Options Value options are listed in the options or config files with their name, and the value that will be #defined to their name at compile time. For section 3.1, assume that the following is in an options file for the config file and program being configured: promptwait 20 # number of 10ths of a second to wait before timing # out while waiting for the user. For each value option, config creates a file in the configuration directory whose name is the name of the option, with a ".h" appended. For the above example, there would be a file "promptwait.h" in the configuration directory. That file contains a single define (or in a special case, is empty) that defines the option name in upper case, and gives it the value specified (or in a special case, just defines it). So, for the above, if the config file doesn't change promptwait, the file promptwait.h in the configuration directory would contain #define PROMPTWAIT 20 If the config file did change promptwait, for example, reducing it to 10: promptwait 10 then config would build promptwait.h to indicate that change: #define PROMPTWAIT 10 3.2 Option Options Option options are listed in the options files with their name, and the value "option". They have no value #defined to their names at compile time - they are either defined or not. For section 3.2, assume that the following is in an options file for the configuration being configured: v11 option # must be defined for the editor to either run on # or compile on a version 1.1 AmigaDOS system. # It mainly wards against bugs in the 1.1 ROM Kernel. Config will create a file whose name is the name of the option, with ".h" appended. So the above option will cause "v11.h" to be created in the configuration directory. If the option is left off, the file is left empty. If the option is turned on, the file will define the option name - once again, in upper case - as a symbol. So if the v11 option is left off, then the file v11.h will contain nothing. If some config file turns v11 on, then v11.h will contain #define V11 As an implementation detail, if any value option has the value of "on" or "off", it will be treated as if it were an option option, and either not defined, or just defined with no value attached to the symbol. This is useful in that it allows for #define symbols whose existence causes code to appear, and whose value is needed by the code if it appears. 3.3 Required Options Required options are listed in the options file with the special value of "required". They will be given a value in every config file that uses that options file, and will cause config to check the options file named by the value given them in the config file. Like the other kinds of options, required options cause a file whose name is their name with a ".h" appended to be created in the configuration directory. Unlike the others, the name of the option is not what is defined. Instead, the value of the option is defined as a symbol, once again in upper case. The symbol is not defined to be any specific value. For instance, if the options file contained compiler required # which C compiler is being used. # currently supports only lattice or manx Then config will create "compiler.h" in the configuration directory. If the config file contained this as: compiler lattice then the contents of compiler.h would be #define LATTICE In addition, the file "lattice.options" will be scanned for further options when the compiler option is specified in the config file. If the config file had instead specified "compiler manx", then MANX would be #defined in compiler.h, and "manx.options" would be scanned. 3.4 Action Options Action options are specified in options files with a name of "action". They are never mentioned in config files, and have no direct effect on the compilation of the program. What action options do is list commands to be issued to the command processor after the include files specified by the other options have been created. The commands are issued in the order they were found in the options file, and have the configuration directory as their current working directory when they are issued. Normally, action commands are used to take care of any preparations needed before the program can be compiled in the configuration directory. Examples are copying makefiles into the directory, building dependency files, or just issuing comments to the user. A typical short example is: action lc:lmk -f /amiga/lmkfile depend action echo "Ready to lmk this configuration of mg." This issues the command "lc:lmk" with the three arguments "-f", "/amiga/lmkfile" and "depend" (which, in this cases, creates a new makefile in the current directory with a dependency list appended to it), and the command "echo" with the argument '"Ready to lmk this configuration of mg."' (which, in this case, tells the user that the configuration directory is ready for the lmk command which will make the program in it). 3.5 Names To Avoid There are some names that you can not use when creating options, mostly became they clash with words that config uses to change the treatment of that option. Most of these words are external: option, required, off, on. The following table lists all such words, and describes where they cannot be used or where they should be used with care. word not valid action as an option name option as an option name, or the default value for an option required as the default value for an option on as an option value off as an option value 'On' and 'off' are special cases, and can be useful as option values if used with care. Their use is described above. In addition, any option name must be valid as the name (without extension) for a header file on your system. Likewise, any required option value must be valid as the name for an options file. Finally, option names and required option values must be valid preprocessor symbols. 4. PROGRAMMING FOR CONFIG This section discusses what form a program must be in so that it can be configured with config. 4.1 Starting Out Once you've decided that a program will be configured by config, it is straightforward - but some work - to insure that config can be used to best advantage. This is best done while the program is still in the design phase. Converting existing programs for configuration via config is possible, but painful. See section 4.6 for information on converting existing programs. Every time you use a new #defined symbol, you must decide which of four classes it belongs to: 1) The symbol in some way distinguishes one version from another. 2) The symbol causes features to appear or disappear from some or all versions. 3) The symbol is a compile-time value that affects some or all versions. 4) The symbol depends on the values of other symbols in a predetermined way. The four cases all require slightly different treatment, and all but the fourth will be discussed in separate sections. in the fourth case, the symbol should be dealt with in source code, and no option of any kind need appear for it in the options files. 4.2 Required Options Symbols that distinguish versions from one another are almost certainly "required" options. They usually come in sets of #defined symbols that are mutually contradictory - if one is on, all the others must be off. Each such set is represented by one required option, where the possible values of that required option are the different symbols that can be defined. When such a symbol is first referenced, you should determine if it belongs to such a set that already exists. If so, then change the comment for the required option for that set to reflect the new possible value, and then create an empty options file with that value as the first part of its name, containing a comment describing what it is. For example, suppose you add a new symbols "sysv" that is appropriate for the following option: system required # which system we're compiling it for # currently supported: amiga, vms, bsd Then add ", sysv" to the end of the second comment, and create an options file "sysv.options", with comment: # # support for System V. No options to choose from (yet). # Any code that needed to be compiled in - or needed to not be compiled in - for the System V version of the program would then have it's conditional compilation depend on the symbol SYSV. Normally, required options are added to a program after it has been working in some incarnation for a while - for instance, when it is being ported to a new machine or operating system. In those cases, if we are adding a value to a required type, you must find all files that include the .H file which config creates for that option and check to see what should be added. In cases where the required option did not exist before, you need to choose a name for the option, and add that as a required option in the appropriate options file with a comment indicating what values it can take on. You should also create the options files for each of those values, as above. Finally, every source file that checks for one of the possible values of the option being defined must be changed to include the .h file with the name you chose for the required option. Any automated compilation facilities should be informed of the change in whatever way is needed. For example, suppose you are adding support for a second terminal type to a program - for example, adding termio to a program that already has termcap support. The option name "tty" or "term" suggests itself, with the possible values of "termcap" and "termio". So you would add the following to the appropriate options file: term required # type of terminal support used. One of: # termcap, termio You should also create the files "termcap.options" and "termio.options", with comments describing what they are. Code should then appear in the source files conditionally compiled on the symbols "TERMCAP" and "TERMIO". Any source file that depends on those symbols should be changed to include the file "term.h". 4.3 Option Options Any symbols that cause code to be conditionally compiled in, but can be left on or off without requiring that any other symbols be on or off (unless they require code that those other symbols control the compilation of, or something similar) should become "option" options. When such a symbol is first referenced, all you need to do is add the name of the symbol and a comment describing it to the appropriate options file. For example, if you were adding mouse support to a program, but wanted to allow people to build the program on systems that didn't have mice attached, you would probably conditionally compile in that extra code if the symbol "MOUSE" were defined. So you would add: mouse option # provide mouse support Every source file that had code that depended on MOUSE should also include "mouse.h", as that is where config will put the #define for MOUSE. 4.4 Value Options If a defined symbol has a value that is used in the code, then you are dealing with a "value" option. It is treated the same as an "option" option, except that the preprocessor symbol will have a value, instead of merely being defined or not defined. When such a symbol is first referenced, follow the same actions as the "option" options, except you must choose a default value for the option and use that for the value in the options file instead of the word "option". For example, if the program had a limit for number of screen lines built into it, and you wished to make that limit available to the person compiling the program so they could raise or lower it as appropriate for their environment. Decide on a reasonable default - say 70 - and add the following line to the appropriate options file: maxlines 70 # maximum number of display lines we can work with You should then change all source files that depend on this maximum to use the symbol MAXLINES, and have them include "maxlines.h" to get the value. You can have a value option do double duty, if you so desire. If one of the values the option can take on is "off", then the symbol will not be defined. This allows you to have code that doesn't appear if the user doesn't want it, and a symbol that holds a needed value in that code. For example, if word-wrap code were part of a program, and you wanted it to be either on by default, off by default, or disabled completely, you might have entries in an option file like so: wrap 1 # word wrap code. Three values are supported: # off -> code isn't there at all # 0 -> code is there, but turned off at startup # 1 -> code is there, and on at startup. and C code that looks like: #ifdef WRAP int wrap_mode = WRAP ; #endif with appropriate code to handle the word wrap. 4.5 The "Appropriate" Options File The previous three sections each mentioned "the appropriate options file." This sections discusses how you would find that file. The matter is actually very simple. The options files for a program can be viewed as a tree, with "config.options" at the root, and branches from each options file for the required options in that file. The various different versions of the program then correspond to the leaves of this tree. The "appropriate" options file for an option to be added to would be the options file farthest from the root which contained all the versions for which that option could apply. Required options separate versions from one another, and would seem to require more work. This is seldom the case. Usually, when required options are first added, there are no options in their options file, so using the same method as described above will work. In practice, there usually isn't any problem. When an option is added, there is a clear idea of what versions it will and won't be appropriate to add to, and the "appropriate" options file will be obvious. 4.6 Converting a Program for Config Converting an existing program for use with config is easy to describe, and for simple programs, is easy to do. However, if you're considering converting a program for use with config, it's probably because the compile-time options have gotten out of hand, so you face a rather large job. There are only four steps to convert a program to config: 1) find all the defined symbols; 2) classify them as described in section 4.1; 3) create the config.options and all options files needed for required options; 4) modify the source as appropriate. We will look at each step in some depth. Finding all the defined symbols is easy if you have a tool for selecting lines by pattern from a file. Find all lines that start with #define or #if in all source files. Delete the preprocessor directives and any extraneous information, and then sort the results. At this point, you should have all symbols which are candidates for options in hand. Classifying symbols is slightly harder. The previous sections discusses the various types of symbols and their characteristics. Start by finding all the symbols which are values for required options. At this point, you can create a first version of config.options with the required options and their possible values listed. You can also create the other options files, with comments describing what version(s) of the program they provide options for. The second step should be to identify those symbols which aren't going to be visible as options to the users. These will have values that can be determined from the other symbols. You should start adding code (if it doesn't exist) to define these symbols, based on the values of the other symbols. Next, go through the remaining symbols and decide if they are "option" options or "value" options, and treat them as described in the preceding sections. By the time you have completed this step, you should have all the options files finished. Then go through the source files and add the appropriate #include's for all defined symbols used in each file, if those symbols are now in files created by config. Finally, delete the definition of those same symbols from wherever they were defined previously - either in build scripts, make files, or .h files. If you are using automated compilation tools, you should also modify the data files for those so that the new dependencies are reflected in them.