• GNU make supports both built-in and user-defined functions.
  • Most built-in functions expand to some value that is then assigned to a variable or passed to a subshell.
  • A user-defined function is stored in a variable or macro and expects one or more parameters to be passed by the caller.
4.1 User-Defined Functions
  • Storing command sequences in variables opens the door to a wide range of applications.
AWK := awk
KILL := kill
KILL_FLAGS := -f
PS := ps
PS_FLAGS := -W
PS_FIELDS := "9 47 100"
# $(call kill-program,awk-pattern)
define kill-program
           @ $(PS) $(PS_FLAGS) |                                                    \
           $(AWK) 'BEGIN { FIELDWIDTHS = $(PS_FIELDS) }    \
                     /$1/  {                                                                         \          /* $1 stands for the first argument passed to the macro */
                                print "Killing " $$3;                                         \          /* $$3 stands for the value of the third field comes from print */
                                system( "$(KILL) $(KILL_FLAGS) " $$1 )  \
                     }'         /* Notice that awk command are quoted */
endef
  • To kill a process we pipe the output of ps to awk.
  • We use the FIELDWIDTHS feature to treat the program name and all its arguments as a single field. Field references in awk are written as $1, $2, etc.
  • awk commands would be treated as make variables if we did not quote them in some way.
  • We can tell make to pass the $n reference to awk instead of expanding it itself by escaping the dollar sign in $n with an additional dollar sign, $$n. make will see the double dollar sign, collapse it to a single dollar sign and pass it to the subshell.
  • The parameters of the macro are referenced within the body of the macro definition with $1, $2, etc.
  • A portable macro for terminating processes:
FOP := org.apache.fop.apps.Fop
FOP_FLAGS := -q
FOP_OUTPUT := > /dev/null
%.pdf: %.fo
           $(call kill-program,AcroRd32)          /* So “AcroRd32: is the first argument for the macro “kill-program” */
           $(JAVA) $(FOP) $(FOP_FLAGS) $< $@ $(FOP_OUTPUT)
  • This pattern rule kills the Acrobat process, if one is running, and then converts an fo (Formatting Objects) file into a pdf file by invoking the Fop processor.
  • The syntax for expanding a variable or macro is:
