Any expression of the format $(name arg1 arg2 arg3) or $(name) where
name is not the name of a variable, is interpreted as a function call. The
name may contain letters, underscores, or hyphens; to avoid confusion, you may
use hyphens or underscores interchangeably, since internally hyphens are
converted to underscores. Evaluating such an expression simply invokes a perl
subroutine. You can define perl subroutines to do whatever you like. See the
sub statement and the section on extending makepp for
more details.
makepp has a number of builtin functions which may be useful. It supports almost all of GNU make's textual functions (see GNU make's documentation for details), and some of its own. The most useful functions are:
$(absolute_filename xyz.c) might return
/usr/src/our_project/subdir/xyz.c.
MODULES := a b c d
X_OLD_STYLE := $(addsuffix $(OBJDIR)/, $(addsuffix .o, $(MODULES)))
X_NEW_STYLE := $(OBJDIR)/$(MODULES).o # Isn't that easier to read?
X_OLD_STYLE := $(addsuffix .o, $(MODULES))
X_NEW_STYLE := $(MODULES).o
$(basename myfile/version-1.0-module.c) is
myfile/version-1.0-module
./ if there is no directory in the filename.
*, ?, and [a-z] are
recognized), or they may have a % character, which means to match any
string at that point (same as *).
*, ?, and [a-z]
are recognized), or they may have a % character, which means to match any
string at that point (same as *).
For example:
libproduction.a: $(filter_out test_*, $(wildcard *.o))
will put all .o files which exist or can be built, except those beginning with test_, into libproduction.a.
For example, if you have a project with many levels of subdirectories, you
could include this common fragment in all of the makefiles (e.g., by using the
include statement):
TOP_LEVEL_INCLUDE_DIR := $(find_upwards includes)
# Searches for a directory that contains the
# includes subdirectory.
%.o : %.c
$(CC) $(CFLAGS) -I$(TOP_LEVEL_INCLUDE_DIR) -c $(input) -o $(output)
(Note that the include statement automatically searches upwards for files,
so there is no need to do something like this:
include $(find_upwards top_level_rules.mk)
Instead, you can just do
include top_level_rules.mk
and it will work just as well.)
ifeq, etc., statements. If the string is not blank
(i.e., the condition is true), the second argument (the then clause) is
returned (after variable expansion); if the string is blank, the third
argument (the else clause) is returned.
For example,
CFLAGS := $(if $(filter gcc egcc, $(CC)), -g -Wall, -g)
defines CFLAGS to be -g -Wall if the variable CC is either gcc or
egcc, and -g otherwise. (This is what the default build rules do.)
$(infer_objects object1.o object2.o, *.o)
If you use standard conventions regarding header file names, makepp is capable
of guessing which .o or .lo files need to be linked with your program.
I use this to pick out files from a library directory which contains modules
used in many different programs. Instead of making a library .a file and
having the linker pick out the relevant modules, makepp can pick out the
relevant modules for you. This way, only the relevant modules get compiled.
Makepp's algorithm for inferring object dependencies depends on the convention
that the implementation of all classes or functions defined in a header file
xyz.h are compiled into an object file called xyz.o (or xyz.lo). So
makepp's algorithm for inferring object dependencies starts with one or a few
objects that we know have to be linked into the program. It looks at which
files were included with #include in those sources, and tries to find
corresponding object files for each of the include files.
$(infer_objects ) needs to be mentioned in the dependency list of a
program, like this:
myprog: $(infer_objects main.o another_object.o, \
**/*.o /other/library/dirs/**/*.o)
$(CXX) $(inputs) -o $(output) $(LIBS)
The infer_objects function takes two arguments (separated by a comma, as
shown). The first is one or a few object files that are known to be required
(wildcards are permissible here). The second is a list of possible objects
(normally you would use a wildcard here) that could be linked in if necessary.
The return value from this function is a list that contains first all of the
objects in the first argument, and then after those, all additional objects
that were contained in the second argument that are required by the objects in
the first argument.
For example, suppose main.o comes from main.cpp, which includes
my_class.h. infer_objects looks for files with the name my_class.o.
If exactly one such file is found, it is added to the list. (If two object
files my_class.o are found in different directories, a warning message is
printed.) infer_objects also examines my_class.cpp to see what it
includes, and what additional object files are implied.
**) in the filenames. (See the
$(wildcard ) function for more details. This can be used for a
clean target, for example:
.PHONY: clean
clean:
rm -f $(only_targets *)
Now if you type makepp clean, it will delete everything it knows how to
build.
Another place where it may be useful is to avoid including stale .o files in your build. For example, if you build a library like this:
mylib.a: *.o
$(RM) -f $(output)
$(AR) cr $(output) $(inputs)
and then you delete some source files but forget to delete the corresponding .o files, the .o files will still be around. This means they will still be incorporated into the library despite the fact that they are not useful any more. If you modify your rule like this:
mylib.a: $(only_targets *.o)
$(RM) -f $(output)
$(AR) cr $(output) $(inputs)
then this problem won't occur.
Note that this refers only to files that are known to be targets at
the time you invoke only-targets. If only_targets appears in the
dependencies or actions of a rule, then all possible targets will be
known because dependencies and actions are not evaluated until the rule
is executed. However, if you evaluate try to evaluate it earlier in the
makefile with a := variable like this:
ALL_TARGETS := $(only_targets *)
target1: dependency1
actions
target2: dependency2
actions
then only_targets will not know about the subsequent rules.
Similarly, only_targets doesn't know about targets produced in
makefiles that are loaded with recursive make. (But you shouldn't be
using recursive make anyway; use use the load_makefile statement, or
implicit makefile loading
instead.)
$(wildcard ) function for more details on makepp's
wildcards). This can be used for generating a distribution target, for
example:
.PHONY: distribution
distribution:
mkdir our_product-$(VERSION)
cp $(filter-out %~, $(only_nontargets *)) our_product-$(VERSION)
tar cf - our_product-$(VERSION) | gzip -9c > our_product-$(VERSION).tar.gz
In this case, the $(only_nontargets *) returns every file in the current
directory that is not a target of some rule. The $(filter_out %~, ...)
removes editor backups.
Similar to only_targets (see above), only_nontargets only knows about
targets that have been defined already. This is only a problem if you use it
to define variables with the := assignment; if you use it in the
dependency list or in the body of a rule, all other rules will already
have been seen.
./ and other
junk from the path:
DIR := .
SUBDIR := ..
FNAME := $(DIR)/../otherdir/$(SUBDIR)/files
X := $(relative_filename $(FNAME))
source_backup.tar:
cd .. && tar cf $(relative_to $(output), ..) $(relative_to ., ..)
% character matches any string. This is best
illustrated by an example:
OBJS = $(patsubst %.c, object_dir/%.o, $(C_SOURCES))
takes every file in C_SOURCES and returns the name of an object file in object_dir. Sometimes it is more concise to use a substitution reference, e.g., the above could have been written as
OBJS = $(C_SOURCES:%.c=object_dir/%.o)
$(phony clean):
rm -f *.o my_program
You can also declare one or more targets as phony with a line like this anywhere in your makefile:
.PHONY: all clean
XYZ := $(print $(patsubst %.c, %o, $(SOURCE_FILES)))
will print out the result of the patsubst call.
XYZ := $(patsubst %.c, %o, $(print $(SOURCE_FILES)))
will print out the last argument to the patsubst
call.
For example:
ARCH := $(shell uname -m)
Makepp supports all the usual shell wildcards (*, ?, and []). It
also has a wildcard ** which matches any number of intervening directories.
(This idea was stolen from zsh.) For example, **/*.c matches all the .c
files in the entire source tree. objects/**/*.o matches all the .o
files contained anywhere in the subdirectory objects or any of its
subdirectories or any of their subdirectories. The ** wildcard will not
follow soft links to directories, nor will it attempt to enter directories
which exist but cannot be read. Also files which exist but cannot be read
will not be returned by $(wildcard ).
makepp also supports many other, less useful functions that GNU make has. See
the GNU make documentation for details, because I don't feel like typing it in
now. (To my knowledge, the only ones it does not support are call,
error, and warning.) These are intended mainly to support existing
makefiles; it's very easy to write your own functions in
perl, or to use the perl_begin or sub statements to
manipulate variables directly in perl inside your makefile, so you can do any
kind of manipulation if one of these built-in functions isn't adequate.