Chapter 4. Configuration of the Project Management Activities

It is possible to adapt prjm to a variety of uses, since its main functionality is the application of actions to sets of packages. This can be specified and implemented in a very general way, since the actual actions performed by prjm are executed by external programs. All actions used have a symbolic name that refers to a sequence of commands which possibly contains one or two variables. The mapping of symbolic actions to command sequences is defined in a configuration file read by prjm each time it starts executing.

Since the actions performed depend on the kind of package they are applied to, the configuration file specifies all symbolic actions for each known kind of package. Kinds of packages are distinguished by their structure and the presence or absence of certain files.

The configuration for the backend of prjm is contained in a resource file which is bundled with the executable, but may be overridden by definitions from the ComPact remote resource service or a real file found in the file system. If the file PkgBase.DefaultData is found in one of the directories of the resource patch configpath (defined in compactrc) or -- if not defined -- at any of the following locations:

 ${HOME}/pkgm
 /usr/contrib/lib/pkgm
 /usr/local/lib/pkgm
     


its definitions will override the values from the bundled and remote configuration resources.

The default search path can be changed via the configpath setting in the global initialization file .compactrc or compactrc, which is usually read from the location specified in the environment variable COMPACTROOT or your home directory.

4.1. Configuring Different Kinds of Packages

The configuration consists of a list of package kind declarations which describe the structure of a software package, additional selection predicates, and the actions associated with this kind of software package. The complete declarations that are currently bundled with prjm can be found in Section A.2 in Elego ComPact Manual Appendices.

We will proceed by having a closer look at the elements of the package kind definitions in this resource file. The definition for Modula-3 packages begins with the following lines:

    # ----------------------------------------------------------------------------
    # DEC SRC Modula-3 packages
    # ----------------------------------------------------------------------------
    pkgkind DEC_SRC_M3_UNIX
      ostype "bsd|unix|linux" has file "src/m3makefile" and dir "src"
       


Every line that begins with a # is a comment line and is ignored by all ComPact programs.

The first meaningful statement is pkgkind DEC_SRC_M3_UNIX, which introduces a new package kind, that is a special type of package with its own structure, elements, operations, and use. The chosen name DEC_SRC_M3 should tell you that it is a package kind for Modula-3 programs and libraries that are compiled with the Modula-3 compiler from the Systems Research Center of the Digital Equipment Corporation. The suffix _UNIX distinguishes this kind of package (in particular the operations that can be performed on it) from similar package kinds for other operating systems (e.g. _WIN32). The use of these suffixes is purely conventional.

The first lines of each package definition must contain a predicate that is used to determine if this package kind is appropriate for a given package (a real structure on the file system). There are several conditions that must all be fulfilled if the package kind definition shall be used for a real package. In the example above, we see that this package kind definition only applies to operating systems of type bsd, unix, and linux. ComPact knows about the type of your operating system from precompiled constants, several system calls, setting of environment and compact-variables, and command line overrides of these values.

The next two conditions define the necessary minimal structure of each DEC_SRC_M3_UNIX package: it must contain a file src/m3makefile and a sub directory src. Please note that these conditions are given in reverse order of the necessary sequence to create a minimal package. Indeed does ComPact use these conditions to create new and empty packages of a given kind, and the reverse order is due to a very simple internal implementation of this action.

There are several more conditions that can be tested in the predicate section of a package definition. Here is a comprehensive list:

dir pathname

is true if a directory with the given pathname exists beneath the package root directory.

file pathname

is true if a file with the given pathname exists beneath the package root directory.

nodir|notdir pathname

is false if a directory with the given pathname exists beneath the package root directory.

nofile|notfile pathname

is false if a file with the given pathname exists beneath the package root directory.

match regex-path-list

is true if the regular expression path list given matches a name of a subdirectory of the package root directory. The delimiter of a pathname element is always the forward slash `/'.

nomatch|notmatch regex-path-list

is false if the regular expression path list given matches a name of a subdirectory of the package root directory. The delimiter of a pathname element is always the forward slash `/'.

