Nokia nmake Product Builder
Customer Support Newsletter

Issue No. 5 - May 12, 2000
  1. Hello
  2. New Mailing List
  3. Licenses - No More User Restrictions
  4. Solaris 2.8
  5. Sun -I- Update
  6. Newsletter Feedback
Technical Notes
  1. Improvements to Probe
  2. Solaris C++ 5.0
  3. Solaris ksh Fixed
  4. Creating Jar Files
  1. Using :L to Autogenerate Lists of Files
  2. :Y and the "o" Option
email: (tech support)
email: (licenses/orders)



Time for another newsletter. Actually this is a couple weeks later than we hoped, but we finally made it. We continue our java discussion from the previous issue with rules for building jar files. This issue seems to be somewhat Sun centric, but that was not intential - just a lot of Sun issues to talk about.


New Mailing List

We now have an automated mailing list set up for announcements. The mailing list, named nmake-announce, is a one-way communication channel for the nmake team to announce product news such as new releases, patches, newsletter publications, etc. It is not a general discussion forum, postings will be from the STC only and traffic is expected to be light. All nmake users and administrators are encouraged to join, so please pass the word to anyone that might be interested.

To subscribe/unsubscribe via email:


Licenses - No More User Restrictions

There has been some confusion with the new nmake licenses being issued. We felt the user ID information in the license was too much of a burden for users to maintain so we have removed the user ID information and the user restrictions that went with it.

The problem is coming when users update their old license to a new one. Some users are trying to merge the new license with the old user ID information which renders the license invalid. The user ID fields in the license include the total, local, and user values. If your new license does not contain any of these values do not add them yourself. Adding any of these will cause nmake to issue an "invalid license" error message. In general the license installed should look exactly like that which we send to you, the only exception being for user restricted licenses where user IDs can be added and deleted from the license. As a general rule, if the license file contains the line local=1 then nothing above that line can be added, edited, or deleted, only the user IDs below local=1 can be changed. If the file does not contain local=1 then nothing in the license can be changed. Any license without user restrictions should not be modified in any way.

The lack of user information in the license does mean anyone with access to your machine can run nmake. If you have a desire to limit the access you can still request a license with user ID information. Just ask for such a license when ordering or renewing your license and the appropriate key will be generated for you.

Also keep in mind the cost of nmake was (and is) not determined by the number of users. We collect that information so we have some idea of our user base. It is important to give an accurate user count but we realize this is sometimes difficult. Just remember not to under estimate for fear of driving the licensing cost up because this count does not affect the cost.


Solaris 2.8

The lu3.2 package has been officially tested on Solaris 2.8 and we are happy to say it runs fine on 2.8. This means the same nmake executables run on Solaris 2.4, 2.5.1, 2.6, 2.7, and 2.8. When upgrading your machine there is no need to get a new nmake package!


Sun -I- Update

Last October we sent email out to our users regarding our petitioning of Sun to add support for the -I- feature in their compilers. For those who are unfamiliar, -I- is required to properly access include files in a viewpathing environment and is the reason nmake ships with its own C preprocessor.

In our email we asked our users who were interested in Sun adding support for -I- to provide us with a local contact name and an estimate of the number of users who would benefit from such an enhancement. We received 26 replies indicating approximately 1748 users. A summary of the information was sent to our contact at Sun. At this point it seems Sun development is investigating the feature, but they have neither committed nor refused to do it.

We would like to thank all the projects that took time out to send us a message of support in our pursuit of this feature. Thanks!


Technical Notes

Improvements to Probe

Probe adapts nmake's behavior to differences in build platforms and compilation systems. Probe runs only once for a given compiler and saves the output in a probe information file that may be quickly found and read in by subsequent nmake invocations. Probe works well most of the time, especially for the most widely used platforms and compilers. In these cases its operation is totally automatic and users do need not be concerned about how probe works. In unusual cases where probe cannot function totally automatically, the administrator must manually edit the probe file. While this approach works, it has a number of drawbacks; for example, after editing the probe file, probe is no longer responsible for the maintenance of that file.

