DocumentationMMK Make UtilityUser's Guide  ·  ESS-MMKUG-001

Chapter 2

Description Files

The key to successfully building your software system with MMK is the creation of a complete and accurate description file. This chapter describes the format for a description file and its components.

2.1. Description File Components

A description file is a collection of the following components:

  • Dependencies, which describe how one object depends on one or more other objects.

  • Actions, which are commands to be executed when an object needs to be built.

  • Macro definitions, for defining symbols that may be used in rules or actions.

  • Inference rule definitions, which are rules based on suffixes (and possibly directories as well), from which MMK can infer dependencies and actions without you having to list them explicitly in your makefile.

  • MMK directives, which provide a means for adding commands to be executed before or after all other actions, provide a simple conditional-build mechanism, and other directives for modifying MMK's behavior (see Section 2.7, “Other Directives”).

Here is an example of a simple description file:

PROGRAM.EXE : MAIN.OBJ,SUBROUTINES.OBJ
    LINK/EXEC=PROGRAM.EXE MAIN.OBJ,SUBROUTINES.OBJ
MAIN.OBJ : MAIN.FOR
    FORTRAN MAIN
SUBROUTINES.OBJ : SUBROUTINES.MAR
    MACRO SUBROUTINES

This is a simple collection of dependencies and actions for building an image called PROGRAM.EXE. PROGRAM.EXE depends on two object files, called MAIN.OBJ and SUBROUTINES.OBJ; MAIN is a FORTRAN module and SUBROUTINES is a MACRO module.

MMK accepts either a colon or the DEC/MMS DEPENDS_ON keyword to separate a target object from its sources. In either case, the separator must be surrounded by blanks—this differs from make, but is consistent with DEC/MMS syntax.

2.2. Using Inference Rules

MMK includes a collection of built-in inference rules and actions for most VMS programming languages. The rules are driven by the file type suffix attached to the object name; you must use the default file types in order to make use of the default rules.

For example, the description file in the last section could be simplified to just:

PROGRAM.EXE : MAIN.OBJ,SUBROUTINES.OBJ
    LINK/EXEC=PROGRAM.EXE MAIN.OBJ,SUBROUTINES.OBJ
MAIN.OBJ : MAIN.FOR
SUBROUTINES.OBJ : SUBROUTINES.MAR

MMK's built-in inference rules automatically define the actions for building a .OBJ file from a .FOR (using the FORTRAN command) and for building a .OBJ file from a .MAR file (using the MACRO command).

The description file could even be simplified further, to just:

PROGRAM.EXE : MAIN.OBJ,SUBROUTINES.OBJ
    LINK/EXEC=PROGRAM.EXE MAIN.OBJ,SUBROUTINES.OBJ

MMK automatically searches the suffixes list when constructing the dependency tree and locates inference rules for the .OBJ files automatically. This illustrates the second use for inference rules: they are used not only for inferring actions for a dependency that omits them, but they may also be used for inferring dependencies themselves based on a combination of source and target suffixes. This second purpose can greatly simplify your makefiles, and makes the build process more automatic.

2.3. Defining Inference Rules

You can define your own inference rules, either to extend or replace the ones built into MMK. You may include these rule definitions in your makefile, or in a separate file called a rules file. Rules files can be included by the use of a logical name or through the /RULES qualifier on the MMK command; see the MMK for further information.

MMK supports two types of inference rules: generic and prefixed. Generic rules are based solely on suffixes (file types), as in:

.C.OBJ :
    CC/OBJECT=$(MMS$TARGET) $(MMS$SOURCE)

which says, “to build filename.OBJ from an existing file called filename.C, use the CC command.” In general, generic rules work best when the source and target files reside in the same directory.

Prefixed inference rules are based on both suffixes and “prefixes” -- device and directory specifications. This provides a way to have MMK automatically infer dependencies between files that reside in different directories. For example: For example, the prefixed rule:

{SRC$:}.C{OBJ$:}.OBJ :
    CC/OBJECT=$(MMS$TARGET) $(MMS$SOURCE)

tells MMK, “to build OBJ$:filename.OBJ from an existing file called SRC$:filename.C, use the CC command.” This works like the generic rule above, but with the additional provision of having the source and target reside in different locations.

You can have more than one prefixed rule for a particular pair of suffixes; you may also mix generic rules and prefixed rules for a pair of suffixes. When attempting to infer a dependency, MMK will first use the prefixed rules, then fall back to using the generic rule.

In prefixed rules, the curly braces (“{” and “}”) are required. One of the two prefixes may be null, but specifying two null prefixes is equivalent to defining a generic rule.

In order to match a prefixed rule, file specification as it exists in the description file must match the prefix in the rule; MMK performs no logical name translation on prefixes, nor can it identify equivalencies between two prefixes that reference the same directory using different syntax.

The first inference rule for a pair of suffixes, whether it is generic or prefixed, must specify an action list; subsequent rules for the same pair of suffixes (with different prefixes) may have the action list omitted, in which case MMK will use the action list from the first rule. For example, MMK already has a built-in generic rule for .C.OBJ, which is:

.C.OBJ :
    $(CC)$(CFLAGS) $(MMS$SOURCE)

If you are simply adding a set of prefixed rules for the .C.OBJ suffix pair, you do not need to specify an action list on those rules; MMK will use the action list from the built-in generic rule.

2.4. Forced Setup/Teardown Actions in Inference Rules

MMK recognizes two special modifiers on action lines specified for inference rules. The setup modifier, “<”, forces the execution of an action prior to any unmodified action. The teardown modifier, “>”, forces the execution of an action after all other actions. Setup and teardown actions are performed for all dependencies matching the inference rule, even if a dependency includes explicit actions.

For example, the inference rule

.C.OBJ :
    < DEFINE/USER DECC$SHR V6_ROOT:[SYSLIB]DECC$SHR
      $(CC)$(CFLAGS) $(MMS$SOURCE)

would cause the logical name DECC$SHR to be defined prior to the invocation of the C compiler for all compilations into .OBJ files. This would apply even on dependencies containing explicit actions, such as

FRED.OBJ : FRED.C
  $(CC)$(CFLAGS)/DEFINE=FRED $(MMS$SOURCE)

2.5. Modifying the Suffix List