$(call macro-name[, param1 . . . ])
  • call is a built-in make function that expands its first argument and replaces occurrences of $1, $2, etc., with the remaining arguments it is given. (In fact, it doesn't really "call" its macro argument at all in the sense of transfer of control, rather it performs a special kind of macro expansion.)
  • The macro-name is the name of any macro or variable (remember that macros are just variables where embedded newlines are allowed).
  • Any number of arguments can be given to call. If a macro references a parameter $n and there is no corresponding argument in the call instance, the variable collapses to nothing. If there are more arguments in the call instance than there are $n references, the extra arguments are never expanded in the macro.
  • If you invoke one macro from another, you should be aware of a somewhat strange behavior in make 3.80 (resolved in 3.81). The call function defines the arguments as normal make variables for the duration of the expansion. So if one macro invokes another, it is possible that the parent's arguments will be visible in the child macro's expansion.
4.2 Built-in Functions
  • All functions have the form:
$(function-name arg1[, argn])
  • The $( is followed by built-in function name and then followed by the arguments to the function. Leading whitespace is trimmed from the first argument, but all subsequent arguments include any leading, embedded, and following whitespace.
  • Many functions accept a single argument, treating it as a list of space-separated words. For these functions, the whitespace between words is treated as a single-word separator and is otherwise ignored.
  • Many make functions accept a pattern as an argument. A pattern may contains a single % with leading or trailing characters (or both). The % character is optional in a pattern and is commonly omitted when appropriate.
4.2.1 String Functions
  • $(filter pattern . . . ,text): The filter function treats text as a sequence of space separated words and returns a list of those words matching pattern. filter can accept multiple patterns.
$(ui_library): $(filter ui/%.o,$(objects))      /* extract the filenames starting with ui/ and ending in .o from a list of filenames */
           $(AR) $(ARFLAGS) $@ $^
  • A pattern can contain only one %. If additional % characters are included in the pattern, all but the first are treated as literal characters.
  • $(filter-out pattern . . . ,text): The filter-out function does the opposite of filter, selecting every word that does not match the pattern.
all_source := count_words.c counter.c lexer.l counter.h lexer.h
to_compile := $(filter-out %.h, $(all_source))      /* select all files that are not C headers */
  • $(findstring string,text): This function looks for string in text. If the string is found, the function returns string; otherwise, it returns nothing. This function returns just the search string, not the word it finds that contains the search string. The search string cannot contain wildcard characters.
  • $(subst search-string,replace-string,text): This is a simple, nonwildcard, search and replace.
sources := count_words.c counter.c lexer.c
objects := $(subst .c,.o,$(sources))  /* replaces all occurrences of ".c" with ".o" anywhere in $(sources), no spaces after the commas */
  • subst doesn't understand filenames or file suffixes, just strings of characters.
  • $(patsubst search-pattern,replace-pattern,text): This is the wildcard version of search and replace, the pattern can contain a single %, the search-pattern must match the entire value of text.
strip-trailing-slash = $(patsubst %/,%,$(directory-path))        /* delete a trailing slash in text */
  • Substitution references are a portable way of performing the same substitution.
$(variable:search=replace)
  • The search text can be a simple string; in which case, the string is replaced with replace whenever it occurs at the end of a word.
  • search can contain a % representing a wildcard character; in which case, the search and replace follow the rules of patsubst.
  • $(words text): This returns the number of words in text.
CURRENT_PATH := $(subst /, ,$(HOME))
words:
           @echo My HOME path has $(words $(CURRENT_PATH)) directories.
  • $(word n,text): This returns the nth word in text. The first word is numbered 1. If n is larger than the number of words in text, the value of the function is empty.
version_list  := $(subst ., ,$(MAKE_VERSION))
minor_version := $(word 2, $(version_list))
current := $(word $(words $(MAKEFILE_LIST)), $(MAKEFILE_LIST))     /* returns the name of the most recently read makefile */
  • $(firstword text): This returns the first word in text. This is equivalent to $(word 1,text).
version_list := $(subst ., ,$(MAKE_VERSION))
major_version := $(firstword $(version_list))
  • $(wordlist start,end,text): This returns the words in text from start to end, inclusive. If start is greater than the number of words, the value is empty. If start is greater than end, the value is empty.
# $(call uid_gid, user-name)
uid_gid = $(wordlist 3, 4, $(subst :, ,$(shell grep "^$1:" /etc/passwd)))
4.2.2 Important Miscellaneous Functions
  • $(sort list): The sort function sorts its list argument and removes duplicates. The resulting list contains all the unique words in lexicographic order, each separated by a single space. sort strips leading and trailing blanks.
$ make -f- <<< 'x:;@echo =$(sort    d  b s   d      t   )='
=b d s t=           /* output */
  • $(shell command): The shell function accepts a single argument that is expanded (like all arguments) and passed to a subshell for execution. The standard output of the command is then read and returned as the value of the function. Sequences of newlines in the output are collapsed to a single space. Any trailing newline is deleted. The standard error is not returned, nor is any program exit status.
REQUIRED_DIRS = ...
_MKDIRS := $(shell for d in $(REQUIRED_DIRS);        \
                                do                                                           \
                                           [[ -d $$d ]] || mkdir -p $$d;        \
                                done)
    • This variable creates the necessary directories by using a bash shell "for" loop to ensure that a set of directories exists.
    • The double square brackets are bash test syntax similar to the test program except that word splitting and pathname expansion are not performed. Therefore if the variable contains a filename with embedded spaces, the test still works correctly (and without quoting).
  • The shell function can be used to invoke any external program, use it carefully.
START_TIME := $(shell date)
CURRENT_TIME = $(shell date)
  • A function for testing whether a value contains duplicates:
# $(call has-duplicates, word-list)    /* return non-null if there are duplicates */
has-duplicates = $(filter                                           \
                                $(words $1)                                \
                                $(words $(sort $1))))
  • A simple way to generate a filename with a timestamp:
RELEASE_TAR := mpwm-$(shell date +%F).tar.gz      /* or */
RELEASE_TAR := $(shell date +mpwm-%F.tar.gz)
mpwm-2003-11-11.tar.gz        /* output */
  • Convert relative paths (possibly from a com directory) into a fully qualified Java class name:
# $(call file-to-class-name, file-name)
file-to-class-name := $(subst /,.,$(patsubst %.java,%,$1))    /* or */
file-to-class-name := $(subst /,.,$(subst .java,,$1))

CALIBRATE_ELEVATOR := com/wonka/CalibrateElevator.java
calibrate:
           $(JAVA) $(call file-to-class-name,$(CALIBRATE_ELEVATOR))
  • Remove parent directory components by passing the root of the directory tree as the first argument:
# $(call file-to-class-name, root-dir, file-name)
file-to-class-name := $(subst /,.,                             \
                                           $(subst .java,,                  \
                                                     $(subst $1/,,$2)))
4.2.3 Filename Functions
  • $wildcard pattern . . . ): The wildcard function accepts a list of patterns and performs expansion on each one. If a pattern does not match any files, the empty string is returned. The normal shell globbing characters are supported: ~, *, ?, [...], and [^...].
sources := $(wildcard *.c *.h)
  • Another use of wildcard is to test for the existence of a file in conditionals.
