Functions in makepp
absolute_filename, addprefix, addsuffix, basename, dir, dir_noslash, filesubst, filter, filter_out, findfile, findstring, find_program, find_upwards, first_available, first_word, if, infer_linker, infer_objects, join, make, makeperl, notdir, only_generated, only_nontargets, only_phony_targets, only_stale, only_targets, origin, patsubst, perl, phony, print, relative_filename, relative_to, shell, sort, sorted_dependencies, sorted_inputs, stem, strip, subst, suffix, wildcard, word, wordlist, words, xargs
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:
Converts relative filenames into absolutes. For example,
$(absolute_filename xyz.c)
might return
/usr/src/our_project/subdir/xyz.c
.
Prepends the prefix string to each of the words. This is mostly for GNU make compatibility; using rc-style expansion, this can be done in a more readable fashion like this:
MODULES := a b c d
X_OLD_STYLE := $(addprefix $(OBJDIR)/, $(addsuffix .o, $(MODULES)))
X_NEW_STYLE := $(OBJDIR)/$(MODULES).o # Isn't that easier to read?
Appends the suffix string to each of the words. This is mostly for GNU make compatibility; using rc-style expansion, this can be done in a more readable fashion like this:
X_OLD_STYLE := $(addsuffix .o, $(MODULES)) X_NEW_STYLE := $(MODULES).o
The basename is the entire file name (with the directory), minus the text
after the last period. E.g., $(basename myfile/version-1.0-module.c)
is
myfile/version-1.0-module
Extracts the directory part of each file in the filename list, including the
trailing slash. Returns ./
if there is no directory in the filename.
Same as $(dir )
except that it doesn't return the trailing slash.
Returns all words in the list that match the patterns. Patterns may simply be
other words, or filename wildcards (i.e., *
, ?
, and [a-z]
are
recognized), or they may have a %
character, which means to match any
string at that point (same as *
).
Returns all words in the list that do not match the patterns. Patterns may
simply be other words, or filename wildcards (i.e., *
, ?
, 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.
Finds a file in the specified path, or in the environment variable PATH if nothing is specified. This can be useful for finding binaries or include files. For example,
TCL_INCLUDE := -I$(dir_noslash $(findfile tcl.h, \ /usr/local/stow/tcl-8.4.5-nothread/include \ /usr/include/tcl8.4 /usr/include/tcl \ /net/na1/tcl8.4a3/include /net/na1/tcl8.4a3/include))
This locates the file tcl.h by searching all of the above
directories. The absolute path to the file is returned. Then
$(dir_noslash )
extracts that directory, and it is put into the
include path.
Return find, if it is a substring of in.
Return the first program in the list that can be found in the PATH. This is useful when there are multiple equivalent programs that may be used, and you just want to pick one of them. For example, here is the default definition of several common variables that makepp supplies if you do not put one in your makefile:
CC = $(find_program gcc egcc pgcc c89 cc) F77 = $(find_program f77 g77 fort77) CXX = $(find_program g++ c++ pg++ cxx CC aCC)
If none of the programs is found, $(find_program )
returns the
first one. This usually won't result in a functional makefile, but it
will tend to make for better error messages. For example, if you do
something like this:
%.o : %.c $(CC) $(inputs) -o $(outputs)
and makepp can't find a C compiler in the list above, it will substitute "gcc" anyway. Otherwise the shell would attempt to execute the source file and the resulting error message might be really strange.
Searches for a file of the given name in the directory ., .., ../.., ../../.., etc., until the file is found or the root directory is reached or the directory is located on a different file system. (This last requirement is to prevent problems with automounters or hung network filesystems.)
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)
Another problem that find_upwards
can help solve is locating the
top-level directory of a build. Often it is useful to define a variable
like this:
TOP := ../../..
if you have some important information located only in the top-level
directory. But this is hard to maintain, because the number of ..
is
different for different levels of the directory tree. Instead, you can
use find_upwards
to locate a file which is known to be present only
in the top level directory. Suppose, for example, that the file
LICENSE
is located only in the top level directory. Then
you could do this:
TOP := $(dir_noslash $(find_upwards LICENSE))
$(find_upwards LICENSE)
returns the full path of the license file;
$(dir_noslash ...)
strips off the filename, returning only the directory.
(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.)
Return the fisrt word.
Return the first file in a list that exists or can be built. This can be useful for adapting your makefiles to work on several different machines or networks, where important files may be located in different places. For example, here's a line from one of my makefiles:
TCL_LIB = $(first_available \ /usr/local/stow/tcl-8.4.5-nothread/lib/libtcl8.4.so \ /usr/lib/libtcl8.4.so /usr/lib/libtcl.so \ /net/na1/tcl8.4a3/lib/libtcl8.4.a \ /net/na1/tcl8.4a3/lib/libtcl8.4.sl)
This line will check for the Tcl library in all of the above places, stopping at the first one that it finds. The link command then includes $(TCL_LIB) so we get the appropriate Tcl library.
An alternative to the 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.
Do a pairwise join of the first words and the second words.
Returns its argument verbatim, but first builds all the files listed. This is useful when a given file is needed when evaluating a make expression. This typically happens when you have a build where the set of files involved is computed by some shell commands. For example,
file_list :
# shell commands to compute a list of files to put into the program
my_program : $(shell cat $(make file_list))
If instead you specified just $(shell cat file_list)
, then makepp
would not force file_list to be up-to-date before it executes the shell
command. Using $(make )
is the best way to solve this problem.
You might be tempted to try other things, like this:
my_program : file_list $(shell cat file_list)
but this won't work because $(shell cat file_list)
is evaluated
before makepp attempts to build file_list
.
This is the same as $(perl )
, but the perlcode first goes through Make-style
variable expansion.
VAR = 1 VAR3 = $(makeperl $(VAR) * 3)
Returns the non-directory portion of the filename(s), i.e., everything after the last slash if there is one, or the whole filename otherwise.
Returns only those filenames in the list that were generated by makepp and not since modified, according to the build info file.
This function is useful in clean target rules:
$(phony clean): $(RM) $(only_generated **/*)
Returns only those filenames in the list that are not targets of any
rule (either explicit or pattern rules). You may specify a wildcard
(see the $(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.
Returns only those names in the list that are phony targets of
some rule (either explicit or pattern rules). You may specify wildcards
(including makepp's special wildcard, **
) in the filenames. (See the
$(wildcard )
function for more details. This can be used for grouping
targets, for example:
tests: $(only_phony_targets */**/tests)
Returns only those filenames in the list that were generated by makepp and not since modified, according to the build info file, but are no longer targets of any rule.
This function is useful for ensuring that there are no dependencies on such files, without forcing a clean build of all of the targets:
$(phony flush): $(RM) $(only_stale **/*)
Actually, it's probably better instead to write a script that calls makepp to generate the list of stale files, and then have that script remove all of the listed files that aren't currently under source control, just in case a generated file becomes a source file. Makepp doesn't have such a function built in because makepp is (and probably ought to remain) agnostic about source control.
Returns only those filenames in the list that are actually targets of
some rule (either explicit or pattern rules). You may specify wildcards
(including makepp's special wildcard, **
) 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.)
Evaluates perlcode in a block and returns the result.
This is the same as makeperl
, but the perlcode does not go through
Make-style variable expansion. Note, that, as with all functions, the
function delimiter used may not appear within the perlcode outside of single
or double quoted strings. So you can't use if( ... ) { ... }
, which
combines both. And you must choose between $(perl )
and ${perl }
as
appropriate.
VAR = 1 VAR1 = ${perl ($VAR + 1) * 3} VAR2 = $(perl do { $VAR *= 3; return $VAR + 1 } if $VAR)
Returns the name of those files relative to the current directory (the one the
makefile is in). This can also be used to clean unnecessary ./
and other
junk from the path:
DIR := . SUBDIR := .. FNAME := $(DIR)/../otherdir/$(SUBDIR)/files X := $(relative_filename $(FNAME))
Returns the name of those files relative to the specified directory. This is typically useful when for whatever reason you have to execute a command from a different directory:
source_backup.tar: cd .. && tar cf $(relative_to $(output), ..) $(relative_to ., ..)
Performs a substitution on each word in the word list. A
%
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)
Indicates that the list of words are actually phony targets, and returns the list of targets. It's intended to be used like this:
$(phony all): my_program $(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
Outputs the text and returns it. This is mostly useful for debugging, when you don't understand why variable substitution has the result that it does. For example,
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.
Returns the output from the given shell command, with newlines replaced by spaces.
For example:
ARCH := $(shell uname -m)
Sorts the words in lexical order and removes duplicates.
Returns the sorted names of all files matching the given pattern which exist, or those files which do not yet exist but can be built based on the rules that makepp knows about at the point when it evaluates the expression. Ignores files and directories that are not readable.
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 )
.
Returns a newline-separated list of commands that each begin with the specified command, and end with as many elements of the list as possible without going over 1000 characters.
The purpose of this is to avoid spilling over the command length limit on your system. For example, if there are a lot of generated files, then you would probably want your clean target to look something like this:
$(phony clean): $(xargs $(RM), $(only_targets **/*))
This also has the side-effect that no command whatsoever is generated if the list happens to be empty.
If a third argument is specified, then it is used to postfix each command. This is useful for specifying redirectors, e.g.:
manifest: $(RM) $@; touch $@ $(xargs echo, $(only_nontargets **/*), >> $@)
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.
Gary Holt (holt-makepp@gholt.net)