10.3.4 Macros

Macros offer a convenient way to replace a particular string in the description file with another string. Macros are useful for a variety of tasks, including the following:

Creating a single description file that works for several projects. You can define a macro that replaces a dummy filename in the description file with the specific filename for a particular project.

Controlling the options NMAKE passes to the compiler or linker. When you specify options in a macro, you can change options throughout the description file in a single step.

You can define your own macros or use predefined macros. This section describes user-defined macros first.

10.3.4.1 User-Defined Macros

You can define a macro with this syntax:

macroname=string

The macroname can be any combination of letters, digits, and the underscore (_) character. Macro names are case sensitive. NMAKE interprets MyMacro and MYMACRO as different macro names.

The string can be any sequence of zero or more characters. (A string of zero characters is called a “null string.” A string consisting only of spaces, tabs, or both is also considered a null string.) For example,

linkcmd=LINK /map

defines a macro named linkcmd and assigns it the string LINK /map.

You can define macros in the description file, on the command line, in a command file (see Section 10.5, “NMAKE Command File”), or in TOOLS.INI (see Section 10.6, “The TOOLS.INI File”). Each macro defined in the description file must appear on a separate line. The line cannot start with a space or tab.

When you define a macro in the description file, NMAKE ignores spaces on either side of the equal sign. The string itself can contain embedded spaces. You do not need to enclose string in quotation marks (if you do, they become part of the string).

Slightly different rules apply when you define a macro on the command line or in a command file. The command-line parser treats spaces as argument delimiters. Therefore, the string itself, or the entire macro, must be enclosed in double quotation marks if it contains embedded spaces. All three forms of the following command-line macro are legal and equivalent:

NMAKE program=sample

NMAKE "program=sample"

NMAKE "program = sample"

The macro program is passed to NMAKE, with an assigned value of sample.

If the string contains spaces, either the string or the entire macro must appear within quotes. Either form of the following command-line macro is allowed:

NMAKE linkcmd="LINK /map"

NMAKE "linkcmd=LINK /map"

However, the following form of the same macro is not allowed. It contains spaces that are not enclosed by quotation marks:

NMAKE linkcmd = "LINK /map"

A macro name can be given a null value. Both of the following definitions assign a null value to the macro linkoptions:

NMAKE linkoptions=

NMAKE linkoptions=" "

A macro name can be “undefined” with the !UNDEF preprocessing directive (see Section 10.3.7, “Preprocessing Directives”). Assigning a null value to a macro name does not undefine it; the name is still defined, but with a null value.

A macro can be followed by a comment, using the syntax described in the preceding section on comments.

10.3.4.2 Using Macros

Use a macro by enclosing its name in parentheses preceded by a dollar sign ($). For example, you can use the linkcmd macro defined above by specifying

$(linkcmd)

NMAKE replaces every occurrence of $(linkcmd) with LINK /map.

The following description file defines and uses three macros:

program=sample

L=LINK

options=

$(program).exe : $(program).obj

$(L) $(options) $(program).obj;

NMAKE interprets the description block as

sample.exe : sample.obj

LINK sample.obj;

NMAKE replaces every occurrence of $(program) with sample, every instance of $(L) with LINK, and every instance of $(options) with a null string.

Summary: An undefined macro is replaced by a null string.

If you use as a macro a name that has never been defined, or was undefined, NMAKE treats that name as a null string. No error occurs.

To use the dollar sign ($) as a literal character, specify two dollar signs ($$).

The parentheses are optional if macroname is a single character. For example, $L is equivalent to $(L). However, parentheses are recommended for consistency.

10.3.4.3 Special Macros

NMAKE provides several special macros to represent various filenames and commands. One use for these macros is in predefined inference rules. (See Section 10.3.5.4.) Like user-defined macro names, special macro names are case sensitive. For example, NMAKE interprets CC and cc as different macro names.

Tables 10.2 through 10.5 summarize the four categories of special macros. The filename macros offer a convenient representation of filenames from a dependency line; these are listed in Table 10.2. The recursion macros, listed in Table 10.3, allow you to call NMAKE from within your description file. Tables 10.4 and 10.5 describe the command macros and options macros that make it convenient for you to invoke the Microsoft language compilers.

Summary: The filename macros conveniently represent filenames from the dependency line.

Table 10.2 lists macros that are predefined to represent file names. As with all one-character macros, these do not need to be enclosed in parentheses. (The $$@ and $** macros are exceptions to the parentheses rule for macros; they do not require parentheses even though they contain two characters.) Note that the macros in Table 10.2 represent filenames as you have specified them in the dependency line, and not the full specification of the filename.

Table 10.2 Filename Macros

Macro
Reference

Meaning

$@ The current target's full name, as currently specified. This is not necessarily the full path name.
$* The current target's full name minus the file extension.
$** The dependents of the current target.
$? The dependents that have a more recent modification time than the current target.
$$@ The target that NMAKE is currently evaluating. You can use this macro only to specify a dependent.
$< The dependent file that has a more recent modification time than the current target (evaluated only for inference rules).

