This example demonstrates the power of Jam as an integral component of an automated build environment with dynamic generation requirements.

 

A user had a build process that used a manual pre-operation configuration and typically ran for most of a day because their default dependency rule was “build it just in case we missed it during configuration”. Moreover, because of differences in generation environments, the development build process was different from the release engineering build process, which was in turn different from the build process used by support. The primary initial project goal was to automate the entire build process. A secondary goal was to make the build process common for all users. Combining Jam with some “glue” scripts achieved both of these goals, and more, in a relatively short period of time.

 

The primary “glue” script scans the current working directory and all of its subdirectories looking for either custom control or Jam files. Custom control and Jam files are uniquely identifiable through naming conventions. Custom control files are dynamically resolved into Jam files. Jam files are referenced directly. All of these Jam files are then referenced by a common include file that forms the input control for a Jam based build master.

 

The general form of the Jam based build master is:

 

# Expected argument sequence:

#    $1 - File to be generated

#    $2 - Primary source for file being generated

#    $3 - Configuration control files for source

#    $4 - Generation program (with options)

 

rule Generate {

     GENERATOR on $(1) = $(4) ;

     CONTROLS on $(1) = "-c "$(3) ;

 

     Depends all : $(1) ;

     Depends $(1) : $(2) $(3) $(4) ;

 

     Generator $(1) : $(2) ;

}

 

actions ignore Generator {

     $(GENERATOR) $(CONTROLS) -o $(1) $(2)

}

 

# What has the script found this time??

 

{

     include ThisRun ;

}

 

The contents of “ThisRun” would reference Jam files that ultimately specified instructions such as this:

 

Generate O : Y : c1 c2 : bin\\gen ;

Generate c1 : Z : c2 : bin\\cgen ;

Generate c2 : Z : : bin\\cgen ;

 

Using this straightforward structure, all users can use the same build process because the process adapts to the user’s environment. A user only needs to populate their environment with the files being worked on. Instead of a manual, and often error prone, specification process the tools dynamically adapt to the user’s environment. Missing environmental dependencies are quickly identified as part of the Jam output. For example, if the cgen generator was missing from your environment in the example above, you’d see something like this as output from Jam and you’d know you were missing the generator program:

 

don't know how to make bin\cgen

found 8 target(s)...

can't find 1 target(s)...

 

And there were other benefits to using Jam in this process. Prior to this process, build times were not particularly predictable. Some types of generators typically ran for seconds, others for minutes, and others for tens of minutes. Evaluating build times or the impact of “what if” scenarios required significant effort. Jam’s –o option presented a solution to this problem. For the example above, running Jam with the –o option would generate output that looked like this:

 

bin\cgen   -o c2  Z

bin\cgen  -c c2  -o c1  Z

bin\gen  -c c1 -c c2  -o O  Y

 

The results are scanned for the presence of specific generators. Each instance of a specific generator is associated with a processing time estimate and thus build times can be predicted. Even for complex environments involving thousands of generated objects the processing time for the “glue” scanner, Jam, and the –o evaluator was typically minutes.

 

Output from Jam’s –o option was also used as input to a pre-generation validation of build results. This was very important in an environment where release build cycles were typically measured in significant fractions of a day. The output from running Jam with –o could be scanned for important build elements. If an expected element build was not going to happen, you knew in minutes rather than in hours. This simple technique had a dramatic impact on release engineering productivity and the usability of releases.

 

This process has adapted and evolved over time to include new elements of the environment. Initially, the process encompassed the user’s custom generation needs. Eventually, the “glue” scanner was enhanced to scan Microsoft Visual Studio VBP and DSP files for dependencies that were used as input to a generator compilation rule.

 

Jam’s inherent separation of platform-dependent issues was also a benefit. Initially, this process was developed and debugged in a Windows environment. Having used Jam as the build manager made adapting these processes to MAC and UNIX systems a straightforward task. The generated Jam scripts transported “as is”. The most complex part of the porting task was adapting the “glue” scanners.