MMK uses a suffix list to determine the inference rules it should search for inferring a dependency. MMK has a built-in suffix list which goes with its list of built-in inference rules; see Appendix B, Built-in Dependency Rules for more information on the built-in rules and suffix list.

You can augment or replace the built-in suffix list with your own suffixes by using the directives listed in Table 2.1, “Suffix List Directives” in a rules file or a makefile.

Table 2.1. Suffix List Directives
DirectiveDescription
.SUFFIXESSpecify no file types to clear the list or append file types to the end of the suffix list
.SUFFIXES_BEFOREInserts a list of file types in the suffixes precedence list before the specified first file type
.SUFFIXES_AFTERInserts a list of file types in the suffixes precedence list after the specified first file type.
.SUFFIXES_DELETERemoves a list of files types from the suffix precedence list.

For example, let's say you have a Modula-2 compiler on your system, whose source files have a file type (suffix) of .MOD. MMK has no built-in inference rules for this file type; you could add one with the following sequence:

.SUFFIXES : .MOD

.MOD.OBJ :
    MODULA2/OBJECT=$(MMS$TARGET) $(MMS$SOURCE)

The .SUFFIXES directive above adds the .MOD suffix to the end of the suffix list. This is followed by the inference rule for creating an object file from a Modula-2 source file.

Specifying the .SUFFIXES directive with nothing to the right of the colon clears the current suffix list. You can do this to prevent MMK from using any inference rules for the current build, or to follow it with another .SUFFIXES directive that specifies only those suffixes for which you want inference rules to be enabled.

It is also possible to determine the current active .SUFFIXES list by using the builtin macro MMSSUFFIXES.

2.6. Using Conditionals

MMK provides several directives that can be used to modify the build sequence based on conditions. These directives are .IF, .IFDEF, .IFNDEF, .ELSE, .ELSIF and .ENDIF. The .IFDEF directive checks if the specified macro is defined and .IFNDEF is the logical inverse, the syntax of which is:

.IFDEF VEGETABLE
...
.ENDIF

or,

.IFNDEF VEGETABLE
...
.ENDIF

The .IF directive supports more general comparison (and thus greater power) by allowing the specification of a boolean expression, like so:

.IF expression
...
[.ELSEIF expression]
[...]
[.ELSE]
[...]
.ENDIF

In the example above, expression can be replaced by any operations consisting of the operators (both MMS and MMK operators are listed) listed in Table 2.2, “Conditional Expression Operators”.

Table 2.2. Conditional Expression Operators
MMS SyntaxMMK SyntaxExample
Boolean Operators
.ANDANDexpression .AND expression
.NOTNOT.NOT expression
.ORORexpression .OR expression
Comparison Operators
.EQEQLexpression .EQ expression
.GEGEQexpression .GE expression
.GTGTRexpression .GT expression
.LELEQexpression .LE expression
.LTLSSexpression .LT expression
.NENEQexpression .NE expression

All expressions evaluate to a boolean result. After all macro substitution is performed and the operand is not an empty string it is the equivalent of a boolean TRUE. All comparisons are performed without regard to upper/lower case.

2.6.1. GNU Make Conditionals

Supplemental to the MMS/MMK conditionals, MMK also supports the specification of the GNU Make-style conditionals ifeq, ifneq, ifdef, ifndef, else and endif. The ifdef and ifndef conditionals behave exactly as their MMS/MMK counter-parts. However, ifeq and ifneq have a slightly different syntax:

ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"

Although the example lists ifeq, it can be replaced with its logical inverse, ifneq. These two directives simply compare their arguments (ignoring case) and test for equality. The following is an example adapted from the GNU Make manual:

ifeq ($(strip $(foo)),)
# Come in here if foo is an empty string
...
endif

In order to use the conditionals it is necessary to first enable them. This can be done from the command line with the specification of the /EXTENDED_SYNTAX=GNU_SYNTAX qualifier or with the .GNU_SYNTAX directive in the description file.

2.7. Other Directives

As well as the suffix list directives (Table 2.1, “Suffix List Directives”) and the conditionals (Section 2.6, “Using Conditionals”), MMK recognizes the following directives in a description file or rules file. Each begins with a period.

.CASE_SENSITIVE

Makes the names of macros and objects case sensitive from this point on. Without it, MMK converts them to upper case, so that foo and FOO are the same macro.

.DEFAULT

Begins a list of actions that MMK uses to build any object for which there is neither a dependency with actions nor an applicable inference rule. The action lines follow the directive, as they follow a dependency.

Unlike DEC/MMS, MMK lets any rule, including a built-in rule, take precedence over the .DEFAULT actions; see Section A.3, “Other Differences”.

.FIRST

Begins a list of actions to be executed before any other action, when MMK starts the subprocess in which actions are executed. The action lines follow the directive. If nothing needs to be built, the .FIRST actions are not executed.

.GNU_SYNTAX

Enables the GNU Make-style conditionals, as the /EXTENDED_SYNTAX=GNU_SYNTAX qualifier does; see Section 2.6.1, “GNU Make Conditionals”.

.IGNORE

Causes MMK to ignore the status of all action lines, as /IGNORE=FATAL does, so that the build continues after an action fails. If /IGNORE or /NOIGNORE is specified on the MMK command, it takes precedence over this directive.

.INCLUDE file-spec

Reads the description file file-spec at this point, as if its contents appeared in place of the directive. The default file type is .MMS, in the current default directory. With /LOG, MMK reports each file it includes.

.LAST

Begins a list of actions to be executed after all other actions, before MMK ends the subprocess in which actions are executed. The action lines follow the directive. Like the .FIRST actions, they are executed only if some other action was.

.SILENT

Stops MMK from displaying action lines before executing them, as if every action line began with “@”. If /VERIFY or /NOVERIFY is specified on the MMK command, it takes precedence over this directive.

For example, the following description file writes a line before the first action and after the last:

.FIRST
    @ WRITE SYS$OUTPUT "Build started"
.LAST
    @ WRITE SYS$OUTPUT "Build finished"

PROGRAM.EXE : MAIN.OBJ,SUBROUTINES.OBJ
    LINK/EXEC=PROGRAM.EXE MAIN.OBJ,SUBROUTINES.OBJ

If PROGRAM.EXE is already up to date, MMK executes no actions, and neither message is displayed.