dot-emacs-exists := $(wildcard ~/.emacs)           /* return a empty string if the user's home directory does not contain a .emacs file */
  • $(dir list . . . ): The dir function returns the directory portion of each word in list.
source-dirs := $(sort                                                  \          /* return every subdirectory that contains C files */
                                $(dir                                             \
                                $(shell find . -name '*.c')))
    • This variable definition uses a simple variable to avoid reexecuting the find each time the variable is used.
  • Implementation that requires a recursive variable:
# $(call source-dirs, dir-list)
source-dirs = $(sort                                                             \
                                $(dir                                                       \
                                           $(shell find $1 -name '*.c'))))
    • The first arguments to find are one or more directories to search. The end of the directory list is recognized by the first dash argument.
  • $(notdir name . . . ): The notdir function returns the filename portion of a file path.
# $(call get-java-class-name, file-name)
get-java-class-name = $(notdir $(subst .java,,$1))       /* return the Java class name from a Java source file */
  • Suppose a custom shell script must be executed in the same directory as the output file it generates.
$(OUT)/myfile.out: $(SRC)/source1.in $(SRC)/source2.in
           cd $(dir $@);
           generate-myfile $^ > $(notdir $@)
  • In command scripts, another way to decompose a filename is through the use of $(@D) and $(@F).
  • $(suffix name . . . ): The suffix function returns the suffix of each word in its argument.
# $(call same-suffix, file-list)
same-suffix = $(filter 1 $(words $(sort $(suffix $1))))  /* test whether all the words in a list have the same suffix */
  • $(basename name . . .): The basename function returns the filename without its suffix. Any leading path components remain intact after the basename call.
# $(call file-to-class-name, root-directory, file-name)
file-to-class-name := $(subst /,.,                             \
                                           $(basename                   \
                                                     $(subst $1/,,$2)))
# $(call get-java-class-name, file-name)
get-java-class-name = $(notdir $(basename $1))
  • $(addsuffix suffix,name . . . ): The addsuffix function appends the given suffix text to each word in name. The suffix text can be anything.
