6.2. Configuration Files

6.2.1. The Basic Concept

As an introductory example of backend configuration, you might want to consider the system command that must be given to compile a C++ source file. Assume the current target/platform configuration was called i386-freebsd2-aout-gcc-debug,optimize, meaning that the GNU C/C++ compiler by the Free Software Foundation is in use, and that optimized code suitable for a debugger is to be generated (cf. Section 2.1.1). In this case, the command in question may schematically be described as

g++ -c -g -O {DEFINES} {INCDIRS} {SOURCE}
with
{DEFINES}= -D<option1> ... -D<option n>
{INCDIRS}= -I<path1> ... -I<path m >, and
{SOURCE} = <source_path>,
           
Note that the working directory of the compilation process is a sub-sub-directory of the package root (cf. Section 2.2.3). That sub-sub-directory's relative path is always obtained from the TPC name by dividing it into two string components:
<package_root> / i386-freebsd2-aout-gcc / debug,optimize
                \_________  _________/   \_____  _____/
                          \/                   \/
                       <string1>            <string2>
-- The path would be of the form

<package_root> / i386-freebsd2-aout-gcc / DEFAULT

in case no compiler options were given. -- For these reasons, the source path has always the form

.. / .. / src / <source_name>

The conclusion is that there is a clear distinction between information from the front end -- options, include paths, and source paths -- and a TPC-dependent way of generating a compiler call from it. This transformation is essentially a simple matter of string and list manipulation. More specifically, the necessary front-end information may be represented by means of these imported variables:

Table 6-1.

PKG_ActRoot -- the local package root
PKG_ActSoure -- the current source path
PKG_ImportRoots -- list of root directories of all imported package installations
PKG_Decls_add_define -- list of all external defines from the PkgDesc
PKG_Decls_add_include -- list of added include paths from the PkgDesc


DEFINES, INCDIRS, and SOURCE can then be obtained as follows:

let DEFINES = {PKG_Decls_add_define}
let INCDIRS = concat(
              addsuffix(append({PKG_ImportRoots}, {PKG_ActRoot}), "/inc"), 
              {PKG_Decls_add_include}
            )
let SOURCE  = {PKG_ActSource}


In consequence, all what is left consists of stating

cc_compilation : compile = "g++ -c -g -O {DEFINES} {INCDIRS} {SOURCE}"



to declare the required action. Note that its name is cc_compilation, and that it has a type, which is called compile. The let statement declares a variable value with dynamic binding and lazy evaluation, that is, the actual value is evaluated just when it is needed in the current context of the application of the variable.

The rest of this section presents the complete picture of that. First of all, ComPact retrieves the backend configuration from a single file, which is searched according to the configpath variable from the initialization file (cf. Section 2.1.2). The file is called pkgconf.cbcl and its format is

# comment

kind declarations:
case <TPC_name>: # zero or more TPC-specific lists 
<kind_decl>;
    .
case <TPC_name>:
<kind_decl>;
    .
default:         # optional default list of kind declarations
<kind_decl>;
    .
end;

derivation declarations:
case <TPC_name>: # zero or more TPC-specific lists 
<derivation_decl>;
    .
case <TPC_name>:
<derivation_decl>; 
    .
default:         # optional default list 
                 # of derivation declarations
<derivation_decl>;
    .
end;

action declarations: 
case <TPC_name>: # zero or more TPC-specific lists 
                       # of variable and action declarations
<variable_decl>;
    .
<action_decl>;
    .
case <TPC_name>:
<variable_decl>;
    .
<action_decl>;
    .
default:         # optional default list 
                 # of variable and action declarations
<variable_decl>;
    .
<action_decl>;
    .
end.
the format of an action declaration being
<action_name> [: <type_name>] = <string_expression>


and the format of a variable declaration being

let <variable_name> = <expression>


The sections declaring kinds and derivations are means of tailoring ComPact for the use with different kinds of languages, compilers, and building procedures. Currently they are only partially evaluated, but future versions of ComPact will make extensive use of them. Usually you don't have to worry with their specifics, as ComPact comes with a variety of predeclared kinds and derivation rules for C, C++, JAVA, and other languages. The complete syntax of the ComPact Backend Configuration Language (CBCL) is given in Section 6.2.9, a short discussion of kinds and derivations is contained in Section 6.2.2 and Section 6.2.3.

Section 6.2.4 presents the complete list of imported variables; Section 6.2.5 describes the means of string and list manipulation; Section 6.2.6 describes what exported variables and actions are understood by ComPact; and Section 6.2.8 presents an example, where a backend for the TPC called i386-freebsd2-aout-gcc is configured.

6.2.2. Kinds of package elements in CBCL

All elements declared in the PkgDesc file are of a certain kind. These kinds are defined in the first section of the backend configuration file. (There are several predefined kinds as well.) ComPact knows about the following kind declarations:



As you may have noticed, there are three different classes of kind declarations: file kind declarations, virtual kind declarations, and compound kind declarations. The first class is by far the most often used, since it contains the descriptions of the elements that are the sources and products of the actions of ComPact. Compound kind declarations are only used to define shorthand notations for multiple other declarations, and virtual kinds are there to support additional parameters needed by the build process.

