voverrides - a guide to building Vesta-2 models with overrides



The existing bridge and standard environment models provide a wide variety of mechanisms for building Vesta-2 models with various forms of overrides. This note describes those mechanisms, and gives examples of their most common usage. In particular, it documents the common/std_env/18 and common/cxx/14 packages (presumably, later versions of these packages will conform to this man page as well).

There are two general classes of overrides: transient and permanent.

Transient overrides are specified through the Vesta control panel for a particular purpose at hand. For example, they include building a program against a locally checked-out version of a library, or building a package with profiling enabled. Since we currently don't have a Vesta control panel application, practically speaking, transient overrides are made by modifying a package's ".main.ves" file. The changes made to that file are transient because they are not visible to clients that import the package.

Permanent overrides, on the other hand, are made to the visible models of a package. Permanent overrides take precedence over transient overrides, so they should only be made if they are absolutely necessary to building the package correctly. For example, in compiling the C source files of a particular package, it might be necessary to include a macro definition on the command-line to the compiler.

The rest of this man page is divided into sections describing different kinds of overrides, showing examples of their use. It may help to be familiar with the vtypes(5) man page, which describes the Vesta-2 types used in the std_env and cxx models, although the HTML version of this document contains many links to portions of that man page.

General vs. Named Overrides

There are two mechanisms for overriding, which work quite differently:

General Overrides

An override may be expressed as a binding (possibly with other bindings as substructure) whose shape resembles a portion of the standard environment. If "." is the environment and "ovs" is a general override, then at some appropriate point in the building process, the statement:
. ++= ovs;
is executed. The resulting environment is then used for the portion of the building process to which the override "ovs" applies. In this particular case, "ovs" will be of type "EnvOvs".

Sometimes, the overrides "ovs" is applied to a part of the standard environment. One common case is when a particular bridge of the standard environment is overridden. This is accomplished by the statement:

. ++= [ $bridge = ovs ];
In this particular case, "ovs" will be of type "BridgeOvs".

Named Overrides

An override may be expressed as a binding (possibly with other bindings as substructure) that is interpreted like a table, with the names being the keys. If "b" is some binding and "ovs" is a named override, then at some appropriate point, the statement:
res = b ++ ovs/$name;
is executed. Here, "b" represents a set of defaults, and "ovs/$name" consists of a collection of overrides to the defaults for a specific case, identified by "$name. The resulting binding "res" is then used for the relevant building action for this specific case.

Common cases of named overrides include bindings of type "NamedCompileOvs" that specify how particular source files should be built, or bindings of type "ToolSwitches" that associate abstract switch names like "debug" and "optimize" with particular tool-specific command-line switches.

We will see concrete examples of both kinds of overrides in the examples that follow.

A Vanilla .main.ves File

Before describing any transient overrides, we first give an example of a vanilla ".main.ves" file that includes no overrides:

import self = build.ves;
from /vesta/src.dec.com/common import std_env/18;
    // construct the standard environment
    . = std_env()/env_build()("AlphaOSF");

    // build the package
    return ./generic/eval_binding_vals(self());
The first line of the model's body initializes the standard environment "." specifying Alpha/OSF as the target platform. The next line invokes the packages top-level build.ves model. This model returns a binding of type PkgResult that maps names to 0-argument closures. The function ./generic/eval_binding_vals iterates over that binding, evaluating each of the closures, and returning a binding with the same names in the same order bound to the result of the corresponding closure. Typically, the names are "lib", "progs", and "tests"; the corresponding closures, when invoked, build the package's library (if it exports one), programs, and test programs.

Transient Package Overrides

Perhaps the most common form of override involves overriding which version of a package is used. Since the repository uses a two-level namespace for naming packages, package overrides are depth-two bindings of type "QNamedPkgOvs" whose leaves correspond to package models. The "Q" in the name of this type stands for "qualified": it indicates the use of a two-level naming structure.

One extremely common case when building a library package is to override the package itself so the package's test programs will be linked against the local version of the package. Using an override of this form is standard procedure for library packages. Here is an example from the "vesta/basics" package:

import self = build.ves;
from /vesta/src.dec.com/common import std_env/18;
    // construct the standard environment
    pkg_ovs = [ vesta = [ basics=self ] ]; // override with "self"
    . = std_env()/env_build()("AlphaOSF", pkg_ovs);

    // build the package
    return ./generic/eval_binding_vals(self());
The "pkg_ovs" variable binds the name of the package to the package's top-level model. As stated before, this value is of type "QNamedPkgOvs".

Another common case occurs when you are building a program in one package and want to build it against a local version of a library defined in a different package. This is done by including a reference to the appropriate checkout session and passing an extra argument to the function for constructing the standard environment. Here is an example from the "vesta/log" package that overrides the "vesta/basics" package:

import self = build.ves;
from /vesta/src.dec.com/common import std_env/18;
from /vesta/src.dec.com/vesta import basics/checkout/20/15;
    // construct the standard environment
    pkg_ovs = [ vesta = [ log=self, basics ] ]; // package overrides
    . = std_env()/env_build()("AlphaOSF", pkg_ovs);

    // build the package
    return ./generic/eval_binding_vals(self());
Any number of different packages may be overridden, including bridge packages and the "common/bridge_generics" package. For example, to use a checked-out version of the "common/cxx" bridge as well as the "vesta/basics" package, you would write:
import self = build.ves;
from /vesta/src.dec.com/common import
from /vesta/src.dec.com/vesta import
    // construct the standard environment
    pkg_ovs = [           // package overrides
      common = [ cxx ],
      vesta = [ log=self, basics ]];
    . = std_env()/env_build()("AlphaOSF", pkg_ovs);

    // build the package
    return ./generic/eval_binding_vals(self());

Transient Build-Wide Overrides

The second-most common form of override is to override the switches used in building an entire program, including how all of the program's libraries are built.

This is done by specifying a general override for the environment. In particular, if you want to override how various tools like the compiler or linker are invoked by specifying particular command-line switches for them, you would override part of the "switches" binding for the bridge in question. To override general build options (such as whether you want to use hierarchical library names), you would override part of the bridge's "options" binding.

Build-wide overrides are accomplished by recursively overlaying a binding on the "env_ovs" field of the standard environment. For example, to compile all files built using the Cxx bridge with the highest warning level, your .main.ves file would be:

import build = build.ves;
from /vesta/src.dec.com/common import std_env/18;
    // construct the standard environment
    . = std_env()/env_build()("AlphaOSF");

    // build-wide override: compile all files with warnings
    . ++= [ env_ovs/Cxx/switches/compile/warnings = "-w0" ];

    // build the package
    return ./generic/eval_binding_vals(build());
Notice that the standard environment "." is overlayed with the extra switch after the environment is constructed but before the package's main model is built.

