Chapter 6. Target/Platform Configuration Management

Table of Contents
6.1. TPC-dependent Build Management
6.2. Configuration Files

The previous chapters have taught you the basic concepts of using Compact for dividing software into packages, building libraries and programs, and maintaining pools of package installations. This knowledge more or less suffices for working with ComPact unless you want to take advantage of functionality from the areas of TPC management (this chapter), version control (Chapter 7), and built-in template instantiation (Chapter 8).

As for TPC-management, this functionality reflects the fact that modern software production often involves supporting a variety of target platforms. The usual response consists of maintaining a heterogenous development environment. Whenever a library or program for some particular platform is built, then its source code is compiled and, in the case of a program, linked together on that same platform. The code is mostly written so that it is always the same, dealing with TPC-dependencies by taking advantage of C/C++ preprocessing or other mechanisms. What files are respectively to be generated -- object files, libraries, programs, and documentation -- and, at the same time, their dependencies will also be the same. The level of package structure, package description, and using pkgm for building, shipping etc. is, therefore, not very deeply influenced by the environment. It suffices to provide this part of ComPact, the front end, with a simple mechanism whereby package installations can be built and shipped on the basis of TPC-dependent package description declarations.

The back end is concerned with the how of generating and handling object files, libraries, programs, and documentation. Obviously, this part depends very much on the operating system, the compiler, and so forth. It is, therefore, more complicated than the front end mechanism of realizing TPC-dependencies. Yet, there is no need whatsoever for taking notice of it once it is configured. In addition, that configuration is not quite as complicated as one might expect it. All what you must do consists of defining a set of exported variables and exported actions on the basis of a set of imported variables, which make up the interface to the front end. These definitions can be given in a purely declarative way by employing auxiliary variables and simple data type of strings and lists of strings.

In sum, software packages and static package description blend very well with using a backend for hiding TPC-dependencies that are not package-specific. The remainder of this chapter reflects the two levels of TPC-management accordingly: Section 6.1 is concerned with the front-end side; Section 6.2 is concerned with the back-end side.

TPC Names  : TPC names are a common aspect of both the front end and the back end. The formal syntax is

<string1>-<string2>-<string3>-<string4>[-<string5>]
meaning that it is simpler and, at the same time, more general than suggested by the pragmatics described in Section 2.1 and Section 2.1.1. In particular, it is possible to use generic TPC names such as i386-any-any-gcc-optimize for designating classes of target/platform configurations. The matching discipline, however, is strict: Any two arbitrary TPC names match if they are the same as strings.

6.1. TPC-dependent Build Management

The starting point of TPC-dependent build management consists of additional package description syntax. This syntax is such that installations can be built and shipped so that certain package elements are considered in a TPC-dependent fashion. As for the technicalities, you might want to recall the three different kinds of sections that may belong to a package description file: There may be a general section, a main section, and zero or more test sections (cf. Chapter 4). Each section is made up of a list of declarations -- (Decl +) -- possibly followed by a list of target declarations -- [ TargetDecl + ]. To make part of the list of declarations TPC-dependent, insert a target selection statement of the form

`target' TargetSpecification/

with

TargetSpecification ::= TokenOrString

into it. -- The instance of TargetSpecification must, of course, contain the name of the TPC in question, or a regular expression matching the current TPC.

The statement applies to all subsequent declarations up until the next target selection statement or the end of the list. More specifically, it determines under what conditions the build process takes notice of the package elements declared inside its scope: They are taken into account if and only if the current TPC is matched by the specified Target Specification.

Complementary to that, TPC-dependencies are reflected with regard to shipments, pools, and imports. For ComPact keeps track of under what TPC any particular installation has been built and shipped. Moreover, package imports are always resolved by considering only those installations that have been shipped under a TPC that is the same as the current one.

The concluding example below shows how to extend the message utility package from Chapter 4 with a TPC-dependent external define. In this particular case, the define is used to switch on debugging code.

MessageUtil / PkgDesc:

c_header("messageutil", exported)
c_source("messageutil")
target "i386-freebsd2-aout-gcc-debug" # This TPC name is, of course,
                                      # an example. You may replace it 
                                      # by any other one making sense 
                                      # in your particular environment.
  add_define("MESSAGEUTILDEBUG")
  library("messageutil")              # exported by default 
              
test "message procedure"
  c_source("messageutiltest")
  program("messageutiltest")
MessageUtil / src /  messageutil.c: 

#include  <stdio.h>
void message ( char* string ) {
#ifdef MESSAGEUTILDEBUG 
  if ( string == NULL ) {
    printf("<< void message invoked with string == NULL >>\n"); 
    return; 
  };
#endif
  printf("\n%s\n\n", string);
}