nmake does some things behind your back that you should know about. You won't need all the details, but a little knowledge is important. For example, realizing the .mo, .ms, and .ml suffixes have been claimed could prevent accidents; knowing something about the extra files nmake creates will help you understand important aspects of nmake's behavior.
nmake builds an objectfile  from a makefile and uses its internal machinery to keep track of both files. If the objectfile is up to date nmake reads it instead of the makefile; otherwise nmake reads the makefile and builds a new objectfile that can be used next time. As far as nmake is concerned, the two files are supposed to be equivalent, but reading the objectfile saves time.
Objectfiles are associated with makefiles using a simple technique: nmake picks the name by removing the makefile's suffix, if there is one, and attaching .mo. For example, hello.mo is the objectfile associated with hello.mk. Even though there's some ambiguity  nmake isn't easily confused, because the name of the associated makefile is also recorded in the objectfile. If you're really curious, look at an objectfile with cat -v or od -c and see if you can find the name.
An objectfile is also sometimes called a "compiled makefile," but the terminology is a bit misleading. It suggests equivalence and implies you get exactly the same results no matter whether nmake reads the makefile or the objectfile. In reality there are differences - we'll give you an easy example.
We mentioned print earlier in the paper when we talked about the special words that sometimes need quoting. It's a simple command  that tells nmake to copy the rest of the line to standard output. If we put
print this message comes from print hello : silent echo "hello, world"
in a file named print.mk and type
nmake -f print.mk
this message comes from print hello, world
No surprises yet, but run nmake again and the message from print disappears. It's easy to show this is due to the objectfile - remove print.mo or tell nmake to ignore the objectfile using the -o reread option
nmake -f print.mk -o reread hello
and the message from print reappears.
The explanation is important, so pay attention. It may look like a mistake, but it's not because the objectfile isn't a translation of the makefile. Instead, most of what you'll find in an objectfile is a dump of several internal symbol tables in a portable format that nmake can digest quickly. nmake ends up with the same data in those symbol tables no matter which file (makefile or objectfile) it reads. But print doesn't create any data, so there's no record of it in the objectfile, and that's why the message disappeared.
By the way, there is an easy way to make sure print runs whenever nmake does, even if the objectfile is used. We'll show you how later in the paper when we talk about .INIT.
nmake needs a statefile to preserve information, like compiler flags, that may not be available the next time it runs, but nmake goes much farther and remembers lots of important information (e.g., file time stamps and when targets were built) in the statefile. nmake reads the statefile when it starts, updates it right before quitting, and builds targets when information it needs is missing from the statefile or conflicts with what's available from another source, like the file system.
Statefiles are associated with makefiles using the technique that we just described for objectfiles, except nmake attaches .ms instead of .mo as the identifying suffix. For example, hello.ms would be the statefile associated with hello.mk.
Put parentheses around a variable name, add it to a prerequisite list, and you've made the target depend on the variable. Here's a simple example that we'll save in a file named state.mk; it should help, as long as you close your eyes when you come to .VIRTUAL in the prerequisite list - we'll explain that one in a few sections:
AUDIENCE = world goodbye : .VIRTUAL (AUDIENCE) silent echo "$(<), $(AUDIENCE)" hello : .VIRTUAL silent echo "$(<), $(AUDIENCE)"
We started with message.mk, split the assertion up, added .VIRTUAL to both prerequisite lists, and then turned AUDIENCE into a state variable by saying goodbye depends on (AUDIENCE). State variables are saved in the statefile and targets, like goodbye, that depend on a state variable are rebuilt when that variable changes.
It's easy to demonstrate the behavior, but first let's see what happens when we build hello, because it doesn't have AUDIENCE as a prerequisite. We get the usual message when we type,
nmake -f state.mk hello
but try it again and nothing happens, and that's why we added .VIRTUAL to the prerequisite list. Change AUDIENCE on the command line and the silence continues. But try the same thing with goodbye and we get the expected message from
nmake -f state.mk goodbye
and another one when we type:
nmake -f state.mk AUDIENCE='New Jersey' goodbye
nmake rebuilds goodbye whenever we change AUDIENCE, and the state variable prerequisite is the reason why.
There's one more special file, but this one is harder to find. nmake creates a lockfile  when it starts and removes it right before it quits, so we have to look for the lockfile while nmake is running. If we put
lock : ls -l lock.ml
in a file named lock.mk and type
nmake -f lock.mk lock
+ ls -l lock.ml ---------- 1 drexler sablime 0 Dec 28 15:06 lock.ml
That's the lockfile - it's empty, has no permissions, ends in .ml, and is associated with a makefile, just like the statefile and objectfile. The lockfile is there to protect the statefile from multiple updates, but it's not much of an obstacle. You get a message something like
make: warning: another make has been running on lock.mk in . for the past 2.00s make: use -K to override
when nmake finds a lockfile that it didn't create. Follow nmake's advice and use the -K option or just remove the lockfile and nmake will run without complaining  but first be sure nmake really isn't using the makefile.
Our last example works because the makefile is named lock.mk, but renaming the makefile breaks the example. nmake remembers the name of your makefile  in a variable named MAKEFILE, so
lock : ls -l `echo $(MAKEFILE) | sed 's/. [^.]*$//' | sed 's/$/.ml/'`
works no matter where we put the makefile. Incidentally, we used sed instead of basename, because nothing says we can't use a non-standard suffix, like .MK, in a makefile name. The sed stuff is ugly, so here's how you would do the same thing if you had more experience with nmake and knew about edit operators:
lock : ls -l $(MAKEFILE:B:S=.ml)
We're not going to explain edit operators in this paper, but we think the example shows their advantage. Edit operators are efficient and often provide elegant solutions.