Software Conventions and Process for Start-ups


Next: , Previous: (dir), Up: (dir)

This is the top of Software Conventions and Process for Start-ups

--- The Detailed Node Listing ---

Version Control

Coding Style

Coding Conventions

Java Coding Hints and Methodology

Multi-use Classes

GUI Guidelines

Build Environment

Information for Build Engineers

Release Information

Editor Customizations


Next: , Previous: Top, Up: Top

1 Introduction

This document is intended to describe coding conventions and configuration management that have been used successfully in companies with up to 100 engineers. By adhering to standard conventions, code may be more readily read by various members of the technical staff. On the configuration management side, files may be more readily found and a good developmental and build environment will ensue.

The presentation of this document is geared towards the developer who has just joined the team. His first question will be: Where will I put my code? Thus the directory tree is described first. Then CVS (Concurrent Versioning System) is introduced so that the developer may populate that hierarchy. The developer will then want to write code, so coding style and conventions are covered next. Finally, configuration management issues are discussed. Some of these issues are applicable to developers and build engineers alike, while the later sections are applicable mainly to the build engineer.

The wording will be familiar to readers of formal standards. In particular, the word must is used to describe a concept that is non-negotiable. The word should is used to indicate that a concept is desirable but not obligatory. Finally, the word may is used to suggest concepts.

The company Newt Software is featured in the examples in this paper. Short identifiers such as ns, package names such as com.newt, directories such as com/newt, and hostnames such as newt.com should obviously be replaced by equivalents in your own company.

Any discussion related to the C programming language is most likely also applicable to C++.

Finally, several files and commands are mentioned in this paper. Sadly, reference implementations are not yet available.


Next: , Previous: Introduction, Up: Top

2 Repositories

There are several types of environments where files need to be saved. One environment includes documentation such as specifications, white papers, and slide presentations that is not intended to be distributed. The other environment contains the files that are needed to build a distribution. This includes source code and user documentation.

The ideas for the directory hierarchies below are based upon common usage and [FHS].

The code control repository contains a repository for each of these environments. They are named:

Repository Description


doc specifications, white papers and slide presentations


sw source code, running system, and user documentation
Table 1: Repositories

The doc repository is organized as follows:

Directory Description


clients documentation of and for clients


marcomm marketing and communications


papers white papers


planning plans and milestones


specs specifications


specs/general general specifications


specs/products specifications related to a specific product
Table 2: doc repository

Subdirectories may be used to organize material. For example, the clients directory may contain a subdirectory for each client.

The sw repository is based upon [FHS] and that document should be referenced for more details. Note that /usr is flattened. The most commonly used directories are listed below.

The third column indicates whether the directory is CVS controlled (that is, the source or content is maintained there). If not, the directory is usually created and/or populated by running make install elsewhere.

Directory Description Source?


bin executables no


build version information that controls builds and is not included in the distribution yes


com/newt/packages all the packages that make up the distribution yes


etc configuration files yes


lib libraries, executables no


share run-time data (lexicons, indices), data used to initialize databases, or documentation yes


share/api class documentation (output of make doc) no


share/doc online miscellaneous documentation yes


share/help online help yes


share/icons shared icons yes


share/sounds shared sounds yes


tools support programs and scripts that are not included in the distribution yes


var run-time, changing files no


var/cache caches no


var/lib variable state information no


var/lock lock files no


var/log run-time logs no


var/spool files saved for later processing no


var/run run-time variable data no
Table 3: sw repository

Typically, the doc and sw repositories are kept in the same physical repository for convenience.

The development and distribution hierarchies should be the same as much as possible. Several of the directories in the development hierarchy have subdirectories for each project (for example, data/project, doc/help/project spool/project), although they may be collapsed in the distribution. Product subdirectories may remain in the distribution if it is possible that the customer may install several products in the same hierarchy.

Files in lib are not under the control of CVS and therefore must be generated by make while files in data are CVS controlled. The contents of both lib and data are intended to be read-only by the customer, while the contents of etc, which contains configuration files, might be modified by the customer.

A few more words about the data directory are required. It may be more appropriate to organize the data directory by data type instead of by package. For example, there may be subdirectories for data on extinct birds or SEC 10-k filings.

In each data directory where the raw data must be transformed for use by the software, there must be a Makefile that performs this process. If scripts to do this are necessary, they should be placed in an appropriate package if they are generic or directly in the data directory if they are data specific.