Here is a short example for every type of kind declaration:

program source kind "c_header" default extension "h" default location "inc";
defines package elements of type c_header which contain program source text, use the file name extension "h" and are located in the subdirectory inc of the package by default.

File kind declarations define the name of the kind and several default attributes. Their meaning is as follows:

You can either use suffix or extension but not both.

compound kind  "c_module" is "c_header" and "c_source";
defines the kind c_module to be a shorthand for the declaration of a c_header and a c_source of the same name (but with a different file name extension).
virtual kind "add_define";
defines add_define declarations to be exported as list of strings to the backend configuration system when computing build actions. To achieve this, a variable with the name PKG_Decls_add_define will be predeclared.

6.2.3. Derivations in CBCL

Derivation rules are mappings from file kinds to file kinds. A typical derivation rule is

 
"c_linkage" : link is "code_object_file"*, "library"* --> "program";


This derivation is of type link which means that it produces an executable program. In order to construct an executable program, zero or more code object files and zero or more libraries need to be combined by a linkage action.

Derivations in ComPact may be of one of the types compile, generate, link, archive, makedepend, instantiate, format, index, convert, translate, hook. Currently only some of them are used. The type is used by ComPact for two purposes: to select appropriate derivation rules for certain kinds of package elements and to link derivation rules to action definitions. Action definitions may be of the same type as derivation rules.

Derivation rules can be thought of as patterns for the computation of dependencies. They contain all the information that ComPact needs to know about all declared kinds to derive the desired compilation and document target files. Action definitions may be regarded as concrete implementations of derivation rules for certain target platform configurations.

6.2.4. Imported Variables

All variables, imported or exported, are of type string, of type int, or of type list. A string is denoted as a sequence of characters enclosed by " characters, an int is denoted as a sequence of digits enclosed by " characters, and the elements of lists are enclosed by parentheses and separated by commas. All types are converted automatically whenever a conversion is needed and possible. Imported variables are instantiated whenever an exported variable or action is requested. The instantiation value is mostly chosen with regard to the local package and the current state of processing that package. There is only one class of exceptions, namely those variables that are instantiated according to the initialization file. This class can be found at the end of this section.



6.2.5. String and List Manipulation

A string expression is either a string enclosed by ", which may contain variable references, or it may be such a reference alone, or it may be built recursively on the basis of the operations shown below. Variable references are always of the form {<variable_name>}.



6.2.6. Exported Variables and Actions

Every exported variable or action is given as a string expression that may, directly or indirectly, involve one or more imported variables. The precise nature of this dependency varies in accordance with the target/platform configuration. In all cases, however, what imported variables an exported entity depends on should be more or less the same. Any differences should have to do with different command names and similar things.

The following list of all entities that may be exported by the backend is rather concise. More specifically, each action is briefly described with regard to the main resource involved in it, be it a source file to be compiled, a library to be shipped, or whatever. Other resources such as, for example, external defines in case of a compilation go unmentioned, since they ought to be obvious from the entity's purpose.



6.2.7. Some words on the selection of actions

ComPact distinguishes several types of actions. The types compile, generate, link, archive, makedepend, instantiate, format, index, convert, translate, hook are predeclared, but currently only the following types of actions are used:

Used types of actions

compile

For all program source kind declarations in PkgDesc, the action <kind prefix>_compilation will be executed.

generate

For all program source kind declarations in PkgDesc, the action <kind prefix>_generation will be executed. After every execution of generation action, the dependency graph is updated to reflect all generated results.

link

For all library declarations in PkgDesc, the action <kind prefix>_linkage will be executed.

archive

For all program declarations in PkgDesc, the action <kind prefix>_linkage will be executed.

makedepend

For all program source kind declarations, the action <program source kind name>_compilation will be executed if it is defined.

instantiate

For all generic instance kind declarations in PkgDesc, the action <kind prefix>_instantiate<rest of kind name> will be executed.

format

For all document source kind declarations in PkgDesc, the action <kind prefix>_format will be executed, as it will be for all derived files of document sources.

The different kinds of actions are always executed in a well specified order during the building of a package. This order is defined by the evaluation of several phony targets of the package dependency graph.

  1. First, the target instantiate is built. This target depends on all actions of type instantiate, so if it succeeds, all instances of generic files that are needed for the current package will exist. After the build, the dependency graph is updated to reflect the state of the newly generated sources.

  2. Second, the target generate is built. This target depends on all actions of type generate. After its completion, all sources generated by other tools than the ComPact instance generator should exist. After every generation step the dependency graph is updated.

  3. Third, the target depend is evaluated. This target depends on all actions of type makedepend. The execution of these actions produces new dependency files, which are then read in and their information added to the dependency graph.

  4. In the main step, the target all is built. all at least depends on the target main, which represents the main section of your package description. During this step, all actions of the types compile, archive, and link are executed.

  5. Last, the target doc is built. This target depends on all actions of the type format, which should be used to format and layout documents.



To determine wether a program target kind is a library or an executable program, the type of the derivations that produce this kind is checked. To determine the actions needed to format all enclosed documentation of a package, all derivations of type format are considered and applied to all source and derived documents.