2.8. Macro Definitions

MMK supports a range of operators for assigning values to macros. To simplify description files it is possible to concatenate and test before assignment. Table 2.3, “Macro Assignment Operator” defines the available assignment operators.

Table 2.3. Macro Assignment Operator
OperatorDescriptionExample
= Assign value. FOO = BAR
+= Concatenate to existing value. FOR += ,NEXT
?= Assign if macro is undefined. FOO ?= FIRST
|= Assign command results. FOO |= DIRECTORY
~= Completely evaluate value before assignment 

Two of these assignment operators behave a little differently than the others. They are both described in more detail below.

2.8.1. Command Assignment

Command assignment is the |= operator. The assigned value is taken as a command and executed, with the results being assigned to the macro. Using this mechanism it would be possible to determine some details of the running system, like so:

FOO |= SHOW SYSTEM/NOPROCESS
ALL :
    @ WRITE SYS$OUTPUT "$(FOO)"

the results of which might look something like:

$ MMK
OpenVMS V7.3  on node BENDER  15-FEB-2013 16:05:10.03  Uptime  37 15:54:40

Note

The '!' character is already a comment delimiter in MMK. To resolve this, the MMK description file parser now tests for a '=' character immediately following a '!'. Usually when detecting a '!' character the remainder of the line (including the comment delimiter) will be stripped from the output. However, in this special case the delimiter and the following characters will not be stripped and instead parsed as an assignment. This may create issues for existing procedures that contain comments that match this pattern.

However, it is worth noting that this restriction only applies to the '!' comment delimiter. It is still possible to use '#' in any place and achieve the desired result.

2.8.2. Evaluated Assignment

Evaluated assignment is the ~= operator. This assignment operator is special in that it completely evaluated the value before assigning it to the macro.

All records in the description file are evaluated and an attempt is made to resolve all macros as they are read in to the parser. However, if an unresolved macro, or a builtin function call is found it is not evaluated to the empty string, rather it is retained for a time when those macros are available. An example from the default definitions within MMK is:

BASIC       = BASIC
BASFLAGS    = /NOLIST/OBJECT=$(MMS$TARGET_NAME)$(OBJ)

The definition of BASFLAGS will not be evaluated fully as the description file is processed, leaving MMS$TARGET_NAME and OBJ unresolved until BASFLAGS is resolved as a part of executing the build actions that include this definition (shown below).

.BAS$(OBJ) :
    $(BASIC)$(BASFLAGS) $(MMS$SOURCE)

Sometimes this is not what is wanted and it is necessary to evaluate the definition completely. The following is an excerpt from the procedure that builds MMK's PCSI Product Definition File.

DOCUMENTATION ~= $(ADDPREFIX $(KITDIR),$(WILDCARD $(KITDIR)*.HTML)) -
                 $(ADDPREFIX $(KITDIR),$(WILDCARD $(KITDIR)*.PDF)) -
                 $(ADDPREFIX $(KITDIR),$(WILDCARD $(KITDIR)*.PS)) -
                 $(ADDPREFIX $(KITDIR),$(WILDCARD $(KITDIR)*.TXT))

Without being able to evaluate the macro definition for DOCUMENTATION the list documentation files would actually evaluate to the function calls, not the results of the builtin function calls.

2.9. Deferring Macro Substitution

MMK provides a way to defer the resolution of a macro that is referenced in the right-hand side of a macro definition, as an extension to MMS. Macros are normally referenced using the $(name) syntax, which causes the value of the macro to be substituted immediately when a line is parsed (except for MMK's “special” macros, such as MMS$SOURCE and MMS$TARGET).

You can defer this substitution in MMK by using the syntax ${name} instead. However, this syntax is only recognized on the right-hand side of a macro definition. This can be useful when defining macros in a rules file that rely on macros that do not get defined until another rules file or a description file gets processed. For example, you might have the following definition in a rules file:

CFLAGS = /OBJECT=$(MMS$TARGET)/NOLIST/DEFINE=(VMS_BUILD,${MOREDEFINES})

then in your description file, you can define the MOREDEFINES macro:

MOREDEFINES = ANOTHER_C_DEFINE

This will complete the CFLAGS macro value when it is referenced later in the description file.

2.10. Macro String Substitution

MMK provides two mechanisms for causing string substitution to occur when resolving a macro reference: suffix substitution and general string substitution.

2.10.1. Suffix Substitution

When a string contains a list of file specifications, you can replace the file type suffixes on each file specification with a different suffix. The general form of this type of substitution is:

$(macro-name:old-sfx=new-sfx)

which causes the replacement of all occurrences of the file type suffix old-sfx with new-sfx. Both suffixes must begin with a dot.

For example, in these macro definitions:

SOURCES = FIRST.C, SECOND.C, THIRD.C
OBJECTS = $(SOURCES:.C=.OBJ)

the OBJECTS macro would have the value “FIRST.OBJ, SECOND.OBJ, THIRD.OBJ”. This form of substitution works with file specification lists separated by either blanks, commas, or both. The substitution rule following the colon may also contain blanks, which are ignored. Substitutions are case-insensitive.

2.10.2. General String Substitution

General string substitution in macro references is an extended feature of MMK. It looks very similar to suffix substitution, but uses a double colon (“::”) instead of a single colon and allows the substitution to occur anywhere within the string. The syntax is:

$(macro-name::old-str=new-str)

which causes the replacement of all occurrences of the string old-str with new-str. You may use a backslash “\” as a “literal-next” escape when one of the strings contains an equals sign. Neither string may contain a right parenthesis character (“)”), even quoted with a backslash, although this restriction will be lifted in a future release of MMK. For example, the following macro definitions:

SOURCES = FIRST.C,SECOND.C,THIRD.C
SOURCEPLUS = $(SOURCES::,=+)

would cause SOURCEPLUS to contain the list of filenames separated with plus signs (“+”) rather than commas.

General string substitutions in macro references are case-insensitive, but do not ignore blanks in the macro value or in the substitution rule. For example, in the following definitions:

TEST      = Xyz xYz xyZ
REPLACED  = $(TEST::YZ =YZ,)

the REPLACED macro would have the value “XYZ,xYZ,xyZ”, due to the case-blind comparisons and the inclusion of the space in the old-str specification.