platform regex

is true if the value if the internal variable platform matches the given regular expression.

hosttype regex

is true if the value if the internal variable hosttype matches the given regular expression.

ostype regex

is true if the value if the internal variable ostype matches the given regular expression.

The keywords dir, file, nodir, notdir, nofile, notfile, match, nomatch, notmatch must all be preceded by has or and. The keywords platform, hosttype, ostype don't require a preceding word.

Since early in the development of ComPact release 1.2, package kind definitions also support inheritance of predicates (single inheritance) and actions (multiple inheritance). This is accomplished with the keywords inherit predicates and inherit actions as in the following examples:

    pkgkind COMPACT_CC_UNIX
      ostype "bsd|unix|linux|sunos5" has dir "src" and dir "inc" and 
      dir "doc" and file "PkgDesc"
      inherit actions COMPACT_VC_UNIX_OVERRIDES
      inherit actions COMPACT_VC
      inherit actions COMPACT_BUILD

    pkgkind COMPACT_SIMPLE_UNIX
      ostype "bsd|unix|linux|sunos5"
      inherit predicates COMPACT_SIMPLE
      inherit actions COMPACT_VC_UNIX_OVERRIDES
      inherit actions COMPACT_SIMPLE


All following definitions that do not match any of the above keywords are considered to be action definitions or the beginning of a new package kind definition. Action definitions begin with the keyword action and have the form

action   symbolic-name  "command sequence (possibly containing variables)"


The semantics of an action definition is that the given command sequence will be used for the ComPact project management action with the given symbolic name in the context of a package of the given package kind (that is, the package fulfills the conditions of the package kind definition).

Command sequences are ordinary commands you can also type on the command line of an interactive shell. ComPact uses a simple built-in shell to be independent of the existence of any external program to execute simple commands, but you can also specify a shell to be used as a special action or a command line parameter. The simple built-in shell only knows about some kinds of output redirection and does not perform expansion of shell globbing patterns as most Unix shells do. It also understands the operators `&&', `;', and `||' that can be used to concatenate program statements. We regret that pipes are currently not implemented, too, but you can always use an external shell for really complicated expressions.

A command sequence may also contain variable reference of the form {?name}, {:name}, {!name}. These patterns are substituted by the value of the internal variable name, which is either defined by default, or in the global initialization file compactrc or .compactrc, or on the command line. If the variable name is preceded by a question mark, then the expansion of the variable is optional, i.e. it need not be defined. If the name begins with a colon, the expansion is mandatory and leads to an error if no corresponding variable is defined. If the name begins with an exclamation mark, the expansion is mandatory and must not yield an empty result. Let's have a look at a few real action definitions for M3 packages:

    action build      "m3build {?OPT} {?M3BUILDOPT} 2> .errors && m3err .errors"
    action buildlocal "m3build {?OPT} {?M3BUILDOPT} -O  2> .errors && m3err .errors"
       


These are the action definitions for building a package, that is, insure that all derived files in the package (object files, libraries, executable programs, formatted documents etc.) exist and are up-to-date. The first form makes use of the package pools that ComPact uses as caches for results of package builds, while the second form should only use locally available packages of the project in the workspace of the developer. In the case of Modula-3 this is achieved by constructing an m3overrides file which contains the location of the project packages. (This is part of the action mkdep.)

The optional variables OPT and M3BUILDOPT may be used to pass further parameters from the command line (which is also done, for example, by the ComPact HTTP daemon ComPactHTTPd that generates an HTML graphical user interface that can be used with all standard web browsers).

The detailed description of every action of every package kind definition contained in ComPact's default resource file would be tedious and boring. Instead we give a comprehensive list of the symbolic action names that are currently used by the ComPact tools and the intended semantics and pragmatics.

build

creates and updates all derived files of a package using all available package pools.

buildlocal

creates and updates all derived files of a package using only locally available packages.

