Jump to: | | OMake Home • Guide Home • Guide (single-page) • Contents (short) • Contents (long) |
Index: | | All • Variables • Functions • Objects • Targets • Options |
.SCANNER
rules
Rules are used by OMake to specify how to build files. At its simplest, a rule has the following form.
<target>: <dependencies> <commands>
The <target>
is the name of a file to be built. The <dependencies>
are a list of
files that are needed before the <target>
can be built. The <commands>
are a list of
indented lines specifying commands to build the target. For example, the following rule specifies
how to compile a file hello.c
.
hello.o: hello.c $(CC) $(CFLAGS) -c -o hello.o hello.c
This rule states that the hello.o file depends on the hello.c file. If the
hello.c file has changed, the command $(CC) $(CFLAGS) -c -o hello.o hello.c
is to
be executed to update the target file hello.o
.
A rule can have an arbitrary number of commands. The individual command lines are executed independently by the command shell. The commands do not have to begin with a tab, but they must be indented from the dependency line.
In addition to normal variables, the following special variables may be used in the body of a rule.
$*
: the target name, without a suffix.
$@
: the target name.
$^
: a list of the sources, in alphabetical order, with
duplicates removed.
$+
: all the sources, in the original order.
$<
: the first source.
For example, the above hello.c
rule may be simplified as follows.
hello.o: hello.c $(CC) $(CFLAGS) -c -o $@ $<
Unlike normal values, the variables in a rule body are expanded lazily, and binding is dynamic. The following function definition illustrates some of the issues.
CLibrary(name, files) = OFILES = $(addsuffix .o, $(files)) $(name).a: $(OFILES) $(AR) cq $@ $(OFILES)
This function defines a rule to build a program called $(name)
from a list of .o
files. The files in the argument are specified without a suffix, so the first line of the function
definition defines a variable OFILES
that adds the .o
suffix to each of the file
names. The next step defines a rule to build a target library $(name).a
from the
$(OFILES)
files. The expression $(AR)
is evaluated when the function is called, and
the value of the variable AR
is taken from the caller’s scope (see also the section on
Scoping).
Rules may also be implicit. That is, the files may be specified by wildcard patterns.
The wildcard character is %
. For example, the following rule specifies a default
rule for building .o
files.
%.o: %.c $(CC) $(CFLAGS) -c -o $@ $*.c
This rule is a template for building an arbitrary .o
file from
a .c
file.
By default, implicit rules are only used for the targets in the current
directory. However subdirectories included via the .SUBDIRS
rules
inherit all the implicit rules that are in scope (see also the section on
Scoping).
Implicit rules may specify the set of files they apply to. The following syntax is used.
<targets>: <pattern>: <dependencies> <commands>
For example, the following rule applies only to the files a.o
and b.o
.
a.o b.o: %.o: %.c $(CC) $(CFLAGS) -DSPECIAL -c $*.c
Frequently, the commands in a rule body are expressions to be evaluated by the shell. omake also allows expressions to be evaluated by omake itself.
The syntax of these “computed rules” uses the section
expression. The following rule uses
the omake IO functions to produce the target hello.c
.
hello.c: section FP = fopen(hello.c, w) fprintln($(FP), $""#include <stdio.h> int main() { printf("Hello world\n"); }"") close($(FP))
This example uses the quotation $""...""
(see also Section B.1.6) to quote the text being
printed. These quotes are not included in the output file. The fopen
, fprintln
, and
close
functions perform file IO as discussed in the IO section.
In addition, commands that are function calls, or special expressions, are interpreted correctly.
Since the fprintln
function can take a file directly, the above rule can be abbreviated as
follows.
hello.c: fprintln($@, $""#include <stdio.h> int main() { printf("Hello world\n"); }"")
Rules can also be computed using the section rule
form, where a rule body is expected instead
of an expression. In the following rule, the file a.c
is copied onto the hello.c
file
if it exists, otherwise hello.c
is created from the file default.c
.
hello.c: section rule if $(target-exists a.c) hello.c: a.c cat a.c > hello.c else hello.c: default.c cp default.c hello.c
In an implicit rule, any special variables, for example the one for the current target ($@
)
or the first prerequisite ($<
) are undefined; substitute $(file TARGET-PATTERN)
and
$(file SOURCE-PATTERN)
:
%.foo: %.bar section rule println($"target: $(file %.foo)") println($"prereq: $(file %.bar)") /usr/bin/printf "prerequisite was $(file %.bar)\n" > $(file %.foo)
However, if the section rule
defines its own rule heads, all special variables work as usual
with respect to their rule heads:
%.cmx: %.ml section rule if $(target-exists %.mli) %.cmx %.o: %.ml %.cmi :scanner: scan-ocaml-%.ml $(OCamlOpt) -c $< elseif $(BYTE_ENABLED) %.cmx %.cmi %.o %.cmo: %.ml :scanner: scan-ocaml-%.ml $(OCamlC) -c $< $(OCamlOpt) -c $< else %.cmx %.cmi %.o: %.ml :scanner: scan-ocaml-%.ml $(OCamlOpt) -c $<
Some commands produce files by side-effect. For example, the
latex(1) command produces an .aux
file as a side-effect
of generating a .dvi
file. In this case, the
:effects:
qualifier can be used to list the side-effect
explicitly. omake is careful to avoid simultaneously running
programs that have overlapping side-effects.
paper.dvi: paper.tex :effects: paper.aux latex paper
In some cases, the contents of a dependency do not matter, only
whether the file exists or not. In this case, the
:exists:
qualifier can be used for the dependency.
foo.c: a.c :exists: .flag if $(test -e .flag) $(CP) a.c $@
In contrary to a :normal:
dependency the target of an
:exists:
prerequisite must be rebuilt if the specified file
appears or disappears, but not if its contents changes. In particular
a target can be built without any prerequisites marked with
:exists:
.
See section 4.15.1.2.
The keyword :normal:
switches back to normal dependency
processing after it may have been altered by any of the other special
dependency modifiers.
:optional:
dependencies are similar to
:exists:
dependencies in that they cause a rebuild if they
appear or disappear. If they exist, however, they take part in the
normal comparison with the target (opposite to
:squash:
dependencies).
:scanner:
indicates that the subsequent prerequisites are the
names of .SCANNER
rules for the target to be built. See
section 8.6.
A :squash:
dependency is only active once to build the target
then it is “squashed” which means, if the prerequisite changes after
the target was built the target will not be considered
out-of-date.
The :value:
dependency is used to specify that the rule
execution depends on the value of an expression. For example, the
following rule
a: b c :value: $(X) ...
specifies that “a” should be recompiled if the value of
$(X)
changes (X
does not have to be a filename). This
is intended to allow greater control over dependencies.
In addition, it can be used instead of other kinds of dependencies. For example, the following rule:
a: b :exists: c commands
is the same as
a: b :value: $(target-exists c) commands
Notes:
$@
, $^
, etc. are legal.
.SCANNER
rulesScanner rules define a way to specify automatic dependency scanning. A .SCANNER
rule has the
following form.
.SCANNER: target: dependencies commands
The rule is used to compute additional dependencies that might be defined in the source files for
the specified target. The result of executing the scanner commands must be a sequence of
dependencies in OMake format, printed to the standard output. For example, on GNU systems the
gcc -MM foo.c
produces dependencies for the file foo.c
(based on #include
information).
We can use this to specify a scanner for C files that adds the scanned dependencies for the
.o
file. The following scanner specifies that dependencies for a file, say foo.o
can
be computed by running gcc -MM foo.c
. Furthermore, foo.c
is a dependency, so the
scanner should be recomputed whenever the foo.c
file changes.
.SCANNER: %.o: %.c gcc -MM $<
Let’s suppose that the command gcc -MM foo.c
prints the following line.
foo.o: foo.h /usr/include/stdio.h
The result is that the files foo.h
and /usr/include/stdio.h
are considered to be
dependencies of foo.o
—that is, foo.o
should be rebuilt if either of these files
changes.
This works, to an extent. One nice feature is that the scanner will be re-run whenever the
foo.c
file changes. However, one problem is that dependencies in C are recursive.
That is, if the file foo.h
is modified, it might include other files, establishing further
dependencies. What we need is to re-run the scanner if foo.h
changes too.
We can do this with a value dependency. The variable $&
is defined as the dependency
results from any previous scan. We can add these as dependencies using the digest
function,
which computes an MD5 digest of the files.
.SCANNER: %.o: %.c :value: $(digest $&) gcc -MM $<
Now, when the file foo.h
changes, its digest will also change, and the scanner will be re-run
because of the value dependency (since $&
will include foo.h
).
This still is not quite right. The problem is that the C compiler uses a search-path for
include files. There may be several versions of the file foo.h
, and the one that is chosen
depends on the include path. What we need is to base the dependencies on the search path.
The $(digest-in-path-optional ...)
function computes the digest based on a search path,
giving us a solution that works.
.SCANNER: %.o: %.c :value: $(digest-in-path-optional $(INCLUDES), $&) gcc -MM $(addprefix -I, $(INCLUDES)) $<
The standard output of the scanner rules will be captured by OMake and is not allowed to contain any
content that OMake will not be able to parse as a dependency. The output is allowed to contain
dependency specifications for unrelated targets, however such dependencies will be ignored. The
scanner rules are allowed to produce arbitrary output on the standard error channel — such output
will be handled in the same way as the output of the ordinary rules (in other words, it will be
presented to the user, when dictated by the --output-
… options enabled).
Additional examples of the .SCANNER
rules can be found in Section 3.5.3.
:scanner:
dependenciesSometimes it may be useful to specify explicitly which scanner should be used in a rule. For
example, we might compile .c
files with different options, or (heaven help us) we may be
using both gcc
and the Microsoft Visual C++ compiler cl
. In general, the target of a
.SCANNER
is not tied to a particular target, and we may name it as we like.
.SCANNER: scan-gcc-%.c: %.c :value: $(digest-in-path-optional $(INCLUDES), $&) gcc -MM $(addprefix -I, $(INCLUDES)) $< .SCANNER: scan-cl-%.c: %.c :value: $(digest-in-path-optional $(INCLUDES), $&) cl --scan-dependencies-or-something $(addprefix /I, $(INCLUDES)) $<
The next step is to define explicit scanner dependencies. The :scanner:
dependency is used
for this. In this case, the scanner dependencies are specified explicitly.
$(GCC_FILES): %.o: %.c :scanner: scan-gcc-%.c gcc ... $(CL_FILES): %.obj: %.c :scanner: scan-cl-%.c cl ...
Explicit :scanner:
scanner specification may also be used to state that a single
.SCANNER
rule should be used to generate dependencies for more than one target. For example,
.SCANNER: scan-all-c: $(GCC_FILES) :value: $(digest-in-path-optional $(INCLUDES), $&) gcc -MM $(addprefix -I, $(INCLUDES)) $(GCC_FILES) $(GCC_FILES): %.o: %.c :scanner: scan-all-c ...
The above has the advantage of only running gcc once and a disadvantage that when a single source file changes, all the files will end up being re-scanned.
In most cases, you won’t need to define scanners of your own. The standard installation includes default scanners (both explicitly and implicitly named ones) for C, OCaml, and LATEX files.
The SCANNER_MODE
variable controls the usage of implicit scanner dependencies.
The explicit :scanner:
dependencies reduce the chances of scanner mis-specifications. In
large complicated projects it might be a good idea to set SCANNER_MODE
to error
and
use only the named .SCANNER
rules and explicit :scanner:
specifications.
The .DEFAULT
target specifies a target to be built by default
if omake is run without explicit targets. The following rule
instructs omake to build the program hello
by default
.DEFAULT: hello
The .SUBDIRS
target is used to specify a set of subdirectories
that are part of the project. Each subdirectory should have its own
OMakefile, which is evaluated in the context of the current
environment.
.SUBDIRS: src doc tests
This rule specifies that the OMakefile
s in each of the src
, doc
, and
tests
directories should be read.
In some cases, especially when the OMakefile
s are very similar in a large number of
subdirectories, it is inconvenient to have a separate OMakefile
for each directory. If the
.SUBDIRS
rule has a body, the body is used instead of the OMakefile
.
.SUBDIRS: src1 src2 src3 println(Subdirectory $(CWD)) .DEFAULT: lib.a
In this case, the src1
, src2
, and src3
files do not need OMakefile
s.
Furthermore, if one exists, it is ignored. The following includes the file if it exists.
.SUBDIRS: src1 src2 src3 if $(file-exists OMakefile) include OMakefile .DEFAULT: lib.a
The .INCLUDE
target is like the include
directive, but it specifies a rule to build
the file if it does not exist.
.INCLUDE: config echo "CONFIG_READ = true" > config echo CONFIG_READ is $(CONFIG_READ)
You may also specify dependencies to an .INCLUDE
rule.
.INCLUDE: config: config.defaults cp config.defaults config
A word of caution is in order here. The usual policy is used for determining when the rule is out-of-date. The rule is executed if any of the following hold.
In some of the cases, this will mean that the rule is executed even if the target file already exists. If the target is a file that you expect to edit by hand (and therefore you don’t want to overwrite it), you should make the rule evaluation conditional on whether the target already exists.
.INCLUDE: config: config.defaults # Don't overwrite my carefully hand-edited file if $(not $(file-exists config)) cp config.defaults config
A “phony” target is a target that is not a real file, but exists to collect a set of dependencies.
Phony targets are specified with the .PHONY
rule. In the following example, the
install
target does not correspond to a file, but it corresponds to some commands that should
be run whenever the install
target is built (for example, by running omake install
).
.PHONY: install install: myprogram.exe cp myprogram.exe /usr/bin
As we have mentioned before, omake is a scoped language. This provides great
flexibility—different parts of the project can define different configurations without interfering
with one another (for example, one part of the project might be compiled with CFLAGS=-O3
and
another with CFLAGS=-g
).
But how is the scope for a target file selected? Suppose we are building a file dir/foo.o
.
omake uses the following rules to determine the scope.
dir/foo.o
(a rule with no
wildcards), the context for that rule determines the scope for building the target.
dir/
must be part of the project. This normally means that a
configuration file dir/OMakefile
exists (although, see the .SUBDIRS
section for
another way to specify the OMakefile
). In this case, the scope of the target is the scope at
the end of the dir/OMakefile
.
To illustrate rule scoping, let’s go back to the example of a “Hello world” program with two
files. Here is an example OMakefile
(the two definitions of CFLAGS
are for
illustration).
# The executable is compiled with debugging CFLAGS = -g hello: hello_code.o hello_lib.o $(CC) $(CFLAGS) -o $@ $+ # Redefine CFLAGS CFLAGS += -O3
In this project, the target hello
is explicit. The scope of the hello
target
is the line beginning with hello:
, where the value of CFLAGS
is -g
. The other
two targets, hello_code.o
and hello_lib.o
do not appear as explicit targets, so their
scope is at the end of the OMakefile
, where the CFLAGS
variable is defined to be
-g -O3
. That is, hello
will be linked with CFLAGS=-g
and the .o
files
will be compiled with CFLAGS=-g -O3
.
We can change this behavior for any of the targets by specifying them as explicit targets. For
example, suppose we wish to compile hello_lib.o
with a preprocessor variable LIBRARY
.
# The executable is compiled with debugging CFLAGS = -g hello: hello_code.o hello_lib.o $(CC) $(CFLAGS) -o $@ $+ # Compile hello_lib.o with CFLAGS = -g -DLIBRARY section CFLAGS += -DLIBRARY hello_lib.o: # Redefine CFLAGS CFLAGS += -O3
In this case, hello_lib.o
is also mentioned as an explicit target, in a scope where
CFLAGS=-g -DLIBRARY
. Since no rule body is specified, it is compiled using the usual
implicit rule for building .o
files (in a context where CFLAGS=-g -DLIBRARY
).
Implicit rules (rules containing wildcard patterns) are not global, they follow the normal
scoping convention. This allows different parts of a project to have different sets of implicit
rules. If we like, we can modify the example above to provide a new implicit rule for building
hello_lib.o
.
# The executable is compiled with debugging CFLAGS = -g hello: hello_code.o hello_lib.o $(CC) $(CFLAGS) -o $@ $+ # Compile hello_lib.o with CFLAGS = -g -DLIBRARY section %.o: %.c $(CC) $(CFLAGS) -DLIBRARY -c $< hello_lib.o: # Redefine CFLAGS CFLAGS += -O3
In this case, the target hello_lib.o
is built in a scope with a new implicit rule for
building %.o
files. The implicit rule adds the -DLIBRARY
option. This implicit rule
is defined only for the target hello_lib.o
; the target hello_code.o
is built as
normal.
.SCANNER
rulesScanner rules are scoped the same way as normal rules. If the .SCANNER
rule is explicit
(containing no wildcard patterns), then the scope of the scan target is the same as the the rule.
If the .SCANNER
rule is implicit, then the environment is taken from the :scanner:
dependency.
# The executable is compiled with debugging CFLAGS = -g hello: hello_code.o hello_lib.o $(CC) $(CFLAGS) -o $@ $+ # scanner for .c files .SCANNER: scan-c-%.c: %.c $(CC) $(CFLAGS) -MM $< # Compile hello_lib.o with CFLAGS = -g -DLIBRARY section CFLAGS += -DLIBRARY hello_lib.o: hello_lib.c :scanner: scan-c-hello_lib.c $(CC) $(CFLAGS) -c $< # Compile hello_code.c with CFLAGS = -g -O3 section CFLAGS += -O3 hello_code.o: hello_code.c :scanner: scan-c-hello_code.c $(CC) $(CFLAGS) -c $<
Again, this is for illustration—it is unlikely you would need to write a complicated configuration
like this! In this case, the .SCANNER
rule specifies that the C-compiler should be called
with the -MM
flag to compute dependencies. For the target hello_lib.o
, the scanner
is called with CFLAGS=-g -DLIBRARY
, and for hello_code.o
it is called with
CFLAGS=-g -O3
.
.PHONY
targetsPhony targets (targets that do not correspond to files) are defined with a .PHONY:
rule.
Phony targets are scoped as usual. The following illustrates a common mistake, where the
.PHONY
target is declared after it is used.
# !!This example is broken!! all: hello hello: hello_code.o hello_lib.o $(CC) $(CFLAGS) -o $@ $+ .PHONY: all
This doesn’t work as expected because the .PHONY
declaration occurs too late. The proper way
to write this example is to place the .PHONY
declaration first.
# Phony targets must be declared before being used .PHONY: all all: hello hello: hello_code.o hello_lib.o $(CC) $(CFLAGS) -o $@ $+
Phony targets are passed to subdirectories. As a practical matter, it is wise to declare all
.PHONY
targets in your root OMakefile
, before any .SUBDIRS
. This will ensure
that 1) they are considered as phony targets in each of the subdirectories, and 2) you can build them
from the project root.
.PHONY: all install clean .SUBDIRS: src lib clib
Note that when a .PHONY
target is inherited by a subdirectory via a .SUBDIRS
, a whole
hierarchy of .PHONY
targets (that are a part of the global one) is created, as described in
Section 8.12.2 below.
Running omake foo
asks OMake to build the file foo
in context of the whole
project, even when running from a subdirectory of the project. Therefore, if bar/baz
is a
regular target (not a .PHONY
one), then running omake bar/baz
and running
(cd bar; omake baz)
are usually equivalent.
There are two noteworthy exceptions to the above rule:
.SUBDIRS
) for it, then
OMake will complain if you try to run it in that directory.
OMakeroot
of its own, this would designate
the subdirectory as a separate project (which is usually a bad idea and is not recommended).
Suppose you have a .PHONY: clean
declared in your root OMakefile
and
both the root OMakefile
and the OMakefile
in some of the subdirectories contain
clean:
rules. In this case
omake clean
in the root directory will execute all the rules (each in the
appropriate directory);
omake clean
in the subdirectory will execute just its local one, as well as the
ones from the subdirectories of the current directory.
The above equally applies to the built-in .PHONY
targets, including .DEFAULT
.
Namely, if OMake is executed (without argument) in the root directory of a project, all the
.DEFAULT
targets in the project will be built. On the other hand, when OMake is executed
(without argument) in a subdirectory, only the .DEFAULT
targets defined in and under that
subdirectory will be built.
The following Section explains the underlying semantics that gives rise to the above behavior.
.PHONY
targetsWhen the the root OMakefile
contains a .PHONY: clean
directive, it creates:
/.PHONY/clean
(note the leading “/
”);
.PHONY/clean
(note
the lack of the leading “/
”);
/.PHONY/clean: .PHONY/clean
.
All the clean: ...
rules in the root OMakefile
following this .PHONY: clean
declaration would be interpreted as rules for the .PHONY/clean
target.
Now when OMake then comes across a .SUBDIRS: foo
directive (when it is in scope of the above
.PHONY: clean
declaration), it does the following:
.PHONY/foo/clean
“relative” phony target;
.PHONY/clean: .PHONY/foo/clean
;
.SUBDIRS: foo
directive, or reads the foo/OMakefile
file, if the body is empty. While doing that, it interprets its instructions relative to the
foo
directory. In particular, all the clean: ...
rules will be taken to apply to
.PHONY/foo/clean
.
Now when you run omake clean
in the root directory of the project, it is interpreted as
omake .PHONY/clean
(similar to how it happens with the normal targets), so both the rules for
.PHONY/clean
are executed and the rules for its dependency
.PHONY/foo/clean
. Running (cd foo; omake clean)
is, as for normal targets, equivalent to running
omake .PHONY/foo/clean
and only those rules that apply to .PHONY/foo/clean
will be executed.
In rules, the targets and dependencies are first translated to file values (as in the
file
function). They are then translated to strings for the command line.
This can cause some unexpected behavior. In the following example, the absname
function
is the absolute pathname for the file a
, but the rule still prints
the relative pathname.
.PHONY: demo demo: $(absname a) echo $< # omake demo a
There is arguably a good reason for this. On Win32 systems, the /
character is viewed as an
“option specifier.” The pathname separator is the \
character. OMake translates the
filenames automatically so that things work as expected on both systems.
demo: a/b echo $< # omake demo (on a Unix system) a/b # omake demo (on a Win32 system) a\b
Sometimes you may wish that target strings to be passed literally to the commands in the rule. One way to do this is to specify them literally.
SRC = a/b $(absname c/d) demo: $(SRC) echo $(SRC) # omake demo (on a Win32 system) a/b c:\...\c\d
Alternately, you might wish that filenames be automatically expanded to absolute pathnames. For
example, this might be useful when parsing the OMake output to look for errors. For this, you can
use the --absname
option (Section A.3.20). If you call omake
with the
--absname
option, all filenames will be expanded to absolute names.
# omake --absname demo (on a Unix system) /home/.../a/b /home/.../c/d
Alternately, the --absname
option is scoped. If you want to use it for only a few rules, you
can use the OMakeFlags
function to control how it is applied.
section OMakeFlags(--absname) demo: a echo $< # omake demo /home/.../a
N.B. The --absname
option is currently an experimental feature.
Jump to: | | OMake Home • Guide Home • Guide (single-page) • Contents (short) • Contents (long) |
Index: | | All • Variables • Functions • Objects • Targets • Options |