Inference rules generalize the build process, which eliminates the need to give omake an explicit rule for each target. For example, compiling C source (.c files) into object files (.obj files) is a common occurrence. Rather than require a statement that each .obj file depends on a like-named .c file, omake uses an inference rule to infer that dependency. The dependency determined by an inference rule is called the inferred dependency.
Inference rules also provide build scripts to update the target from the inferred dependency. The target inherits these build scripts if it does not have its own.
omake predefines several inference rules, and you can change their definitions or define your own rules.
Inference rules (also called metarules) are identified by the use of the rule character (%) in the dependency line. This character is a wildcard, matching zero or more characters. For example, here is an inference rule for building .obj files from .c files:
%.obj : %.c
$(CC) $(CFLAGS) -c $(.SOURCE)
This rule states that a .obj file can be built from a corresponding .c file with the build-script line $(CC) $(CFLAGS) -c $(.SOURCE). The .c and .obj files share the same root of the file name.
When the dependency and target have the same file name except for their extensions, this rule can be specified in an alternative way:
.c.obj :
$(CC) $(CFLAGS) -c $(.SOURCE)
The alternative form is compatible with other make utilities.
omake predefines the %.obj : %.c inference rule as listed above so the example now becomes much simpler:
OBJS = main.obj io.obj
CC = cc
CFLAGS = /Zi
project.exe : $(OBJS)
link /out:$(.TARGET) $(OBJS)
[ Tp ] % [ Ts ] [ attribute ... ] : [ Sp ] % [ Ss ]
build script
.
.
.
On the target side of the dependency line is a pattern: a target prefix Tp, a %, and a target suffix Ts. Any target attributes (see Target Attributes) appear next. On the dependency side of the line is a dependency prefix Sp, a %, and a dependency suffix Ss. The prefixes can contain any character except % and, in particular, can be a directory. The suffixes can contain any character (including %, which is taken literally).
In order for a rule to match a target, Tp and Ts must match the first and last parts of the target name, with % matching everything in between. The inferred dependency name is Sp followed by the characters matched by %, followed by Ss.
Following the dependency line are the build-script lines that update the target from the inferred dependency. If a target doesn't have its own build scripts, it inherits the build scripts and attributes of the inference rule. Target attributes take precedence over rule attributes.
Inference rules can also be defined in this form:
.source_extension.target_extension :
build script
.
.
.
The source_extension is the extension of the dependency. The target_extension is the extension of the target. This alternative form is compatible with other make utilities and is discussed in Compatibility with Suffix Rules (.SUFFIXES). The suffix-only form is converted by omake to the equivalent metarule form:
%.target_extension : %.source_extension
build script
.
.
.
omake uses inference rules when building a target that has no build scripts. Even when targets have build scripts, you can cause omake to use inference rules by giving the target the .INFER target attribute.
In either case, omake first builds the target's explicit dependencies (those listed on dependency lines), and then uses its inference rules to search for an inferred dependency. The search proceeds by finding all rules that match the target, building each possible inferred dependency name in turn, and checking whether the inferred dependency exists as a file. If it exists, omake proceeds as follows:
If the target has no build scripts, the target inherits the inference-rule build scripts and attributes. When this is a conflict between the attributes, the target's attributes take precedence.
The inferred dependency is added to the target as a dependency.
The inferred dependency is built.
Inference rules can build several targets (a target group) from a single dependency. To do this put the targets on the target side of the rule, with a plus sign (+) between them. (There must be white space between the + and the target names.) When omake executes the rule's build scripts to update any target in the group, all targets are updated.
For example, here is a rule for building both .c and .h files with a yacc program, which takes a .y file as input:
%.h + %.c : %.y
$(YACC) -d $(YFLAGS) $(.SOURCE)
copy y.tab.h $(.TARGET,1)
copy y.tab.c $(.TARGET,2)
del ytab.*
Note the use of $(.TARGET,num), which evaluates to the names of both the .h and .c files. The num macro modifier selects the numth element of the macro. So, for example, $(.TARGET,1) is parse.h when the inferred dependency is parse.y.
When omake tries to find the inferred dependency of a target, it first tries all rules that directly produce the target. If the inferred dependency cannot be found with one rule, omake chains rules into twos, threes, and so on. omake always chooses the smallest number of rules (the shortest path) between a target and its inferred dependency.
If a multiple-rule path is found, omake creates the targets between the inferred dependency and the target, and chains them. Chained targets have special properties:
omake deletes chained targets as part of the build; as soon as it finishes running the build scripts, it deletes any intermediate chained targets. A chained target is not considered a sibling derived object.
For the purposes of time stamp comparisons, a chained target is the same age as the target that depends on it. This allows omake to compare the time stamp of the target at one end of the chain with the time stamp of the inferred dependency at the other end and correctly determine whether the target needs updating.
NOTE: Although the characters matched by % are the same within a single inference rule, the % can match different characters for the different rules of a multiple-rule path.
To illustrate how the deletion of chained targets works, here are rules for extracting a .obj file from a .c file and for compiling the .obj file into a .exe file:
%.obj : %.c
$(CC) $(CFLAGS) -c $(.SOURCE)
%.exe : %.obj
$(LINK) /out:$(.TARGET) $(LINKFLAGS) $(.SOURCE)
Assume that omake is trying to build main.exe and that main.c exists, but main.obj does not. omake finds the two-rule inference that uses the %.obj : %.c rule to produce main.obj, and the %.exe : %.obj rule to produce main.exe. omake retrieves main.c and compiles it to produce main.obj. It then links main.obj to produce main.exe. After running the %.exe and %.obj build-script lines, omake deletes main.obj because it was marked as being chained.
To prevent this deletion, write a rule that combines the rules in the multiple-rule path but that has a shorter path. A one-step rule between main.exe and main.c that leaves behind main.obj is
%.exe : %.c
$(CC) $(CFLAGS) -c $(.SOURCE)
$(LINK) /out:$(.TARGET) $(LINKFLAGS) $(.SOURCE)
A second way to prevent this deletion is to use the .PRECIOUS attribute. omake does not delete targets with this attribute. Modify the %.obj : %.c rule as follows:
%.obj .PRECIOUS : %.c
$(CC) $(CFLAGS) -c $(.SOURCE)
A target with the .NOCHAIN attribute instructs omake to try only the one-step paths for finding the inferred dependency.
A rule with the .NOCHAIN attribute cannot be used in the middle of a multiple-step rule. That is, it means this rule is terminal.
To determine which inference rules to use, omake gathers all rules that can build the current target and sorts them from best score to worst score. For each rule in this sorted list, omake constructs the dependency name and tests whether it exists as a file. If so, omake chooses this rule as the inference rule, and this file as the inferred dependency.
The score is defined as the number of characters in the target name that % matches; the best score is zero. Often, many rules may have the same score (for example, all rules that build %.obj targets), and omake puts these rules in creation order. For identical scoring rules, the rule created first (usually built in to omake or in make.ini) is first on the list.
Sometimes the rule ordering is inappropriate. For example, you may have both video.c and video.cpp that can be used to build video.obj, and you want omake to use video.c. By making video.obj depend on video.c, omake chooses the %.obj : %.c rule to update video.obj:
video.obj : video.c
If you find yourself doing this, you can prevent omake from doing the inference rule search by supplying your own build scripts. For example:
video.obj : video.c
$(CC) $(CFLAGS) -c $(.SOURCE)
One problem with supplying the build scripts explicitly is that they must change if the %.obj : %.c rule changes. To avoid this problem, use the %do directive to execute the build scripts of the %.obj : %.c rule:
video.obj : video.c
%do "%.obj : %.c"
When the target name has a directory component (for example, objs\video.obj), omake uses the following methods to find a rule:
The rules with directory components in their targets are matched against the complete target name.
If no rules match in Step #1, the rules without directory components in their targets are matched against the file name of the target, by default. The .UNIXPATHS directive changes this behavior. When you use .UNIXPATHS, the rules without directory components are matched against the full target name.
These operations on inference rules are performed most often:
Redefining an inference rule
Inference rules can be redefined any number of times. To redefine an inference rule, provide a new definition in one of two places:
In make.ini, to give every makefile access to the rule
In your makefile, if you want a local variant
Changing an inference rule's attributes
To change an inference rule's attributes, use a dependency line with the new attributes and don't specify a build script. These attributes are combined with the current attributes.
Disabling an inference rule
To disable an inference rule, use a dependency line with no attributes and no build script:
%.obj : %.c
...or...
.c.obj :
Disabling the search for an inferred dependency
omake searches for an inferred dependency for targets that have no build script. You can omit the inference search with the .NOINFER attribute. For example:
all .NOINFER : import test export
The all target only drives the build of its dependencies, so no inference search is needed.
Using % in nonrules
A % on the target side of a dependency line indicates that this line is an inference rule. To use a literal % without indicating a rule, give the target the .NORULE attribute:
percent%.obj .NORULE : percent%.c
Changing the rule character
The .RULE_CHAR directive sets the rule character. For example, to change it to an asterisk:
.RULE_CHAR : *
omake predefines several rules. You can reject them by using the -r (reject rules) command-line option or the .REJECT_RULES directive. The built-in rules are listed in Inference Rules.
Many make utilities, such as PM/CB, NMAKE, and Borland Make, have a simple style of inference rule, called suffix rules. These rules use only the suffix (extension) of the file name. Here is a suffix rule:
.c.obj :
$(CC) $(CFLAGS) -c $<
Some make utilities also use a .SUFFIXES directive to control the search order of the suffix rules. The .SUFFIXES directive lists the suffixes in the order that inference rules are to be attempted. Each appearance of .SUFFIXES prepends to the current list of suffixes, but a .SUFFIXES directive with nothing on the dependency side clears the list:
.SUFFIXES : |
(clears list) |
.SUFFIXES : .lib |
(list is: .for .lib) |
.SUFFIXES : .exe .obj |
(list is: .exe .obj .lib) |
When looking for an inference rule to build a target, the list is traversed from left to right with each extension being combined with the target extension to form the name of a suffix rule. If that suffix rule exists, omake forms the name of the corresponding inferred dependency and, if it exists, the inference search is complete.
omake converts suffix rules into equivalent omake inference rules. If .SUFFIXES is used, omake uses the suffixes order to sequence only its suffix rules. Nonsuffix rules are left in creation order. By default, all inference rules are in creation order. Note that a .SUFFIXES directive with no suffixes on the dependency side effectively disables all suffix rules, but nonsuffix rules remain enabled.
Feedback on the documentation in this site? We welcome any comments!
Copyright © 2001 by Rational Software Corporation. All rights reserved. |