3.5 Macros

A macro definition associates a name and a value. The macro expansion of a macro name returns the value. Macros are used at read time to organize names of files, compiler options, and so on. At run time, macros provide information about the current target being built.

Macro Modifiers

When a macro is referenced, the expanded value can be modified through the use of macro modifiers. To modify a macro, expand it with:

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

Everything between $( and ) is expanded, the value of name is expanded, and then each modifier is applied in succession to the expanded value. The separator between name and modifier can be a comma or colon, but subsequent modifiers must follow a comma. A literal comma is available by using \,. Some modifiers use regular expressions. The treatment of the backslash, can make it awkward to use a modifier when a backslash also terminates its argument. You can work around this by using two backslashes before a comma separator(\\,) or
if possible, move the awkward modifier to the end. If this not possible, use two modifications. For example,

OBJS = $(SRCS,<obj\,R,>.obj)

does not work, but this pair of macro definitions does:

OBJS = $(SRCS,<obj\)
OBJS := $(OBJS,R,>.obj)

Considering the macro value as a list of macro elements separated by white space, the first modifier is applied to all elements, then the next modifier is applied, and the next, and so on. The following list of modifiers is organized into functional groups. Examples of each modifier use the following macro definition:

SRCS = \src\main.c \sys\sub.cpp io.cpp

Filename Components

B

The base name part of the element. This is the file name without an extension.

$(SRCS,B) is main sub io

D

The directory part of the element. For X:\file, X:file, \file, or dir\file, the directory part is X:\, X:, \, or dir, respectively. If there are no directory separators the directory part is the current directory ( . ).

$(SRCS,D) is \src \sys .

E

The extension (or suffix) of the element. This is "" if there is no extension.

$(SRCS,E) is .c .cpp .cpp

F

The file name part of the element. The part after the last directory separator.

$(SRCS,F) is main.c sub.cpp io.cpp

P

The path part of the element. This is similar to the D modifier, but includes the last directory separator. So for X:\file, X:file, \file, dir\file, or file the path part is X:\, X:, \, dir\ or "", respectively.

$(SRCS,P) is \src \sys

R

The root part of the element. The same as its full name minus its extension.

$(SRCS,R) is \src\main \sys\sub io

Z

The drive letter part of the element (or the current drive), terminated by a colon

$(SRCS,Z) is V: V: V: (if "V" is the current drive)

Absolute Pathname

A[sep]

Convert the element to an absolute pathname. If the element is not already an absolute path, this modifier prepends the current drive and working directory. Then, the sep separator replaces the slash and backslash. Finally, .. and . directory parts are removed. For example, if the current working directory is v:\stage, then:

$(SRCS,A) is v:\src\main.c v:\sys\sub.cpp v:\stage\io.cpp

$(SRCS,A/) is v:/src/main.c v:/sys/sub.cpp v:/stage/io.cpp

If sep is missing, the backslash is used.

Append and Prepend Strings

>string

Appends string to each element of the macro.

$(SRCS,B,>.obj) is main.obj sub.obj io.obj

<string

Prepends string to each element of the macro.

Change Case

LC

Lowercase. Change the letters of the element to lowercase.

$(SRCS,Z,LC) is d: c: c:

UC

Uppercase. Change the letters of the element to uppercase.

$(SRCS,B,>.obj,UC) is MAIN.OBJ SUB.OBJ IO.OBJ

Expand Pathnames

X

This modifier expands elements into their pathnames. As an example:

.PATH.obj = objs

OBJS = 1.obj 2.obj

project.exe : $(OBJS)

link $(.TARGET), $(OBJS,X);

The .PATH.obj macro defines a search directory where .obj files are located. The X modifier causes omake to search for each element in its search directories. Assuming 1.obj and 2.obj are in the objs directory:

$(OBJS) is 1.obj 2.obj

$(OBJS,X) is objs\1.obj objs\2.obj

Include File

@

If the macro's value names a file, the value of the macro modification is the contents of the file with spaces, tabs, and newlines collapsed to single spaces.

