2.7 Macros

A macro is a makefile line that consists of a macro name, an equal sign ( = ), and a macro value. In the makefile, a macro reference is an expression of the form $(name) or ${name}. The reference is macro-expanded to produce value. When name is a single character, the ( ) or { } around name are optional; for example, $X, $(X), and ${X} all specify the value of macro X. The macro character ($), always instructs omake to expand the macro that follows. When you need a dollar sign in your makefile, you must use $$.

In the following makefile, the text main.obj io.obj occurs more than once. To reduce the amount of repeated text, you can use a macro definition to assign a symbol to the text.

project.exe : main.obj io.obj
link /out main.obj io.obj

main.obj : main.c
cl /c main.c

io.obj : io.c
cl /c io.c

Here is the same makefile written with the introduction of four macros:

OBJS = main.obj io.obj
CC = cl
CFLAGS = /c

project.exe : $(OBJS)
link $(OBJS) /out : project.exe

main.obj : main.c
$(CC) $(CFLAGS) main.c

io.obj : io.c
$(CC) $(CFLAGS) io.c

The value of the OBJS macro is the list of object files to be compiled. The macro definitions for CC and CFLAGS make it easier to change the name of the C compiler and its options.

omake imports environment variables as macros, so you can refer to things like $(COMSPEC) or $(PATH) in your makefile without having to define them in the makefile.

Macro Precedence

Macros can be defined in makefiles, defined on the command line, or predefined by omake. omake also accesses the values of environment variables as if they were macros.

Where a macro is defined determines its precedence. To redefine an existing macro, the new definition must have at least as high a precedence. This is the order of precedence:

  1. Macros predefined by omake

  2. Command-line definition

  3. Environment definition (with -e command-line option)

  4. makefile (and make.ini) definition

  5. Environment definition (default)

By default, the lowest precedence for a macro is the environment definition. The -e command-line option gives the environment definition a higher precedence than the makefile definition. With this option, a macro definition in a makefile does not redefine a macro from the environment.

Defining Macros in the Makefile

Macros are defined at read time in a makefile with macro definition lines of the form

name

= [ text ]

standard definition

name

?= [ text ]

conditional definition

name

:= [ text ]

expanded definition

name

+= [ text ]

appended definition

where name is the macro name starting in the first column of the makefile. The name can include any characters except the equal sign (=), the colon (:), and white space. By convention, the name is composed of uppercase letters, periods (.), and underscores (_). Any macro references in name are expanded; if you want a literal dollar sign in name you must use $$.

The text is arbitrary text and can reference the value of other macros with expressions of the form $(other_macro). There are four forms of macro definition:

Read-Time Expansion of Macros

At read time, some parts of a makefile are macro-expanded; other parts are not expanded until later.

For macro definitions, expansion depends on the separator between the macro name and the value:

=

macro name is expanded; value is not expanded until referenced
(In NMAKE compatibility mode, value is expanded before assignments)

?=

macro name is expanded; value is not expanded until referenced

+=

macro name is expanded; value is not expanded until referenced

:=

macro name is expanded; value is expanded

The makefile can also contain conditional directives such as these:

%if condition
%elif condition

The condition is macro-expanded and evaluated only if the previous enclosing condition is true. For example:

%if condition_1

(expanded & evaluated)

% if condition_2

(expanded & evaluated only if condition_1 is true)

% endif

%else

% if condition_3

(expanded & evaluated only if condition_1 is false)

% endif

%endif

Standard Macro Definition: name = [ text ]

This definition defines macro name and sets its value to text. If text is not given, the value of the macro is the null string, "". White space before or after the = is ignored.

Conditional Macro Definition: name ?= [ text ]

This definition is like the standard macro definition, but the macro is defined only if it isn't already defined. White space between name and ?= and between ?= and text is ignored.

Expanded Macro Definition: name := [ text ]

This definition defines macro name and sets its value to the macro-expansion of text. The text is arbitrary text and can refer to the value of other macros with expressions of the form $(other_macro), which are expanded. If text is not given, the value of the macro is the null string (""). White space between name and := and between := and text is ignored. Expanded macro definitions are useful when the text is expensive to calculate (it may require reading a file, for example).

Appended Macro Definition: name += [ text ]

This definition appends text to the current value of macro name. White space between name and += is ignored. If there is no white space between += and text, the new value is OLDVALUEtext. Otherwise, the new value is OLDVALUE <SPACE> text (with one intervening space). If name is an undefined macro, this definition is the same as a standard macro definition.

Case-Sensitivity of Macro Names

By default, for Windows NT omake macro names are case-insensitive. The .CASE_MACRO and .NOCASE_MACRO directives turn case-sensitivity on and off.

The Location of Macro Definitions

By convention, macro definitions appear at the beginning of the makefile. Macros must be defined before they are expanded; if they are not, their expanded value is the null string.

Indenting Macro Definitions

Usually name starts in the first column of the makefile line, but macro definitions can be indented (for example, inside an %if...%endif conditional directive). Indenting is allowed only before the first target in the makefile, or after a nonindented macro definition. This restriction is a consequence of omake's use of indenting to indicate a target's build scripts.