2.11. Builtin Functions

As well as symbol substitution it is also possible to call a number of builtin functions that can process text to automate and simplify build definitions. The following reference describes what each of these functions does, with examples.

2.11.1. ADDPREFIX

Prefix List Elements

$(ADDPREFIX prefix,text[ ...])

Description

ADDPREFIX treats text as a whitespace delimited list of words. Each word is prefixed with prefix and returned in a space delimited list.

Examples

The following examples show how to call the ADDPREFIX builtin function and demonstrate the expected output.

LIST = CAT, DOG, SECRET, HEAVY
ALL :
    @ WRITE SYS$OUTPUT "Unprefixed = $(LIST)"
    @ WRITE SYS$OUTPUT "Prefixed   = $(ADDPREFIX TOP ,$(LIST))"

Description file to demonstrate the ADDPREFIX function.

Unprefixed = CAT, DOG, SECRET, HEAVY
Prefixed   = TOP CAT, TOP DOG, TOP SECRET, TOP HEAVY

The output the above description file would generate.

2.11.2. ADDSUFFIX

Suffix List Elements

$(ADDSUFFIX suffix,text[ ...])

Description

ADDSUFFIX treats text as a whitespace delimited list of words. Each word is suffixed with suffix and returned in a space delimited list.

Examples

The following examples show how to call the ADDSUFFIX builtin function and demonstrate the expected output.

LIST = OX VAX QUICK LENGTH STRENGTH
SUFFIX = EN,
ALL :
    @ WRITE SYS$OUTPUT "Unsuffixed = $(LIST)"
    @ WRITE SYS$OUTPUT "Suffixed   = $(ADDSUFFIX $(SUFFIX),$(LIST))"

Description file to demonstrate the ADDSUFFIX function. Notice that the suffix to be appended to each word is a symbol. This is so that the text EN, will recognize the ',' character as part of the suffix and not an argument delimiter in the builtin function.

Unsuffixed = OX VAX QUICK LENGTH STRENGTH
Suffixed   = OXEN, VAXEN, QUICKEN, LENGTHEN, STRENGTHEN,

The output the above description file would generate.

2.11.3. AND

Logical And

$(AND condition[,condition[,...]])

Description

The AND function provides a logical and operation. Each of the condition arguments are expanded until one evaluates to the empty string. The arguments are evaluated individually and consecutively, so if one should fail (evaluate to an empty string) then the remaining conditions will not be evaluated.

If all conditions evaluate to a non-empty string, then the result of this function is the expansion of the final condition. Otherwise this function will return the empty string.

Examples

The following examples show how to call the AND builtin function and demonstrate the expected output.

A = A
B = B
C = C
D =
FOO = $(AND $(A),$(B),$(C))
BAR = $(AND $(A),$(B),$(C),$(D))
ALL :
    @ WRITE SYS$OUTPUT "FOO = $(FOO)"
    @ WRITE SYS$OUTPUT "BAR = $(BAR)"

Description file to demonstrate the AND function.

FOO = C
BAR =

The output the above description file would generate.

2.11.4. BASENAME

Get File Specification Without Type

$(BASENAME text[ ...])

Description

BASENAME returns the node, device, directory and file name portions of the specifications found in text. The result does not include the file type or version. If multiple specifications are supplied, they will each be evaluated and returned in a space delimited list.

MMK checks only the syntax of each specification: the files need not exist, and only the parts actually written in text are returned.

Examples

The following examples show how to call the BASENAME builtin function and demonstrate the expected output.

FSPEC = DISK$USER:[PROJ.SRC]MAIN.C;3
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "FOO = $(BASENAME $(FSPEC))"
    @ $(ECHO) "BAR = $(BASENAME A.A;2 B.B C.C;12)"

Description file to demonstrate the BASENAME function.

FOO = DISK$USER:[PROJ.SRC]MAIN
BAR = A B C

The output the above description file would generate.

2.11.5. CALL

Call User-defined Function

$(CALL macro[,param[,...]])

Description

The CALL function makes it possible for a user to develop their own macro functions. To call a macro function, specify the name in macro, not the substitution, and pass up to 31 arguments in param.

Before macro is call (looked up and resolved) the arguments passed in param are resolved and defined as symbols $(1)-$(31), with $(0) being macro.