This modifier is best used with the := expanded macro definition so the file or project is read only once, when the macro definition occurs. For example:

FILE = link.rsp

(response file)

OBJS := $(FILE,@)

(read contents of link.rsp)

Include File with Regular Expression Matching

@d regex d subst d

Include file, with regular-expression matching. [A regular expression . matches any single character; a regular expression \. is a literal period (or dot). Several regular expression characters that omake uses are poor choices when the regular expression is used to match file names. omake allows you to redefine these characters with the .REGEX_CHAR and .REGEX_WILD directives.]

The macro's value is the name of a file to be read. Each line in the file is examined with the regular expression regex. If regex matches the line, the matched part of the line is replaced with the substitution string subst. If regex does not match, the line is skipped.

The d is a single-character delimiter that cannot appear in regex or subst and cannot be a comma (usually a slash (/) or single quote ( ' ) is used).

Member and Nonmember

Mregex

The member selector. This selects elements matching regular expression regex.

$(SRCS,M\.c$$) is \src\main.c

After macro expansion, \.c$$ is \.c$, a regular expression that matches .c at the end of the element. This matches .c but not .cpp.

M"spec"

The member selector, where spec is enclosed in double quotes and is a Windows NT file specification, not a regular expression:

$(SRCS,M"\src\main.c") is \src\main.c

$(SRCS,M"*.cpp") is io.cpp

One use of this modifier is to determine whether the current target belongs to some special list of names. For example:

SPEC_OBJS = objs\spawn.obj objs\sync.obj

%.obj : %.c

if '$(SPEC_OBJS,M"$(.TARGET)")'if $(.TARGET) is in SPEC_OBJS

special commands to handle special objects

endif

Nregex

The nonmember selector. It selects elements that do not match regex.

$(SRCS,N\.c) is io.cpp

N"spec"

The nonmember selector, where spec is a Windows NT file specification, not a regular expression:

$(SRCS,N"\src\main.c") is \sys\sub.cpp io.cpp

$(SRCS,F,N"*.c") is \sys\sub.cpp io.cpp

Select a Particular Element

number

Selects the numberth element of this macro value. If the macro value doesn't have this numberth element, the modified value is the empty string. This modifier is particularly useful for inference rules that build multiple targets from a single dependency.

$(SRCS,2) is \sub.cpp

String Substitution
Sd regex d subst d

The substitution modifier. The d is a single-character delimiter that is neither in regular expression regex nor in substitution string subst and isn't a comma. If regex matches some part of a macro element, the matched part of the element is replaced with subst. The element is not changed if regex does not match it.
If regex is omitted, the last regular expression is reused. This is useful in combination with the member modifier, M. For example, given that CFLAGS has the value -AX -Ifoo -Ibar -DX=-IT:
$(CFLAGS,M^-I,S'^-I'') is foo bar
$(CFLAGS,M^-I,S''') is foo bar
The M modifier selects elements that start with -I, and the S operator substitutes the -I with nothing. This is quite different from
$(CFLAGS,S'^-I'') is -AX foo bar -DX=-IT
Notice how the elements unmatched by regex ^-I are not changed.
from=to

This modifier replaces occurrences of from string with to string. If from does not appear in a macro element, the element is not changed.
from and to can be simple strings or can use the percent sign (%) as a wildcard. If % is used, the search for from is anchored to the end of the element.
$(SRCS,%.c=%.obj) is \src\main.obj \sys\sub.cpp io.cpp
$(SRCS,.c=.obj) is \src\main.\sys\obj sub.cpp io.cpp
The use of % anchors the search for %.c so it matches main.c but not sub.cpp. The simple string substitution .c=.obj replaces any occurrence of .c with .obj so matches and changes sub.cpp to sub.objpp.
The delimiter between strings is the equal sign, but it is treated literally when prefixed with a backslash (\=). If fromString ends with a backslash, you must use two (\\=) to mean literal backslash followed by a delimiter. (Under PM/CB or NMAKE emulation, the delimiter is not quotable. This means that \= is taken literally, and there is no need to double the backslash).
For historical reasons, when fromString and toString are simple strings, this modifier operates on the entire macro value rather than on its elements, and can be used to replace the white space between elements. However, the W modifier is better suited for this task.
Tokenize

Wstr


This modifier replaces the white space that separates macro elements with str, where str is treated literally except for the following special sequences:

\n
\r
\t
\\
\ddd
\xdd

A newline character
A return character
A tab character
A backslash
An octal character ddd (1 to 3 digits)
A hexadecimal character dd (1 or 2 digits)

This example can be used to prepare an inline response file with the OBJS definition of the X modifier listed above:

$(OBJS,X,W+\n) is objs\1.obj+
objs\2.obj
project.exe;

Wildcard Expand File and Directory Names

* or *F

The macro's value is a wildcard file specification used to match file names. The value of the macro modification is the list of files that matches the specification. This list includes any directory components. For example:

SPEC

= obj\*.obj

(set up file spec)

OBJFILES

:= $(SPEC,*F)

(get list of obj\*.obj files)

*D

Like the *F modifier but matches directory names. For example:

SPEC

= *

(set up directory spec)

SUBDIRS

:= $(SPEC,*D)

(get list of subdirectories)

Regular Expressions

The M, N, and S modifiers use pattern-matching strings known as regular expressions. The regular expression (or regex) matches characters in strings, both literally and with wildcards, and the match is case-sensitive.

Configuring Regular Expressions

Two directives control the special characters that appear in regular expressions:

The backslash and period are standard characters in UNIX-styled regular expressions, but are awkward to use in Windows NT because the backslash is the directory separator and the period is the file extension separator. The .REGEX_CHAR and .REGEX_WILD directives can be used to change these characters to some other characters.

The default characters are most problematic for the M and N modifiers when you are trying to match a file name. For this case, use the M"spec" and N"spec" modifiers because the spec is a file specification rather than a regular expression.

Regular Expressions for the M Modifier

Assume the following macro definitions:

SRCS = main.c sub.cpp io.cpp
CFLAGS = -AX -Ifoo -Ibar /Ibaz -DX=-IT xI.c yi.c

To select files whose names include .c:

$(SRCS,M\.c) is main.c sub.cpp

To select files that end in .c, the search can be anchored to the end with the regex character $. To get $ to the regular expression, you need to use $$ in the makefile:

$(SRCS,M\.c$$) is main.c

You can also select .c files with the M"spec" modifier:

$(SRCS,M"*.c") is main.c

Analogous to the $ anchor, the ^ regex character anchors the search to the front of the macro element:

$(CFLAGS,M-I) is -Ifoo -Ibar -DX=-IT
$(CFLAGS,M^-I)
is -Ifoo -Ibar

The [set] regex characters indicate a set of characters, where the set can be single characters ([aA@] matches a, A or @), a range of characters ([a-z] matches a through z), or a mixture. For example:

$(CFLAGS,M^[-/]I) is -Ifoo -Ibar /Ibaz

Regular Expressions for the S Modifier

One powerful feature of regular expressions is that when used in substitutions, they can access the matched parts of the string. The down side of the more powerful regular expressions is that the expressions can be hard to read. For example, when DIR = NT_L, the expression $(DIR,S/\(.*\)_.*/\1/) is NT.

The \(, \) pair surround a part of the regular expression that is referenced later. Inside the pair is .*, which matches any character (.) repeated zero or more times (*). Taken together they instruct omake to match any character, zero or more times, and attach a tag to it. The rest of the regular expression is _ , which matches _, and .*, which matches any character repeated zero or more times.

The expression \1 is the stuff matched in the first pair of \( \), so the substitution evaluates to NT. Simple regular expressions are easy to read, but if you write more complicated expressions, be sure to provide thorough comments.

With Configuring .REGEX_CHAR and .REGEX_WILD

If omake is configured for alternative regular expression characters, the following directives appear in your make.ini:

.REGEX_CHAR : ~
.REGEX_WILD : ?

These examples are then easier to read:

$(SRCS,M.c) is main.c sub.cpp
$(SRCS,M.c$$) is main.c
$(CFLAGS,M-I) is -Ifoo -Ibar -DX=-IT
$(CFLAGS,M^-I) is -Ifoo -Ibar
$(CFLAGS,M^[-/]I) is -Ifoo -Ibar /Ibaz
$(DIR,S/~(?*~)_?*/~1/) is NT

Predefined and Built-In Macros

omake defines several macros before it reads the initialization file and makefiles. These macros come in two general types:

Predefined Macros: Run-Time Macros

The run-time macros are changed dynamically by omake according to the current target being built. A key feature of the run-time macros is that their values are pathnames. A target's pathname is the location the target was found on disk. The run-time macros evaluate to pathnames, which is important for use on build scripts where the executed command needs the location of the targets.

All run-time macros have names of the form .name, and several have one- or two-letter aliases that you are likely to see in makefiles of other make utilities.

Run-Time Macros

.NEWSOURCES (alias is ?)

This run-time macro evaluates to the pathnames of all dependencies newer than the current target. When configuration lookup is enabled, it evaluates to the pathnames of all dependencies for the current target, unless that behavior is modified with the .INCREMENTAL_TARGET directive (see the description in the section Dot Directives), in which case it evaluates to the pathnames of all dependencies different from the previously recorded versions.
.SOURCE (alias is <)

This run-time macro evaluates to the pathname of the inferred dependency that satisfied the inference rule, or the pathname of the first explicit dependency if no inferred dependency was found. Here is a sample inference rule for updating an object file with the Borland C compiler:
%.obj : %.c
cl -c $(.SOURCE)
Using an alias, the second line becomes
cl -c $<
.SOURCES (aliases are ^ or **)

This run-time macro evaluates to the pathnames of all dependencies of the current target. Here is an example that updates an executable with all object files that are its dependencies:
project.exe : main.obj io.obj
link /out :$(.TARGET), $(.SOURCES);
Using an alias, the second line becomes
link /out : $@, $**;
NOTE: The ** macro is the exception to the rule that multicharacter macros need parentheses or braces around them. The expression $** is the same as $(**).
.TARGET (alias is @)

This run-time macro evaluates to the pathname of the target currently being made. For example:
%.obj : %.c
cl -o$(.TARGET) -c $(.SOURCE)
Using an alias, the second line becomes
cl -o$@ -c $<
This macro can also be used at read time on a dependency line and its value is the name of the target currently getting dependencies. For example:
main.obj io.obj : $(.TARGET,R).c io.h
Using an alias, the line becomes
main.obj io.obj : $(@,R).c io.h
The .TARGET macro value is set to the name of each target, in turn. The dependencies are then macro-expanded and added to the target's list of dependencies. The R macro modifier selects the root of the target name. The above dependency line is equivalent to the two lines:
main.obj : main.c io.h
io.obj : io.c io.h
For compatibility with other Make utilities. omake treats $$@ on the dependency side of a dependency line just like $(.TARGET).
.TARGETROOT (alias is *)

This run-time macro evaluates to the pathname of the target currently being made, minus its extension. This macro can also be used at read time on the dependency side of a dependency line. Here is the read-time example from the .TARGET macro rewritten using this macro:
main.obj io.obj : $(.TARGETROOT).c io.h
Using an alias, the line becomes
main.obj io.obj : $*.c io.h

Predefined Macros: General Macros

The predefined macros in Table 3 give you information about the current make process. One of the most important predefined macros is the status macro at the bottom of the table.

Table 3 General Macros


General Macro

Value

$


When you need a literal dollar sign, you must use $$.


BUILTINS


The name of the initialization (built-ins) file.


CWD


The current working directory (same as MAKEDIR).


FIRSTTARGET


The first command-line target or, if one isn't given, the default makefile target.


INPUTFILE


The name of the current makefile. omake can read multiple makefiles, either with multiple -f command-line options or with %include directives. The INPUTFILE macro's value is the current makefile being read.


MAKEARGS


The command line with which omake was started, including command-line options, macros, and targets.


MAKEDIR


The current working directory (same as CWD).


MAKEMACROS


The command-line macros with which omake was started. Any macros that have spaces are enclosed in double quotes so they can be used on a command line again.


MAKESTATUS


The exit status of omake. This can be used in the .AFTER special target to determine whether omake is exiting with an error.


MAKETARGETS


The list of command-line targets passed to omake.


MAKEVERSION


The version number for omake. Its format is X.Y, where X and Y are the major and minor release numbers.


OPUS


Defined to the value 1. This can be used to test if you are running omake or some other Make.


status


The exit status of the last build script. It is used with conditional directives to execute other build scripts. For historical reasons, the name is lowercase.

Predefined Macros: State Macros

The state macros provide information about the state of command-line options and read-time directives. Most command-line options have an equivalent directive, and the value of the state macro is the same regardless of whether the option or directive was used.

In the list of macros in Table 4, the macro values that are the state of a directive or command-line option mean the value is 1 if the directive or command-line option was used; otherwise the value is 0.

NOTE: In terms of implementation, .ALWAYS, .IGNORE, and .SILENT are not directives; they are target attributes. They look like directives when they appear in a makefile on the target side of a dependency line when there are no dependencies. For example,

.IGNORE :

sets the .IGNORE attribute for every target created after the appearance of this line. The .ALWAYS, .IGNORE, and .SILENT macros have the correct value, as if these attributes were directives.

NOTE: Exactly one of $(.MS_NMAKE), $(.omake) or $(.POLY_MAKE) is 1.

Table 4 State Macros


State Macro

Value

.ALWAYS


The state of the .ALWAYS directive and -a command-line option. [In terms of implementation, .ALWAYS, .IGNORE, and .SILENT are not directives but target attributes. They look like directives when they appear in a makefile on the target side of a dependency line when there are no dependencies. For example,
.ALWAYS :
sets the .ALWAYS attribute for every target created after the appearance of this line. The .ALWAYS, .IGNORE, and .SILENT macros have the correct value, as if these attributes were directives.]


.CASE_MACRO


The state of the .CASE_MACRO directive.


.CASE_TARGET


The state of the .CASE_TARGET directive.


.DEBUG


The debug options as set by the .DEBUG directive and -# command-line option.


.DEBUG_PRINT


The state of the .DEBUG_PRINT directive and -p command-line option.


.DEBUG_RUN


The state of the .DEBUG_RUN directive and -d command-line option.


.ENVMACROS


The state of the .ENVMACROS directive.


.ENV_OVERRIDE


The state of the .ENV_OVERRIDE directive and -e command-line option.


.IGNORE


The state of the .IGNORE directive and -i command-line option.


.IGNORE_MFLAGS


The state of the -z command-line option.


.KEEPDIR


The state of the .KEEPDIR directive and -D command-line option.


.KEEPWORKING


The state of the .KEEPWORKING directive and -k command-line option.


.MAKE_MAKEFILE


The state of the .MAKE_MAKEFILE directive and -M command-line option.


.MS_NMAKE


The state of the .MS_NMAKE directive (exactly one of $(.MS_NMAKE), $(.omake) or $(.POLY_MAKE) is 1) and -EN command-line option.


.OPUS_52X


The list of Opus Make v5.2x compatibility features, as set by the -E2 command-line option.


.omake


The state of the .omake directive and -EO command-line option.


.POLY_MAKE


The state of the .POLY_MAKE directive and -EP command-line option.


.QUERY


The state of the -q command-line option.


.REGEX_BACK


The regular expression literal backslash (\). This is provided for writing regular expressions that are independent of the value of .REGEX_CHAR. If .REGEX_CHAR is \,its value is \\; otherwise, its value is \.


.REGEX_CHAR


The regular expression escape character set by the .REGEX_CHAR directive.


.REGEX_DOT


The regular expression literal dot (or period). If .REGEX_WILD is . its value is ${REGEX_CHAR}.; otherwise its value is .


.REGEX_WILD


The regular-expression wildcard character set by the .REGEX_WILD directive.


.REJECT_RULES


The state of the .REJECT_RULES directive and -r command-line option.


.REREAD


The state of the .REREAD directive.


.RULE_CHAR


The regular expression character as set by the .RULE_CHAR directive.


.SHELL


The command to execute the shell program as set by the .SHELL directive.


.SILENT


The state of the .SILENT directive or -s command-line option.


.SUFFIXES


The list of suffixes as set by the .SUFFIXES directive.


.UNIXPATHS


The state of the .UNIXPATHS directive.

An Example Use of the State Macros

Here is an example of how these macros can be used. Suppose you want to debug the contents of your makefile with the -#1 command-line option. However, this also produces output that comes from your make.ini file, which probably doesn't need debugging. Modify your make.ini file with the following:

_OLD_DEBUG := $(.DEBUG)

(get current state)

.NODEBUG : 1

(turn off "-#1" if set)

[Original make.ini goes here]

.DEBUG : $(_OLD_DEBUG)

(restore state)

Built-in Macros

omake defines the built-in macros with a default value, but they have the same precedence as normal makefile macros, so you can be redefine them.

Table 5 Built-in Macros


Built-in Macro

Definition

CC


Used in the %.obj : %.c rule, the name of the C compiler. The default value is cl, the name of the Microsoft C Compiler.


FC


Used in the %.obj : %.for rule, the name of the FORTRAN compiler. The default value is f77l, the name of the Lahey FORTRAN Compiler.


LIBEXE


Used in the %.lib : %.obj rule, the name of the object librarian. The default value is lib, the name of the Microsoft Librarian.


LINK


Used in the %.exe : %.obj rule, the name of the object linker. The default value is link, the name of the Microsoft Linker.


MAKE


The value is the full pathname of the omake executable.

The MAKE macro is special in that its appearance on a build script overrides the -n (no execute) command-line option for that line. This can be used in a recursive make, to have omake do the recursion.


MAKE_TMP


The name of the directory omake uses for temporary files (response files under Windows NT).

Initially MAKE_TMP is undefined and omake uses the current directory for temporary files. If you define MAKE_TMP its value must be an absolute directory (such as D:\ or D:\tmp, preferably on a RAM disk. As an example, the environment variable TMP names the temporary directory some compiler vendors use. You can put the following in make.ini:

MAKE_TMP = $(TMP)


MFLAGS


After all makefiles have been read, omake defines MFLAGS with all the command-line options. MFLAGS is useful for invoking omake recursively, as shown in the definition of the MAKE macro.

NOTE: MFLAGS is a built-in macro only if you are using native omake mode (that is, if you are not using an emulation mode). In the emulation modes, it is not predefined by omake; however, you can redefine it.

A second usage of MFLAGS is to pass initial options to omake. If MFLAGS is defined in the environment or in make.ini its value specifies additional command-line options. For example, the following macro definition placed in make.ini turns on the keep-directory mode (the -D command-line option):

MFLAGS = -D

Because there are directives for all command-line options, this second usage of MFLAGS is discouraged, and you should use the directives.


OS


The operating system. Its value is NT.


RC


Used in the %.res : %.rc rule, the name of the resource compiler. The default value is rc, the name of the Microsoft Resource Compiler.


SHELLCOMMANDS


Alphabetical list of commands known to need execution by the shell program. If you define this macro, omake uses it to detect when to use the shell program. This macro is not defined initially; omake uses an internal list of commands. See Auto-Detection Mode.


SHELLSUFFIX


Suffix used by omake for the batch files it generates. The default value is .bat.

Compatibility with Other Make Utilities

Through emulation, omake supports all PM/CB and NMAKE macros.