MAKEPP_FUNCTIONS(1) | Makepp | MAKEPP_FUNCTIONS(1) |
makepp_functions -- Functions in makepp
A: absolute_filename,
absolute_filename_nolink,
abspath,
addprefix,
addsuffix,
and, B: basename,
C: call, D: dir,
dir_noslash, E: error,
F: filesubst,
filter,
filter_out,
filter_out_dirs,
findfile,
find_first_upwards,
find_program,
findstring,
find_upwards,
first_available,
firstword,
foreach, I: if,
iftrue,
infer_linker,
infer_objects,
info, J: join, M: make,
makemap,
makeperl,
map,
"mktemp", N: notdir,
O: only_generated,
only_nontargets,
only_phony_targets,
only_stale,
only_targets,
or,
origin, P: patsubst,
perl,
phony,
prebuild,
print, R: realpath,
relative_filename,
relative_to, S: shell,
sort,
strip,
subst,
suffix, T: temporary, W: warning,
wildcard,
word,
wordlist,
words, X: xargs
Any expression of the format "$(name)", where "name" is not the name of a variable, or "$(name arg1 arg2 arg3)" 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. If "name" is preceded by "&" it runs the builtin command or script of that name within the makepp process, and returns the standard output. This requires perl to be built for PerlIO. If the name does not name a function it is transformed to an invocation of call.
As with variables you have a choice of "$(name ...)" or "${name ...}". If you want to embed the same parenthesis, it must be paired, the other doesn't matter: "$(name ...(){..." or "${name ...{}(...}". (However for map and perl the first closing paren ends the expression.) Doubling allows the arguments to span several lines. The newlines are then treated as spaces, except maybe in "define". There is also the syntax "$[name ...]" or $[[name ...]], which gets evaluated while reading the makefile, before grokking rules and other constructs.
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. You can define Perl subroutines to do whatever you like. See the "sub" statement and the section on extending makepp for more details.
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.)
"iftrue" is similar to "if", except that the string 0 is treated as blank.
$(filesubst ./src/%.c, %.o, $(wildcard src/*.c))
will work with filesubst but not with patsubst.
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.
CC = $(find_program gcc egcc pgcc c89 cc) # and more, depending on machine 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 string not-found, and logs what was not found. 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 not-found. Otherwise the shell would attempt to execute the source file and the resulting error message might be really strange.
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.)
If the file is not found, "find_upwards" will abort the build with an error message.
If you specify more than one file, find_upwards will search for the first one, then the second one, and so on. In other words,
$(find_upwards file1 file2)
is equivalent to
$(find_upwards file1) $(find_upwards file2)
If you want to look for any one of the files, then use "find_first_upwards" instead.
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.
$(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.
Any number of upper case "X"s at the end of the argument are replaced by that many random letters and digits. The more there are, the less likely this is to collide with other processes, so if you give a prefix like "/tmp/abc.", you should have enough "X"s. If there is more than one X, the first character comes from the process id. If there are none, it is as though there were ten, which is supposedly enough (8.4e17 possibilities or 3.7e15 on Windows). If there is no argument, the prefix defaults to "tmp." in the current directory.
Note that you don't want to give such a name as rule targets and dependencies. The result would be correct, but it would be recreated every time you run makepp.
Also, as it is always different, you should use this in a rule action only if you use ":build_check ignore_action":
TMPFILE ;= $(mktemp) # 1 call; "=" would mean 3 calls: 3 files A-count B-count: :build_check ignore_action produce-As-and-Bs >$(TMPFILE) &grep -c /A/ $(TMPFILE) -o A-count &grep -c /B/ $(TMPFILE) -o B-count
Or you should export it and let the Shell evaluate it:
export TMPFILE ;= $(mktemp) A-count B-count: produce-As-and-Bs >$$TMPFILE # makepp doesn't see the var value fgrep -c A $$TMPFILE >A-count fgrep -c B $$TMPFILE >B-count
The last form repeats the previous return value, so you can use it in a pattern rule:
%.x: %.y &grep foo $(input) -o $(mktemp) &sed bar $(mktemp /) -o $(output) # Operate on the output of &grep
This function is useful in clean target rules (though of course "makeppclean" is the preferred variant):
$(phony clean): &rm -f $(only_generated **/*)
.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.
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 -f $(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.
.PHONY: clean clean: &rm -f $(only_targets *)
Now if you type "makepp clean", it will delete everything it knows how to build. But don't create a clean target, use "makeppclean" instead!
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.)
DIR := . SUBDIR := .. FNAME := $(DIR)/../otherdir/$(SUBDIR)/files X := $(relative_filename $(FNAME))
If slash is true (usually 1) the returned filenames are guaranteed to contain a slash by prepending "./" if necessary, so that you can use it as an executable name without worrying about the command search path overriding the directory location.
If the path goes by the root directory, the parent of either your home directory or the "$(ROOT)" of your build system, or on Windows a drive's root (depending on the environment, this also happens for /cygdrive/c or /c), an absolute path will be returned instead.
source_backup.tar: cd .. && tar cf $(relative_to $(output), ..) $(relative_to ., ..)
For example,
$(suffix src/foo.c src-1.0/bar.c hacks)
produces the result ".c .c".
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 at any level, nor will it attempt to enter directories which exist but cannot be read. Also files and directories which exist but cannot be read will not be returned by "$(wildcard )".
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?
X_OLD_STYLE := $(addsuffix .o, $(MODULES)) X_NEW_STYLE := $(MODULES).o
During expansion of the macro, the temporary variables $1, $2, "..." refer to the arguments given to "call" during its invocation. The variable $0 will be expanded to the name of the macro (i.e. variable) that "call" is currently expanding.
There is no limit, how many arguments a macro may be "call"ed with or how many parameters a macro may expect. If you pass more arguments to "call" as the macro need, all exceeding arguments will be discarded. If you pass less arguments than a macro expect, all exceeding parameters collapse into the empty string.
First a simple example:
rest = $(wordlist 2, $(words $(1)),$(1)) list = A B C D E butfirst := $(call rest,$(list))
Here, the variable "$(butfirst)" will contain the list "B C D E".
And now for a more complex example to show what is possible:
rest = $(wordlist 2,$(words $(1)),${1}) mymap = $(if $2,$(call $1,$(firstword $2)) $(call $0,$1,$(call rest,$2))) downcase = ${makeperl lc("$1")} UCWORDS = ALL THESE WORDS ARE UPCASE DCWORDS := $(call mymap,downcase,$(UCWORDS))
Now "$(DCWORDS)" contains "all these words are upcase". By the way: it makes no difference, whether we access the arguments via $1, "${1}" or "$(1)" within a macro.
You can directly use the variable as though it were a function, if there is no function of that name. This is internally converted to "call", so these are equivalent:
discussion = The $0 turned into $1 $2. direct = $(discussion an,argument) called = $(call discussion,an,argument)
It might seem debatable whether "$[call]" should also expand the macro's "$[]" expressions, or whether a function should always do the same thing, no matter how it is called. The latter was chosen, because with normal make syntax it would be impossible to get "$[1], $[2]..." into a variable (they'd get replaced by nothing, before the assignment even takes place.) Hence, if you have a macro for defining a rule, you want expressions like "$(output)" to be seen when the rule gets parsed, so you must protect them from "call":
define myrule $2: $1 mycommand $$(input) -o $$(output) endef $[myrule myinput,myoutput]
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.
The words are in $_ and are returned unless you undef $_. This is intended for modifications not easily handled by "patsubst". Only the first comma is a separator, any others are considered part of the perlcode.
# Switch words. Double parens, to allow parens in perlcode, or use ${}: X = $((map $(VALUES), s/(.+)-(.+)/$2-$1/)) # You can use make expressions, but then you must use $$ for Perl $: Y = $(makemap $(VALUES), tr/$(OLDCHARS)/$(NEWCHARS)/ or $$_ = 'failed') # You can eliminate candidates: Y = $(map $(VALUES), undef $_ if /no_good/)
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)
$(subst ee,EE,feet on the street)
substitutes the string "fEEt on the strEEt".
This simple example sets the variable files to the list of all files in the directories in the list dirs:
dirs := a b c d files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
Here text is "$(wildcard $(dir)/*)". The first repetition finds the value "a" for dir, so it produces the same result as "$(wildcard a/*)"; the second repetition produces the result of "$(wildcard b/*)"; and the third, that of "$(wildcard c/*)".
This example has the same result (except for setting "dirs") as the following example:
files := $(wildcard a/* b/* c/* d/*)
When text is complicated, you can improve readability by giving it a name, with an additional variable:
find_files = $(wildcard $(dir)/*) dirs := a b c d files := $(foreach dir,$(dirs),$(find_files))
Here we use the variable find_files this way. We use plain "=" to define a recursively-expanding variable, so that its value contains an actual function call to be reexpanded under the control of foreach; a simply-expanded variable would not do, since wildcard would be called only once at the time of defining find_files.
Note: Don't confuse this with the "$(foreach)" special variable.
file_list : # shell commands to compute a list of files to put into the program my_program : $(&cat $(prebuild file_list))
If you need the list in more than one rule, it would be more efficient to use an expand at most once variable:
file_list ;= $(&cat $(prebuild file_list)) my_program1 : a.o $(file_list) my_program2 : b.o $(file_list)
If instead you specified just "$(&cat file_list)", then makepp would not force file_list to be up-to-date before it executes the shell command. Using "$(prebuild )" is the best way to solve this problem. You might be tempted to try other things, like this:
my_program : file_list $(&cat file_list)
but this won't work because "$(&cat file_list)" is evaluated before makepp attempts to build "file_list".
$(phony tests): $(only_phony_targets */**/tests)
Note, that, as with all functions, the function delimiter used may not appear within the perlcode outside of single or double quoted strings. But you can double it as in the last example:
VAR = 1 VAR1 = ${perl ($VAR + 1) * 3} VAR2 = $(perl do { $VAR *= 3; return $VAR + 1 } if $VAR) VAR3 = $(makeperl $(VAR1) * 3 + $$VAR) # one Make var and one Perl var VAR = $((perl if( ... ) { ... }))
$(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
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.
Note, that, as with all functions, the function delimiter used may not appear within the shell-command outside of single or double quoted strings. But you can double it as in the second example:
date = $(shell date) # better: $(perl scalar localtime) VAR = ${{shell f() { echo hello; }; f}}
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 (which you should not have, because "makeppclean" is more efficient) 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. But in this case it would be better to use the builtin &rm, because the arguments to the builtin commands are only limited by Perl's memory:
$(phony clean): &rm -f $(only_targets **/*)
If a third argument is specified, then it is used to postfix each command. This is useful for specifying redirectors, e.g. (though here again &echo would help):
manifest: &rm -f $@ &touch $@ $(xargs echo, $(only_nontargets **/*), >> $@)
Some of this documentation is based on the GNU make documentation.
Please note that if a function gets called during makefile initialization, e.g. the expansion of export variables, error or warning messages will report line number 0.
Gary Holt (holt-makepp@gholt.net)
2021-01-06 | perl v5.32.0 |