Chapter 5. Defining View Configurations

This chapter presents a series of config specs that accomplish useful configuration management goals. For specificity, we use the following development environment:

Developers use a VOB whose VOB-tag is /proj/monet, which has this structure:

/proj/monet          (VOB-tag, VOB mount point)
src/              (C language source files)
include/          (C language header files)
lib/              (project's libraries)

For the purposes of this chapter, suppose that the lib directory has this substructure:

lib/
libcalc.a            (checked-in “staged” version of library)
libcmd.a             (checked-in “staged” version of library)
libparse.a           (checked-in “staged” version of library)
libpub.a             (checked-in “staged” version of library)
libaux1.a            (checked-in “staged” version of library)
libaux2.a            (checked-in “staged” version of library)
libcalc/             (sources for `calc' library)
libcmd/              (sources for `cmd' library)
libparse/            (sources for `parse' library)
libpub/              (sources for `pub' library)
libaux1/             (sources for `aux1' library)
libaux2/             (sources for `aux2' library)

Sources for libraries are located in subdirectories of lib. After a library is built in its source directory, it can be “staged” to /proj/monet/lib by checking it in as a DO version. The build scripts for the project's executable programs can instruct the link editor, ld(1), to use the libraries in this directory (the “library staging area”) instead of a more standard location (for example, /usr/local/lib).

The following version labels have been assigned to versions of monet elements:

Version Labels

Description

R1.0

First customer release

R2_BL1

Baselevel 1 prior to second customer release

R2_BL2

Baselevel 2 prior to second customer release

R2.0

Second customer release

These version labels have been assigned to versions on the main branch of each element. Most of the project's development tasks take place on the main branch. For some special tasks, however, development takes places on a subbranch::

Subbranches

Description

major

Used for work on the application's graphical user interface, certain computational algorithms, and other major enhancements

r1_fix

Used for fixing bugs in Release 1.0

The following sections present ClearCase config specs, explaining in detail how each one achieves a particular configuration management goal.

Dynamic `Mainline' View

This config spec defines a dynamic configuration, which automatically “sees” changes made on the main branch of every element — throughout the entire source tree, by any developer. See Example 5-1.

Example 5-1. Spec #1


element * CHECKEDOUT                                      (1)
element * /main/LATEST                                    (2)

This is ClearCase's default config spec, to which each newly-created view is initialized. (The mkview command automatically uses the contents of file /usr/atria/default_config_spec.)

A view with this config spec provides a private work area that “sees” your checked-out versions (Rule 1). By default, a checkout command processes the currently-selected branch — in this case, the main branch (Rule 2). As long as an element remains checked-out, you can change it without affecting anyone else's work. As soon as you perform a checkin, the changes become visible instantly to other users whose views select /main/LATEST versions.

The view also “sees” all other elements (that is, all elements that you have not checked out), on a read-only basis. If another user checks in a new version on the main branch of such an element, the new LATEST version appears in this dynamic view, automatically and instantly.

The Standard Configuration Rules

The two configuration rules in the default config spec will reappear in many of this chapter's examples. The CHECKEDOUT rule enables modification of existing elements. You can perform checkout commands in a view that lacks this rule, but your view_server process will complain:

% cleartool checkout -nc cmd.c
cleartool: Warning: Unable to rename "cmd.c" to "cmd.c.keep": 
Read-only filesystem.
cleartool: Warning: Checked out version, but could not copy to "cmd.c": File exists.
cleartool: Warning: Copied checked out version to "cmd.c.checkedout".
Checked out "cmd.c" from version "/main/7".

In this example, the view continues to select version 7 of element cmd.c, which is read-only. A read-write copy of this version, cmd.c.checkedout, is created in view-private storage. (This is not a recommended way of working!)

The /main/LATEST rule selects the most recent version on the main branch to appear in the view. Often, this is the version that represents the state-of-the-art for that element.

In addition, a /main/LATEST rule is required to enable creation of new elements in a view. More precisely, you can create a new element in the absence of such a rule, but your view will then be unable to “see” the element you just created. (Element creation involves creating a main branch, and an empty version, /main/0).

Omitting the Standard Configuration Rules

It makes sense to omit one or both of the standard configuration rules only if a view is not going to be used to modify data. For example, you might configure a “historical” view, to be used only for browsing old data, not for creating new data. Similarly, you might configure a view in which to compile and test only, or to verify that sources have been properly labeled.

Frozen View, Defined by Version Labels

This config spec defines a “frozen” configuration. See Example 5-2.

Example 5-2. Spec #2

element * R1.0 -nocheckout                                (1)



Note: The view always selects the same set of versions — the ones that have been labeled R1.0. In this scenario, all these versions are on the main branch of their elements; but this config spec works even if the R1.0 version is on a subbranch.This assumes the R1.0 label type is “one-per-element”, not “one-per-branch” — see the mkbrtype manual page

To reinforce “frozenness”, the -nocheckout qualifier prevents any element from being checked out in this view. (It also prevents creation of new elements, since this requires the parent directory element to be checked-out.) Thus, there is no need for the standard CHECKEDOUT configuration rule.


Note: This configuration is not completely frozen, since version labels can be moved and deleted. For example, using the command mklabel –replace to move R1.0 from version 5 of an element to version 7 would automatically change which version appears in the view. Similarly, using rmlabel would suppress the specified element(s) from the view. (The ClearCase ls command lists them with a [no version selected] annotation.) If the label type is locked with the lock command, the configuration becomes truly frozen.

This configuration is not appropriate for development. It might be used to rebuild Release 1.0, thus verifying that all source elements have been labeled appropriately. It might also be used by a developer or maintenance engineer to browse the old release.

As noted above, elements that have no version labeled R1.0 will be suppressed from the view. This might include recently-created elements, elements from which the R1.0 label has been removed, and elements in other VOBs.

Frozen View, Defined by Time

This config spec defines a “frozen” configuration in a slightly different way than the preceding one. See Example 5-3.

Example 5-3. Spec #3


element * /main/LATEST -time 4-Sep.02:00 -nocheckout      (1)

This configuration is “more frozen” than the preceding one: for each element, it selects the version that was the most recent on the main branch on September 4 at 2am (presumably, a time when no development was taking place). Subsequent checkout/checkin activity cannot change which versions satisfy this criterion — only deletion commands such as rmver or rmelem can change the configuration. As with the preceding config spec, the - nocheckout qualifier prevents elements from being checked out or created.

This configuration might be used to “roll back the clock” to a point when a consistent set of versions existed. If modifications must be made to this source base, you must modify the config spec to “unfreeze” the configuration (see Example 5-5).

View That Allows an `Old' Configuration to be Modified

This config spec allows modifications to be made to a configuration defined with version labels. See Example 5-4.

Example 5-4. Spec #4


element * CHECKEDOUT                                      (1)
element * .../r1_fix/LATEST                               (2)
element * R1.0 -mkbranch r1_fix                           (3)

The configuration initially selects the same set of versions as Spec #2 in Example 5-2. This set of versions constitutes a baselevel configuration, which can then be modified:

  • Elements can be checked out (Rule 1).

  • The checkout command automatically creates a branch named r1_fix at the initially selected version (the auto-make-branch clause in Rule 3).

A key aspect of this scheme is that the same branch name, r1_fix, is used in every modified element. The only administrative overhead is the creation of a single branch type, r1_fix, with the mkbrtype command.

This config spec is efficient: just two rules (Rules 2 and 3) configure the appropriate versions of all elements:

  • For elements that have not been modified, it is the most recent version on the main branch (Rule 2).

  • For elements that have been modified, it is the most recent version on the r1_fix subbranch (Rule 3).

Figure 5-1 illustrates the two kinds of elements. In this illustration, the r1_fix branch is a subbranch of the main branch. But Rule #2 handles the more general case, too: the “...” wildcard allows the r1_fix branch to occur anywhere in any element's version tree, and at different locations in different elements' version trees.

Figure 5-1. Making a Change to an Old Version


Where Is the `/main/LATEST' Rule?

This config spec lacks the standard /main/LATEST rule. It is not useful for work with VOBs in which the version label R1.0 does not exist. In addition, it is not useful in situations where new elements are created, as described in “Composing Your Own Config Spec”. If your organization forbids creation of new elements during maintenance of an old configuration, the lack of a /main/LATEST rule is appropriate.

To allow creation of new elements during the modification process, add a fourth configuration rule:

element * CHECKEDOUT                          (1)
element * /main/r1_fix/LATEST                 (2)
element * R1.0 -mkbranch r1_fix               (3)
element * /main/LATEST -mkbranch r1_fix       (4)

When a new element is created with mkelem, the -mkbranch clause in Rule 4 causes the new element to be checked out on the r1_fix branch (which is automatically created). This conforms to the scheme of localizing all changes to r1_fix branches.

Variations on the Theme

This config spec as shown in Example 5-5 combines aspects of Spec #3 found in Example 5-3 and Spec #4 found in Example 5-4.

Example 5-5. Spec #5


element * CHECKEDOUT                                      (1)
element * /main/r1_fix/LATEST                             (2)
element * /main/LATEST -time 4-Sep:02:00 -mkbranch r1_fix (3)

This baselevel configuration is defined not with version labels like Rule 3 in Spec #4 (See Example 5-4), but with a -time rule as in Spec #3 (See Example 5-3).

View for New Development on a Branch

You can use this config spec for work that is to be isolated on branches named major as shown in Example 5-6.

Example 5-6. Spec #6


element * CHECKEDOUT                                      (1)
element * .../major/LATEST                                (2)
element * BASELEVEL_X -mkbranch major                     (3)
element * /main/LATEST -mkbranch major                    (4)

The scheme is essentially similar to the one introduced above, in which all “fixup” work is performed on branches named r1_fix. Here, all work on a project (say, a command-line syntax overhaul) is isolated on branches named major (Rule 2).

Once again, major branches should be created at versions that constitute a consistent baselevel: a major release, a minor release, or just a set of versions that produces a working version of the application. In this config spec, the baselevel is defined by the version label BASELEVEL_X.

Variations on the Theme

Turning back the clock on a recent change — Sometimes, other developers checkin versions that become visible in your view, but which are incompatible with your own work. In such cases you can “turn back the clock” to a time before those changes were made. For example, Rule 2 in this variant shown in Example 5-7 turns back the clock on the branch to 4:00 PM on November 12.

Example 5-7. Spec #7


element * CHECKEDOUT                                      (1)
element * /main/major/LATEST -time 12-Nov.16:00           (2)
element * BASELEVEL_X -mkbranch major                     (3)
element * /main/LATEST -mkbranch major                    (4)


Note: Your own checkouts are unaffected by this rollback.

Config spec include files — ClearCase supports an “include file” facility that makes it easy to ensure that all members of the group are using the same config spec. For example, the configuration rules in Spec #7 as shown in Example 5-7 might be placed in file /public/config_specs/major.cspec. Each developer then needs just a single-line config spec as shown in Example 5-8.

Example 5-8. Spec #8


include /public/config_specs/major.cspec                  (1)

If the project leader decides to modify this config spec (for example, to adopt the no-directory-branching policy), only the contents of /public/config_specs/major.cspec need be changed. You can use this command to reconfigure your view with the modified spec:

% cleartool setcs -current

View That Implements Multiple-Level Branching

This config spec shown in Example 5-9 is a variant of Spec #6 (See Example 5-6); it implements and enforces consistent multiple-level branching:

Example 5-9. Spec #9


element * CHECKEDOUT             (1)
element * .../major/autumn/LATEST  (2)
element * .../major/LATEST -mkbranch autumn   (3)
element * BASELEVEL_X -mkbranch major      (4)
element * /main/LATEST -mkbranch major     (5)

A view configured with this config spec is appropriate in the following situation:

  • As in Spec #6, all changes from the baselevel designated by the BASELEVEL_X version label must take place on a branch named major.

  • Moreover, you are working on a special side-project, whose changes are to be made on a subbranch of major, named autumn.

(It is important for each modified element to have a major branch; it will be used to integrate all the changes made in your side-project and other sub-projects.) Figure 5-2 shows what happens in such a view when you checkout an element that has not been modified since the baselevel.

Figure 5-2. Multiple-Level Auto-Make-Branch


For more on multiple-level branching, see the config_spec and checkout manual page.

View That Selects Versions Using `External Criteria'

Suppose that some members of the development group working on the major branch (see Spec #6 in Example 5-6) are designated as the “QA team”. Individual developers are responsible for making sure that their modules pass a lint(1) check. The QA team builds and tests the application, using the most recent versions that have passed lint.

The QA team might work in a view with this config spec as shown in Example 5-10.

Example 5-10. Spec #10


element -file src/* /main/major/{lintOK=="Yes"}           (1)
element * /main/LATEST                                    (2)

This scheme calls for an attribute type, lintOK, to be created. Whenever a new version that passes lint is checked in on the major branch, an instance of lintOK with the value "Yes" is attached to that version. (This might be performed manually or with a ClearCase trigger.)

If an element in the /src directory has been edited on the major branch, this view selects the branch's most recent version that has been marked as passing lint (Rule 1). If no version has been so marked, or if no major branch has been created, the most recent version on the main branch is used (Rule 2).


Note: Rule 1 on this config spec does not provide a match if an element has a major branch, but no version on the branch has a lintOK attribute. This command can locate the no-such-attribute branches:


% cleartool find . -branch '{brtype(major) \
&& \! attype_sub(lintOK)}' -print

The backslash “\” character is required in the C shell only, to keep the exclamation point “!” from indicating a history substitution. The attype_sub primitive searches for attributes throughout an element — on its versions and branches, as well as on the element itself.

This scheme allows the QA team to track the progress of the rest of the group, without having to keep absolutely up-to-date. The development config spec always selects the most recent version on the major branch, but the QA config spec may select an intermediate version (Figure 5-3).

Figure 5-3. Development Config Spec vs. QA Config Spec


Can This Configuration Be Used for Development?

It might be tempting to add a “CHECKEDOUT” rule to the above config spec, turning the “QA configuration” into a development configuration” shown in Example 5-11.

Example 5-11. Spec #11


element * CHECKEDOUT                                      (0)
element -file src/* /main/major/{lintOK=="Yes"}           (1)
element * /main/LATEST                                    (2)

More generally, it may seem desirable to use attributes, or other kinds of meta-data in addition to (or instead of) branches to control version- selection in a development view. But such schemes involve complications. Suppose that the config spec above selects version /main/major/2 of element .../src/cmd.c (Figure 5-4).

Figure 5-4. Checking Out a Branch of an Element


Performing a checkout in this view checks out version /main/major/3, not version /main/major/2:

cleartool: Warning: Version checked out is different from version previously selected by view.
Checked out "cmd.c" from version "/main/major/3".

This reflects the ClearCase restriction that new versions can be created only at the end of a branch. While such operations are possible, they are potentially confusing to users. And in this situation, it is almost certainly not what the developer performing the checkout desired.

You can avoid the “wrong-version-checked-out” problem by modifying the config spec and creating another branching level at the attribute-selected version. The new config spec might be as shown in Example 5-12.

Example 5-12. Spec #12


element * CHECKEDOUT                                      (0)
element * /main/major/temp/LATEST                        (0a)
element -file src/* /main/major/{lintOK=="Yes"} 
  -mkbranch temp                                          (1)
element * /main/LATEST                                    (2)

View That Shows Only One Developer's Changes

This config spec shown in Example 5-13 makes it easy to peruse all of the changes a developer has made since a certain milestone.

Example 5-13. Spec #13


element * '/main/{created_by(jackson) && created_since(25-Apr)} '           (1)
element * /main/LATEST -time 25-Apr


Note: Rule 1 must be wholly contained on a single physical text line.

A particular date, April 25, is used as the milestone. The configuration is a “snapshot” of the main line of development at that date (Rule 2), overlaid with all changes that user jackson has made on the main branch since then (Rule 1).

Directory listings made by the ClearCase ls command distinguish jackson's files from the others: each listing entry includes an annotation as to which configuration rule applies to the selected version.

This is a “perusal view”, not a development view. The selected set of files may not be consistent: some of jackson's changes may rely on changes made by others, and those other changes are excluded from this view. Thus, this config spec lacks the standard “CHECKEDOUT” and “/main/LATEST” rules.

View That Restricts Changes to a Single Directory

This config spec shown in Example 5-14 is appropriate for a developer who is to be restricted to making changes in just one directory, /proj/monet/src.

Example 5-14. Spec #14


element * CHECKEDOUT                                      (1)
element src/* /main/LATEST                                (2)
element * /main/LATEST -nocheckout                        (3)

The most recent version of each element is selected (Rules 2 and 3), but Rule 3 prevents checkouts to all elements except those in the desired directory.


Note: Rule 2 matches elements in any directory named src, in any VOB. The pattern /proj/monet/src/* would restrict matching to just one VOB.

This config spec can easily be extended with additional rules that allow additional areas of the source tree to be modified.

View That Uses Results of a Nightly Build

Many development organizations use scripts to perform unattended software builds each night. Such “nightly builds” verify that the application is still buildable. In layered build environments, they can also provide up-to-date versions of lower-level software: libraries, utility programs, and so on.

Suppose that each night, a script:

  • builds libraries in various subdirectories of /proj/monet/lib

  • checks them in as DO versions in the “library staging area”, /proj/monet/lib

  • labels the versions LAST_NIGHT

You can use this config spec shown in Example 5-15 if you wish to use the libraries produced by the nightly builds.

Example 5-15. Spec #15


element * CHECKEDOUT                                    (1)
element lib/*.a LAST_NIGHT                              (2)
element lib/*.a R2_BL2                                  (3)
element */main/LATEST                                   (4)

The LAST_NIGHT version of a library is selected whenever such a version exists (Rule 2). If a nightly build fails, the previous night's build will still have the LAST_NIGHT label, and will be selected. If no LAST_NIGHT version exists (the library is not currently under development), the stable version labeled R2_BL2 is used instead (Rule 3).

For each library, selecting on the LAST_NIGHT label rather than simply taking the most recent version in the staging area allows new versions to be staged during the next day, without affecting developers who use this config spec.

Variations on the Theme

The scheme described above uses version labels to select particular versions of libraries. For more flexibility, the LAST_NIGHT version of some libraries might be selected, the R2_BL2 version of others, and the most recent version of still others as shown in Example 5-16.

Example 5-16. Spec #16


element * CHECKEDOUT                                      (1)
element lib/libcmd.a LAST_NIGHT                          (2a)
element lib/libparse.a LAST_NIGHT                        (2b)
element lib/libcalc.a R2_BL2                             (3a)
element lib/*.a /main/LATEST                             (3b)
element * /main/LATEST                                    (4)

(Rule 3b is not required here, since Rule 4 would handle “all other libraries”. It is included for clarity only.)

Other kinds of meta-data could also be used to select library versions. For example, lib_selector attributes might take values such as "experimental", "stable", and "released". A config spec might mix-and-match library versions as shown in Example 5-17.

Example 5-17. Spec #17


element * CHECKEDOUT                                      (1)
element lib/libcmd.a {lib_selector=="experimental"}       (2)
element lib/libcalc.a {lib_selector=="experimental"}      (3)
element lib/libparse.a {lib_selector=="stable"}           (4)
element lib/*.a {lib_selector=="released"}                (5)
element * /main/LATEST                                    (6)

Playing Mix-and-Match with Application Subsystems

This config spec shown in Example 5-18 extends the scheme used in Spec #15 through Spec #17 from programming libraries to the application's subsystems.

Example 5-18. Spec #18


element * CHECKEDOUT                                      (1)
element /proj/monet/lib/... R2_BL1                        (2)
element /proj/monet/include/... R2_BL2                    (3)
element /proj/monet/src/... /main/LATEST                  (4)
element * /main/LATEST                                    (5)

In this situation, a developer is making changes to the application's source files on the main branch (Rule 4). Builds of the application use the libraries in directory /lib that were used to build Baselevel 1, and the header files in directory /include that were used to build Baselevel 2.

Selecting Versions That Built a Particular Program

This config spec shown in Example 5-19 defines a “sparse view”, which sees just enough files to rebuild a particular program or peruse its sources.

Example 5-19. Spec #19


element * -config /proj/monet/src/monet                   (1)

All elements that were not involved in the build of monet will be listed by ClearCase ls with a [no version selected] annotation.

This config spec selects the versions listed in the config rec of a particular derived object (and in the config recs of all its build dependencies). It can be a derived object that was built in the current view, or another view, or it can be a DO version.

In this config spec as shown in Example 5-20, monet is a derived object in the current view. You can reference a derived object in another view with an extended pathname that includes a DO-ID.

Example 5-20. Spec #20


element * -config /proj/monet/src/monet@@09-Feb.13:56.812

But typically, this kind of config spec is used to configure a view from a derived object that has been checked in as a DO version.

Configuring the Makefile

By default, a derived object's config rec does not list the version of the makefile that was used to build it. Instead, the config rec includes a copy of the build script itself. (Why? — when a new version of the makefile is created with a revision to one target's build script, the config recs of all other derived objects built with that makefile are not rendered out-of-date.)

But if the monet program is to be rebuilt in this view using clearmake (or even standard make), a version of the makefile must be selected somehow. You can have clearmake record the makefile version in the config rec by including the special clearmake macro $(MAKEFILE) in the target's dependency list:

monet: $(MAKEFILE) monet.o ...
    cc -o monet ...

clearmake always records the versions of explicit dependencies in the config rec.

Alternatively, you can configure the makefile at the source level: attach a version label to the makefile at build time, then use a config spec like Spec #2 or Spec #4 to configure a view for building.

Making a Fix in the Program

If a bug is discovered in the monet program, as rebuilt in a view configured with Spec #19 (See Example 5-19), it is easy to convert the view from a perusal/build configuration to a development configuration. As usual when making changes in “old” sources, the strategy is to:

  • create a branch at each version to be modified

  • use the same branch name (that is, create an instance of the same branch type) in every element

If the “fixup” branch type is r1_fix, then this modified config spec shown in Example 5-21 reconfigures the view for performing the fix.

Example 5-21. Spec #21


element * CHECKEDOUT                                      (1)
element * .../r1_fix/LATEST                               (2)
element * -config /proj/monet/src/monet -mkbranch r1_fix  (3)
element * /main/LATEST -mkbranch r1_fix                   (4)

Selecting Versions That Built a Set of Programs

It is easy to expand Spec #19 (See Example 5-21) so that it configures a view with the sources used to build a set of programs, rather than a single program as shown in Example 5-22.

Example 5-22. Spec #22


element * -config /proj/monet/src/monet                   (1)
element * -config /proj/monet/src/xmonet                  (2)
element * -config /proj/monet/src/monet_conf              (3)

There can be version conflicts in such configurations, however. For example, different versions of file params.h might have been used in the builds of monet and xmonet. In this situation, the version used in monet is configured, since its configuration rule came first. Similarly, there can be conflicts when using a single -config rule: if the specified derived object was created by actually building some targets, but simply using DO versions of other targets, multiple versions of some source files might be involved.

You can apply a transformation to this config spec similar to the one that transforms Spec #19 to Spec #21, in order to change the perusal/build configuration to a development configuration.