Secondary data, or data that is generated or processed from the raw data may be checked in if it takes a long time to generate. Since it is preferable to keep the build process short, a `long time' is defined to be about one hour.

Because this data can be awfully large, it is desirable to place the data in its own CVS repository and preferably on its own partition so that checking in large data files that overflows the partition does not impact the revision control of the rest of the software.


Next: , Previous: Repositories, Up: Top

3 Version Control

All code, documentation, and other files that may change over time must be version controlled. That is, all versions of a file must be saved. The author, date and log information must be stored as well.

CVS (Concurrent Versions System) can be used to accomplish this goal. It allows multiple authors to work on the same document concurrently and makes it easy to check out an entire hierarchy.

CVS has an online manual. It is strongly suggested to read this manual to take full advantage of CVS.

The Java package jCVS can be used by those who prefer a graphical interface to CVS over the line-oriented one. It is still suggested that jCVS users be familiar with the CVS commands.

These tools were chosen because of their ease of installation and use, their price (free), and because they run on both Unix and NT platforms.


Next: , Previous: Version Control, Up: Version Control

3.1 Creating Trees

Several types of trees exist. They range from the fully loaded build tree or integration tree (see Integration Tree) to the sparsely populated developmental tree (see Sparse Trees). The gaps in the sparse tree are filled during compile- or run-time from the reference tree which is usually the integration tree.

CVS provides two modes of operation: local and client/server. Both are described here. You can always use the client/server modes while you can only use the local mode if you are on the same machine as the repository.

The first thing to do is set your CVSROOT environment variable. Set this variable to /ns/cvsroot or :pserver:login@cvs.newt.com:/ns/cvsroot depending on whether you are using local or client/server mode respectively. You do need an account on the machine containing the repository (in this case, cvs.newt.com).

If using the client/server mode, the second thing to do is to save your password:

         cvs login

When prompted, enter your password on the machine containing the repository. This command creates a file called .cvspass in your "home" directory that is used in subsequent CVS commands. You can also specify the location of this file with the CVS_PASSFILE environment variable.

Finally, check out the software with cvs checkout. There are two ways to do this. You can specify the directories and packages directly, or you can check out a module. For example, to check out the package tools, run one of the following:

         cvs checkout sw/tools
     
         cvs checkout tools

Both of these methods create a sw directory in your working directory and copies the requested files there. Once this has been done, directories may also be checked out as follows:

         cvs update -d tools

To get a list of the available modules, use:

         cvs checkout -c

To check out the tools package in jCVS, start jCVS, select the Checkout tab and enter the following information:

User Name: your User name on the machine with the CVS repository


Password: your password


CVS Module: the module or package you are interested in checking out (e.g., sw/tools). See Repositories for the directory structure.


CVS Server: cvs.newt.com


CVS Repository: /ns/cvsroot


Checkout Directory: your working directory (with drive letter on NT). For example, If you select /home/wohler, then the files will be checked out into /home/wohler/sw/tools or, on NT, if you select c:\, then the files will be checked out into c:\sw\tools.

Then, create a project for the directory you have just checked out. The easier, but less useful, way to do this is by selecting File->Add To Work Bench in the Project window. The better way is to go to the main window, select the WorkBench tab, select the Work Bench folder, and click on the Add New Project icon. In the Select Project to Add dialog, open sw/CVS/Entries. Enter sw1 in the Brief Name field, sw in the Display Name field, and a description of your choosing in the Description field. The content of the Brief Name field is arbitrary, but it must be unique between projects in the selected folder. This project is then added to the Work Bench folder.

Double-click on the new sw project to open it.


Next: , Previous: Creating Trees, Up: Version Control

3.2 Creating Directories

It is important to discuss the need of a new directory with the configuration management team. They may suggest better alternatives, or encourage you to proceed. First, create a directory in the proper place in the working hierarchy created above with mkdir. If this is a package that will contain source that should be version controlled, you need to tell CVS about it. This can be done with cvs add directory. In jCVS, first create files in this new directory and tell CVS about them as described in the following section.

The naming convention for directories should follow Unix conventions whereby directory names should be lowercase, short and should not contain any spaces.

Some packages in the sw/com/newt directory may be ephemeral in nature, such as directories used to house software for project or demos. Project directories should be named after the project, while demo directories should be named as if the demo were a product. In time, the demo may become a product and this naming convention will prevent a later name change. In the case of the project directories, the individual software components therein may be generalized and moved into other standard directories.


Next: , Previous: Creating Directories, Up: Version Control

3.3 Creating Files

The initial content of source files should come from templates. The use of templates ensures a similar look and feel of the files throughout the organization. See Templates, to find the template for your new file.

The naming conventions for files follows the usual conventions. For Java files, it is the same as the name of the contained class, plus a .java extension. Word documents may be capitalized and contain spaces.

Use the following to add the file to the source control repository and check the software in:

         cvs add [-kb] file
         cvs commit -m"1. Initial revision." file

Important: Use the -kb option for any non-ASCII files including binaries, libraries, and even Word documents. Also, before checking in Word documents, ensure that Word's Tools->Options->Save->Allow Fast Saves checkbox is not checked. This option tends to make the Word document extremely large and slow to load.

In the jCVS project window, select Misc->Add File, find the new file in the dialog, and click on Open. The new file then becomes visible in the jCVS project window. Right-click on the new file and select Commit from the menu. Enter Initial revision in the text box that appears and click on OK. Important: Use Misc->Add Binary File for libraries, binaries, and Word documents.


Next: , Previous: Creating Files, Up: Version Control

3.4 Check in Software

Files may be edited without an explicit checkout, but they are checked in with cvs commit. Ensure that the line length in your comments is limited to 80 characters (except when entering URLs which should not be folded) and includes enough information for people to figure out what was changed in the file simply by running cvs log. Comments should be more substantial than simply, "Added variable x" although this is far better than "Added variables." A good comment might read, "Added variable x to enable feature y."

Also, list any issue tracking numbers that the update resolves, but constrain a revision that includes bug fixes to only one bug fix and no other features.

Number each item and indent four spaces (to keep text lined up with 10 or more items) to provide for a consistent look of the log messages. Use full sentences which start in capitals and end in periods as much as possible. For example,

         1.  Made window a bit smaller (750x562) in order to
             fit in lower resolution screens (fixes bug #3422).

or

         1.  Made the name of the resource bundle fully qualified.
         2.  Added empty function calls for rest of menu items.

In jCVS, the updated files should be highlighted in red as a reminder that the file has been modified. If not, try running the Update file option. If that fails, check the date on the file and ensure that it is changing when the file is saved. We have found that Word does not update the modification time of the file unless characters were typed which may be the case if only pictures were included, for example. Finally, there may be a bug related to Daylight Savings Time that keeps CVS from knowing that the file has been modified. The workaround is either to wait an hour, or to advance the time an hour in the appropriate entry in the CVS/Entries file.

An easy way to see which files need to be committed is to run cvs update (or cvs -n update if you do not want any files to actually get updated). This command prints out a list of files that have been added, modified, or forgotten.


Next: , Previous: Check in Software, Up: Version Control

3.5 Renaming and Removing Files

Use the following to remove a file from the source control:

         cvs remove -f file
         cvs commit -m"1.  This file made obsolete by modern technology." file

Files are renamed by removing them and adding them back to the repository as follows:

         mv old new
         cvs remove old
         cvs add new
         cvs commit -m"1.  Moved old to new." old new


Previous: Renaming and Removing Files, Up: Version Control

3.6 CVS Utilities

Several CVS commands allow you to view the changes that you have made, the change log, and status of the file. The following table shows the actual CVS commands, the items in the jCVS file operations menu, and a short description. In the jCVS project window, you will see these choices by either right-clicking on a file or by selecting one or more files and using the Selection menu. Move the cursor over the filename and use the right mouse button to bring up a menu. The items that you will use the most include:

CVS Command jCVS Menu Item Description


cvs diff [file] File diffs Display the changes that you have made


cvs log [file] File log Display the change log


cvs stat [file] File status Display the status of the file


cvs update [file] Update file Incorporate changes that others have made into your working version


cvs commit [file] Commit file Check in your changes. Enter a good description of your changes that will be useful and can be later used in release notes. Include any relevant information such as issue tracking numbers.

Table 4: CVS and jCVS commands

If you do not specify any files on the command line, then CVS applies the command to all files in the current directory.


Next: , Previous: Version Control, Up: Top

4 Coding Style

A couple of points are presented here that can be applied to both code and documentation. Writing should be professional at all times, from comments in code to external documentation and communications. One should strive to use proper English and diction.

The language should be American English. In addition, a list of common terms is given in Style Sheet to ensure that the terms are used consistently. This list should be extended with local terms.

When dates are used in documation, they must conform to ISO 8601 to avoid international ambiguity and to continue across centuries (e.g., 1997-03-17). In programs, dates must either conform to ISO 8601 or use NLS (Natural Language Support) to display dates in the local format. In Java, the Locale and SimpleDateFormat can be used.

When numbers are used in documentation, they must conform to the IEEE convention that spaces separate every three digits, and the decimal place should be represented by a dot (e.g., 1 000.01). In programs, numbers must be displayed in the local format using NLS. In Java, the Locale and NumberFormat can be used.


Next: , Previous: Coding Style, Up: Coding Style

4.1 Style

A company does not need to generate its own artistic style. Several coding standards already exist and are listed in References.

One of the important results of coding conventions is uniform code. Uniform code is code that easier to read and understand much as a book in English is far easier for speakers of the English language to read as a book in French. It is possible to read, but just takes longer. Henry Spencer said it best in the The Ten Commandments for C Programmers:

Thou shalt make thy program's purpose and structure clear to thy fellow man by using the One True Brace Style, even if thou likest it not, for thy creativity is better used in solving problems than in creating beautiful new impediments to understanding.

In particular, when coding in Java, the [JCC] must be used. When coding in Perl, the conventions in [PSG] must be used. Since there are not any "official" style guides in C, the standards in [IH] are suggested.


Next: , Previous: Style, Up: Coding Style

4.2 Templates

As each file should have a similar look and feel, templates should be used when developers create a new file. Several templates are necessary to cover the different programming languages. Each template below has two forms: a Perl script that generates the template which fills in some dynamic information and some sample output of the script. A new source file is created by running the appropriate template script and redirecting the output into the new file, or by copying the sample template into the new file.

Class Template Script Sample Template


C-like languages template-c.pl template-c


Shell and scripting languages template-shell.pl template-shell


Perl template-perl.pl template-perl


Miscellaneous languages template-make.pl template-make

The Perl scripts fill in the Copyright information from the current date and environment variable ORGANIZATION. Then they use the AUTHOR environment variable to fill out the Author fields.

The Java template reminds the programmer in which order the attributes and methods should occur and contains comments that should be used to begin each section so that developers may more easily navigate strange code. Inserting a newline character (C-l) before each section is useful for paginating printouts and skipping sections in an editor.


Next: , Previous: Templates, Up: Coding Style

4.3 Naming Conventions

Java naming conventions as described in Chapter 6.8, Naming Conventions of [JAVA] as amended by [JCA] must be followed.

The top-level package name for use in the Java package and import directives at Newt Software is com.newt. Package names should be short and descriptive, or may be two or three letter abbreviations of a longer name (e.g. com.newt.ui for User Interface).

Naming conventions for Perl are described in [PSG].

Naming conventions for C are similar to those for Perl.

Function names and global variables in C must start with the two or three letter lowercase package name (or an alias for a longer package name) to aid in symbol identification and debugging and to reduce the chance of symbol conflicts. For example, the term uiOpenWindow is a function that opens a window in the ui (User Interface) package.


Previous: Naming Conventions, Up: Coding Style

4.4 Indentation Level and Tabs

The indentation level must be four spaces. Hard tabs must not be used because they expand differently depending on the hardware, software, and personal settings. Most reasonable editors allow you to define the tab stop and to insert spaces when the TAB key is pressed. See Editor Customizations.

All code should begin at the current indentation level. Left justified "temporary" debugging statements rarely are temporary and make the code hard to read.


Next: , Previous: Coding Style, Up: Top

5 Coding Conventions


Next: , Previous: Coding Conventions, Up: Coding Conventions

5.1 C Include Files

C include files should be bracketed by an ifdef to prevent multiple inclusion. For example, foo.h would have the following structure:

         #ifndef FOO
         #define FOO
         Body of foo.h
         #endif /* FOO */

Then, a source file should take advantage of this symbol and speed up preprocessing:

         #ifndef FOO
         #include <foo.h>
         #endif /* FOO */

An include file should include all files–but no more–using the format above, which it needs to compile. In addition, the angle brackets must be used in this case to ensure that the correct file is included. To see why angle brackets are necessary, consider an include file in a shared area that includes an include file in your working directory. If double quotes are used, the included file would come from the same directory of the file that included it–the shared directory–and not the working directory. The Makefile's VPATH will ensure that the working directory is scanned first when angle brackets are used.

Note that fully qualified paths should be used (for example, com/newt/ui/ui.h). See Sparse Trees for information on developing with a sparse tree, and how using fully qualified paths facilitates this feature.


Next: , Previous: C Include Files, Up: Coding Conventions

5.2 Java Import Statements

Fully qualified classnames (such as com/newt/ui/UserInterface) must be used in the code and in import statements. In particular, wildcards as in import package.*; must not be used.

The reasons for this include better documentation, ensures that you understand what you are doing, makes it easier for automated tools to remove obsolete import statements and makes it possible to do a global search and replace in case the class is moved from one package to another.

A stronger reason is that you get the classes you expect. The wild card rules are a bit strange when handling multiple classes of the same name in different packages. If the signatures match, the compilation fails entirely.

An even stronger reason is that in the wildcard form, the Java compiler will check every file in the package and recompile them. At best, it is a waste of time. At worst, the class may be compiled incorrectly if more than a simple compilation with javac is required.


Next: , Previous: Java Import Statements, Up: Coding Conventions

5.3 Perl use Statements

Like in Java and C, Perl use statements should also use fully qualified path names (for example, com::newt::ui::UserInterface). See Sparse Trees for information on developing with a sparse tree, and how using fully qualified use statements facilitates this feature.


Next: , Previous: Perl use Statements, Up: Coding Conventions

5.4 Variable Scoping

In C, global variables, even static within a file, must not be used except when they contain read-only data, or are protected by a mutex. This is mandatory if the code is multi-threaded. Even if the code is not multi-threaded today, it does not mean that it will not be multi-threaded tomorrow.

Similarly in Java, static attributes must be declared final or be protected by synchronized methods or blocks. In addition, all fields must be declared private, except under the following conditions:

The reasons for this are threefold:

  1. Caching code cannot be made to work easily if attributes are accessed directly. If all accesses are via methods, then the methods can automatically manage the validity of the local values of the attributes in the cache.

    <p>

  2. Use of the Reflection package is greatly simplified if all fields are accessed via corresponding get/set/is methods.

    <p>

  3. Style - an object is responsible for maintaining the integrity of its data; exposing the data to the outside world compromises the object's ability to guarantee its data.


Next: , Previous: Variable Scoping, Up: Coding Conventions

5.5 Constants

Constants in C must be coded using const or enum; never with #define (unless these constants are to be declared in header files and the compiler prevents the const or enum from appearing there). Symbolic uppercase names must be used for values.

In Java, the static final declaration must be used. It is especially important that strings are kept in constants so that only one copy of a particular string is created. Every time a hard-coded string is encountered, a new object is created: memory is wasted and performance is lessened. The use of constants in place of strings also allows the compiler to catch typos.


Next: , Previous: Constants, Up: Coding Conventions

5.6 Return Values

In general, methods return references to objects. In some cases, where the class wants to be absolutely sure that a client does not modify the object, it can return a copy of the object.


Next: , Previous: Return Values, Up: Coding Conventions

5.7 Size

Files or classes should be well less than 1000 lines. For example, the average length of the java.lang classes is a healthy 250 lines. Smaller files enable compiles to run quicker and reduce the chance of editing conflicts.

Methods should also be small to enable understanding and to reduce complexity in testing. This translates to a maximum of two printed pages (120 lines) including comments (excluding the javadoc method header) or about 30-50 statements. Programmers should, however, limit methods to one printed page, or 10-20 statements.


Next: , Previous: Size, Up: Coding Conventions

5.8 API Documentation

Each method must have a header that contains enough information so that a programmer can use the method without having to read the code. Generally, this documentation will be written during the design phase before the method is written. In particular, the format for javadoc must be used in Java files. Unless a particular language already has a documentation format (such as POD for Perl), the javadoc format must be used as javadoc may be extended to cover other languages in the future.

The Java template describes the look and feel of these javadoc headers.

Invoke javadoc with make doc to generate all of the documentation and place it in sw/doc/api. You can also build the documentation for a single class with make Class.javadoc.


Next: , Previous: API Documentation, Up: Coding Conventions

5.9 Method Deprecation

When changing the signature of methods, it is preferable not to delete the old signature right away. Instead, add the @deprecated Javadoc keyword to the method's comment header. For example:

         * @deprecated Use foo(int arg1, int arg2).

Then, when people compile, instead of compilation error they will get a "deprecated" warning. They can check your documentation to see how they should invoke the method.

When the deprecated method is no longer called from the integration code base, you can safely remove it.


Next: , Previous: Method Deprecation, Up: Coding Conventions

5.10 Compiling

Code must not be published that contains compiler (or lint) errors or warnings (using the compiler's highest warning level). The code should also compile cleanly with the compiler set for maximal portability, unless the code is platform-specific.


Previous: Compiling, Up: Coding Conventions

5.11 History

Do not clutter the code with your initials or dates. That is what CVS is for. Years from now, they become a useless legacy.

This does mean that you should not comment your code. On the contrary, if you add a line of code to fix a bug, you might want to indicate its importance to prevent someone who does not understand it from taking it out in the future. In addition, it is very important to provide good comments when checking files into CVS so that CVS tools can be used to generate a good historical record of the file. See Check in Software for logging conventions.


Next: , Previous: Coding Conventions, Up: Top

6 Java Coding Hints and Methodology

This section is similar to Coding Conventions, but the topics are more like hints than conventions. Since developers should use these techniques, they are still considered to be conventions. This section pertains solely to Java.


Next: , Previous: Java Coding Hints and Methodology, Up: Java Coding Hints and Methodology

6.1 Constructors

When there are more constructors than just the default, put all of the initialization into the constructor with the most arguments and write all the other constructors in terms of that constructor. For example:

         public class Foo {
             private Type a;
             private Type b;
     
             Foo() {
                 this(default_for_a, default_for_b);
             }
     
             Foo(Type a) {
                 this(a, default_for_b);
             }
     
             Foo(Type a, Type b) {
                 this.a = a;
                 this.b = b;
             }
         }


Next: , Previous: Constructors, Up: Java Coding Hints and Methodology

6.2 Setting Attributes

Attributes that are exposed through public methods should be sanitized in set* accessor methods so that the object can skip checks that this attribute is non-null; moreover, clients can assume the value is non-null as well.

If a class does employ this methodology, the behavior of the set* accessor methods upon receipt of a null must be documented. The get* accessor methods must be documented that they never return null. The behavior may also be documented in the constructors and preamble as necessary.

Here is an example that demonstrates:

         public class Foo {
             public static final String DEFAULT = "Foo";
             private String name;
     
             /**
              * Create object Foo and assign a default name.
              */
             public Foo() {
                 this(null);
             }
     
             /**
              * Create object Foo and assign the given name.  If the name given is
              * <code>null</code>, then a non-null default name will be
              * used.
              *
              * @param name The name to be given to the class;
              * <code>null</code> may be used to request the
              * default (non-null) name.
              */
             public Foo(String name) {
                 setName(name);
             }
     
             /**
              * Set the name of this object class.<p>
              *
              * If the name specified is <code>null</code>, then a
              * non-null default name is assigned to the class.
              *
              * @param name The name to be given to the class;
              * <code>null</code> may be used to request the
              * default (non-null) name.
              */
             public void setName(String name) {
                 name = (name == null) ? DEFAULT : name;
             }
     
             /**
              * Get the name of this object.  Guaranteed to be non-null.
              */
             public String getName() {
                 return name;
             }
     
             private doit() {
                 // Use name with impunity.
                 System.out.println(name + ", length = " + name.length());
             }
         }
     
         public class Bar {
             private doit() {
                 // Use name with impunity.
                 Foo foo = new Foo();
     
                 System.out.println(foo.getName() + ", length = "
                         + foo.getName().length());
             }
         }

Furthermore, when changing the value of an attribute is costly or involves significant work to keep the entire object consistent, or when there are other expensive side effects, the setting of an attribute should not occur if the value of the new attribute is the same as the existing value. In the trivial case (where all values are valid, and there are no side effects), it is better just to set the value with a simple assignment statement. The test and branch may itself cost as much as the set.


Next: , Previous: Setting Attributes, Up: Java Coding Hints and Methodology

6.3 Exceptions

A reference must be checked for non-null if it can be null during normal processing. If the value should be normally non-null, or it is guaranteed to be non-null as in Setting Attributes, then the code can rely on exceptions to catch invalid dereferences.

The legal values for each parameter should be documented with the Javadoc @param keyword. The system or programmer can then throw an IllegalArgumentException or a NullPointerException if a caller violates the "contract."

Guidelines for using exceptions include:

Once a method has caught an exception, the user may need to be notified. See Notes what should happen next.

For more information on exceptions, see Peter Haggar's Java exception handling presentation (previously at http://www.ibm.com/software/developer/library/javaexc/jexcept.htm) or the Java Tutorial.


Next: , Previous: Exceptions, Up: Java Coding Hints and Methodology

6.4 Verifying References

The value of any reference that can be manipulated outside of a class must be checked for non-null before dereferencing if it is not guaranteed to be non-null as in Setting Attributes. These include non-private references (which should not be used in the first place as described in Variable Scoping) or private references that can be manipulated by non-private methods.

References may either be checked directly, or be contained within a try block with a specific NullPointerException catch block. Performance-wise, the direct check for non-null is faster than a try/catch block, unless the test is in a loop with thousands of iterations. However, the try/catch block may be more readable.


Next: , Previous: Verifying References, Up: Java Coding Hints and Methodology

6.5 Iterating Collections

There are two ways to iterate through a collection. The old fashioned way is:

         for (int i = 0, size = fooList.size(); i < size; i++) {
             foo = (Foo)fooList.get(i);
             // Do stuff with foo.
         }

However, it is preferable to iterate through the list with:

         Iterator fooIterator = fooList.iterator();
         while (fooIterator.hasNext()) {
             foo = (Foo)fooIterator.next();
             // Do stuff with foo.
         }

There are several reasons for this. First, if the list is a generic collection, you can not use get to obtain the ith element at all.

Even if the collection is a list, it is better to do the loop with an iterator because it puts fewer restrictions on the collection so that you can actually refer to the collection as a collection instead of a list. This allows for future flexibility.

In the performance arena, the iterator will run as fast as or faster than the direct access, depending on the underlying implementation. It will never run slower.


Next: , Previous: Iterating Collections, Up: Java Coding Hints and Methodology

6.6 Resources

Resources must be used for any strings that the user may see. The interface may then easily be translated, or overall changes in wording may be accomplished by editing a single file. Help topics can be associated with user interface elements via the resource files. Finally, users can customize the interface via private resource files.


Next: , Previous: Resources, Up: Java Coding Hints and Methodology

6.7 Output

In command-line applications, the method System.out.println is used for output and System.err.println is used for errors. Code must not be checked in with debugging output.

In applications that run as daemons, code must not be checked in that contains System.out.println or System.out.println statements that are not commented out. Instead, any output must be written to a log file via a logging class.

In applications that have GUI interfaces, all output must appear in the GUI or optionally in a log file via a logging class. Code must not be checked in with System.err.println statements that are not commented out.


Next: , Previous: Output, Up: Java Coding Hints and Methodology

6.8 Naming Threads

In order to make Java core dumps more readable, threads should be named. Thread names should take the form:

         Identifier-Class[-(#|Description)]

Here are some examples:

         NS-SomeApplication-0, NS-SomeApplication-1, ...
         NS-Log-Writer
         NS-Log-Reader


Next: , Previous: Naming Threads, Up: Java Coding Hints and Methodology

6.9 Swing Listener Paradigm

The Swing listener paradigm should be used for most inter-class communication for consistency and flexibility. Most of the code is boilerplate.

  1. Create a FooListener interface that sub-classes EventListener that defines appropriate methods.

    <p>

  2. Create a FooEvent class that sub-classes EventObject that contains attributes and code that your callbacks will need. The idea is to pack this class with enough information so that the listener will not have to query the source object upon receiving this event.

    <p>

  3. In your observed class, add the following. Note that you will replace the selectedItem and selectedVersion arguments to fireFooXXX with objects that are found in your event object. The method fooXXX is defined in FooListener. This is documented in EventListenerList.
                  private EventListenerList listenerList = new EventListenerList();
              
                  public void addFooListener(FooListener l) {
                      listenerList.add(FooListener.class, (EventListener)l);
                  }
              
                  public void removeFooListener(FooListener l) {
                      listenerList.remove(FooListener.class, (EventListener)l);
                  }
              
                  protected void fireFooXXX(Object selectedItem, String selectedVersion) {
                      FooEvent fooEvent = null;
              
                      // Guaranteed to return a non-null array
                      Object[] listeners = listenerList.getListenerList();
              
                      // Process each of the listeners, notifying
                      // those that are interested in this event
                      for (int i = listeners.length-2; i >= 0; i -= 2) {
                          if (listeners[i] == FooListener.class) {
                              if (fooEvent == null) {
                                  fooEvent = new FooEvent(this, selectedItem,
                                                          selectedVersion);
                              }
                              ((FooListener)listeners[i+1]).fooXXX(FooEvent);
                          }
                      }
                  }
    
  4. Implement FooListener in your observer class, and register it with the observable class with observable.addFooListener(this).


Previous: Swing Listener Paradigm, Up: Java Coding Hints and Methodology

6.10 Multi-use Classes

Multi-use classes are classes which are used in client/server architectures, in native code implementations on multiple platforms, or in any other situation where multiple implementations of a single class are required. Maintaining a single source across all uses of a class reduces the amount of coding required, but more importantly, ensures that classes match across each use.

In addition, it is possible that the classes differ only in how they are compiled, not in how they are defined.

The following sections describe conventions that resolve these issues.

First, there is a subdirectory for each of the uses of the multi-use class. Suggested directory names include client and server in a client/server architecture, or and generic, solaris, linux, and nt in the implementation of native classes on multiple platforms.

One of these subdirectories is called a primary package. It is suggested that the primary package should be client in the client/server architecture. In the case of the multiple platform implementation, the primary package is the generic package which could contain a software implementation that would run, albeit slowly, on all platforms.

The rest of the subdirectories are called secondary packages.


Next: , Previous: Multi-use Classes, Up: Multi-use Classes

6.10.1 Interface

Each multi-use class has a common interface in the package directory (for example, com.newt.pkg.Class). This is what users of the class typically reference. By using an interface, code can work with objects the same way across multiple uses.


Next: , Previous: Interface, Up: Multi-use Classes

6.10.2 Abstract Class

The abstract class is the file containing most of the substance of the class. It implements the entire interface, and contains implementations which are common across all uses. The abstract class is named by taking the name of the interface and adding AC (for example, ClassAC).

The abstract class is located in the primary package.

If the class to be implemented is Class in package pkg and the primary package is client then the abstract class must be written as:

         package pkg.client;
     
         import  pkg.Class;               // import interface
         import  another_pkg.client.AnotherClassAC;
     
         abstract class ClassAC implements Class {
             // Private Attributes
             private another_classAC member;
     
             // Protected Constructors
             protected ClassAC(another_classAC member), ...) {
                 this.member = member;
                 ...
             }
             ...
         }

Any references to classes contained in the primary packages must be referenced via an import directive (import pkg.client.ClassAC). This is because the copies of this class in the other uses is created by taking this file and replacing, for example, .client. with .server., but only in package and import directives.

The abstract class' constructor(s) should be protected; an abstract class can only be instantiated by its implementation.

All members should be private, and accessed strictly via set/get methods. This allows all implementations to manipulate the attributes as necessary.


Next: , Previous: Abstract Class, Up: Multi-use Classes

6.10.3 Implementation

The implementation contains, at a minimum, a constructor for the abstract class. It may, in addition, contain platform-specific code. There must be separate implementations for each of the existing uses of the class. Each implementation extends the corresponding abstract class, and imports the corresponding interface. The name of the implementation starts with the name of the interface and adds Impl (for example, ClassImpl).

Hints for structuring platform-specific code: Often, the same service will be provided in all uses, but one use may have to do more work. For example, a client may do everything locally, and then tell the server to perform the same task. One can write most of the logic as common code in the abstract class, and have that code invoke a protected method, defined in the abstract class to do nothing. Then one or more implementations may override the method, adding the additional functionality that is specific to the particular platform. If each platform will override the helper method, then the abstract class should not provide a default implementation; rather, the method should be declared protected abstract.

Sometimes, different implementations may support a subset of the methods for the abstract class, but not all of them. There are two approaches here. One is to have an abstract class which is little more than an interface; it declares all the methods abstract. Then each implementation must provide definitions for all the methods, though the unsupported ones may return errors, or throw exceptions. This approach works well if most methods are supported by each implementation. If implementations will not support several methods, an alternative approach is to provide default implementations in the abstract class which return errors or throw exceptions, which may then be overridden.

The only place in which code cannot access an object via its interface is when creating it. To invoke a constructor, one must reference the implementation directly. As with the abstract class, one should import the specific implementation (e.g., import pkg.client.ClassImpl), and then refer to the implementation by its short name (e.g., ClassImpl).


Previous: Implementation, Up: Multi-use Classes

6.10.4 Makefiles and Code Generation

The Makefile in the primary package is fairly simple. It can follow the general pattern:

         TOP = ../../../..              # Root of tree (sw)
     
         include $(TOP)/tools/Makefile.vars
     
         CLASSES = Class ...      # List of classes for which there are
                                        # AC and Impl files
         OBJECTS := $(CLASSES:=IMPL.class) $(CLASSES:=AC.class)
     
         include $(TOP)/tools/Makefile.rules

The CLASSES keyword is simply there to allow you to list the classes in one place, rather than duplicate or triplicate the class names elsewhere in the Makefile. The OBJECTS line tells make(1) to generate the objects on that line. Specifically, it says to generate the ClassImpl.class and ClassAC.class files. The reason why it is necessary to specify both sets of files is that if the AC file changes but the implementation file does not, the AC file will not be recompiled implicitly; it must be listed as one of the targets. (This still does not solve the problem of what to do if the interface file changes, but the AC and Impl files do not; in this case, one must execute make clean.)

Other targets may also be added to the OBJECTS definition.

The Makefiles in the secondary packages are still short, but significantly more complex, because they generate the abstract class (AC) file from the version in the primary package. They follow the general pattern:

         TOP = ../../../..              # Root of tree (sw)
     
         include $(TOP)/tools/Makefile.vars
     
         CLASSES = Class ...      # List of classes for which there are
                                        # Impl files, and for which AC files
                                        # will be generated
         ACFILES := $(CLASSES:=AC.java) # Abstract class files for CLASSES
         PREFILES := $(ACFILES)         # Files that must be pre-generated
         OBJECTS := $(PREFILES) $(CLASSES:=Impl.java) $(CLASSES:=AC.java)
         CLEAN += $(PREFILES)
     
         include $(TOP)/tools/Makefile.rules

This secondary package Makefile differs from the primary package Makefile in the second half. ACFILES is the list of abstract class files that must be generated from the client-side versions. You must use the keyword ACFILES. Makefile.rules contains rules that generate the ACFILES.

PREFILES is a list of files and/or directories that must be "pre-generated". When make is run with DOPRE=true, it will pre-generate all the PREFILES before it does the "real" make. This is necessary because a class in one package may depend upon an abstract class in another package. So all the abstract classes must be "pre-generated" prior to compiling. You must use the name PREFILES for this purpose.

The OBJECTS line lists not only the implementation and abstract class files (as in the primary package), but also the PREFILES. This is because the implementation files do not (as far as make is concerned) depend upon the abstract class files. So, if a primary package abstract class file were to change, the secondary package implementation file would be compiled against the old, previously generated secondary package abstract class file, unless we forced that to be regenerated first. One could position the abstract class file before the implementation file in the OBJECTS list to solve this problem. But generally, that is inefficient, because it results in two compiles: one for the abstract class, then one for the implementation. If the implementation gets compiled, the Java compiler will compile the abstract class automatically (if needed). So it is better to put the implementation files before the abstract class files.

Makefiles are discussed further in Building Software.


Next: , Previous: Java Coding Hints and Methodology, Up: Top

7 GUI Guidelines

The guidelines put forth in the Java Look and Feel Design Guidelines should be followed. The following sections review some areas where mistakes commonly occur.


Next: , Previous: GUI Guidelines, Up: GUI Guidelines

7.1 Labels

Most items in the application interface should use headline capitalization, which is the style traditionally used for book titles (and the section titles in this book). Capitalize every word except articles ("a," "an," and "the"), coordinating conjunctions (for example, "and," "or," "but," "so," "yet," and "nor"), and prepositions with fewer than four letters (like "in"). The first and last words are always capitalized, regardless of what they are. See [JLFDG] for further information.

However, full sentences should use sentence capitalization and end in a period. Only the first word is capitalized in sentence capitalization.

All labels are followed by a colon.

The ellipsis in a menu item or command button indicates that more information will be required to complete the action which usually means that a dialog will appear. The ellipsis is never hard-coded in a resource or in the code but rather the logic is found in a single place to make it easier to modify the dialog indicator. Internationalization issues also have to be considered since the ellipsis is not used in all languages. For example, to indicate that more information will be required, the JMenuItem or JButton classes could be extended and a indicateDialog parameter could be added to the constructor.

The application name should be in the title of the dialog (for example, Foo: Open File).


Next: , Previous: Labels, Up: GUI Guidelines

7.2 Buttons

Dialog command buttons that apply to the dialog as a whole should appear in a row at the bottom of the dialog and the button group should be right-aligned, not centered. The default button should be the first to appear. Buttons that cause things to be lost should never be a default button.

Whenever possible, use a command name that describes the action (such as Print, Find, Log In, Replace) instead of OK. Otherwise, the following button names should be used:

OK
If this button is pushed, the settings in the dialog take effect and the dialog is dismissed.

<p>

Apply
If this button is pushed, the settings in the dialog take effect immediately and the dialog remains visible. A subsequent Cancel will not undo the changes.

<p>

Reset
This button restores the fields to the specified values of the last Apply or OK.

<p>

Cancel
If this button is pushed, the dialog is dismissed and any display changes from pressing the Preview button are undone.

<p>

Preview
Pushing this button changes the display per the current dialog settings. The changes are undone if the user presses Cancel to dismiss the dialog.

<p>

Close
If this button is pushed, the dialog is dismissed. It is used for inquiry dialogs that do not change state (such as Find) where the buttons above would be inappropriate. It is always used alone.


Next: , Previous: Buttons, Up: GUI Guidelines

7.3 Icons

Use button graphics that are either 16 x 16 or 24 x 24 pixels (but not both in the same toolbar), depending on the space available in your application.

Panels should access the icons they need from a IconFactory class, so as to avoid duplicating icons in memory, as any icon could potentially be used by several objects. Placing the icons in a factory class allows icons to be either loaded from images as needed, or generated on the fly.

Icons are stored as JPG images in the /lib/images/ directory. The syntax for icon names is: panel name, followed by a dash, followed by on for the active state, or off for the inactive state, followed by .jpg: for example, openfile-on.jpg.


Next: , Previous: Icons, Up: GUI Guidelines

7.4 Menus

If all the items in a menu are unavailable, do not make the menu unavailable. In this way, users can still display the menu and view all its (inactive) items.

Place commands that apply to the document (or another object) or application as a whole in the File menu. Thus, the Preferences menu item should be in the File menu.


Previous: Menus, Up: GUI Guidelines

7.5 Notes, Cautions, and Warnings

GUI applications should never emit any output to stdout or stderr. If necessary, the user should be notified via a dialog, usually via JOptionPane.showMessage.

Alert boxes should have a brief header in boldface and headline capitalization. The following HTML can be used:

         <b>header</b><br>
         message

Notes, Cautions, and Warnings provide important information that diverges from the current task or topic under discussion in written documentation. While they are useful in the documentation, Cautions are even more useful as part of the software. Consider adding Caution dialogs in areas where the user could do real damage to the data or software.

Here is a brief description and example of each type of user message:

Notes provide related, parenthetical information, such as an explanation, tip or comment. Notes are important, but not imperative information. A note supplies information that may apply only in special cases such as memory limitations, equipment configurations, or details that apply to specific versions of a program. In code, a JOptionPane with an INFORMATION_MESSAGE should be used. For example:

         <b>Not Supported</b><br>
         This feature is not supported in this release.

Cautions are mandatory text that you must provide to advise the user that failure to take or avoid a specified action could result in loss of data. In code, a JOptionPane with an WARNING_MESSAGE should be used. For example:

         <b>Save Your Work</b><br>
         If you proceed before saving data, you may lose all changes.

Warnings advise users that failure to take or avoid a specific action could result in physical harm to the user or the hardware. Use a warning, not a caution, when physical damage is possible. In code, a JOptionPane with an ERROR_MESSAGE should be used. For example:

         <b>Avoid Moisture</b><br>
         Do not let your keyboard come in contact with water or other
         fluids.  Excessive moisture may damage the keyboard's internal
         circuitry.


Next: , Previous: GUI Guidelines, Up: Top

8 Build Environment

The appearance of the build environment is described in Repositories. This section goes further and discusses how software is built, tested, and released to other developers. Then, integration trees as well as subsets of this tree called sparse trees are described. Finally, some suggestions on creating Web services are given.


Next: , Previous: Build Environment, Up: Build Environment

8.1 Building Software

Packages are built by simply typing make. GNU make is used on all platforms in order to keep the Makefiles consistent.

It is essential that no boilerplate information appear in the Makefiles, but rather in a central location such as Makefile.rules and Makefile.vars in sw/tools, so that changes to the entire system may be made easily. The goal is that Makefiles in the packages should only contain variables which contain the name of the files that comprise the package. The package Makefiles should almost never have any targets, and only if the rules for these targets will never, ever be used elsewhere. An exception is a target may be used so long as it has no rules.

For example, a Makefile might look like this:

         TOP = ../../..
     
         include $(TOP)/tools/Makefile.vars
     
         DIRS = action test
     
         OBJECTS = Bar.class                     \
                   Foo.class
     
         include $(TOP)/tools/Makefile.rules

See Common Makefile Targets for more information on Makefiles.

The package directory should only contain those files that go into the package; test programs should go into the test subdirectory (see Running Unit Tests).

The NS_PRODUCT environment variable should be set to the name of the product or project. See Common Makefile Targets for details.

Developers may use the IDE of their choice for developing software. However, they must maintain the package Makefiles so that the software may be built by others as well as the automated build system. The IDE project file may be kept in the package directory. However, if the IDE requires more than one project file, the files should be kept in subdirectories of the package to keep the packages tidy. For consistency, the directories should named as follows:

IDE Directory Name


Symantec Cafe sc


Visual C++ vc

Note that files generated from project files (such as *.mak) must not be checked into CVS. In VC++, msdev, which reads the project files directly, can be used instead of nmake.


Next: , Previous: Building Software, Up: Build Environment

8.2 Running Unit Tests

The unit tests for a package are kept in a subdirectory of that package called test. One script in that directory executes the various tests and is called the driver. It must have the same name as the package. For example, if the package is called util, then the driver is called util also. This convention must be followed so the build tools can find this driver.

The driver must return zero if the tests passed, and one if the tests failed.

The output of the driver should be brief and may include a diff to help understand the problem and possibly to help update the expected output if necessary. Please use the following formats so that post-processing software may more easily parse the testing output:

Normal Output
          Testing Bar...Passed
          Testing Foo...Passed
          Package util passes all tests.

A Test with Problems
          Test 1...Passed
          Test 2...Failed
          <diff or error output here>
          Test 3...Passed
          Package util failed 1 of 3 tests.

The driver should remove any temporary files it creates.

The command nstest is used to execute all of the unit tests. It will optionally send email to the author of any broken tests.


Next: , Previous: Running Unit Tests, Up: Build Environment

8.3 Releasing Software

When a package is ready for primetime, it can be made available to other developers with the nspublish command. Having a separate step allows the developer to check in intermediate versions at the file level and not worry about affecting fellow developers. When software is released, the developer drafts a message that briefly describes the change and lists any issue tracking numbers that the release resolves. This message is then mailed to the other developers.

The nspublish command does the following:


Next: , Previous: Releasing Software, Up: Build Environment

8.4 Building Trees

The command nsbuild is used to build fully populated trees. The command will optionally send email to the author of any software that fails to build.


Next: , Previous: Building Trees, Up: Build Environment

8.5 Integration Tree

One specific tree is called the integration tree. The integration tree contains the latest published code tagged with ns-build. The integration tree is populated with cvs update -rns-build -d -P -C which checks out the latest published software, creates new directories, prunes empty directories, and–since the integration tree is intended to be read-only–clobbers locally modified files.

The integration tree is built every night with nsbuild and the unit tests are run with nstest. Any compilation errors or unit test failures are mailed to the author of the defective software. It is the responsibility of the programmer to ensure that software is fixed immediately. After fixing the software, the engineer notifies the configuration manager who then handles the physical rebuilding of the integration tree.


Next: , Previous: Integration Tree, Up: Build Environment

8.6 Sparse Trees

The developer can also check out stable versions of the software with cvs update -rns-build. This cvs command creates a sticky tag which the programmer must remove with cvs update -A before modifying the software. However, the use of sparse trees is preferred.

Sparse trees are subsets of the entire build tree. With the proper environment and environment variables, the developer can compile and run programs in a sparse tree. The advantages of sparse trees include faster CVS updates, much lower disk usage and much faster compilations.

Sparse trees make use of reference trees. These reference trees are fully populated trees with a relatively stable set of the software. The reference tree may be an integration tree (see Integration Tree) which is populated by recently released software (see Releasing Software) and built every night. A reference hierarchy might also be a more stable production release. In either case, the use of stable software makes development less frustrating.

In order to compile or run a Java, Perl, or C program using a local directory if present, or a reference hierarchy if not present, certain environment variables need to be adjusted. Assuming that an integration tree in /ns/integ is to be used, a reference hierarchy can be defined for Java, Perl and C respectively with:

         CLASSPATH=../../..:/ns/integ/sw
         PERL5LIB=../../..:/ns/integ/sw
         C_INCLUDE_PATH=../../..:/ns/integ/sw
         CPLUS_INCLUDE_PATH=../../..:/ns/integ/sw

In order to keep these paths this short, the Java import statements, Perl use statements and C include statements should all use fully qualified path names which are rooted at directories specified by the directories listed in the variables above. For example:

         import com.newt.util.Log;                     // Java
         use "com::newt::util::Log";                   # Perl
         include <com/newt/util/Log.h>                 /* C */

Note that C_INCLUDE_PATH and CPLUS_INCLUDE_PATH are currently only used by the gcc. The -I compilation flag must be used in other environments.


Previous: Sparse Trees, Up: Build Environment

8.7 Web Services

Software that is run as a Web service takes the form of HTML pages, CGI scripts and servlets. The layout of the software in a Web environment is no different from the development environment. The following steps should be taken to create the Web service in a robust and repeatable fashion:


Next: , Previous: Build Environment, Up: Top

9 Run-time Environment

Programs must use the NS_HOME environment variable to find files in the sw hierarchy. Additional environment variables may be provided to allow customers to place directories in their own partition. For example, $NS_DATA can be used to override $NS_HOME/data, and $NS_SPOOL can be used to override $NS_HOME/spool and so on. The software must always fall back to $NS_HOME/directory if the NS_directory environment variable is not present.


Next: , Previous: Run-time Environment, Up: Top

10 Information for Build Engineers


Next: , Previous: Information for Build Engineers, Up: Information for Build Engineers

10.1 Version Numbers and Tags

The customer sees version numbers for the software of the standard form:

major.minor[[.maintenance][.patch]]

However, the CVS tags look like the following:

product-major_minor[_maintenance][_patch]ibgrevision

where:

Patch and maintenance revisions may have internal and beta identifiers, but typically only have a gold identifier.

For example, the tags for a normal development cycle may have the following values:

ns-1_0i1, ns-1_0i2, ..., ns-1_0b1 (ns-1_1i1 or ns-2_0i1 would be the next mainline version at this time), ns-1_0b2, ..., ns-1_0g1, ns-1_0_1b1, ns-1_0_1g1, ns-1_0_2g1, ...

The following details are directed at the configuration manager and may be skipped by others.

A tag may "slide" until the software the tag represents is released and available to others, usually while the software is still in the QA process. Once the software is released however, the tag is forever frozen and must not be changed.

At the time the first beta (e.g., ns-1_0b1) is released, a branch is made. This is the release branch. In this example, the command is cvs rtag -b -rns-build ns-1_0 ns. The tag ns-1_0 now points to this branch. All beta, gold, and maintenance tags refer to versions on this branch (although the branch may be moved up the main branch at the discretion of the configuration manager to make things easier for developers).

Once the release branch is created, no more internal (i.e., i) releases are made with this major.minor combination. Instead, the minor number is incremented, or the major number is incremented and the minor number is set to 0, and the internal number goes back to 1. For an example, see the progression above.

At the time the release branch in the example above is created, mainline development for version 1.1 (or 2.0) can continue. Any development on the release branch is performed by first running cvs update -rns-1_0. This development on the release branch may be optionally merged into the mainline.

To prepare for merging into the mainline, run cvs rtag -b -rns-build ns-1_0-merged ns at the time the branch is created. Then, merge the bug fixes on the branch into the mainline with:

         $ cvs update -A [file]
         $ cvs update -j ns-1_0-merged -j ns-1_0 [file]
         $ cvs tag -rns-1_0 ns-1_0-merged [file]

This clears the sticky tag and checks out the most recent code on the mainline. Any recent changes on the branch are then merged into the mainline. Finally, the tag used to determine which changes have already been incorporated into the mainline is update.

It is also conceivable that a developer may which to start work on version 2.0 software before version 1.1 has come out with a beta release. This forces an early branch of the 1.1 version.

There are no alpha releases per se as all of the internal releases are considered alpha releases.

It is suggested that internal releases be given code names that do not have numbers in them since they can easily get out of sync with the version numbers when there is more than one release per internal release to fix unexpected bugs. On the other hand, it is usually not a problem to use numbers for beta releases (for example, b1 is Beta 1). There is typically only one gold release, although the revision is retained for consistency; subsequent releases would be maintenance (e.g., ns-1_0_1g1 for 1.0.1) or rarely, patch releases (e.g., ns-1_0_0_1g1 for 1.0.0.1). In dire circumstances, a letter may be appended to the version to keep the internal or beta number the same as the external name.

If a custom release is made, the base part of the tag contains the version the release was based upon, followed by a hyphen, and an identifier that describes this release (e.g., ns-1_0i7-javaone).

It is suggested that one class should be used by programs to show the current version in a user-friendly way. This class (such as com.newt.ns.Version) should be assigned a state which contains the same name as the tag.


Next: , Previous: Version Numbers and Tags, Up: Information for Build Engineers

10.2 Release Information

This section describes the steps used in releasing the software and includes some hints for doing it safely. It can be safely skipped by developers.


Next: , Previous: Release Information, Up: Release Information

10.2.1 Full Release

The following steps are applicable when all of the software is being released.

  1. Check the integrity of the ns-build tag.

    <p>

    In the integration directory (/ns/integ), check for files that might have been removed but still have the ns-build tag. Also check for files whose ns-build has not yet been updated. Use:

                  cvs -n update -A
    

    Tags may be easily deleted with the following:

                  cvs tag -d ns-build $(cvs diff --brief -rHEAD -rns-build 2>&1 | grep "new entry" | awk '{print $3}')
    
  2. Set the state.

    <p>

    This step assumes a version class in com.newt.ns.Version and should be run from the root of the integration directory /ns/integ/sw. Note that a revision of a file can only have one state. A new version of Version.java is checked in to force updates of this file in the various work directories to ensure that the version in the Help->About box is correct.

                  cvs commit -f -m"1. Release tag." sw/com/newt/ns/Version.java
                  cvs tag -F -r1 ns-build sw/com/newt/ns/Version.java
                  cvs admin -stag
    
  3. Tag the release.

    <p>

    Once the discrepancies have been fixed, the release can be tagged based upon the tag ns-build with the following. It can be run from any directory. Tag names are described in Version Numbers and Tags.

                  cvs rtag -rns-build tag ns
    
  4. Create a release hierarchy.
                  mkdir /ns/build/tag
                  cd /ns/build/tag
                  cvs -d/ns/cvsroot checkout -rtag -P sw
    
  5. Build the release.
                  cd sw
                  CLASSPATH= make [DOPRE=true] dist
    


Previous: Full Release, Up: Release Information

10.2.2 Partial Release

The following steps are used to release a subset of the entire software suite.

  1. Create the hierarchy.

    <p>

    It is suggested not to use cvs update -dl (non-recursive) at the top level but to allow the update to recurse at the package level (for example, cvs update -d util) so that entire packages are checked out.

    <p>

  2. Tag the files.
                  cvs tag tag sw
    
  3. Build manifest.

    <p>

    The manifest contains a list of all the files checked out in the previous two steps and their version. This manifest also contains a list of all the third-party software required to build the software, including the version number and the commands that were used to build the software to make it very easy to recreate the environment in the future.

    The manifest must be stored in the file sw/build/manifest.$NS_PRODUCT and must be under revision control. It should be given the same tag as the software that it documents. In addition, an archive of the original distributions of the third party software must be kept in /usr/local/src or /opt/src as appropriate.

    <p>

  4. Create a Makefile to build distribution (if necessary).

    <p>

    See the documentation above the dist target in Makefile.rules for specific information. See also Common Makefile Targets for information regarding the use of the environment variable NS_PRODUCT. The goal for these Makefiles is similar to package Makefiles in that the product Makefiles should simply define variables that are acted upon by targets and rules in Makefile.rules.

    <p>

  5. Create a release hierarchy.
                  mkdir /ns/build/tag
                  cd /ns/build/tag
                  cvs -d/ns/cvsroot checkout -rtag -P sw
    
  6. Create distribution.
                  cd sw
                  make dist
    

    The distribution file can then be found in lib.


Next: , Previous: Release Information, Up: Information for Build Engineers

10.3 Slipping a Tag

Once software is released, the tags are frozen. However, before the software is released, the release tag may be updated if QA discovers problems in a build that must be remedied before it is released. This section can be safely skipped by developers.

There are two steps in slipping a tag:

  1. Create a list of files that have changed.

    This can be done by looking at the differences between the integration build and the release build:

                  cvs diff --brief -rtag -rns-build | grep ^Index | sed 's/Index: //'
    
  2. Slip the tags.

    For each file, the state of the old version needs to be reset, the tag needs to be moved, and the state of the new version needs to be set. Since this is tedious and error-prone, the semi-automatic script nsretag can be used as follows:

                  nsretag tag file ...
    


Previous: Slipping a Tag, Up: Information for Build Engineers

10.4 Miscellaneous Hints

Here is a collection of commands that can be used to check the integrity of various components. Developers are encouraged to run these tests before checking in their software. In the future, these tests may be prerequisites for a check-in.

Look for incorrect state ident $(find . -name \*.java -o -name Makefile) 2>&1| egrep -v '(warning|Revision|^$)' | less


Look for state duplicates for i in $(find . -name \*.java); do [ `cvs log $i | grep -c "state: tag"` -gt 1 ] && cvs log $i; done | less


Look for valid CVS Revision tags find . -name \*.java -o -name \*.p[lm] | xargs ident | egrep '(Revision|Id)'


Next: , Previous: Information for Build Engineers, Up: Top

11 Platforms

This section describes the platforms a company is targeting for their software. For example, Newt Software develops software for the Java and Linux platforms.


Next: , Previous: Platforms, Up: Top

12 Issue Tracking

Issues ranging from bug reports, feature requests, and normal development need to be tracked. Newt Software employs GNATS and Bugzilla.


Next: , Previous: Issue Tracking, Up: Top

Appendix A Style Sheet

This appendix contains a list of words and terms that often appear in various forms. For consistency's sake, they should be written as given below. The trademark symbol must be used the first time a trademarked name appears in the document. Thereafter, it must be omitted.

          Internet (when referring to the Internet)
          Web (when referring to the Web)
          email
          offline
          online


Next: , Previous: Style Sheet, Up: Top

Appendix B Coding Style Sheet

Common action words and nouns that are used in class, variable, and method names are enumerated here for the sake of consistency.

          Added
          Button
          Modified
          Removed
          getAttribute (or isAttribute)
          setAttribute


Next: , Previous: Coding Style Sheet, Up: Top

Appendix C Editor Customizations

This section suggests some ways to customize your editor to make it easier to comply with the coding conventions described in this document.


Next: , Previous: Editor Customizations, Up: Editor Customizations

C.1 Emacs

Add the following to your .emacs file:

         (add-hook 'c-mode-common-hook
                   '(lambda ()
                      (setq indent-tabs-mode nil)
                      (if (< emacs-major-version 20)
                          (c-set-offset 'substatement-open 0)
                        (c-set-offset 'inline-open 0)
                        (c-set-offset 'topmost-intro-cont 0))
                      (setq comment-column 40)))
     
         (add-hook 'java-mode-hook
                   '(lambda ()
                      (setq c-basic-offset 4)))

This makes tabs insert spaces, tweaks the indentation settings, makes in-line comments start in column 40, and sets the indentation level to four. Use CTRL-J instead of carriage return to indent automatically, or c-auto-newline can be set to t to insert newlines and indent automatically when the left curly brace ({) or semi-colon (;) are entered.


Next: , Previous: Emacs, Up: Editor Customizations

C.2 Vi

Add the following to your .profile or equivalent for your shell:

         export EXINIT="set sw=4 |map! ^T ^[a    "

This sets the shiftwidth to four so that the >> and << commands support the correct indentation. The map command allows you to indent with CTRL-T (you can not map tabs or other white space characters) by inserting four spaces. Note that in vi you have to enter control characters by quoting them with CTRL-V. The ^[ character is the escape key.


Previous: Vi, Up: Editor Customizations

C.3 Symantec Cafe

First, bring up the text customization screen through Environment->Editing/Browsing Settings->Text. In this screen check the box for Expand tabs with spaces, set the Spacing box to 4, and check the Autoindent box.

Then bring up the C++ customization screen (next to the Text tab) and check the Indent after { box.

With these settings, you can simply type a carriage return and the editor will automatically indent for you. If you do need to use the tab key, four spaces will be used instead of a hard tab character.


Next: , Previous: Editor Customizations, Up: Top

Appendix D Common Makefile Targets

all This is the default target. It builds everything in the current directory, and recursively builds everything in subdirectories.


all-safe Executes the targets all-server and all-client which makes everything "safely" by having javac check dependencies. This may rebuild more files than is really necessary.


all-server Make all servers safely.


all-client Make all clients safely.


clean This removes any intermediary file that can be created during the compilation process excluding the final target. It also removes any files created by the unit tests.


clobber This removes all files that the clean target removes, as well as the final target.


doc Builds the API documentation from the Javadoc comments. The POD documentation for Perl modules is also built.


file.javadoc Builds the API documentation from the Javadoc comments for the given file only.


jar Create all jar files and install in lib.


dist Create the distribution specified by the NS_PRODUCT environment variable and install in lib.


test This builds the unit tests.


validate Check for any classes missing from the OBJECTS variable.

Product-specific targets should be defined in a separate Makefile. This Makefile is named Makefile.$NS_PRODUCT and is placed in the sw/tools directory. The environment variable NS_PRODUCT is used to control which targets should be defined.


Next: , Previous: Common Makefile Targets, Up: Top

Appendix E References

[AMBY] Scott Ambler, AmbySoft Inc. Java Coding Standards, http://www.ambysoft.com/essays/javaCodingStandards.html, 2000-01-15.

[CVS] Concurrent Versions System Manual, http://ximbiot.com/cvs/wiki/CVS–Concurrent%20Versions%20System%20v1.12.12.1.

[FHS] The Filesystem Hierarchy Standard, http://www.pathname.com/fhs/.

[IH] Henry Spencer, et al., Indian Hill C Style and Coding Standards as amended for U of T Zoology UNIX, http://www.psgd.org/paul/docs/cstyle/cstyle.htm, University of Toronto.

[JAVA] James Gosling, Bill Joy and Guy Steele, The Java Language Specification, Version 1.0, http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html.

[JCA] Clarifications and Amendments to The Java Language Specification, http://java.sun.com/docs/books/jls/clarify.html.

[JCC] Code Conventions for the Java Programming Language, http://java.sun.com/docs/codeconv/, 1999-04-20.

[JLFDG] Java Look and Feel Design Guidelines, Version 2.0, http://java.sun.com/products/jlf/ed2/book/index.html, 2001-02.

[LEA] Doug Lea, Draft Java Coding Standard, http://g.oswego.edu/dl/html/javaCodingStd.html, 1997-10-29.

[PSG] Perl Style Guide, http://www.perl.com/CPAN-local/doc/manual/html/pod/perlstyle.html.


Previous: References, Up: Top

Appendix F Index