Future versions of ComPact will only use the derivation rules and no further conventions to determine all the actions chosen to build a package.

6.2.8. A Complete Example

The example shows that it is entirely possible to leave some exported entities undefined. Also, it shows how auxiliary variables can be used for enhancing comprehensibility. Please refer to Section A.3 in Elego ComPact Manual Appendices for the example file.

6.2.9. The complete CBCL Syntax

Here is a short summary of the ComPact Backend Configuration Language:

  Here is a short summary of the language:

  Root ::= TopLevelDecl "." | TopLevelDecl ";" Root
        
  TopLevelDecl ::= KindSection | DerivationSection | ActionSection
  
  KindSection ::= "kind" "declarations" ":" KindPart* "end"
  DerivationSection ::= "derivation" "declarations" ":" DerivationPart* "end"
  ActionSection ::= "action" "declarations" ":" ActionPart* "end"
  
  KindPart ::= ( DefaultKindPart | TPCSpecificKindPart ) ";"
  
  DefaultKindPart     ::= "default" ":"     KindDecl*
  TPCSpecificKindPart ::= "case" String [ "," String ]* ":" KindDecl*
  
  DerivationPart ::= ( DefaultDerivationPart | TPCSpecificDerivationPart ) ";"
  
  DefaultDerivationPart     ::= "default" ":"     DerivationDecl*
  TPCSpecificDerivationPart ::= "case" String [ "," String ]* ":" 
                                DerivationDecl*
  
  ActionPart ::= ( DefaultActionPart | TPCSpecificActionPart ) ";"
  
  DefaultActionPart     ::= "default" ":"     EnvironmentBinding* ActionDecl*
  TPCSpecificActionPart ::= "case" String [ "," String ]* ":" 
                            EnvironmentBinding* ActionDecl*
  
  KindDecl ::= ( FileKindDecl | VirtualKindDecl | CompoundKindDecl ) 
                 PkgElemAttr* ";"

  FileKindDecl ::= "program" "source" "kind" FileKindAttributes
                |  "document" "source" "kind" FileKindAttributes
                |  "generic" "source" "kind" FileKindAttributes
                |  "generic" "instance" "kind" FileKindAttributes
                |  "derived" "kind" FileKindAttributes
                |  "compilation" "target" "kind" FileKindAttributes
                |  "document" "target" "kind" FileKindAttributes
                |  "dummy" "target" "kind" FileKindAttributes
                |  "unprocessed" "source" "kind" FileKindAttributes
                |  "unprocessed" "document" "kind" FileKindAttributes
                |  "unprocessed" "data" "kind" FileKindAttributes
                |  "unprocessed" "file" "kind" FileKindAttributes
  FileKindAttributes ::= String [ "default" "prefix" String ]
                                [ "default" "extension" String |
                                  "default" "suffix" String ]
                                [ "default" "location" String ]

  PkgElemAttr  ::= "preserve" "path"
                |  "document" "source"
                |  "program" "element"
                |  "produces" "object"
                |  "object"
                |  "source"
                |  "header"
                |  "interface"
                |  "generic"
                |  "derived"
                |  "compound"
                |  "virtual"
                |  "library"
                |  "executable"
                |  "program" "target"
                |  "document" "target"
  
  VirtualKindDecl ::= String
  
  CompoundKindDecl ::= String "is" String ( ( "and" | "," ) String )*
  
  DerivationDecl ::= String [ ":" DerivationType ] "is" 
                     DerivationSourceDecls "-->" DerivationTargetDecls ";"
  DerivationType ::= Identifier
  DerivationSourceDecls ::= DerivationSourceDecl 
                         |  DerivationSourceDecl "," DerivationSourceDecls
  DerivationSourceDecl ::= String [ "*" ]
  DerivationTargetDecls ::= DerivationTargetDecl 
                         |  DerivationTargetDecl "," DerivationTargetDecls
  DerivationTargetDecl ::= String [ "*" ]
  
  EnvironmentBinding ::= "let" Identifier "=" Expression ";"
  ActionDecl ::= StringOrId [ ":" ActionType ] ( "is" | "=") String ";"
  ActionType := Identifier
  
  StringOrId ::= String | Identifier

  Expression ::= String
              |  List
              |  VariableReference
              |  FunctionApplication
  
  List         ::= "(" ListElements ")"
  ListElements ::= ListElement | ListElement "," ListElements
  ListElement  ::= Expression
  
  VariableReference ::= "{" Identifier "}"
  
  FunctionApplication ::= FunctionName "(" ArgumentList ")"
  ArgumentList ::= Argument | Argument "," ArgumentList
  Argument     ::= Expression
  
  FunctionName ::= "string"  | "append" | "prepend"   | "concat"  | "subst" |
                   "flatten" | " split" | "addprefix" | "addsuffix" |
                   "combine" | "locate" | "dirname"   | "basename" |
                   "join"    | "ext"    | "noext"     | "setext" |
                   "env"     | "pid"    | "store"     | "def" |
                   "val"     | "file"   | "fileapp"   | "fileval"