Multiple overrides may be specified simply by creating the appropriate binding structure. For example, to compile with debugging and maximal warnings, and to link programs with optimization, you would overlay the standard environment by writing:

    // build-wide overrides
    . ++= [ env_ovs/Cxx/switches = [
      compile = [ debug = "-g", warnings= "-w0" ],
      program = [ optimize = "-O1" ]
In this binding, the names "compile" and "program" are special, since they are hard-wired into the bridge's so-called expert functions. The names for the individual switches are not special, except that switches are overridden by these names. Hence, if you want to override the default switches installed by the bridge, you will have to know the name given by the bridge to each switch category. You can find out the default switch names and their values by printing the value of "./<bridge>/switches" after the call that creates the standard environment.

You can also use build-wide overrides to set build options. As another example, you could specify the following overrides to enable hierarchical library naming and to disable the bridge's verbose mode, respectively.

    // use hierarchical library naming
    . ++= [ env_ovs/Cxx/options/flat_lib_names = FALSE ];

    // make the bridge quiet
    . ++= [ env_ovs/Cxx/options/verbose_bridge = FALSE ];

Transient Library Overrides

This section describes how to override the construction of a leaf library or an entire umbrella library. For more information about leaf and umbrella libraries, see the vlibraries(5) man page.

The construction of a leaf or umbrella library is overridden by augmenting the "lib_ovs" field of the standard environment. The value of this field is of type "NamedLibOvs". As described with the documentation of that type, it is either a flat binding of type "NamedLibDescs" or a binding tree whose leaves are of that type. Whether the value is flat or not depends on whether flat library names are being used or not.

First, consider the case where flat library names are being used. Then the "lib_ovs" field of the environment must be overridden by a value of type "NamedLibDescs". This type is a binding mapping library names to one of the subtypes of a "LibDesc" value, most likely a value of type "LeafLibDesc" or "UmbrellaLibDesc". The important thing about both of these types is that that have a field name "ovs" of type "BridgeOvs".

To determine the name of the leaf or umbrella library you want to override, you have to look at the model that constructs that library. The first argument to the "leaf" or "umbrella" bridge function will be the library name.

For example, to compile the "libVestaBasics.a" leaf library with debugging symbols, you would add the following overlay to your ".main.ves" file:

    // library overrides
    . ++= [ lib_ovs/libVestaBasics.a =
      [ ovs/Cxx/switches/compile/debug = "-g" ]];
To build an entire umbrella library in a particular way, just name the umbrella library instead:
    // library overrides
    . ++= [ lib_ovs/libVestaBasicsUmb =
      [ ovs/Cxx/switches/compile/debug = "-g" ]];
This causes all leaf libraries that are descendants of the named umbrella library to be built with the specified override.

It is also possible to override both an umbrella and a leaf with a single override. For example, to build the entire umbrella with debugging, but the leaf library with simple optimization, the override would be:

    // library overrides
    . ++= [ lib_ovs = [
      libVestaBasicsUmb/ovs/Cxx/switches/compile/debug = "-g",
      libVestaBasics.a/ovs/Cxx/switches/compile/optimize = "-O1" ]];
So much for overriding libraries when flat library naming is being used.

If hierarchical library names are being used, then the value of the "lib_ovs" field should be a binding. For example, if the "libVestaBasics.a" leaf library resides in the "libVestaBasicsUmb" umbrella, an override for the leaf library might take the form:

    // library overrides
    . ++= [ lib_ovs/libVestaBasicsUmb/libVestaBasics.a =
      [ ovs/Cxx/switches/compile/debug = "-g" ]];
As before, we can specify overrides for both umbrellas and leaves in a single override. The override in the hierarchical case corresponding to the last example presented above for the flat case is:
    // library overrides
    . ++= [ lib_ovs/libVestaBasicsUmb = [
      ovs/Cxx/switches/compile/debug = "-g",
      libVestaBasics.a/ovs/Cxx/switches/compile/optimize = "-O1" ]];
The only difference between the flat case and the hierarchical case is that the overrides for "libVestaBasics.a" are underneath "libVestaBasicsUmb" in the hierarchical case; in the flat case, they are at the same level.

Transient Library File Overrides

It is also possible to override how a single file in a particular leaf library is compiled. This is accomplished using a named override.

In particular, the "per_file_ovs" field of the leaf library in question must be overridden to contain a binding of type "NamedCompileOvs", a binding that maps filenames to values of type "CompileOvs". Each of these has at most the following two fields:

lang: text
This field specifies the name of the bridge to use to build the program.

ovs: BridgeOvs
This field specifies an overlay for the bridge used to build the program. Notice that as opposed to the "ovs" field of a leaf or umbrella library description, which is of type "EnvOvs", this is an override for a particular bridge, not the whole environment. This means the bridge name (such as "Cxx") should not appear in the override.

For example, here is a per-file override that specifies that the file named "Basics.C" in the "libVestaBasics.a" library should be compiled with optimization:

    // per-file library override
    . ++= [ lib_ovs/libVestaBasics.a/per_file_ovs = [
      Basics.C = [ ovs/switches/compile/optimize = "-O1" ]]];
If you were using hierarchical library naming, you would instead write:
    // per-file library override
    . ++= [ lib_ovs/libVestaBasicsUmb/libVestaBasics.a/per_file_ovs = [
      Basics.C = [ ovs/switches/compile/optimize = "-O1" ]]];
Of course, in either case, the "per_file_ovs" binding is allowed to contain names of multiple files, with each being bound to a potentially different set of overrides instructions.

Permanent Library Overrides

Permanent overrides work similarly to transient overrides. One main difference is that they are usually expressed by supplying override arguments to bridge functions, rather than by overlaying a binding over the current environment.

To override how a library is constructed, you pass an overrides binding as an explicit argument to the "umbrella" or "leaf" bridge function. For example, assuming the identifiers "c_files", "h_files", and "priv_h_files" have been bound to the appropriate file bindings in the files clause, here is a function that produces a leaf library all of whose files will be compiled with the compiler switch "-DDEBUG"":

    lib() {
	ovs = [ Cxx/switches/compile/def_debug = "-DDEBUG" ];
	return ./Cxx/leaf("libLeaf.a",
          c_files, h_files, priv_h_files, ovs);
The value "ovs" is a general override. It should be of type "EnvOvs", i.e., it is a binding that is recursively overlayed on the environment in which the library is built.

You can also specify a named override for a leaf library by passing a binding for the "per_file_ovs" parameter of the "leaf" function. This parameter is of type "NamedCompileOvs"; it maps files names to values of type "CompileOvs". For example, here is how to build the above leaf library with the file "runtime.C" compiled with optimization:

    lib() {
	ovs = [ Cxx/switches/compile/def_debug = "-DDEBUG" ];
        per_file_ovs = [ runtime.C =
          [ ovs/switches/compile/optimize = "-O1" ]];
	return ./Cxx/leaf("libLeaf.a",
          c_files, h_files, priv_h_files, ovs, per_file_ovs);
Overrides for umbrella libraries work similarly. Umbrella overrides are specified by passing a non-empty value for the "ovs" field of the "umbrella" function. As before, this parameter is a general override of type "EnvOvs". That is, it is a binding that is recursively overlayed on the environment in which the umbrella library and all of its descendant libraries is built.

For example, to specify that an entire umbrella library should be built with optimization, you could write:

    // select libraries
    libs = [ ./Cxx/libs/vesta/pkg1, ./Cxx/libs/vesta/pkg2 ];

    // compile umbrella with optimization
    ovs = [ Cxx/switches/compile/optimize = "-O1" ];

    // build the umbrella
    return ./Cxx/umbrella("libExampleUmb", libs, ovs);

Permanent Program Overrides

There are three distinct parts to building a program, and permanent overrides can be specified for each. First, the libraries on which the program depends must be compiled. Then, the sources comprising the program itself must be compiled. Finally, the compiled sources must be linked with the libraries.

To build a program, you use the "program" bridge function. In addition to the sources and libraries from which the program should be built, this function takes three additional arguments for specifying overrides; they are named "ovs", "env_ovs", and "lib_ovs".

The "ovs" parameter is a general override of type "EnvOvs" applied to the environment in which the program's sources are compiled and in which the final program is linked.

The "env_ovs" parameter is also a general override of type "EnvOvs". It is applied to the environment in which the program's libraries are built.

Finally, the "lib_ovs" is a named override of type "NamedLibOvs" that can be used to specify overrides for how particular libraries or files within those libraries are built.

Notice that the general overrides "ovs" and "env_ovs" apply to independent parts of the build process. If you want the same override to apply to both parts, you have to specify that same override in both parameters. (Another alternative is to specify the override in "./env_ovs" as described below.)

Here is a simple example, taken from the "vesta/basics" package. This model builds several test programs; it demonstrates the use of the "ovs" and "env_ovs" parameters.

    TestIntSeq = [ TestIntSeq.C ];
    TestIntTbl = [ TestIntTbl.C ];
    TestOS     = [ TestOS.C ];
    TestText   = [ TestText.C ];
    TestThread = [ TestThread.C ];
    TestVAF    = [ TestVestaAtomicFile.C ];
    libs = < ./Cxx/libs/vesta/basics >;
    ovs = [ Cxx/switches/program/shared_libs = "-non_shared" ];
    env_ovs = [ Cxx/options/useGClib = "libgcthrd" ];
      ./Cxx/program("TestText",   TestText,   [], libs, ovs) +
      ./Cxx/program("TestTextGC", TestText,   [], libs, ovs, env_ovs) +
      ./Cxx/program("TestIntSeq", TestIntSeq, [], libs, ovs) +
      ./Cxx/program("TestIntTbl", TestIntTbl, [], libs, ovs) +
      ./Cxx/program("TestOS",     TestOS,     [], libs, ovs) +
      ./Cxx/program("TestThread", TestThread, [], libs, ovs) +
      ./Cxx/program("TestVAF",    TestVAF,    [], libs, ovs);
The "env_ovs" override specifies that the program should be built to use the garbage collector library. That override is only supplied when the "TextTextGC" program is built, so all the other programs are linked without the garbage collector.

All three of the "program" overrides can also be specified indirectly through overrides to the current environment. In particular, the general overrides in "./env_ovs" are applied both to the construction of the program's libraries, and to the construction and linking of the program itself. The named overrides in "./lib_ovs" are applied during the construction of the libraries, as if they had been passed via the explicit "lib_ovs" parameter. For more information about these two types of overrides, see the above sections on build-wide overrides, library overrides, and library file overrides. By placing such files in regular models instead of the special ".main.ves" model, they become permanent instead of transient. In cases where both an override through "." and one through an explicit parameter are specified, the former takes precedence.

Passing explicit values is convenient if you are building several programs with different overrides, as the example above demonstrates. When all of the programs are being built with the same overrides, it may be more convenient to specify them via an override to the standard environment. As a final example, here is a model from the "vesta/fp" package that uses a combination of the two styles:

files TestFP = [ TestFP.C ];
import lib = build.ves;
    // required libraries
    v_libs = ./Cxx/libs/vesta;
    libs = < v_libs/fp, v_libs/srpc, v_libs/log, v_libs/basics >;

    // overrides for this program
    ovs = [ Cxx/switches/program/shared_libs = "-non_shared" ];
    . ++= [ env_ovs/Cxx/options/useGClib = "libgcthrd" ];

    // build program
    return ./Cxx/program("TestFP", TestFP, [], libs, ovs);
The "ovs" parameter specifies that the program should be statically linked, and the override to "./env_ovs" specifies that the program should be built with the GC version of the libraries.

See Also

vlibraries(5), vmodels(5), vtypes(5).


Allan Heydon

Last modified on Fri Nov  9 12:14:21 EST 2001 by ken@xorian.net
     modified on Mon Mar 16 17:08:47 PST 1998 by heydon
This page was generated automatically by mtex software.