omake-rules(1)
NAME
- omake is a flexible build system designed for building a
- wide variety of projects. This document describes the syntax and
- semantics of rule definitions, which specify commands to build
- files in a project. For an overview of omake, see the omake(1)
- man page.
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 <de
- pendencies> are a list of files that are needed before the <tar
- get> can be built. The <commands> are a list of indented lines
- specifying commands to build the target. For example, the follow
- ing 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 hel
- lo.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 indi
- vidual 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 ex
- panded 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).
- IMPLICIT RULES
- 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).
- BOUNDED IMPLICIT RULES
- 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- SECTION
- 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:sectionFP = fopen(hello.c, w)
fprintln($(FP), $""#include <stdio.h> intmain() { printf("Hello world0); }"")
close($(FP)) - This example uses the quotation $""..."" 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 dis
- cussed in the IO section.
- In addition, commands that are function calls, or special
- expressions, are interpreted correctly. Since the fprintln func
- tion can take a file directly, the above rule can be abbreviated
- as follows.
hello.c:fprintln($@, $""#include <stdio.h> int main() {printf("Hello world0); }"")- SECTION RULE
- 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 ruleif $(target-exists a.c)hello.c: a.ccat a.c > hello.celsehello.c: default.ccp default.c hello.c
SPECIAL DEPENDENCIES
- :EXISTS:
- 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: .flagif $(test -e .flag)$(CP) a.c $@ - :EFFECTS:
- Some commands produce files by side-effect. For example,
- the latex(1) command produces a .aux file as a side-effect of
- producing 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.auxlatex paper - :VALUE:
- 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 de
- pendencies. For example, the following rule:
a: b :exists: ccommands- is the same as
a: b :value: $(target-exists c)commands- Notes:
- * The values are arbitrary (they are not limited to
- variables)
- * The values are evaluated at rule expansion time, so
- expressions containing variables like $@, $^, etc are legal.
- Scanner rules define a way to specify automatic dependency
- scanning. A .SCANNER rule has the following form.
.SCANNER: target: dependenciescommands- The rule is used to compute additional dependencies that
- might be defined in the source files for the specified target.
- The scanner produces dependencies for the specified target (which
- may be a pattern) by running the commands, which must produce
- output that is compatible with omake. For example, on GNU sys
- tems 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: %.cgcc -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/in
- clude/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, estab
- lishing further dependencies. What we need is to re-run the scan
- ner 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 com
- putes 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 de
- pendency (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 sev
- eral versions of the file foo.h, and the one that is chosen de
- pends on the include path. What we need is to base the dependen
- cies 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)) $<
- NAMED SCANNERS, AND THE :SCANNER: TARGET
- Sometimes 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 gen
- eral, the target of a .SCANNER is not tied to a particular tar
- get, and we may name it as we like.
.SCANNER: scan-gcc-%.c: %.c :value: $(digest-in-pathoptional $(INCLUDES), $&)gcc -MM $(addprefix -I, $(INCLUDES)) $<.SCANNER: scan-cl-%.c: %.c :value: $(digest-in-pathoptional $(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-%cgcc ...- $(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.
- NOTES
- 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. See the documentation for the SCANNER_MODE
- variable in omake-root(1) for detail.
- 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.
OTHER SPECIAL TARGETS
- There are several other special targets that define spe
- cial actions to be take by omake.
- .DEFAULT
- The .DEFAULT target specifies a target to be built by de
- fault if omake is run without explicit targets. The following
- rule instructs omake to build the program hello by default
.DEFAULT: hello - .SUBDIRS
- The .SUBDIRS target is used to specify a set of subdirec
- tories 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 OMakefiles in each of the
- src, doc, and tests directories should be read.
- In some cases, especially when the OMakefiles 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 src3println(Subdirectory $(CWD))
.DEFAULT: lib.aIn this case, the src1, src2, and src3 files do not needOMakefiles. Furthermore, if one exists, it is ignored. The following includes the file if it exists.
.SUBDIRS: src1 src2 src3if $(file-exists OMakefile)include OMakefile.DEFAULT: lib.a - .INCLUDE
- The .INCLUDE target is like the include directive, but it
- specifies a rule to build the file if it does not exist.
.INCLUDE: configecho "CONFIG_READ = true" > configecho CONFIG_READ is $(CONFIG_READ) - .PHONY
- 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 in
- stall 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: installinstall: myprogram.execp myprogram.exe /usr/bin
RULE SCOPING
- 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.
- * First, if there is an explicit rule for building
- dir/foo.o (a rule with no wildcards), the context for that rule
- determines the scope for building the target.
- * Otherwise, the directory dir/ must be part of the
- project. This normally means that a configuration file dir/OMake
- file 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
sectionCFLAGS += -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).
- SCOPING OF IMPLICIT RULES
- Implicit rules (rules containing wildcard patterns) are
- not global, they follow the normal scoping convention. This al
- lows different parts of a project to have different sets of im
- plicit rules. If we like, we can modify the example above to pro
- vide 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 on
- ly for the target hello_lib.o; the target hello_code.o is built
- as normal.
- SCOPING OF .SCANNER RULES
- Scanner 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
sectionCFLAGS += -DLIBRARY
hello_lib.o: hello_lib.c :scanner: scan-c-hello_lib.c$(CC) $(CFLAGS) -c $<# Compile hello_code.c with CFLAGS = -g -O3
sectionCFLAGS += -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.
- SCOPING FOR .PHONY TARGETS
- Phony 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: hellohello: hello_code.o hello_lib.o$(CC) $(CFLAGS) -o $@ $+.PHONY: all - This doesn't work as expected because the .PHONY declara
- tion 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: allall: hellohello: 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 sbdirectories, and 2)
- you can build them from the project root.
.PHONY: all install clean- .SUBDIRS: src lib clib
REFERENCES
- SEE ALSO
- omake(1), omake-quickstart(1), omake-options(1), omake
- root(1), omake-language(1), omake-shell(1), omake-rules(1),
- omake-base(1), omake-system(1), omake-pervasives(1), osh(1),
- make(1)
- VERSION
- Version: 0.9.6.9 of April 11, 2006.
- LICENSE AND COPYRIGHT
- (C)2003-2006, Mojave Group, Caltech
- This program is free software; you can redistribute it
- and/or modify it under the terms of the GNU General Public Li
- cense as published by the Free Software Foundation; either ver
- sion 2 of the License, or (at your option) any later version.
- This program is distributed in the hope that it will be
- useful, but WITHOUT ANY WARRANTY; without even the implied war
- ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- the GNU General Public License for more details.
- You should have received a copy of the GNU General Public
- License along with this program; if not, write to the Free Soft
- ware Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- AUTHOR
- Jason Hickey et. al..br Caltech 256-80
Pasadena, CA 91125, USA
Email: omake-devel@metaprl.org WWW: http://www.cs.caltech.edu/~jyh - Build Tools April 11, 2006