As a result of customer feedback, we are investigating ways of improving the probe tool to decrease the administrative overhead and reduce errors. We have identified several possibilities for improvement.

Support More Compilers

Ideally, probe would work out of the box with all compilers in use with nmake. We want to identify all compilers used with nmake and support them to the extent possible.

Improve Probe Variable Documentation

When manual intervention is necessary, the administrator must know which probe variables exist and what they do. To facilitate this, we are planning to improve documentation of probe variables in the user documentation. In additional, we plan to insert documentation blocks into the probe file which describe the purpose and use of each probe variable at the point of definition.

Introduce `Probe Hints' File

We are considering letting administrators provide a `probe hints' file that is separate from the automatically generated probe file. Probe would consult the hints file whenever it generates a probe file, and use the hints to guide the probe. This would eliminate the need to edit the probe file directly.

The proposed `hints file' approach has a number of advantages over the current scheme:

The nmake team welcomes any comments/suggestions you might have about the proposed hints file, or about how to improve probe in general.


Solaris C++ 5.0

A few issues using Sun's new Solaris C++ 5.0 compiler have been reported to us.

C++ 4.x Compatibility Mode

Sun's 5.0 C++ compiler offers a mode for compatibility with their 4.x series of C++ compilers. The compiler flag -compat=4 is used to activate this mode. In 4.x compatibility mode the compiler will access the following directories for headers and libraries:


If you wish to use 4.x compatibility mode we recommend the compiler be probed in this mode so nmake will know about these directories. This is accomplished by setting -compat=4 as part CC and not CCFLAGS. For example, when using 4.x compatibility mode you would set the following:

    CC = /opt/SUNWspro/bin/CC -compat=4

CC Flag -library=<name>

The -library=<name> flag incorporates the named CC libraries into compilation and linking. The necessary -I, -L, -Y P, and -R paths, and -l options are set for the compiler and linker.

This flag does not integrate well with nmake! First consider that nmake must keep track of prerequisite headers and libraries in order to trigger an update of a target. To do this these, all prerequisite files must be searched. By using this flag the compiler will know where to look but Lucent® nmake may not. Any extra header and library directories must be specified with .SOURCE.h and .SOURCE.a so nmake can find them.

Additionally, the flag tells the compiler what libraries to link with, but again nmake will not know. In order for changes to these libraries to trigger a re-link of an executable the desired libraries must be specified as prerequisites in the Makefile.

We recommend avoiding the use of the -library=<name> flag and sticking with traditional nmake usage. As an example, customers were commonly trying to use the following option:

    CCFLAGS += -library=rwtools7,iostream

Instead, the Makefile should be more explicit as follows. In this case only the additional include directory for iostream must be added to the search, and both libraries are listed as prerequisites:

.SOURCE.h : /opt/SUNWspro/SC5.0/include/CCios
target :: source.C -lrwtool -liostream

Compile Errors

Some folks are experiencing errors such as below. These errors are caused by the preprocessor and have been fixed in lu3.2 patch 01. If you have not yet applied the patch please do so.

+ ppcc -i -l /opt/nmakelu3.2/lib/cpp /opt/SUNWspro/SC5.0/bin/CC -O -I-
D/opt/nmakelu3.2/lib/probe/C/pp/0C08CA320binCC -I/opt/SUNWspro/SC5.0/i
nclude/CC -I- -c prog.c
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 149: Error: Invalid
 template parameter default     .
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 152: Error: Invalid
 template parameter default     .
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 162: Error: Invalid
 template parameter default     .
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 163: Error: Multipl
e declaration for InputIterator.
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 165: Error: Invalid
 template parameter default     .
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 168: Error: Invalid
 template parameter default     .
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 169: Error: Multipl
e declaration for OutputIterator.
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 173: Error: Invalid
 template parameter default     .
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 174: Error: Multipl
e declaration for InputIterator.
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 176: Error: Invalid
 template parameter default     .
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 177: Error: Multipl
e declaration for OutputIterator.
"/opt/SUNWspro/SC5.0/include/CC/rw/rwlocale", line 179: Error: Invalid
 template parameter default     .
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 460: Error: numpunct
 is not a member of std::numpunct<>.
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 461: Error: ")" expe
cted instead of "refs".
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 463: Error: charT is
 not defined.
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 463: Error: Template
 parameter __rws td::charT requires a type argument.
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 464: Error: Could no
t find a match for __rwstd::numpunct_impl<<unknown>>::nump
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 464: Error: "," expe
cted instead of "{".
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 475: Error: A declar
ation was expected instead of "protected".
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 476: Error: "virtual
" is not allowed here.
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 476: Error: Illegal 
number of arguments for std::~std().
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 487: Error: A declar
ation was expected instead of "}".
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 499: Error: The type
 "std::numpunct_byname<char>" is incomplete.
"/opt/SUNWspro/SC5.0/include/CC/rw/numeral", line 504: Error: The type
 "std::numpunct_byname<wchar_t>" is incomplete.
"/opt/SUNWspro/SC5.0/include/CC/rw/vendor", line 74: Error: The type "
std::numpunct_by name<char>" is incomplete.
Compilation aborted, too many Error messages.
make: *** exit code 1 making prog.o


Solaris ksh Fixed

If you are running nmake on a Solaris platform you probably already know about the Solaris ksh problem which we address by shipping STC's ksh in the Solaris nmake packages. In case you are unfamiliar there are problems with traps in the Solaris ksh which causes nmake to appear to hang during install operations or when silent is used.

The good news is these problems are fixed in Solaris 2.8! We have tested the ksh that is shipped with 2.8 and it did not exhibit any of the ill behavior we have seen with previous releases. If you are using Solaris 2.8 you are no longer forced to use the STC ksh. (The first ksh in the PATH is the one nmake uses for shell actions.)

We also believe Sun has ksh patches available for other Solaris releases. When we find out details of the patch numbers we will post them in our Solaris ksh faq.


Creating Jar Files

In the previous issue we discussed ways to build java code using nmake. This time we'll look at collecting your class files into a jar file, a java archive. A jar file is similar in concept to an archive C library. Where an archive library is a collection of .o object files to be accessed at link time, a java archive is a collection of class files to be used at build time and run time. Actually the java archive may contain other types of files as well, but we will be focusing on class files made from java source code.

There are other important differences between jar files and archive libraries. A nice feature of archive libraries is the ability to replace a single .o file in the library without affecting the other objects in the library. When only one .o is updated in a build we can replace that one object file without regenerating the entire archive, the other objects need not be available, they are preserved in the archive. On the other hand this is not possible with jar files. When a java archive needs to be updated the entire jar file must be regenerated. This makes things difficult in a viewpathing environment, all the class files for the java archive must be under a single directory node, they cannot be scattered through the viewpath. When updating a single class file, the updated class and all the old class files down the viewpath must be copied to a common node in order to regenerate the jar file. In addition, a jar file may contain directory information so the files in the archive may be in different sub-directories.

We have developed some rules to handle these requirements to create jar files. When a jar file is to be made the following actions take place:

  1. a temporary directory is created in the current directory
  2. class files to be archived are copied from the viewpath into the temporary directory
  3. relative "package" directories are preserved under the temporary directory
  4. the jar file is created from the collection of files and directories under the temporary directory

Variations of these jar rules are currently being used by a few projects, and in some cases for as long as a year or more. However these rules are still somewhat experimental and are changing as necessary to support a wider range of usage. The hope is to have a general set of rules that can be used with no changes by the end user.

The jar rules have recently undergone a lot of changes to make them easier to use and more versatile. If you previously received a set of rules from us you may want to check these out and upgrade to them. The original form of these rules had two restrictions: 1) only one jar file could be created from a makefile, and 2) the local makefile using these rules had to be at the root of the class package directories. That is, the package directories containing the class files had to be underneath the directory containing the makefile which made the jar file. Both of these restrictions have been eliminated in our latest jar rules. This will hopefully make it easier for projects to take advantage of these rules.

Get the jar rules.

Now lets look at an example. The following makefile builds three jar files, each used to demonstrate some aspect of the rules.

:ALL: stc.jar test.jar extern.jar

stc.jar :JAR:       \
	com/lucent/stc/client                       \
	com/lucent/stc/gui                          \
	com/lucent/stc/gui/map                      \
	com/lucent/stc/server                       \
	com/lucent/switch                           \
	com/lucent/stc/test/demo.class              \
	com/lucent/stc/test/common.class            \

test.jar :JAR: com/lucent/stc/test

extern.jar :JAR:    \
	$(VROOT)/prod/classes/mlc                   \
	$(VROOT)/prod/classes/pop                   \
	$(VROOT)/prod/classes/images/pop.png        \
	$(VROOT)/prod/classes/images/mlc.png        \

From the user's point of view you just use :JAR: to make a jar file. The left-hand-side specifies the target jar file to be made. The right side is a list of prerequisites.

The list of prerequisites can specify either directories, files, or both. When a directory is specified all the .class files from that directory will be recursively picked up for the jar file. This will find not only .class files in the specified directory, but also .class files in subdirectories (and sub-subdirectories, etc.). So in order to get an entire tree you can just specify the top node. Or for more control you can specify only the specific leaves needed.

When a file is specified then that specific file is picked up. In cases where you do not want all the files from a directory you can use this to specify only the files you want instead of specifying the directory to pick up everything.

There are also two variables which may be specified either in the makefile or in the prerequisite list. When specified as a regular nmake variable in the makefile they will apply to all jar files in the makefile. When specified in the prerequisite list they will apply only to the specific jar file.

JARTYPES specifies the types of files to be archived in the jar file. All the files matching this pattern in the prerequisite directories will be archived. Use shell pattern matching with a pipe ("|") separating each type (ie. *.class|*.jpg). Default is "*.class".
The root of the package of class files for the jar archive. The default is the current directory, so any package directory would be a subdirectory under the directory holding the Makefile. For example, if the Makefile is in src/java/ but the classes are under src/java/pkgs/<package>/, then use JARROOT=$(VROOT)/src/java/pkgs or JARROOT=pkgs to get <package>/file.class in the archive. If the Makefile is in src/java/pkgs then no setting is required.

Let's take a closer look at each target in the above example. First, stc.jar is made up of several directories, a couple specific class files, and includes *.class and *.png files by default.

stc.jar :JAR:       \
	com/lucent/stc/client                       \
	com/lucent/stc/gui                          \
	com/lucent/stc/gui/map                      \
	com/lucent/stc/server                       \
	com/lucent/switch                           \
	com/lucent/stc/test/demo.class              \
	com/lucent/stc/test/common.class            \

From this we get the following build output. Assume the jar rules are in

$ nmake -g stc.jar
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/client/a1.class stc.
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/client/a2.class stc.
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/client/a3.class stc.
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/c1.class stc.tmp
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/c2.class stc.tmp
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/c3.class stc.tmp
+ mkdir -p stc.tmpjar/com/lucent/stc/gui/map
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/map/china.class 
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/map/china.png st
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/map/na.class stc
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/map/na.png stc.t
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/map/sa.class stc
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/map/sa.png stc.t
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/gui/splash.png stc.t
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/server/b1.class stc.
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/server/b2.class stc.
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/server/b3.class stc.
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/test/common.class st
+ cp com/lucent/stc/test/demo.class stc.tmpjar/com/lucent/stc/test/demo.
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/a.class stc.tmpja
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/b.class stc.tmpja
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/c.class stc.tmpja
+ mkdir -p stc.tmpjar/com/lucent/switch/data
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/data/d1$a.class s
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/data/d1$b.class s
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/data/d1.class stc
+ mkdir -p stc.tmpjar/com/lucent/switch/voice
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/voice/v1.class st
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/voice/v2$a.class 
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/voice/v2$b.class 
+ cp /home/bugati/richb/build/v3/src/com/lucent/switch/voice/v2.class st
+ cd stc.tmpjar
+ jar cf ../stc.jar com

Note that only demo.class is in the top node of the viewpath, all other files come from down the viewpath. Several .png files as well as .class files are picked up. All the files to be archived are copied to the stc.tmpjar temporary directory. The temporary directory is named after the basename of the target jar file so there is no conflict when building more than one jar file. stc.jar is finally created from the files copied to stc.tmpjar.

Also note that com/lucent/switch is one of the prerequisite directories. Files are automatically picked up from com/lucent/switch/data and com/lucent/switch/voice. The final contents of stc.jar is:

$ jar tvf stc.jar
  4199 Mon May 15 14:03:48 EDT 2000 META-INF/MANIFEST.MF
     0 Mon May 15 14:03:36 EDT 2000 com/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/client/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/client/a1.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/client/a2.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/client/a3.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/c1.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/c2.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/c3.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/map/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/map/china.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/map/china.png
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/map/na.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/map/na.png
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/map/sa.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/map/sa.png
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/gui/splash.png
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/server/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/server/b1.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/server/b2.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/server/b3.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/test/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/test/common.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/stc/test/demo.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/a.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/b.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/c.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/data/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/data/d1$a.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/data/d1$b.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/data/d1.class
     0 Mon May 15 14:03:38 EDT 2000 com/lucent/switch/voice/
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/voice/v1.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/voice/v2$a.class
     0 Mon May 15 14:03:36 EDT 2000 com/lucent/switch/voice/v2$b.class
     0 Mon May 15 14:03:38 EDT 2000 com/lucent/switch/voice/v2.class

Next is the simple test.jar with only one directory prerequisite. test.jar ties together with stc.jar above. Notice stc.jar includes demo.class and common.class from com/lucent/stc/test. On the other hand test.jar has the directory specified as a prerequisite and gets all the class files from that directory. So we see how specifying specific files can be used to ignore the other files in the same directory.

test.jar :JAR: com/lucent/stc/test
$ nmake -g test.jar
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/test/common.class te
+ cp com/lucent/stc/test/demo.class test.tmpjar/com/lucent/stc/test/demo
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/test/test1.class tes
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/test/test2.class tes
+ cp /home/bugati/richb/build/v3/src/com/lucent/stc/test/test3.class tes
+ cp /home/bugati/richb/build/v2/src/com/lucent/stc/test/test4.class tes
+ cd test.tmpjar
+ jar cf ../test.jar com
$ jar tvf test.jar
   925 Mon May 15 15:06:18 EDT 2000 META-INF/MANIFEST.MF
     0 Mon May 15 15:06:06 EDT 2000 com/
     0 Mon May 15 15:06:06 EDT 2000 com/lucent/
     0 Mon May 15 15:06:06 EDT 2000 com/lucent/stc/
     0 Mon May 15 15:06:08 EDT 2000 com/lucent/stc/test/
     0 Mon May 15 15:06:06 EDT 2000 com/lucent/stc/test/common.class
     0 Mon May 15 15:06:06 EDT 2000 com/lucent/stc/test/demo.class
     0 Mon May 15 15:06:06 EDT 2000 com/lucent/stc/test/test1.class
     0 Mon May 15 15:06:08 EDT 2000 com/lucent/stc/test/test2.class
     0 Mon May 15 15:06:08 EDT 2000 com/lucent/stc/test/test3.class
     0 Mon May 15 15:06:08 EDT 2000 com/lucent/stc/test/test4.class

Last is extern.jar. This demonstrates the use of JARROOT and also shows that JARTYPES does not affect file prerequisites. By default JARTYPES=*.class, but we have two *.png file prerequisites. These two png files are picked up for the archive and only *.class files are picked up from the two specified directories.

In this example the class files are installed under a different directory than where the makefile is located. JARROOT points to the root of the installed class files so nmake knows that only the directories under the root will be preserved in the jar file. JARROOT is set to $(VROOT)/prod/classes so directories mlc, pop and images will appear in the archive.

extern.jar :JAR:    \
	$(VROOT)/prod/classes/mlc                   \
	$(VROOT)/prod/classes/pop                   \
	$(VROOT)/prod/classes/images/pop.png        \
	$(VROOT)/prod/classes/images/mlc.png        \
$ nmake -g extern.jar
+ cp /home/bugati/richb/build/v3/prod/classes/images/mlc.png extern.tmpj
+ cp /home/bugati/richb/build/v3/prod/classes/images/pop.png extern.tmpj
+ cp /home/bugati/richb/build/v3/prod/classes/mlc/s.class extern.tmpjar/
+ cp /home/bugati/richb/build/v3/prod/classes/mlc/t.class extern.tmpjar/
+ cp /home/bugati/richb/build/v3/prod/classes/pop/cfs/aom.class extern.t
+ cp ../prod/classes/pop/cfs/xyzee.class extern.tmpjar/pop/cfs/xyzee.cla
+ cp /home/bugati/richb/build/v3/prod/classes/pop/tid/cudi.class extern.
+ cp /home/bugati/richb/build/v3/prod/classes/pop/tid/do.class
+ cp /home/bugati/richb/build/v2/prod/classes/pop/tid/wus.class extern.t
+ cd extern.tmpjar
+ jar cf ../extern.jar images mlc pop
$ jar tvf extern.jar
  1233 Mon May 15 15:29:52 EDT 2000 META-INF/MANIFEST.MF
     0 Mon May 15 15:29:42 EDT 2000 images/
     0 Mon May 15 15:29:42 EDT 2000 images/mlc.png
     0 Mon May 15 15:29:42 EDT 2000 images/pop.png
     0 Mon May 15 15:29:42 EDT 2000 mlc/
     0 Mon May 15 15:29:42 EDT 2000 mlc/s.class
     0 Mon May 15 15:29:42 EDT 2000 mlc/t.class
     0 Mon May 15 15:29:42 EDT 2000 pop/
     0 Mon May 15 15:29:42 EDT 2000 pop/cfs/
     0 Mon May 15 15:29:42 EDT 2000 pop/cfs/aom.class
     0 Mon May 15 15:29:42 EDT 2000 pop/cfs/xyzee.class
     0 Mon May 15 15:29:42 EDT 2000 pop/tid/
     0 Mon May 15 15:29:42 EDT 2000 pop/tid/cudi.class
     0 Mon May 15 15:29:42 EDT 2000 pop/tid/do.class
     0 Mon May 15 15:29:42 EDT 2000 pop/tid/wus.class

Hopefully those projects using java will find these jar rules useful. They are intended to be flexible and general enough to be used without modification. If you try them out feel free to let us know how it goes, especially if you have any problems or suggestions.



Using :L to Auto-generate Lists of Files

The :L (list) edit operator is used to return the contents of a directory. This feature can be used to automatically generate lists of files to be built. This is useful when you have a very large number of files such that maintaining them in a Makefile becomes difficult or impractical. However it is still wise to explicitly list source files in a Makefile when possible as this serves to document your build.

The following example illustrates the use of :L operator to build and install documentation files. In this example .fm files are converted to .pdf files with the .pdf files being installed, and .doc files are installed as-is with no conversion.

/* 1. Define the metarule to convert fm to pdf */
%.pdf :
	fm2pdf $(>) $(<)

/* 2. Define the search directories for each type */  : . ../manual
.SOURCE.doc : . ../manual

/* 3. Define the installation directory */

/* 4. Get listing of documents relative with each type */
	return $(*<=*.fm)

	return $(*.SOURCE.doc:L<=*.doc)

/* 5. Install the documents when 'nmake install' is used */

/* 6. By default generate the pdf files */
:ALL: $(FM_FILES:B:S=.pdf)

Here are some details on the above steps.

  1. Defines a standard metarule for making the .pdf files. (The fm2pdf tool was made up for the example, we don't really have one.)
  2. Tells nmake where to look for the various source files. These settings really serve two purposes, 1) to generate the :L list of files, and 2) to find the files when building and installing them. That may seem odd but :L only returns an unbound list of filenames without any directory information. So when we build the files nmake still needs to search for them to bind them.
  3. This is where we will install the files.
  4. Here we have the actual :L magic. Notice the :L expansion is done inside .FUNCTIONAL action blocks. FM_FILES and DOC_FILES are variable names, when referenced the result of their action blocks will be returned. Now someone is saying, "why not just use
    	FM_FILES = $(*<=*.fm)
    instead of these goofy functions?" That is a good question... At first the more simple form above appears to work ok, but actually has problems when files are added for in incremental build. The contents of the variable is compiled into the .mo file and the variable isn't actually expanded again for subsequent runs. So if new files are added to an incremental build they will not be picked up by the Makefile. By making the variables functions they will be properly evaluated each time, and the new files will be recognized.

    The next important piece is the use of $(* and $(*.SOURCE.doc). In step 2 we set these to the proper search directories for our .fm and .doc files respectively. Now we are using them with :L to get the contents of these directories. The "*." on the front returns the prerequisites for the atom, which will be the list of directories we gave in step 2. The great part is that the list of directories will be returned as expanded through the viewpath! Any relative path we gave it will now include all the corresponding directories in the viewpath, so :L will list all the files in the viewpath for these directories. Yes, it is not necessary to write a for-loop and parse the VPATH manually!

    Finally, :L is used with the option "<=*.fm" and "<=*.doc". You can probably guess "=*.fm" will return only the "*.fm" files. The "<" sorts the list in ascending order.

  5. This step just defines a standard install assertion to install the documentation files.
  6. The :ALL: operator is used to define the default targets when none are specified. In this case we will generate all the .pdf files by default.


:Y and the "o" Option

If you are not familiar with the :Y edit operator it may be one of the more frightening looking edit operators. But :Y is very useful and actually very easy to understand. The standard usage looks like this:


What's that supposed to mean? If $(VAR) has some value (it is not null) then evaluate and return the non-null-expression (the expression between the first pair of question marks). If $(VAR) is null then evaluate and return the null-express (the expression between the second and third question marks). The expressions can be simple strings, variables, variables with more edit operators, or null. As you can imagine a few of these nested will appear quite overwhelming.

If you ever browse the default you will see some of these operations without the "Y". The "Y" is optional if "?" is used as the delimiter. So you can think of :Y and :? as the same thing. It is also true that you can use any delimiter when Y is specified.

VAR may have other edit operations performed before :Y is applied. Simply string the edit operations together as you normally would.


The "o" option

Sometimes you will want to return the non-null variable value itself in the non-null case and some default value for the null case. The obvious way is to repeat the original variable expression between the first two delimiters. Building on the example above we have:


That doesn't look very pleasant. This is where the "o" option comes in. The above may be rewritten by leaving the first expression null and adding "o" to the very end:


This option only works for the non-null-expression, there is no shorthand expansion for the null-expression.


Newsletter Feedback

We are always interested in feedback! Let us know what you think of the newsletter, how we may improve, or ideas you have for future issues. Send us a note at All ideas, suggestions, and comments are welcome.


<<home / newsletters