Undefining Macros

Macros can be undefined with the %undef directive at read time:

%undef CFLAGS

Some macros that omake predefines cannot be undefined with this directive.

Example Macro Definitions

Defining Macros on the Command Line

Macros can be defined on the command line, and the value of the command-line macro overrides a makefile macro or environment definition with the same name. Only standard macro definitions are allowed on the command line. A command-line macro that contains spaces must be enclosed in double quotes. (To include a literal double quote, use \".) For example, the command line

omake BSCFLAGS=/n "CFLAGS=-Zi -Od"

runs omake with BSCFLAGS defined with the value -n and CFLAGS defined with the value -Zi -Od.

Dynamic Macros

omake defines some special macros whose values are dynamic. These run-time macros return information about the current target being built. For example, the .TARGET macro is name of the current target, the .SOURCE macro is the name of the inferred dependency (from an inference rule) or the first of the explicit dependencies, and the .SOURCES macro is the list of all dependencies.

Using run-time macros, the example can be written as follows:

OBJS = main.obj io.obj
CC = cl
CFLAGS =

project.exe : $(OBJS)
link /out:$(.TARGET) $(OBJS)

main.obj : main.c
$(CC) $(CFLAGS) -c $(.SOURCE)

io.obj : io.c
$(CC) $(CFLAGS) -c $(.SOURCE)

As you can see, the build scripts that update main.obj and io.obj are identical when dynamic macros are used. Dynamic macros are important for generalizing the build process with inference rules, as shown in Inference Rules.

Macro Modifiers

Macros can be used to reduce the amount of repeated text. They are also used at run time to generalize the build process with inference rules. You often want to start with the value of a macro and modify it in some manner. For example, to get the list of source files from the OBJS macro you can define this macro:

SRCS = $(OBJS,.obj=.c)

This definition uses the from=to macro modifier to replace the from text in the expansion of OBJS with the to text. The result is that $(SRCS) is main.c io.c. In general, to modify a macro, expand it with

$(name,modifier[,modifier ...])

Each modifier is applied in succession to the expanded value of name. Separate modifier with comma.

File Name Components

There is a complete set of macro modifiers for accessing parts of file names. For example, with this macro definition:

SRCS = \src\main.c parse.l

Table 1 lists some of the modifiers.

Table 1 Macro Modifiers


Modifier and description

Example

Value

D, the directory


$(SRCS,D)


\src .


E, the extension (or suffix)


$(SRCS,E)


.c.l


F, the file name


$(SRCS,F)


main.c parse.l

Tokenize

The Wstr modifier replaces white space between elements of the macro with str, a string. The str can be a mix of regular characters and special sequences, the most important sequence being \n, which represents a newline character (like pressing the <ENTER> key). For example:

$(OBJS,W +\n) is main.obj +
io.obj

Other Modifiers

Other modifiers include @ (include file contents), LC (lowercase), UC (uppercase), M (member), and N (nonmember). The M and N modifiers and the S (substitute) modifier use regular expressions for powerful and flexible pattern-matching. See Macro Modifiers for more information.

Environment Variables

Environment variables are placed into the environment with this command:

set name=value

NOTE: This example applies to cmd.exe; the command varies depending on the shell you use.

By default, omake preloads all environment variables as macros. The .NOENVMACROS directive prevents this loading and hence prevents omake from accessing the value of any environment variables.

Macro Expansion or Macro Referencing

Expanding a macro also expands any macro references recursively. For example:

CDEFS = -DDEBUG -DNT
COPTS = -Ot
CFLAGS = -Zi $(CDEFS) $(COPTS)

The expression $(CFLAGS) evaluates to -Zi -DDEBUG -DNT -Ot.

In a macro reference $(name), name can reference other macros. For example, given the definitions

SYS = NT
NTFLAGS = -DNT -UNT

the expression $($(SYS)FLAGS) evaluates to -DNT -UNT.

Run-Time Expansion of Macros

Macros in build scripts are expanded immediately before the build scripts are executed. There are several macros whose values change dynamically at run time. They are discussed in Predefined Macros: Run-Time Macros.

Recursive Macro Definitions

NOTE: NMAKE supports recursive macro definitions as shown in this section. When omake is emulating NMAKE, recursive macro definitions are supported.

A macro value may reference other macros. If the value circularly references itself, omake displays a warning message when the macro is expanded. For example, the values

A = A $B
B = B1 $A B2
%echo $A

generate this message:

omake: file (line num): Recursive macro 'A = A $B' (warning).
A B1 B2

The expression $A expands to A expansion_of_B. In turn, the expansion of B is B1 expansion_of_A B2. When omake tries to expand a macro that is already being expanded, it displays the warning message and ignores the recursive expansion. The file and num depend on where the example lines were defined.

You can make this kind of recursive reference by using the expanded macro definition:

B := B1 $A B2

This type of definition expands any references in the right side before the macro definition of B occurs.