The example below uses the $? macro, which represents all dependents that are more recent than the target. The ! command modifier causes NMAKE to execute a command once for each dependent in the list (see Table 10.1). As a result, the LIB command is executed up to three times, each time replacing a module with a newer version.

trig.lib : sin.obj cos.obj arctan.obj

!LIB trig.lib -+$?;

In the next example, NMAKE updates files in another directory by replacing them with files of the same name from the current directory. The $@ macro is used to represent the current target's full name:

#Files in objects directory depend on versions in current directory

DIR=c:\objects

$(DIR)\globals.obj : globals.obj

COPY globals.obj $@

$(DIR)\types.obj : types.obj

COPY types.obj $@

$(DIR)\macros.obj : macros.obj

COPY macros.obj $@

Summary: Macro modifiers specify parts of the predefined filename macros.

You can append one of the modifiers in the following list to any of the filename macros to extract part of a filename. If you add one of these modifiers to the macro, you must enclose the macro name and the modifier in parentheses.

Modifier Resulting Filename Part  
D Drive plus directory  
B Base name  
F Base name plus extension  
R Drive plus directory plus base name  

For example, assume that $@ has the value C:\SOURCE\PROG\SORT.OBJ. The following list shows the effect of combining each modifier with $@:

Macro Reference Value  
$(@D) C:\SOURCE\PROG  
$(@F) SORT.OBJ  
$(@B) SORT  
$(@R) C:\SOURCE\PROG\SORT  

If $@ has the value SORT.OBJ without a preceding directory, the value of $(@R) is just SORT, and the value of $(@D) is a dot (.) to represent the current directory.

Summary: Recursion macros let you use NMAKE to call NMAKE.

Table 10.3 lists three macros that you can use when you want to call NMAKE recursively from within a description file.

Table 10.3 Recursion Macros

Macro
Reference

Meaning
$(MAKE) The name used to call NMAKE recursively. The line on which it appears is executed even if the /N command-line option is specified.
$(MAKEDIR) The directory from which NMAKE is called.
$(MAKEFLAGS) The NMAKE options currently in effect. This macro is passed automatically when you call NMAKE recursively. You cannot redefine this macro. Use the preprocessing directive !CMDSWITCHES to update the MAKEFLAGS macro. (See Section 10.3.7, “Preprocessing Directives.”)

To call NMAKE recursively, use the command

$(MAKE) /$(MAKEFLAGS)

The MAKE macro is useful for building different versions of a program. The following description file calls NMAKE recursively to build targets in the \VERS1 and \VERS2 directories.

all : vers1 vers2

vers1 :

cd \vers1

$(MAKE)

cd ..

vers2 :

cd \vers2

$(MAKE)

cd ..

The example changes to the \VERS1 directory and then calls NMAKE recursively, causing NMAKE to process the file MAKEFILE in that directory. Then it changes to the \VERS2 directory and calls NMAKE again, processing the file MAKEFILE in that directory.

You can add options to the ones already in effect for NMAKE by following the MAKE macro with the options in the same syntax as you would specify them on the command line. You can also pass the name of a description file with the /F option instead of using a file named MAKEFILE.

Deeply recursive build procedures can exhaust NMAKE's run-time stack, causing an error. If this occurs, use the EXEHDR utility to increase NMAKE's run-time stack. The following command, for example, gives NMAKE.EXE a stack size of 16,384 (0x4000) bytes:

exehdr /stack:0x4000 nmake.exe

Summary: Command macros are shortcut calls to Microsoft compilers.

NMAKE defines several macros to represent commands for Microsoft products. (See Table 10.4.) You can use these macros as commands in a description block, or invoke them using a predefined inference rule. (See Section 10.3.5, “Inference Rules.”) You can redefine these macros to represent part or all of a command line, including options.

Table 10.4 Command Macros

Macro Reference Command Action Predefined Value
$(AS) Invokes the Microsoft Macro Assembler AS=ml
$(BC) Invokes the Microsoft Basic Compiler BC=bc
$(CC) Invokes the Microsoft C Compiler CC=cl
$(COBOL) Invokes the Microsoft COBOL Compiler COBOL=cobol
$(FOR) Invokes the Microsoft FORTRAN Compiler FOR=fl
$(PASCAL) Invokes the Microsoft Pascal Compiler PASCAL=pl
$(RC) Invokes the Microsoft Resource Compiler RC=rc

Summary: Options macros pass preset options to Microsoft compilers.

The macros in Table 10.5 are used by NMAKE to represent options to be passed to the commands for Microsoft languages. By default, these macros are undefined. You can define them to mean the options you want to pass to the commands. Whether or not they are defined, the macros are used automatically in the predefined inference rules. If the macros are undefined, or if they are defined to be null strings, a null string is generated in the command line. (See Section 10.3.5.4, “Predefined Inference Rules.”)

