Happy new year! We made it to year 2000, like most everyone, without much excitement. We are happy to say there were no problems reported during the new year weekend and there have been no y2k related problems yet reported. If you are still using an unsupported, legacy release (anything older than 3.1.2) it isn't too late to upgrade to a certified one.
The team issued their first official patch release in November 1999. We plan to refine our patch process and issue patches on an as needed bases between scheduled releases. This is a change from the old policy of only releasing complete packages and issuing fixes to specific customers as requested. The need for a patch release will be determined by the impact of a problem found in a general release, patches will not contain new features. We hope patches will not be an often occurrence but realisticly this will allow projects to keep their installation current, and consistent with other projects in an official manor.
Installation is automated and the process backs up the files that are touched in the patch. Patch 01 for lu3.2 addresses the following issues, for full details, instructions, and download see the lu3.2 patch 01 page.
- 990014 - cpp dumps core with a huge string constant
- 980035 - cpp ANSI mode token-based preprocessing error
- 990052 - cpp C++ output ">>" should be "> >"
- 990031 - localprobe fixes: PROBE, PROBEBASE, -I-D
- 990062 - update makelog package
lu3.2 has been ported to Amdahl UTS 2.1.2 and Intel based Solaris 2.6 platforms. They can be found on the download page.
The development team is working on many new features, enhancement, and bug fixes. Here are brief descriptions of a couple of the new features and enhancements we are currently working on for inclusion in upcoming releases of nmake. Obviously, this list is not exhaustive.
Include expansion feature
The new include expansion feature produces a file with all
include statements expanded. This feature is expected to be
especially useful for debugging makefiles in projects with several levels of
nested includes, particularly where selection of
include files is
dynamic. It is analogous to the
-E command in
that an output file is created in which the contents of all
include statements are textually inserted into the file, along
with information identifying which file was inserted. However,
the situation is a bit more involved since
include is a
full-fledged statement in the nmake programming language and, unlike in the
case of the C preprocessor, may be executed at run time as well as at compile
time. To provide as useful a diagnostic aid as possible, the include
expansion feature inserts lines into the output file in the same order and
position as nmake reads them.
Additional way to mark targets recursive
In nmake, a recursive target is one which is passed down as a
target on the command line of recursive nmake invocations. Release
lu3.2 provided a way to indicate that an arbitrary target is recursive by
preceding it with the
recurse assertion on the command line.
(This is a generalization of the method previously used to force recursion for
certain common actions.) We are currently investigating the possibility of
providing a way to specify a top-level target as recursive from within the
makefile in addition to allowing the
recurse assertion on the
.ERROR special atom provides a way to gain control when an
error occurs during processing of a shell action block. The
.ERROR target is executed when an error occurs; further
processing depends upon the value returned from the
action: if a
value of 0 is returned, control returns to the point after the error; if a -1
is returned, error processing continues and nmake process exits. However, currently
no documented way is provided to allow the
.ERROR action to
access the nmake error message and control the printing of that message. In
fact nmake does provide this
functionality: if the return code from the
.ERROR action is 0,
nmake suppresses printing of the error message and makes it available as the
$(.ERROR). This functionality exists in
the current version of nmake but was never documented. The documentation
will be updated to properly reflect this feature.
If you have tried to build java code with nmake then you already know nmake does not contain "out-of-the-box" support for java. We are looking into adding official support for the next release. There are many details to work out to do it correctly, proper java support is not trivial.
In the mean time we can say that many projects are currently building java code successfully with nmake, the solutions are just not as elegant as we would like. Below we offer a couple current solutions to hold you over for official support. We would also love to see other solutions that have been developed by our users. If have your own java nmake rules and you are happy with their results, please consider sharing your rules with us at email@example.com. Any new information we get will help our development of official rules.
You may use either of the rules below to build java files, it is not necessary to use both rules. In both cases the .SOURCE.class atom is used to list the directories to search for .class files. It is important to note that any relative path listed will be expanded through the viewpath by the $(*.SOURCE.class) reference in the action block. This will allow the java compiler to pick up .class files down the viewpath instead of being limited to the current build node. It is also important to realize that the java compiler may output more than one .class file for a single .java file. The "extra" .class files will be unknown in that nmake will not keep track of them, it will not know it made them, and it will not remove them with the clobber common action. This is one of the problems the official rules will need to deal with.
The first rule uses :JOINT: to make all the .class files from all the .java files in a single call. All the .java files are passed to the compiler at the same time. Because this eliminates dependency issues between the files this is the method we presently recommend most. Keep in mind if only one .java file is touched then all the files are still passed to the compiler. This will cause some overhead for the compiler but again it eliminates dependency issues and seems safest at this time.
The settings are defined as follows:
- JAVAFILES - list all your .java files to compile in this variable.
- CLASSFILES - nmake will set this to a corresponding list of .class files.
- CLASSDIR - Destination directory of the .class files. Using $(PWD) makes nmake happiest.
- .SOURCE.class - Search directories for .class files. See above.
JAVAC = javac JAVACFLAGS = JAVAFILES = a.java b.java c.java CLASSFILES = $(JAVAFILES:B:S=.class) CLASSDIR = $(PWD) .SOURCE.class : . $(VROOT)/lib/classes /opt/jdk/lib/classes $(CLASSFILES) :JOINT: $(JAVAFILES) $(JAVAC) $(JAVACFLAGS) -d $(CLASSDIR) -classpath $(*.SOURCE.class:C/ /:/g):$(CLASSPATH) $(*)
Using a metarule
You may also use a metarule to make a .class file from a .java file, in much the same way a .o is created from a .c file. This will only recompile the .java files that changed but may present some problems because the java compiler can compile more than the specified .java file when it deems necessary. If a .class file gets updated without nmake knowing it could cause nmake to remake that .class file next time.
The referenced variables and .SOURCE.class atom have the same usage as above.
JAVAC = javac JAVACFLAGS = CLASSDIR = $(PWD) .SOURCE.class : . $(VROOT)/lib/classes /opt/jdk/lib/classes %.class : %.java (CLASSDIR) (CLASSPATH) $(JAVAC) $(JAVACFLAGS) -d $(CLASSDIR) -classpath $(*.SOURCE.class:C/ /:/g):$(CLASSPATH) $(>)
Should I switch?
If you are using a different solution than one of the above and you are happy with it, then we suggest you stick with your approach. Since official java support is not yet available there is no real reason to change rules as long as you are satisfied with the results. And again, if you have some rules you would like to share please send them to us!
Next time we'll plan to talk about creating a .jar file unless we have some more exciting java news to discuss. In the mean time if you are interested, contact firstname.lastname@example.org and we'll send you the rules we have put together which will collect files through the viewpath to be archived with jar.
The team is considering the release of a set of tools to enhance a viewpathing work environment. We realize many projects have home-grown tools but many others have none. It would be nice to make a standard set available to all users so those who would benefit from them can. It may also bring some consistency across projects and aid developers who move from one project to another.
A couple obvious candidates for the tool set are:
- A tool to report which node(s) in the VPATH a file exists.
- A tool to display or edit the first occurrence of a file in the VPATH.
Now we are looking for user feedback. Is this tool set something the user community would like to see? What other features would you find useful for a VPATH tool set? And if you have any VPATH tools you would like to share with us please send them in. As always you can reach us at email@example.com.
For this article I want to do two things: introduce nmake's installation operators to those who aren't familiar with them, and point out some of the lesser known features of these operators. We do occasionally come across makefiles that still have their own custom installation rules when they aren't necessary. Hopefully this article will give everyone a better understanding of the built-in installation operators and will encourage some folks to try them and replace their custom rules.
The basic use of the installation operators are to install files when a makefile is built using the install common action, such as when nmake install is executed on the command line. Running nmake can then be used to update targets in a makefile and nmake install used when you want the targets installed. Of course nmake install will do both in one shot but the idea is a developer may want to do several test nmake runs before doing the install. Both install operators can also set file ownership and permissions of the files they install.
What's the difference?
:INSTALL: installs a source file specified on the right hand side (rhs) to the destination file specified on the left hand side (lhs). Since a filename is specified on the lhs this operator is used when the rhs file needs to be renamed when installed.
:INSTALLDIR: installs one or more source files specified on the rhs to the destination directory specified on the lhs. Since a target directory is specified on the lhs the rhs may contain a list of files separated by white space. Each file will be copied to the target directory while keeping the same filename.
What about permissions?
File ownership and permissions may be set on the installed files by specifying user=<user_id>, group=<group_id>, and mode=<permission_mode> on the rhs. For example, the following will install the file orb to directory $(BINDIR) and then set the owner, group, and permissions on $(BINDIR)/orb as specified:
$(BINDIR) :INSTALLDIR: orb user=bin group=bin mode=0555
Of course it also works with :INSTALL:
$(BINDIR)/orb :INSTALL: theorb user=bin group=bin mode=0555
And symbolic modes can be used.
$(BINDIR)/orb :INSTALL: theorb user=bin group=bin mode=u-w
So where are the Secrets?
Well these really shouldn't be secrets but we have realized they remain unknown to a lot of users.
Those .old files
When nmake installs a file, if a file already exists at the destination the original file will be moved to <filename>.old and then the new file is installed. If you do not wish to preserve the old files you can suppress this behavior using the clobber baserules variable.
clobber = 1
Set clobber=1 to simply remove an existing installed file and install the new file. No .old file is created. It is best to set clobber=1 in your global makefile, but like any nmake variable it may be set in the local makefile, shell environment, or on the nmake command line.
I touched the file but it didn't reinstall
A file will not be reinstalled just because the time stamp changed. As part of the install operation the Unix cmp command is used to compare the new file to be installed and an existing installed file. If cmp says the files are different then the new file is installed. If cmp says the files are the same then the new file is not installed. Of course if there is no existing installed file then the new one will be installed.
If you do not want the compare to take place it can be turned off using the compare baserules variable.
compare = 0
Set compare=0 to turn off the comparison check. When turned off all files are reinstalled when they are changed. Again it is best to set compare=0 in your global makefile but it may also be set in the local makefile, shell environment, or on the nmake command line.
When compare is turned on (as it is by default) this feature sometimes causes confusion. Some compilers, such as the HP-UX /bin/cc compiler, generates a different binary each time an executable is made, even if the code has not changed. Users will notice simply touching a .c causes an executable to rebuild and reinstall, but they do not expect the installation. They assume since the code did not change the executable did not change so the executable should not be reinstalled. However the compiler does create a different executable, probably because an internal date stamp, so cmp reports the new file to be different and it does get reinstalled.
Look ma, no left hand side
Both :INSTALL: and :INSTALLDIR: can be used without specifying anything on the lhs, but they have very different meanings.
Omitting the lhs with :INSTALL: means making the rhs when the install common action is made. In the following example, ctek is built when nmake install is executed, but when only nmake is run ctek is not made by default.
Omitting the lhs with :INSTALLDIR: means do not install the file(s) specified on the rhs. This is useful if you do not want to automatically install a :: target. In the following example libpoppie.a is made using the :: operator, but the default installation to $(LIBDIR) is suppressed.
:INSTALLDIR: libpoppie.a libpoppie.a :: cp.c rp.c gp.c ap.c fp.c
There you have it. We hope this brings a greater understanding and awareness to these two powerful operators. They should be some of the most commonly used tools in your nmake toolbox.
Many projects use a proc compiler to process C or C++ files with embedded SQL statements. These files typically use the file suffix .pc. The proc compiler processes a .pc file and outputs a C or C++ file (ie a .c file).
There are no default rules for handling .pc files. Some projects create a custom rule to make a .o object file from the .pc. However we recommend simply outputting a .c file and letting the default rules do the rest of the work.
Below is a sample metarule to make a .c file from a .pc. We also include the statements necessary to scan the .pc file for included files and to build a list of include directories to search. The include directories are specified to the proc compiler using a series of include=<directory> arguments.
/* The proc compiler, may also specify full path */ PROC = proc /* Scan strategy which enables scanning for candidate state variables */ /* then includes .SCAN.F which supports a combination of SQL and C code */ .SCAN.proc : .SCAN O|S| $(@.SCAN.F) /* Use .SCAN.proc to scan .pc files */ .ATTRIBUTE.%.pc : .SCAN.proc (PROC) (PROCFLAGS) : .PARAMETER PROCFLAGS &= $$(.INCLUDE. F include=) /* Note: &= instead of += */ /* Assume .pc and included files are in the .SOURCE.pc and .SOURCE.h dirs */ .SOURCE.%.SCAN.proc : .FORCE $$(*.SOURCE.pc) $$(*.SOURCE.h) %.c : %.pc (PROC) (PROCFLAGS) $(PROC) $(PROCFLAGS) iname=$(>) oname=$(<)
Some notes on the above.
- .SCAN.proc enables scanning for candidate state variables ('O|S|'), which allows nmake to generate -D define flags for corresponding state variables. If this feature is not needed then you can use .SCAN.F directly with .ATTRIBUTE.%.pc.
- The .SCAN.F scanrule is already defined in Scanrules.mk but does not enable scanning for state variables. After enabling this feature we just include the predefined .SCAN.F in .SCAN.proc.
- (PROC) and (PROCFLAGS) are tagged with the .PARAMETER attribute so nmake does not treat them as state variables used to indicate #defines in source code.
- PROCFLAGS is defined with &=, this sets the auxiliary value of PROCFLAGS. The auxiliary value is expanded when $(PROCFLAGS) is expanded but the value is not stored in the state file. This is done so if the list of include search directories changes (such as a new node is added to the viewpath) the change does not cause all the .pc files to rebuild, which would normally happen since (PROCFLAGS) is a state file prerequisite. This is the same way -I arguments are handled with C and C++ rules.
We are looking for 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 firstname.lastname@example.org. All ideas, suggestions, and comments are welcome.