# $(call find-program, filter-pattern)           /* filter-style globbing patterns (using % only) */
find-program = $(filter $1,                                                                       \          /* find all the files in the PATH that match an expression */
                                $(wildcard                                                                                                             \
                                           $(addsuffix /*,                                                                                             \
                                                     $(sort                                                                                                           \
                                                                $(subst :, ,                                                                              \
                                                                           $(subst ::,:.:,                                                                \
                                                                                     $(patsubst :%,.:%,                                          \
                                                                                                $(patsubst %:,%:.,$(PATH)))))))))
find:
           @echo $(words $(call find-program, %))
    • The inner-most three substitutions account for a special case in shell syntax. An empty path component is taken to mean the current directory. To normalize this special syntax we search for an empty trailing path component, an empty leading path component, and an empty interior path component, in that order. Any matching components are replaced with ".".
    • Next, the path separator is replaced with a space to create separate words.
    • The sort function is used to remove repeated path components.
    • Then the globbing suffix /* is appended to each word and wildcard is invoked to expand the globbing expressions.
    • Finally, the desired patterns are extracted by filter.
  • Move the $1 argument inside the call to wildcard, the call to filter and changes addsuffix to use the caller's argument.
# $(call find-program,wildcard-pattern)     /* wildcard-style globbing patterns (~, *, ?, [...], and [^...]) */
find-program = $(wildcard                                                                                                      \
                                $(addsuffix /$1,                                                                                         \
                                           $(sort                                                                                                           \
                                                     $(subst :, ,                                                                              \
                                                                $(subst ::,:.:,                                                                \
                                                                           $(patsubst :%,.:%,                                           \
                                                                                     $(patsubst %:,%:.,$(PATH))))))))
find:
           @echo $(words $(call find-program,*))
  • $(addprefix prefix,name . . . ): The addprefix function is the complement of addsuffix.
# $(call valid-files, file-list)
valid-files = test -s . $(addprefix -a -s ,$1) /* whether a set of files exists and is nonempty */
    • This function is different from most of the previous examples in that it is intended to be executed in a command script.
    • It uses the shell's test program with the -s option ("true if the file exists and is not empty") to perform the test.
    • Since the test command requires a -a (and) option between multiple filenames, addprefix prepends the -a before each filename.
    • The first file used to start the "and" chain is dot, which always yields true.
  • $(join prefix-list,suffix-list): It accepts two lists and concatenates the first element from prefix-list with the first element from suffix-list and so on.
4.2.4 Flow Control
  • $(if condition,then-part,else-part): The if function selects one of two macro expansions depending on the "value" of the conditional expression. The condition is true if its expansion contains any characters (even space). make evaluates the condition by first removing leading and trailing whitespace, then expanding the expression.
  • An easy way to test whether the makefile is running on Windows. Look for the COMSPEC environment variable defined only on Windows:
PATH_SEP := $(if $(COMSPEC),;,:)
  • Check the version of make:
$(if $(filter $(MAKE_VERSION),3.80 3.81 3.90 3.92),, $(error This makefile requires one of GNU make version ....))
  • $(error text): The error function is used for printing fatal error messages. After the function prints its message, make terminates with an exit status of 2. The output is prefixed with the name of the current makefile, the current line number, and the message text.
# $(call assert,condition,message)
define assert
           $(if $1,,$(error Assertion failed: $2))          /* test first argument and prints the user's error message if it is empty */
endef
# $(call assert-file-exists,wildcard-pattern)
define assert-file-exists
           $(call assert,$(wildcard $1),$1 does not exist)   /* test a wildcard pattern yields an existing file, the argument can include any number of globbing patterns. */
endef
# $(call assert-not-null,make-variable
define assert-not-null
           $(call assert,$($1),The variable "$1" is null)
endef
error-exit:
           $(call assert-not-null,NON_EXISTENT)
    • The third function is a very useful assert that relies on computed variables. A make variable can contain anything, including the name of another make variable.
    • First expand the argument, $1, to access the variable name, then expand again, $($1), to determine if it has a value.
  • If a variable contains the name of another variable, expand the variable twice can get the value of that variable.
NO_SPACE_MSG := No space left on device
NO_FILE_MSG  := File not found.
...;
STATUS_MSG   := NO_SPACE_MSG
$(error $($(STATUS_MSG)))
    • STATUS_MSG is set to one of several error messages by storing the error message variable name. When it comes time to print the message, STATUS_MSG is first expanded to access the error message variable name, $(STATUS_MSG), then expanded again to access the message text, $($(STATUS_MSG)).
  • $(foreach variable,list,body): The foreach function provides a way to expand text repeatedly while substituting different values into each expansion. expand list and set the value to variable -> execute body.
letters := $(foreach letter,a b c d,$(letter))
show-words:
           # letters has $(words $(letters)) words: '$(letters)'
$ make
# letters has 4 words: 'a b c d'
    • When this foreach is executed, it sets the loop control variable, letter, to each value in a b c d and expands the body of the loop, $(letter), once for each value. The expanded text is accumulated with a space separating each expansion.
  • A function to test if a set of variables is set:
VARIABLE_LIST := SOURCES OBJECTS HOME
$(foreach i,$(VARIABLE_LIST),$(if $($i),, $(shell echo $i has no value > /dev/stderr)))       /* the pseudo file /dev/stderr in the shell function requires setting SHELL to bash */
    • The test expression inside the if first evaluates $i to get the variable name, then evaluates this again in a computed expression $($i) to see if it is non-null.
    • if we omit the redirection from the echo, the output of the shell command will be substituted into the makefile, yielding a syntax error.
  • a function that gathers all the words that contain a substring from a list, this function does not accept patterns:
# $(call grep-string, search-string, word-list)
define grep-string
           $(strip $(foreach w,$2,$(if $(findstring $1, $w), $w)))
endef
words := count_words.c counter.c lexer.l lexer.h counter.h
find-words:
           @echo $(call grep-string,un,$(words))
  • The make manual uses parentheses for virtually all variables, even single character variables, only automatic variables are universally written without parentheses.
  • Automatic variables are universally written without parentheses even in the GNU make manual.
4.2.5 Less Important Miscellaneous Functions
  • $(strip text): The strip function removes all leading and trailing whitespace from text and replaces all internal whitespace with a single space.
  • $(origin variable): The origin function returns a string describing the origin of a variable.
# $(call assert-defined,variable-name)
define assert-defined
           $(call assert,(filter-out undefined,$(origin $1)),'$1' is undefined)
endef
  • The possible return values of origin are:
    • undefined: The variable has never been defined.
    • default: The variable's definition came from make's built-in database. If you alter the value of a built-in variable, origin returns the origin of the most recent definition.
    • environment: The variable's definition came from the environment (and the —environment-overrides option is not turned on).
    • environment override: The variable's definition came from the environment (and the --environment-overrides option is turned on).
    • file: The variable's definition came from the makefile.
    • command line: The variable's definition came from the command line.
    • override: The variable's definition came from an override directive.
    • automatic: The variable is an automatic variable defined by make.
  • $(warning text): The warning function is similar to the error function except that it does not cause make to exit. the output is prefixed with the name of the current makefile and the current line number followed by the message text. The warning function expands to the empty string so it can be used almost anywhere.
$(if $(wildcard $(JAVAC)),,$(warning The java compiler variable, JAVAC ($(JAVAC)), is not properly set.))
4.3 Advanced User-Defined Functions
arrow
arrow
    全站熱搜

    nix 發表在 痞客邦 留言(0) 人氣()