Table 10.5 Options Macros

Macro Reference Passed to
$(AFLAGS) Microsoft Macro Assembler
$(BFLAGS) Microsoft Basic Compiler
$(CFLAGS) Microsoft C Compiler
$(COBFLAGS) Microsoft COBOL Compiler
$(FFLAGS) Microsoft FORTRAN Compiler
$(PFLAGS) Microsoft Pascal Compiler
$(RFLAGS) Microsoft Resource Compiler

10.3.4.4 Substitution within Macros

Summary: You can replace text in a macro as well as in the description file.

Just as macros allow you to substitute text in a description file, you can also substitute text within a macro itself. The substitution is temporary; it applies only to the current use of the macro and does not modify the original macro definition. Use the following form:

$(macroname:string1=string2)

Every occurrence of string1 is replaced by string2 in the macro macroname. Do not put any spaces or tabs between macroname and the colon. Spaces between the colon and string1 or between string1 and the equal sign are part of string1. Spaces between the equal sign and string2 or between string2 and the right parenthesis are part of string2. If string2 is a null string, all occurrences of string1 are deleted from the macroname macro.

Macro substitution is case sensitive. This means that the case as well as the characters in string1 must exactly match the target string in the macro, or the substitution is not performed. It also means that the string2 substitution is exactly as specified.

Example 1

The following description file illustrates macro substitution:

SOURCES = project.for one.for two.for

project.exe : $(SOURCES:.for=.obj)

LINK $**;

COPY : $(SOURCES)

!COPY $** c:\backup

The predefined macro $** stands for the names of all the dependent files (see Table 10.2).

If you invoke the example file with a command line that specifies both targets,

NMAKE project.exe copy

NMAKE executes the following commands:

LINK project.obj one.obj two.obj;

COPY project.for c:\backup

COPY one.for c:\backup

COPY two.for c:\backup

The macro substitution does not alter the SOURCES macro definition. Rather, it replaces the listed characters. When NMAKE builds the target PROJECT.EXE, it gets the definition for the predefined macro $** (the dependent list) from the dependency line, which specifies the macro substitution in SOURCES.

The same is true for the second target, COPY. In this case, however, no macro substitution is requested, so SOURCES retains its original value, and $** represents the names of the FORTRAN source files. (In the example above, the target COPY is a pseudotarget; Section 10.3.2 describes pseudotargets.)

Example 2

If the macro OBJS is defined as

OBJS=ONE.OBJ TWO.OBJ THREE.OBJ

with exactly one space between each object name, you can replace each space in the defined value of OBJS with a space, followed by a plus sign, followed by a newline, by using

$(OBJS: = +^

)

The caret (^) tells NMAKE to treat the end of the line as a literal newline character. This example is useful for creating response files.

10.3.4.5 Substitution within Predefined Macros

You can also substitute text in any predefined macro except $$@. The principle is the same as for other macros. The command in the following description block substitutes within a predefined macro. Note that even though $@ is a single-character macro, the substitution makes it a multi-character macro invocation, so it must be enclosed in parentheses.

target.abc : depend.xyz

echo $(@:targ=blank)

If dependent depend.xyz has a later modification time than target target.abc, then NMAKE executes the command

echo blanket.abc

The example uses the predefined macro $@, which equals the full name of the current target (target.abc). It substitutes blank for targ in the target, resulting in blanket.abc.

10.3.4.6 Inherited Macros

When NMAKE executes, it inherits macro definitions equivalent to every environment variable. The inherited macro names are converted to uppercase.

Inherited macros can be used like other macros. You can also redefine them. The following example redefines the inherited macro PATH:

PATH = c:\tools\bin

sample.exe : sample.obj

LINK sample;

Summary: Inherited macros take their definitions from environment variables.

No matter what value the environment variable PATH had before, it has the value c:\tools\bin when NMAKE executes the LINK command in this description block. Redefining the inherited macro does not affect the original environment variable; when NMAKE terminates, PATH still has its original value.

Inherited macros have one restriction: in a recursive call to NMAKE, the only macros that are preserved are those defined on the command line or in environment variables. Macros defined in the description file are not inherited when NMAKE is called recursively. To pass a macro to a recursive call:

Use the SET command before the recursive call to set the variable for the entire NMAKE session.

Define the macro on the command line for the recursive call.

The /E option causes macros inherited from environment variables to override any macros with the same name in the description file.

10.3.4.7 Precedence among Macro Definitions

If you define the same macro name in more than one place, NMAKE uses the macro with the highest precedence. The precedence from highest to lowest is as follows:

1.A macro defined on the command line

2.A macro defined in a description file or include file

3.An inherited environment-variable macro

4.A macro defined in the TOOLS.INI file

5.A predefined macro such as CC and AS