It is important to remember certain escaping rules when defining callable macros. MMK supports the concept of deferred substitution (see Section 2.9, “Deferring Macro Substitution” by using ${ and } as the substitution delimiters. It is necessary to use this method of substitution when specifying the macro arguments outside of any builtin function call, for example:

REVERSE = ${2} ${1}
FOO = $(CALL REVERSE,A,B)

Examples

The following examples show how to call the CALL builtin function and demonstrate the expected output. Another example can be found under the documentation for FINDSTRING.

ORIGINS = $(FOREACH VAR,$(1),$(ORIGIN $(VAR)))
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "$(CALL ORIGINS,MMSALPHA MMSIA64 MMSVAX)"

This description file shows the definition of the user defined function ORIGINS, which is a multi-argument version of the ORIGIN builtin.

This example also demonstrates that because the $(1) substitution is made within the FOREACH call, there is no need to use deferred substitution.

UNDEFINED UNDEFINED SPECIAL

The output the above description file would generate when run on a VAX system.

2.11.6. COLLAPSE

Collapse Whitespace

$(COLLAPSE text)

Description

The COLLAPSE function removes all whitespace from text, except within quoted strings: text between a pair of apostrophes (') or quotation marks (") is kept as it is, quotes included.

Examples

The following examples show how to call the COLLAPSE builtin function and demonstrate the expected output.

FOO = $(COLLAPSE 1   2  3      4       5       6 7) 8 9 10
BAR = $(COLLAPSE 't e s t i n g'     asdfasdf)
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "FOO = $(FOO)"
    @ $(ECHO) "BAR = $(BAR)"

Description file to demonstrate the COLLAPSE function.

FOO = 1234567 8 9 10
BAR = 't e s t i n g'asdfasdf

The output the above description file would generate.

2.11.7. DEVICE

Get Device

$(DEVICE text[ ...])

Description

DEVICE returns the device portion of the specifications found in text, including the colon. If multiple specifications are supplied, they will each be evaluated and returned in a space delimited list.

MMK checks only the syntax of each specification: the files need not exist, and only the parts actually written in text are returned.

To retrieve the device and directory together, see DIR.

Examples

The following examples show how to call the DEVICE builtin function and demonstrate the expected output.

FSPEC = DISK$USER:[PROJ.SRC]MAIN.C;3
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "FOO = $(DEVICE $(FSPEC))"

Description file to demonstrate the DEVICE function.

FOO = DISK$USER:

The output the above description file would generate.

2.11.8. DIR

Get Device and Directory

$(DIR text[ ...])

Description

DIR returns the node, device and directory portions of the specifications found in text. If multiple specifications are supplied, they will each be evaluated and returned in a space delimited list. A specification with no directory gives [], so each entry in the result corresponds to one in text.

MMK checks only the syntax of each specification: the files need not exist, and only the parts actually written in text are returned.

To retrieve only the directory, please see DIRECTORY.

Examples

The following examples show how to call the DIR builtin function and demonstrate the expected output.

FSPEC = DISK$USER:[PROJ.SRC]MAIN.C;3
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "FOO = $(DIR $(FSPEC))"
    @ $(ECHO) "BAR = $(DIR DISK:[A]X.C A.C DISK:[B.C]Y.OBJ Z)"

Description file to demonstrate the DIR function.

FOO = DISK$USER:[PROJ.SRC]
BAR = DISK:[A] [] DISK:[B.C] []

The output the above description file would generate.

2.11.9. DIRECTORY

Get Directory

$(DIRECTORY text[ ...])

Description

DIRECTORY returns only the directory portion of the specifications found in text. This is different to DIR which also returns the node and device.

If multiple specifications are supplied, they will each be evaluated and returned in a space delimited list. A specification with no directory gives [].

MMK checks only the syntax of each specification: the files need not exist, and only the parts actually written in text are returned.

Examples

The following examples show how to call the DIRECTORY builtin function and demonstrate the expected output.

FSPEC = DISK$USER:[PROJ.SRC]MAIN.C;3
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "FOO = $(DIRECTORY $(FSPEC))"
    @ $(ECHO) "BAR = $(DIRECTORY DISK:[A]X.C A.C DISK:[B.C]Y.OBJ Z)"

Description file to demonstrate the DIRECTORY function.

FOO = [PROJ.SRC]
BAR = [A] [] [B.C] []

The output the above description file would generate.

2.11.10. ERROR

Signal an Error and Stop

$(ERROR text)

Description

ERROR displays text as an MMK fatal error message, %MMK-F-ERROR, and stops MMK. No further targets are built.

Use it to stop a build early when a description file finds a condition it cannot handle, such as a required macro that has not been defined. To report a problem and carry on, see WARN.

Examples

The following examples show how to call the ERROR builtin function and demonstrate the expected output.

CHECK ~= $(IF $(TARGET_CPU),,$(ERROR TARGET_CPU is not defined))

ALL :
    @ WRITE SYS$OUTPUT "Building for $(TARGET_CPU)"

Description file to demonstrate the ERROR function. Unless TARGET_CPU is defined, for example with /MACRO=TARGET_CPU=X86_64, MMK stops before building anything.

%MMK-F-ERROR, TARGET_CPU is not defined

The output the above description file would generate.

2.11.11. FILENAME

Get Filename

$(FILENAME text[ ...])

Description

FILENAME returns only the file name portion of the specifications found in text, in the case it was given. If multiple specifications are supplied, they will each be evaluated and returned in a space delimited list.

MMK checks only the syntax of each specification: the files need not exist, and only the parts actually written in text are returned.

Examples

The following examples show how to call the FILENAME builtin function and demonstrate the expected output.

FSPEC = DISK$USER:[PROJ.SRC]MAIN.C;3
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "FOO = $(FILENAME $(FSPEC))"
    @ $(ECHO) "BAR = $(FILENAME A.C B A.OBJ;2 C.)"

Description file to demonstrate the FILENAME function.

FOO = MAIN
BAR = A B A C

The output the above description file would generate.

2.11.12. FILETYPE

Get File Type

$(FILETYPE text[ ...])

Description

FILETYPE returns only the file type portion of the specifications found in text, including the leading period, in the case it was given. If multiple specifications are supplied, they will each be evaluated and returned in a space delimited list.

A specification with no type gives an empty entry, so the result keeps one entry for each specification (note the two spaces in the second example). If none of the specifications has a type, the result is empty.

MMK checks only the syntax of each specification: the files need not exist, and only the parts actually written in text are returned.

Examples

The following examples show how to call the FILETYPE builtin function and demonstrate the expected output.

FSPEC = DISK$USER:[PROJ.SRC]MAIN.C;3
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "FOO = $(FILETYPE $(FSPEC))"
    @ $(ECHO) "BAR = $(FILETYPE A.C B A.OBJ;2 C.)"

Description file to demonstrate the FILETYPE function.

FOO = .C
BAR = .C  .OBJ .

The output the above description file would generate.

2.11.13. FILEVERSION

Get File Version

$(FILEVERSION text[ ...])

Description

FILEVERSION returns only the file version portion of the specifications found in text, including the leading semicolon. If multiple specifications are supplied, they will each be evaluated and returned in a space delimited list.

A specification with no version gives an empty entry, so the result keeps one entry for each specification (note the two spaces in the second example). If none of the specifications has a version, the result is empty. A specification that ends in a semicolon alone gives ;.

MMK checks only the syntax of each specification: the files need not exist, and only the parts actually written in text are returned.

Examples

The following examples show how to call the FILEVERSION builtin function and demonstrate the expected output.

FSPEC = DISK$USER:[PROJ.SRC]MAIN.C;3
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "FOO = $(FILEVERSION $(FSPEC))"
    @ $(ECHO) "BAR = $(FILEVERSION A.A;2 B.B C.C;12)"
    @ $(ECHO) "BAZ = $(FILEVERSION A.A B.B)"

Description file to demonstrate the FILEVERSION function.

FOO = ;3
BAR = ;2  ;12
BAZ =

The output the above description file would generate.

2.11.14. FILTER

Filter Words

$(FILTER pattern[ ...],text[ ...])

Description

FILTER treats text as a whitespace delimited list for words. It compares each word against pattern, which itself is a whitespace delimited list of patterns, to determine if it should be included in the result. If the pattern matches the word, the word is included in the space delimited result list. The pattern wildcard characters are '%' and '*'.

Examples

The following examples show how to call the FILTER builtin function and demonstrate the expected output.

SOURCES = FOO.C BAR.C BAZ.S UGH.H
ALL :
    @ WRITE SYS$OUTPUT "$(FILTER *.C *.S,$(SOURCES))"
    @ WRITE SYS$OUTPUT "$(FILTER *.H,$(SOURCES))"
    @ WRITE SYS$OUTPUT "$(FILTER *.T,$(SOURCES))"

Description file to demonstrate the FILTER function.

FOO.C BAR.C BAZ.S
UGH.H

The output the above description file would generate.

2.11.15. FILTER-OUT

Filter Out Words

$(FILTER-OUT pattern[ ...],text[ ...])

Description

FILTER treats text as a whitespace delimited list for words. It compares each word against pattern, which itself is a whitespace delimited list of patterns, to determine if it should be included in the result. If the pattern matches the word, the word is not included in the space delimited result list. The pattern wildcard characters are '%' and '*'.

Examples

The following examples show how to call the FILTER-OUT builtin function and demonstrate the expected output.

SOURCES = FOO.C   BAR.C   BAZ.S   UGH.H
ALL :
    @ WRITE SYS$OUTPUT "$(FILTER-OUT *.C *.S,$(SOURCES))"
    @ WRITE SYS$OUTPUT "$(FILTER-OUT *.H,$(SOURCES))"
    @ WRITE SYS$OUTPUT "$(FILTER-OUT *.T,$(SOURCES))"

Description file to demonstrate the FILTER-OUT function.

UGH.H
FOO.C BAR.C BAZ.S
FOO.C BAR.C BAZ.S UGH.H

The output the above description file would generate.

2.11.16. FINDSTRING

String Search

$(FINDSTRING find,text)

Description

FINDSTRING searchs text for any occurrence of find. If find is found then the result is find. Otherwise the result is an empty string.

Examples

The following examples show how to call the FINDSTRING builtin function and demonstrate the expected output.

ISPRESENT = ${IF $(FINDSTRING $(1),$(2)),YES,NO}

TEXT = KERMIT PIGGY FOZZIE
ALL :
    @ WRITE SYS$OUTPUT "LIST = $(LIST)"
    @ WRITE SYS$OUTPUT "1. KERMIT? $(CALL ISPRESENT,KERMIT,$(TEXT))
    @ WRITE SYS$OUTPUT "2. GONZO? $(CALL ISPRESENT,GONZO,$(TEXT))"

Description file to demonstrate the FINDSTRING function. In this example FINDSTRING is being called indirectly via a macro call. For further information on this, please see the documentation for the CALL builtin function.

LIST = KERMIT PIGGY FOZZIE
1. KERMIT? YES
2. GONZO? NO

The output the above description file would generate.

2.11.17. FIRSTWORD

Get First Word

$(FIRSTWORD text[ ...])

Description

FIRSTWORD fetches the first word in the whitespace separated list in text. This is equivalent to:

$(word 1, text)

Examples

The following examples show how to call the FIRSTWORD builtin function and demonstrate the expected output.

LIST = kermit the frog
FOO = $(FIRSTWORD $(LIST))
BAR = $(WORD 1,$(LIST))
ALL :
    @ WRITE SYS$OUTPUT "$(FOO) = $(BAR)"

Description file to demonstrate the FIRSTWORD function.

kermit = kermit

The output the above description file would generate.

2.11.18. FOREACH

Repeat Expansion

$(FOREACH macro,list[ ...text],text)

Description

FOREACH provides a basic "for" loop construct by treating list as a whitespace delimited lost of words and executing text for each word in list. The temporary variable macro is defined to the next word before each execution of text.

Examples

The following examples show how to call the FOREACH builtin function and demonstrate the expected output. An example of FOREACH can also be found in the examples for CALL.

LETTERS = A B C D
ALL :
    @ WRITE SYS$OUTPUT "$(FOREACH LETTER,$(LETTERS),A$(LETTER))"

Description file to demonstrate the FIRSTWORD function.

AA AB AC AD

The output the above description file would generate.

2.11.19. IF

Conditional Evaluation

$(IF condition,then[,else]])

Description

The IF function evaluates condition. If it evaluates to a non-empty string, then the then argument is evaluated and returned as the result. Otherwise, if condition evaluates to an empty string (false) and the else argument is present, it is evaluated and returned as the result.

The then and else arguments are not evaluated until condition has been evaluated. Then only the appropriate argument is evaluated.

Examples

The following examples show how to call the IF builtin function and demonstrate the expected output.

TRUE = TRUE
FALSE =

FOO = $(IF $(TRUE),TRUE)
BAR = $(IF $(FALSE),,FALSE)
ALL :
    @ WRITE SYS$OUTPUT "FOO = $(FOO)"
    @ WRITE SYS$OUTPUT "BAR = $(BAR)"

Description file to demonstrate the IF function.

FOO = TRUE
BAR = FALSE

The output the above description file would generate.

2.11.20. INFO

Display an Informational Message

$(INFO text[,text[,...]])

Description

INFO displays each text argument as an MMK informational message, %MMK-I-INFO, one message per argument. MMK carries on, and the function returns an empty string, so it can be placed wherever a function call is allowed without changing the text around it.

See also WARN and ERROR.

Examples

The following examples show how to call the INFO builtin function and demonstrate the expected output.

START ~= $(INFO Starting the build,Target CPU is $(TARGET_CPU))

ALL :
    @ CONTINUE

Description file to demonstrate the INFO function, run with /MACRO=TARGET_CPU=X86_64. The evaluated assignment (~=, see Section 2.8.2, “Evaluated Assignment”) makes MMK call INFO as it reads the line.

%MMK-I-INFO, Starting the build
%MMK-I-INFO, Target CPU is X86_64

The output the above description file would generate.

2.11.21. JOIN

Merge Lists

$(JOIN list,text)

Description

JOIN merges the whitespace delimited lists in list and text. If the length of the two lists are not the same, then the remainder of the longer list is concatenated to the result.

Examples

The following examples show how to call the JOIN builtin function and demonstrate the expected output.

FOO = $(JOIN A B C D, 1 2 3 4)
BAR = $(JOIN A      , 1 2 3 4)
ALL :
    @ WRITE SYS$OUTPUT "FOO = $(FOO)"
    @ WRITE SYS$OUTPUT "BAR = $(BAR)"

Description file to demonstrate the JOIN function.

FOO = A1 B2 C3 D4
BAR = A1 2 3 4

The output the above description file would generate.

2.11.22. LASTWORD

Get Last Word

$(LASTWORD text ...)

Description

LASTWORD fetches the last word in the whitespace separated list in text. This is equivalent to:

$(word $(words text), text)

Examples

The following examples show how to call the LASTWORD builtin function and demonstrate the expected output.

LIST = kermit the frog
FOO = $(LASTWORD $(LIST))
BAR = $(WORD $(WORDS $(LIST)),$(LIST))
ALL :
    @ WRITE SYS$OUTPUT "$(FOO) = $(BAR)"

Description file to demonstrate the LASTWORD function.

frog = frog

The output the above description file would generate.

2.11.23. NOTDIR

Get Filename and Type

$(NOTDIR text[ ...])

Description

NOTDIR is the opposite of DIR. NOTDIR returns the file name and type portion of the specifications found in text, without the version. If multiple specifications are supplied, they will each be evaluated and returned in a space delimited list.

MMK checks only the syntax of each specification: the files need not exist, and only the parts actually written in text are returned.

To retrieve only filename, type or version, consider using FILENAME, FILETYPE or FILEVERSION respectively.

Examples

The following examples show how to call the NOTDIR builtin function and demonstrate the expected output.

FSPEC = DISK$USER:[PROJ.SRC]MAIN.C;3
ECHO = WRITE SYS$OUTPUT

ALL :
    @ $(ECHO) "FOO = $(NOTDIR $(FSPEC))"
    @ $(ECHO) "BAR = $(NOTDIR [A]X.C;3 [B]Y.OBJ;7 Z)"

Description file to demonstrate the NOTDIR function.

FOO = MAIN.C
BAR = X.C Y.OBJ Z

The output the above description file would generate.

2.11.24. OR

Logical Or

$(OR condition[,condition[,...]])

Description

The OR function provides a logical or operation. Each of the condition arguments are expanded until one evaluates to a non-empty string. The arguments are evaluated individually and consecutively, so if one should succeed (evaluate to a non-empty string) then the remaining conditions will not be evaluated.

If all conditions evaluate to an empty string, then the result of this function is the empty string. Otherwise this function will return the result of the last condition evaluated.

Examples

The following examples show how to call the OR builtin function and demonstrate the expected output.

A =
B =
C =
D = D
FOO = $(OR $(A),$(B),$(C))
BAR = $(OR $(A),$(B),$(C),$(D))
ALL :
    @ WRITE SYS$OUTPUT "FOO = $(FOO)"
    @ WRITE SYS$OUTPUT "BAR = $(BAR)"

Description file to demonstrate the OR function.

FOO =
BAR = D

The output the above description file would generate.

2.11.25. ORIGIN

Symbol Origin

$(ORIGIN macro)

Description

ORIGIN returns a string describing in which context macro was defined. Table 2.4, “Macro Definition Origins” lists the possible results and what they mean.

Table 2.4. Macro Definition Origins
TypeDescription
UNDEFINEDThe symbol is not yet defined.
FILEThese symbols have been defined within the description file.
COMMAND LINEThese symbols have been defined through use of the /MACRO= command line qualifier.
SPECIALSpecial symbols are defined per target, these include $(MMS$TARGET) and $(MMS$SOURCE).
DEFAULTThis includes all symbols defined by the default rules.
CLI SYMBOLThis macro takes its definition from a DCL symbol.
TEMPORARYThese symbols are defined through the use of CALL, like $(0) amd $(1), or FOREACH and the definition of the loop symbol.

Examples

The following examples show how to call the ORIGIN builtin function and demonstrate the expected output.

FOO = BAR
BAR = $(ORIGIN 0)
ALL :
    @ WRITE SYS$OUTPUT "MMSVAX              = $(ORIGIN MMSVAX)"
    @ WRITE SYS$OUTPUT "MMSALPHA            = $(ORIGIN MMSALPHA)"
    @ WRITE SYS$OUTPUT "CC                  = $(ORIGIN CC)"
    @ WRITE SYS$OUTPUT "CALL ARGUMENT 0     = $(CALL BAR)"
    @ WRITE SYS$OUTPUT "FOO                 = $(ORIGIN FOO)"
    @ WRITE SYS$OUTPUT "VARIANT             = $(ORIGIN VARIANT)"
    @ WRITE SYS$OUTPUT "CLISYM              = $(ORIGIN CLISYM)"

Description file to demonstrate the ORIGIN function.

MMSVAX              = SPECIAL
MMSALPHA            = UNDEFINED
CC                  = DEFAULT
CALL ARGUMENT 0     = TEMPORARY
FOO                 = FILE
VARIANT             = COMMAND LINE
CLISYM              = CLI SYMBOL

The output the above description file would generate if run on a VAX system. With the following commands:

$ CLISYM == 1
$ MMK/MACRO=VARIANT=1

2.11.26. PATSUBST

Pattern Substitution

$(PATSUBST pattern, to, text)

Description

PATSUBST replaces all instances of pattern with to in text. However, unlike the function SUBST this builtin allows the specification of a pattern. Table 2.5, “PATSUBST Pattern Operators” details the pattern operators.

Table 2.5. PATSUBST Pattern Operators
OperatorMatchReplace
* Match any sequence of any length.Insert the entire matched sequence.
% Match any single character.Insert the first character of the matched sequence.

When matching the words in text if a match is found, the wild-carded portions of the match are placed in a queue. Therefore, when specifying to it is also possible to include pattern operators which will insert the wildcarded portions back into the output.

Note

Unlike the substitution mentioned in Section 2.10, “Macro String Substitution” the comparison done by PATSUBST is case-sensitive.

Examples

The following examples show how to call the PATSUBST builtin function and demonstrate the expected output.

LIST = FIRST.C SECOND.C THIRD.C

FOO = $(PATSUBST *.C,*.OBJ,$(LIST))
BAR = $(PATSUBST *.*,*.%,$(FOO))

ALL :
    @ WRITE SYS$OUTPUT "FOO = $(FOO)"
    @ WRITE SYS$OUTPUT "BAR = $(BAR)"

Description file to demonstrate the PATSUBST function.

FOO = FIRST.OBJ SECOND.OBJ THIRD.OBJ
BAR = FIRST.O SECOND.O THIRD.O

The output the above description file would generate.

2.11.27. SORT

Sort Words

$(SORT text[ ...])

Description

SORT treats text as a whitespace delimited list of words. These words are then sorted into lexical order, removing duplicates and returned to the caller.

Examples

The following examples show how to call the SORT builtin function and demonstrate the expected output.

ECHO = WRITE SYS$OUTPUT
MUPPETS = PIGGY FOZZIE KERMIT GONZO BEAKER ROWLF
DILBERT = DILBERT ALICE WALLY ASOK DOGBERT RATBERT
ALL :
    @ $(ECHO) "$(SORT $(DILBERT))
    @ $(ECHO) "$(SORT $(MUPPETS))

Description file to demonstrate the SORT function.

ALICE ASOK DILBERT DOGBERT RATBERT WALLY
BEAKER FOZZIE GONZO KERMIT PIGGY ROWLF

The output the above description file would generate.

2.11.28. STRIP

Compress Whitespace

$(STRIP text)

Description

STRIP replaces all whitespace characters with a single space as well as removing the leading and trailing whitespace from text.

Examples

The following examples show how to call the STRIP builtin function and demonstrate the expected output.

FOO = $(STRIP    This   has        lots of   space   )
ALL :
    @ WRITE SYS$OUTPUT "FOO = $(FOO)"

Description file to demonstrate the STRIP function.

FOO = This has lots of space

The output the above description file would generate.

2.11.29. SUBST

String Substitution

$(SUBST from,to,text)

Description

SUBST replaces all instances of from found in text with to.

Note

This builtin function, like PATSUBST, is case sensitive in its matching. For case-insensitive string substitution please use, Section 2.10, “Macro String Substitution”.

Examples

The following examples show how to call the SUBST builtin function and demonstrate the expected output.

ALL :
    @ WRITE SYS$OUTPUT "$(SUBST ee,EE,feet on the street)"
    @ WRITE SYS$OUTPUT "$(SUBST EE,ee,feet on the street)"

Description file to demonstrate the SUBST function.

fEEt on the strEEt
feet on the street

The output the above description file would generate.

2.11.30. WARN

Display a Warning Message

$(WARN text[,text[,...]])
$(WARNING text[,text[,...]])

Description

WARN displays each text argument as an MMK warning message, %MMK-W-WARN, one message per argument. MMK carries on, and the function returns an empty string.

WARNING is another name for WARN; the two are the same function.

See also INFO and, to stop the build, ERROR.

Examples

The following examples show how to call the WARN builtin function and demonstrate the expected output.

CHECK ~= $(IF $(DEBUG),$(WARN DEBUG is set: building without optimization))

ALL :
    @ CONTINUE

Description file to demonstrate the WARN function. Run with /MACRO=DEBUG=1.

%MMK-W-WARN, DEBUG is set: building without optimization

The output the above description file would generate.

2.11.31. WILDCARD

File Search

$(WILDCARD text)

Description

WILDCARD searches for the files matching the specification in text and returns the name and type portions as a space delimited list. It is also possible to list multiple specifications in text, each must be separated by whitespace. The wildcard characters '%' and '*' can also be used.

It is possible to specify multiple search specifications by separating them with space.

Examples

The following examples show how to call the WILDCARD builtin function and demonstrate the expected output.

FOO = $(WILDCARD BLISS32_BLD:LEX*.BLI)
BAR = $(WILDCARD BLISS32_BLD:K*.BLI BLISS32_BLD:LEX*.BLI)
ALL :
    @ WRITE SYS$OUTPUT "FOO = $(FOO)"
    @ WRITE SYS$OUTPUT "BAR = $(BAR)"

Description file to demonstrate the WILDCARD function.

FOO = LEXAN.BLI LEXFUN.BLI
BAR = KFOLD.BLI KOST.BLI LEXAN.BLI LEXFUN.BLI

The output the above description file would generate.

2.11.32. WORD

Return the n'th Word

$(WORD n, text)

Description

WORD treats text as a space delimited (multiple consecutive whitespace characters are treated as one) array of words. The parameter n is an index into this array, specifying which word to return.

If you specify a value of n that is less than 1 or greater than the number of elements in text, then the result will be an empty string, "". The builtin function WORDS can be used to determine the upper limit of the array in text.

Examples

The following examples show how to call the WORDS builtin function and demonstrate the expected output.

FOO = $(WORD 2, first second third)
BAR = $(WORD 5, kermit the frog)
ALL :
    @ WRITE SYS$OUTPUT "FOO = $(FOO)"
    @ WRITE SYS$OUTPUT "BAR = $(BAR)"

Description file to demonstrate the WORD function.

FOO = second
BAR =

The output the above description file would generate.

2.11.33. WORDLIST

Extract Words

$(WORDLIST start,end,text[ ...])

Description

WORDLIST treats text as a whitespace delimited list of words. The words, start with start and ending at end (inclusive) are extracted from text and returned as a space delimited list. The first word in text is 1.

Examples

The following examples show how to call the WORDLIST builtin function and demonstrate the expected output.

ALL :
     @ WRITE SYS$OUTPUT "$(WORDLIST 2, 3, FOO BAR BAZ)"

Description file to demonstrate the WORDLIST function.

BAR BAZ

The output the above description file would generate.

2.11.34. WORDS

Count Words

$(WORDS text)

Description

WORDS counts the number of words in text. The contents of text is treated as a space delimited (multiple consecutive whitespace characters are treated as one) array of words.

Examples

The following examples show how to call the WORDS builtin function and demonstrate the expected output.

FOO = $(WORDS first second third)
BAR = $(WORDS )
ALL :
    @ WRITE SYS$OUTPUT "FOO = $(FOO)"
    @ WRITE SYS$OUTPUT "BAR = $(BAR)"

Description file to demonstrate the WORDS function.

FOO = 3
BAR = 0

The output the above description file would generate.