shipglobal

ships the package to the global package pool.

shipproject

ships the package to the project package pool.

shiplocal

ships the package to the local package pool.

clean

removes all derived files of a package, but not necessarily depending information and other files that may be used for the build process.

realclean

removes really all derived files of a package.

mkdep

computes the dependencies of the package, that is, all used packages and stores the result in the file PkgDep. This file must contain standard makefile dependency lines, i.e. target: dep1 dep2 ...

isrelease

must return 0 if the package is checked out as release.

conflicts

must return 0 if the package is in conflict with changes in the repository.

uptodate

must return 0 if the package is up-to-date.

modified

must return 0 if the package is locally modified.

checkrelease

same as isrelease, but may output verbose information.

checkconflicts

same as conflicts, but should output a list of conflicting files.

checkuptodate

same as up-to-date, but should output a list of up-to-date files.

checkmodified

same as modified, but should output a list of modified files.

currentdeveltag

must write the current development tag of the package to the file PkgCDT.

currentreleasetag

must write the current release tag of the package to the file PkgCRT.

currenttag

must write the current tag of the package to the file PkgCT.

commitdevelmajor

must commit all local changes of the package to the version repository and increase the major version number for the new version. The new version must be committed to the tip of the main development trunk.

commitdevelminor

same as commitdevelmajor, but must increase only the minor version number.

commitdevelpatch

same as commitdevelmajor, but must increase only the patch level of the version number.

commitreleasemajor

must commit all local changes of the package to the version repository and increase the major version number for the new version. The new version must be committed as a new release, i.e. a new release branch is created and the new version is placed at its tip.

commitreleaseminor

must commit all local changes of the package to the version repository and increase the minor version number for the new version. The new version must be committed as a new release, i.e. a new release branch is created (which is probably forked from an other release branch) and the new version is placed at its tip.

commitreleasepatch

must commit all local changes of the package to the version repository and increase the patch level of the version number. The new version must be committed to an existing release branch (on which the package must be checkd out) and placed at its tip. This commit is supposed to contain only bugfixes for an existing release.

update

has the mandatory parameter {:TAG}. The package must be updated to the given symbolic name (the value of TAG), i.e. after the update the package version is based on the given symbolic name plus all local modifications.

checkout

has three mandatory parameters: {!LOCATION}, {!TAG} and {!PKG}. The package with the given name must be checked out at the given location and the version of the specified symbolic name (tag).

getlabel

must write the label of the package version specified by the mandatory parameter {:TAG} to the file .label.

currentlabel

must write the current label of the package (better: package version) to the file .label.

setlabel

must use the parameters {:TAG} and {:LABEL} to set the label of the specified package version.

listlabels

must write all package version labels to the file .labels.

externalshell

This is a pseudo-action that is not actually executed, but defines an external shell to be used for the execution of all other actions.



These actions are the atoms of the project management procedures performed by the ComPact programs prjm and ComPactHTTPd. They may be called in a variety of combinations and sequences for one, several, or all packages of a project, depending on the task to be performed. Future versions of ComPact are likely to use yet more predefined actions for project management.

If you create new package kinds by adding definitions to the PkgBase.DefaultData resource, you must be careful to define all needed actions and to keep conform with the expected file names and formats. Thus said is should not be difficult for you to create your own package kinds using the above descriptions of actions.

You should also have a look at the complete resource. Just type

prjm -dumpkinds
. You will easily understand the package kind definitions and be able to construct your own. You can start by copying an existing definitions into a PkgBase.DefaultData file in the local resource path (configpath), renaming it and trying some changes. The project manager will pick up local file resources last and their definitions will override those contained in bundled or remote resources.
prjm -listkinds
will list all the package kinds defined in all available PkgBase.DefaultData resources.

There's just one more peculiarity that should be mentioned here: If you want to add a comment line, make sure it starts with a `#' and a blank ` '. Otherwise the parser won't recognize it as a comment.