--- 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
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.
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:
Table 1: Repositories
Repository Description
doc
specifications, white papers and slide presentations
sw
source code, running system, and user documentation
The doc
repository is organized as follows:
Table 2: doc repository
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/
productsspecifications related to a specific product
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.
Table 3: sw repository
Directory Description Source?
bin
executables no
build
version information that controls builds and is not included in the distribution yes
com/newt/
packagesall 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
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.
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.
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 selectc:\,
then the files will be checked out intoc:\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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
static final
(typically, a manifest
constant or enumerated value).
<p>
protected
(or "default", i.e., no scope
qualifier), and the containing class is declared abstract
.
The reasons for this are threefold:
<p>
get
/set
/is
methods.
<p>
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.
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.
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.
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
.
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.
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.
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.
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.
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; } }
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.
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:
<p>
<p>
<p>
Exception
in main to exit gracefully. Consider
the OutOfMemoryException
.
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.
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.
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.
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.
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.
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
The Swing listener paradigm should be used for most inter-class communication for consistency and flexibility. Most of the code is boilerplate.
FooListener
interface that sub-classes
EventListener that defines appropriate methods.
<p>
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>
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); } } }
FooListener
in your observer class, and
register it with the observable class with
observable.addFooListener(this)
.
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.
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.
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.
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
).
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.
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.
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
).
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:
<p>
<p>
<p>
<p>
<p>
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
.
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.
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.
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.
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
.
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:
Testing Bar...Passed Testing Foo...Passed Package util passes all tests.
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.
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:
cvs -n tag ns-build [
file...]
to see which
files would be tagged and queries the user whether nspublish
should continue. This helps to prevent the accidental tagging of other
developer's files.
<p>
cvs tag -F ns-build [
file...]
.
<p>
ns-build
tag for files that have been removed. This
is accomplished with cvs tag -d ns-build
file.
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.
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.
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.
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:
make dist
<p>
The tarball created in the previous step must be copied to the Web machine, whether by ftp, or Samba. Once this is done, the following steps are suggested on a Unix machine. Equivalent steps and locations on a Windows machine can be substituted.
cd /usr/local/lib gunzip -c tarball | tar xf -
<p>
This may be done by creating links from existing Apache directories, or by configuring Apache to reference the software directly.
<p>
NS_HOME
.
<p>
The specifics of this step remain to be determined. However, the
Apache PassEnv
configuration keyword may be used in a CGI
environment while either an initialization file, configuration entry,
or environment variable may be used in a servlet context.
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.
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]ibg
revision
where:
<p>
<p>
<p>
<p>
<p>
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.
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.
The following steps are applicable when all of the software is being released.
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}')
<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
<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
mkdir /ns/build/tag cd /ns/build/tag cvs -d/ns/cvsroot checkout -rtag -P sw
cd sw CLASSPATH= make [DOPRE=true] dist
The following steps are used to release a subset of the entire software suite.
<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>
cvs tag tag sw
<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>
<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>
mkdir /ns/build/tag cd /ns/build/tag cvs -d/ns/cvsroot checkout -rtag -P sw
cd sw make dist
The distribution file can then be found in lib
.
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:
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: //'
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 ...
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)'
This section describes the platforms a company is targeting for their software. For example, Newt Software develops software for the Java and Linux platforms.
Issues ranging from bug reports, feature requests, and normal development need to be tracked. Newt Software employs GNATS and Bugzilla.
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
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
This section suggests some ways to customize your editor to make it easier to comply with the coding conventions described in this document.
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.
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.
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.
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
andall-client
which makes everything "safely" by havingjavac
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 inlib
.
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.
[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.