In this lesson, you'll (finally) fix the bug first discovered in the introduction to Lesson 1. You'll work in a new view, created expressly for maintenance work. This view's config spec will take advantage of an important ClearCase feature—the ability to create branches in an element's version tree.
ClearCase makes it easy to implement this strategy for fixing a bug:
You start with the exact version of each source file that was used to build the “broken” executable.
For each source file that must be changed to fix the bug, you create versions on a subbranch of the element's version tree, not on the main branch.
The same branch name is used in the version tree for every file element involved in the bugfix.
Using branches allows two or more projects to “grow” an element's version tree independently. For example, it might take you several weeks to fix a particular bug. (Don't worry—the bug in this lesson will take about one minute to fix.) Working on a branch means that the element does not go “out of play” while you're implementing the fix. Another developer is free to modify the same element for a different purpose, as long as he or she works on a different branch.
At any time, the changes made on one branch can be merged into any other branch. You'll perform a merge in Lesson 6.
ClearCase views support the branch-oriented approach to maintenance in a natural way. When starting your bugfix work, you establish a view that is configured to meet the guidelines listed above. You'll create a new view with the appropriate config spec:
element * CHECKEDOUT element * .../rel2_bugfix/LATEST element * REL2 |
Let's see how the rules in this config spec meet the three guidelines listed above.
Guideline #1
You need a view that selects the version of each source file that went into the building of the second release. When it created that release, the REL1REL2 script attached version label REL2 to the then-current source versions:
cleartool mklabel REL2 Makefile hello.c hello.h util.c . .. |
Note that the “.” and “..” at the end of this command name the current working directory and its parent, the VOB's top-level directory. You saw in Lesson 4 that the ability to access different versions of directories plays a critical role in “turning back the clock” to a previous release.
Now, a single config spec rule selects all those versions:
element * REL2 |
Since every element involved in the second release was labeled REL2, this one rule “reconstructs” the entire source environment for the release.
Guidelines #2 and #3
The config spec must also reflect the policy that all bugfixes to a file be made on a branch off the REL2 version of that file.
If the branch named rel2_bugfix is used in each element to be modified for the bugfix, a single rule configures your view to see the maintenance work:
element * .../rel2_bugfix/LATEST |
You also need one of the rules from the default config spec:
element * CHECKEDOUT |
As always, this rule allows you to work with checked-out files. Note that this rule need not be modified to work with files on a branch.
The order of the three rules is important. In particular Rule 2 precedes Rule 3 because your view must “prefer” the maintenance branch (if it exists in a particular element) to the main branch.
The “...” notation in Rule2 is a ClearCase extension, used here to match zero or more intervening branch levels. Rule2 matches any element with a rel2_bugfix branch, whether it “sprouted” from the main branch (/main/rel2_bugfix) or from some other branch (/main/nt_port/rel2/rel2_bugfix, for example).
Variations on this Config Spec
You might also include the default config spec's last rule as your last rule:
element * /main/LATEST |
This rule is not required in this tutorial, because every element you need to access has been labeled REL2 and, thus, is matched by the element * REL2 rule. If you wish to access version-controlled data not involved in the hello project (and not labeled REL2), you would need this extra rule, too.
Config specs have a feature that both automates the process of creating branches and ensures consistent naming of those branches. This “auto-make-branch” feature is turned on by modifying Rule 3:
element * REL2 -mkbranch rel2_bugfix |
With this rule, developers do not need to enter mkbranch commands. Instead, whenever a checkout of a REL2 version is performed, ClearCase automatically creates a branch with the name specified in the config rule, and performs a checkout on that branch.
At the end of the preceding lesson, you were in your original directory (we suggested that you start this tutorial in your home directory), in a shell that was not set to any view. Verify that you are still in the same situation.
% cleartool pwv -short ** NONE ** % pwd HOME |
In this tutorial, we have instructed you to exit one view before entering another one. In practice, you would more likely create a new window for your work with a new view. There is no need to “shut down” a view (or the shells that are set to it) before you use another one. Using multiple windows allows you to switch back and forth between views easily.
If you've gotten lost, find out whether your current shell is set to a view:
cleartool pwv |
If you are set to a view, exit the shell process, in order to return to a shell that is not set to a view. (Try cleartool pwv again, to make sure!) Then return to the directory you were in when you started this tutorial (for example, with the command cd $HOME).
First, create the new view, placing its storage directory next to that of the existing USER_HOST_tut view (which you created in Step 10).
% cleartool mkview -tag USER_HOST_fix 'pwd'/tut/fix.vws Created view. Host-local path: HOST:HOME/tut/fix.vws Global path: /net/HOST/HOME/tut/fix.vws It has the following rights: User : USER : rwx Group: GROUP : rwx Other: : r-x |
As before, your umask value determines the permissions on the new view.
You have created a new view, USER_HOST_fix, but you are not yet using it. Set the new view with a setview command.
% cleartool setview USER_HOST_fix % cleartool pwv -short USER_HOST_fix |
You are now in a new shell process, which is set to view USER_HOST_fix.
Every view is created with the default config spec, which is stored in file /usr/atria/default_config_spec. You can edit a view's config spec with a text editor (edcs command), or replace the contents of the config spec by copying an ordinary ASCII file (setcs command). To avoid typing mistakes, use the copying method.
% cleartool setcs /usr/atria/doc/tutorial/cs.1 % cleartool catcs element * CHECKEDOUT element * .../rel2_bugfix/LATEST element * REL2 |
The derived objects you built in Step 23 are still in the USER_HOST_tut view, but you can't see them from here. A view makes source elements appear automatically, but it acquires derived objects only when you enter clearmake commands to build them. Since you are using a newly-created view, USER_HOST_fix, it contains no derived objects (yet).
To gain another perspective on this situation, consider that a view serves two functions—it selects versions of elements to appear in development directories, and it provides an isolated work area. Derived objects pertain to the isolated-work-area role of a view, not to its version-selection role.
% pwd HOME % cd VOBTAG/src % cleartool ls Makefile@@/main/2 Rule: REL2 hello.c@@/main/3 Rule: REL2 hello.h@@/main/1 Rule: REL2 util.c@@/main/1 Rule: REL2 |
We're now going to make a (harmless) mistake, in order to emphasize a point. The file util.c needs to be edited to fix the bug in the time string. According to policy, you must make a branch in its version tree.
% cleartool mkbranch rel2_bugfix util.c cleartool: Error: Type not found: "rel2_bugfix". |
What happened? Before a branch can be created in any element's version tree, it is first necessary to define the name rel2_bugfix for use in the current VOB, using the mkbrtype (“make branch type”) command.
Several aspects of ClearCase adhere to this two-step model. For example, the version labels REL1 and REL2 are attached to the source versions used to build the first two product releases. The mklabel command attached these labels, but only after the mklbtype command created a corresponding version label type.
Separating the definition of information from the application of that information to source elements allows the establishment of administrative controls, and facilitates such operations as changing all existing version labels REL2 to RELEASE2.0.
We re-emphasize here a point made earlier: you should create only one branch type. The strategy is to make branches with the same name for all elements that require modifications for the bugfix.
% cleartool mkbrtype -c "fix: date-string bug" rel2_bugfix Created branch type "rel2_bugfix". |
As with checkin, you can (and should) enter a comment describing the meaning and intended usage of a branch type. Such comments are listed with the lstype –brtype command.
This bugfix is simple—it requires that only one file, util.c, be modified. The mkbranch command automatically performs a checkout on the branch it creates. The terminal message indicates this fact, and the ls command verifies it.
% cleartool mkbranch -nc rel2_bugfix util.c Created branch "rel2_bugfix" from "util.c" version "/main/1". Checked out "util.c" from version "/main/rel2_bugfix/0". % cleartool ls util.c util.c@@/main/rel2_bugfix/CHECKEDOUT from /main/rel2_bugfix/0 Rule: CHECKEDOUT |
You can see that:
Before the checkout, your view selects the version of util.c labeled REL2 (using Rule 3 in the config spec).
The mkbranch command creates a branch named rel2_bugfix, “sprouting” it from the REL2 version. Version 0 on this branch has the same data as the version at which the branch was created.
After the checkout, your view selects the checked-out version.
The fix itself is easy: use string-manipulation functions to remove the trailing newline character from the time string returned by env_time().
% cp /usr/atria/doc/tutorial/ut.3 util.c
% cleartool diff -pred util.c
********************************
<<< file 1:VOBTAG/src/util.c@@/main/rel2_bugfix/0
>>> file 2: util.c
********************************
-------------[after 25]-------|--------[inserted 26]---------
-| char *s;
|-
------------[changed 28]------|-------[changed to 29-31]-----
return ctime(&clock); | s = ctime(&clock);
-| s[ strlen(s)-1 ] = '\0';
| return s;
|-
|
![]() | Note: If you are working at a graphics display that is running the X Window System, you might wish to try this variant of the above command: |
cleartool xdiff -pred util.c |
This shows the differences between the two versions in a separate, scrollable X window. You can close the xdiff window with the Quit option on the Panel pull-down menu.
That's all there is to fixing the bug—now let's test the fix.
% clearmake -v hello |
No candidate in current view for "hello.o"
Wink in derived object "VOBTAG/src/hello.o
(derived object 'hello.o' built in another view gets winked-in to this view)
No candidate in current view for "util.o"
======== Rebuilding "util.o" ========
cc -c util.c
Will store derived object "VOBTAG/src/util.o"
========================================================
Must rebuild "hello" - due to rebuild of subtarget "util.o"
======== Rebuilding "hello" ========
cc -o hello hello.o util.o
Will store derived object "VOBTAG/src/hello"
========================================================
|
Note that clearmake doesn't need to build hello.o, even though there is no such file in this view. Since you made no change to any of the dependencies of hello.o, the instance previously built in view USER_HOST_tut qualifies for wink-in.
Did you fix the bug?
% ./hello Hello, USER! Your home directory is /net/HOST/home/USER. It is now DATESTRING. |
Yes, you did!
The VOB now catalogs several builds of the hello program, performed in several views:
The REL1REL2 script built hello twice in view USER_HOST_old, once for the REL1 release and once for the REL2 release.
In Lesson 2, you built a hello in view USER_HOST_tut. This build modified the string reported as the user's home directory.
In this lesson, you built a hello in view USER_HOST_fix, fixing the bug in the time string.
By default, the lsdo command shows these builds in reverse-chronological order.
% cleartool lsdo -l -zero hello DATESTRING-1 (USER.GROUP@HOST) (in the 'fix' view) create derived object "hello@@DATESTRING.nnn" references: 1 => HOST:HOME/tut/fix.vws DATESTRING-2 (USER.GROUP@HOST) (in the 'tut' view) create derived object "hello@@DATESTRING.nnn" references: 1 => HOST:HOME/tut/tut.vws DATESTRING (USER.GROUP@neptune) (in the 'old' view (Release 2)) create derived object "hello@@DATESTRING.nnn" references: 1 => HOST:HOME/tut/old.vws DATESTRING (USER.GROUP@neptune) (in the 'old' view (Release 1)) create derived object "hello@@DATESTRING.nnn" references: 0 => HOST:HOME/tut/old.vws |
The references: 0 annotation means that the data for this instance of hello is no longer available. The Release 2 build of hello in the USER_HOST_old view overwrote the Release 1 build. You can still examine its config rec (until ClearCase automatically “scrubs” it), but there is no longer any file to be executed.
The lsdo command omits zero-referenced derived objects unless you specify the -zero option.
The diffcr (“diff config recs”) command compares different builds of a program by their configurations—that is, on the basis of what source versions were used, what build script, what build options, and so on. For example, you can compare the configuration of your build of hello with that of the REL2 version.
% cleartool diffcr -flat hello ../bin/hello@@/main/REL2 MVFS objects: ---------------------------- ---------------------------- < First seen in target "hello" < 1 VOBTAG/src/hello@@DATESTRING.nnn > First seen in target "hello" > 1 VOBTAG/src/hello@@DATESTRING.nnn ---------------------------- < First seen in target "util.o" < 1 VOBTAG/src/util.c <DATESTRING> > First seen in target "util.o" > 1 VOBTAG/src/util.c@@/main/1 <DATESTRING> ---------------------------- < First seen in target "hello" < 2 VOBTAG/src/util.o@@DATESTRING.nnn > First seen in target "hello" > 2 VOBTAG/src/util.o@@DATESTRING.nnn |
You can see that the only difference between the builds at the source level is in the one file, util.c. This is a comparison of a derived object in your view with one that has been checked in as a version of an element. You can also compare derived objects in two different views. For example, how does your current build differ from the one you performed in Lesson 2, using view USER_HOST_tut?
% cleartool diffcr -flat hello /view/USER_HOST_tut/`pwd`/hello MVFS objects: ---------------------------- ---------------------------- < First seen in target "hello" < 1 VOBTAG/src/hello@@DATESTRING.nnn > First seen in target "hello" > 1 VOBTAG/src/hello@@DATESTRING.nnn ---------------------------- < First seen in target "util.o" < 1 VOBTAG/src/util.c <DATESTRING> > First seen in target "util.o" > 1 VOBTAG/src/util.c@@/main/2 <DATESTRING> ---------------------------- < First seen in target "hello" < 2 VOBTAG/src/util.o@@DATESTRING.nnn > First seen in target "hello" > 2 VOBTAG/src/util.o@@DATESTRING.nnn |
Note that view-extended naming provides an alternative to the setview command as a means of accessing a view. setview is ideal if you want to use just a single view—you can “set it, then forget it.” But the extended naming scheme enables access to two views at once (in this example, USER_HOST_tut and USER_HOST_fix), all through the UNIX file system.
Derived objects can also be referenced using their derived object IDs, irrespective of which view they were created in. For example, you can essentially repeat the preceding command by comparing the first two instances of hello in the Step 57 listing—the ones whose timestamps are DATESTRING-1 and DATESTRING-2.
% cleartool diffcr -flat hello@@DATESTRING-1 hello@@DATESTRING-2 ---------------------------- MVFS objects: ---------------------------- ---------------------------- < First seen in target "hello" < 1 VOBTAG/src/hello@@DATESTRING.nnn > First seen in target "hello" > 1 VOBTAG/src/hello@@DATESTRING.nnn ---------------------------- < First seen in target "util.o" < 1 VOBTAG/src/util.c@@/main/rel2_bugfix/1 <DATESTRING> > First seen in target "util.o" > 1 VOBTAG/src/util.c@@/main/2 <DATESTRING> ---------------------------- < First seen in target "hello" < 2 VOBTAG/src/util.o@@DATESTRING.nnn > First seen in target "hello" > 2 VOBTAG/src/util.o@@DATESTRING.nnn |
The fix is implemented and tested, so let's save your work in the version tree.
% cleartool checkin -c "fix bug: extra NL in time string" util.c Checked in "util.c" version "/main/rel2_bugfix/1". |
Since util.c is no longer checked-out, Rule 1 of the config spec no longer applies. Now, the version-selection process falls through to Rule 2, which selects the most recent version on the rel2_bugfix branch—that's the version you just created.
% cleartool ls util.c util.c@@/main/rel2_bugfix/1 Rule: .../rel2_bugfix/LATEST |
To get an idea of what you've done so far, examine the version tree of util.c. In Lesson 2, you created version /main/2. In this lesson, you created version /main/rel2_bugfix/1.
% cleartool lsvtree -all util.c util.c@@/main/0 util.c@@/main/1 (REL2) util.c@@/main/rel2_bugfix (name of branch) util.c@@/main/rel2_bugfix/0 (same as version /main/1) util.c@@/main/rel2_bugfix/1 (version you just created) util.c@@/main/2 |