4.2 Built-in Functions

     

Once you start down the road of using make variables for more than just simple constants you'll find that you want to manipulate the variables and their contents in more and more complex ways. Well, you can. GNU make has a couple dozen built-in functions for working with variables and their contents. The functions fall into several broad categories: string manipulation, filename manipulation, flow control, user -defined functions, and some (important) miscellaneous functions.

But first, a little more about function syntax. All functions have the form:

 $(function-name arg  1  [, arg  n  ]) 

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 (and, of course, embedded and following) whitespace.

Function arguments are separated by commas, so a function with one argument uses no commas, a function with two arguments uses one comma, etc. 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.

I like whitespace. It makes the code more readable and easier to maintain. So I'll be using whitespace wherever I can "get away" with it. Sometimes, however, the whitespace in an argument list or variable definition can interfere with the proper functioning of the code. When this happens, you have little choice but to remove the problematic whitespace. We already saw one example earlier in the chapter where trailing whitespace was accidentally inserted into the search pattern of a grep command. As we proceed with more examples, we'll point out where whitespace issues arise.

Many make functions accept a pattern as an argument. This pattern uses the same syntax as the patterns used in pattern rules (see the Section 2.4 in Chapter 2). A pattern contains a single % with leading or trailing characters (or both). The % character represents zero or more characters of any kind. To match a target string, the pattern must match the entire string, not just a subset of characters within the string. We'll illustrate this with an example shortly. The % character is optional in a pattern and is commonly omitted when appropriate.

4.2.1 String Functions

Most of make 's built-in functions manipulate text in one form or another, but certain functions are particularly strong at string manipulation, and these will be discussed here.

A common string operation in make is to select a set of files from a list. This is what grep is typically used for in shell scripts. In make we have the filter , filter-out , and findstring 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 . For instance, to build an archive of user-interface code, we might want to select only the object files in the ui subdirectory. In the following example, we extract the filenames starting with ui/ and ending in .o from a list of filenames. The % character matches any number of characters in between:

 $(ui_library): $(filter ui/%.o,$(objects))         $(AR) $(ARFLAGS) $@ $^ 

It is also possible for filter to accept multiple patterns, separated by spaces. As noted above, the pattern must match an entire word for the word to be included in the output list. So, for instance:

 words := he the hen other the% get-the:         @echo he matches: $(filter he, $(words))         @echo %he matches: $(filter %he, $(words))         @echo he% matches: $(filter he%, $(words))         @echo %he% matches: $(filter %he%, $(words)) 

When executed the makefile generates the output:

 $ make he matches: he %he matches: he the he% matches: he hen %he% matches: the% 

As you can see, the first pattern matches only the word he , because the pattern must match the entire word, not just a part of it. The other patterns match he plus words that contain he in the right position.

A pattern can contain only one % . If additional % characters are included in the pattern, all but the first are treated as literal characters.

It may seem odd that filter cannot match substrings within words or accept more than one wildcard character. You will find times when this functionality is sorely missed. However, you can implement something similar using looping and conditional testing. We'll show you how later.


$(filter-out pattern . . . , text )

The filter-out function does the opposite of filter , selecting every word that does not match the pattern. Here we select all files that are not C headers.

 all_source := count_words.c counter.c lexer.l counter.h lexer.h to_compile := $(filter-out %.h, $(all_source)) 


$(findstring string , text )

This function looks for string in text . If the string is found, the function returns string ; otherwise, it returns nothing.

At first, this function might seem like the substring searching grep function we thought filter might be, but not so. First, and most important, this function returns just the search string, not the word it finds that contains the search string. Second, the search string cannot contain wildcard characters ( putting it another way, % characters in the search string are matched literally).

This function is mostly useful in conjunction with the if function discussed later. There is, however, one situation where I've found findstring to be useful in its own right.

Suppose you have several trees with parallel structure such as reference source, sandbox source, debugging binary, and optimized binary. You'd like to be able to find out which tree you are in from your current directory (without the current relative path from the root). Here is some skeleton code to determine this:

 find-tree:         # PWD = $(PWD)         # $(findstring /test/book/admin,$(PWD))         # $(findstring /test/book/bin,$(PWD))         # $(findstring /test/book/dblite_0.5,$(PWD))         # $(findstring /test/book/examples,$(PWD))         # $(findstring /test/book/out,$(PWD))         # $(findstring /test/book/text,$(PWD)) 

(Each line begins with a tab and a shell comment character so each is "executed" in its own subshell just like other commands. The Bourne Again Shell, bash , and many other Bourne-like shells simply ignore these lines. This is a more convenient way to print out the expansion of simple make constructs than typing @echo . You can achieve almost the same effect using the more portable : shell operator, but the : operator performs redirections. Thus, a command line containing > word creates the file word as a side effect.) When run, it produces:

 $  make  # PWD = /test/book/out/ch03-findstring-1 #  #  #  #  # /test/book/out # 

As you can see, each test against $(PWD) returns null until we test our parent directory. Then the parent directory itself is returned. As shown, the code is merely as a demonstration of findstring . This can be used to write a function returning the current tree's root directory.

There are two search and replace functions:


$(subst search-string , replace-string , text )

This is a simple, nonwildcard, search and replace. One of its most common uses is to replace one suffix with another in a list of filenames:

 sources := count_words.c counter.c lexer.c objects := $(subst .c,.o,$(sources)) 

This replaces all occurrences of " .c " with " .o " anywhere in $(sources) , or, more generally , all occurrences of the search string with the replacement string.

This example is a commonly found illustration of where spaces are significant in function call arguments. Note that there are no spaces after the commas. If we had instead written:

 sources := count_words.c counter.c lexer.c objects := $(subst .c, .o, $(sources)) 

(notice the space after each comma), the value of $(objects) would have been:

 count_words .o counter .o lexer .o 

Not at all what we want. The problem is that the space before the .o argument is part of the replacement text and was inserted into the output string. The space before the .c is fine because all whitespace before the first argument is stripped off by make . In fact, the space before $(sources) is probably benign as well since $(objects) will most likely be used as a simple command-line argument where leading spaces aren't a problem. However, I would never mix different spacing after commas in a function call even if it yields the correct results:

 # Yech, the spacing in this call is too subtle. objects := $(subst .c,.o, $(source)) 

Note that subst doesn't understand filenames or file suffixes, just strings of characters. If one of my source files contains a .c internally, that too will be substituted. For instance, the filename car.cdr.c would be transformed into car.odr.o . Probably not what we want.

In Section 2.7 in Chapter 2, we talked about dependency generation. The last example makefile of that section used subst like this:

 VPATH    = src include CPPFLAGS = -I include SOURCES  = count_words.c \            lexer.c       \            counter.c count_words: counter.o lexer.o -lfl count_words.o: counter.h counter.o: counter.h lexer.h lexer.o: lexer.h include $(subst .c,.d,$(SOURCES)) %.d: %.c         $(CC) -M $(CPPFLAGS) $< > $@.$$$$;                      \         sed 's,\($*\)\.o[ :]*,.o $@ : ,g' < $@.$$$$ > $@;     \         rm -f $@.$$$$ 

The subst function is used to transform the source file list into a dependency file list. Since the dependency files appear as an argument to include , they are considered prerequisites and are updated using the %.d rule.


$(patsubst search-pattern , replace-pattern , text )

This is the wildcard version of search and replace. As usual, the pattern can contain a single % . A percent in the replace-pattern is expanded with the matching text. It is important to remember that the search-pattern must match the entire value of text . For instance, the following will delete a trailing slash in text , not every slash in text :

 strip-trailing-slash = $(patsubst %/,%,$(directory-path)) 

Substitution references are a portable way of performing the same substitution. The syntax of a substitution reference is:

 $(   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. That is, whenever it is followed by whitespace or the end of the variable value. In addition, search can contain a % representing a wildcard character; in which case, the search and replace follow the rules of patsubst . I find this syntax to be obscure and difficult to read in comparison to patsubst .

As we've seen, variables often contain lists of words. Here are functions to select words from a list, count the length of a list, etc. As with all make functions, words are separated by whitespace.


$(words text )

This returns the number of words in text .

 CURRENT_PATH := $(subst /, ,$(HOME)) words:         @echo My HOME path has $(words $(CURRENT_PATH)) directories. 

This function has many uses, as we'll see shortly, but we need to cover a few more functions to use it effectively.


$(word n , text )

This returns the n th 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)) 

The variable MAKE_VERSION is a built-in variable. (See the Section 3.8 in Chapter 3.)

You can always get the last word in a list with:

 current := $(word $(words $(MAKEFILE_LIST)), $(MAKEFILE_LIST)) 

This 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. As with the word function, the first word is numbered 1. If start is greater than the number of words, the value is empty. If start is greater than end , the value is empty. If end is greater than the number of words, all words from start on are returned.

 # $(call uid_gid, user-name) uid_gid = $(wordlist 3, 4, \             $(subst :, ,   \               $(shell grep "^:" /etc/passwd))) 

4.2.2 Important Miscellaneous Functions

Before we push on to functions for managing filenames, let's introduce two very useful functions: sort and shell .


$(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. In addition, sort strips leading and trailing blanks.

 $ make -f- <<< 'x:;@echo =$(sort    d  b s   d      t   )=' =b d s t= 

The sort function is, of course, implemented directly by make , so it does not support any of the options of the sort program. The function operates on its argument, typically a variable or the return value of another make function.


$(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.

 stdout := $(shell echo normal message) stderr := $(shell echo error message 1>&2) shell-value:         # $(stdout)         # $(stderr) 

As you can see, messages to stderr are sent to the terminal as usual and so are not included in the output of the shell function:

 $ make error message # normal message # 

Here is a loop to create a set of directories:

 REQUIRED_DIRS = ... _MKDIRS := $(shell for d in $(REQUIRED_DIRS); \              do                               \                [[ -d $$d ]]  mkdir -p $$d;  \              done) 

Often, a makefile is easier to implement if essential output directories can be guaranteed to exist before any command scripts are executed. 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). By placing this make variable assignment early in the makefile , we ensure it is executed before command scripts or other variables use the output directories. The actual value of _MKDIRS is irrelevant and _MKDIRS itself would never be used.

Since the shell function can be used to invoke any external program, you should be careful how you use it. In particular, you should consider the distinction between simple variables and recursive variables.

 START_TIME   := $(shell date) CURRENT_TIME =  $(shell date) 

The START_TIME variable causes the date command to execute once when the variable is defined. The CURRENT_TIME variable will reexecute date each time the variable is used in the makefile .

Our toolbox is now full enough to write some fairly interesting functions. Here is a function for testing whether a value contains duplicates:

 # $(call has-duplicates, word-list) has-duplicates = $(filter               \                    $(words )          \                    $(words $(sort )))) 

We count the words in the list and the unique list, then "compare" the two numbers . There are no make functions that understand numbers, only strings. To compare two numbers, we must compare them as strings. The easiest way to do that is with filter . We search for one number in the other number. The has-duplicates function will be non-null if there are duplicates.

Here is a simple way to generate a filename with a timestamp:

 RELEASE_TAR := mpwm-$(shell date +%F).tar.gz 

This produces:

 mpwm-2003-11-11.tar.gz 

We could produce the same filename and have date do more of the work with:

 RELEASE_TAR := $(shell date +mpwm-%F.tar.gz) 

The next function can be used to 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,%,)) 

This particular pattern can be accomplished with two subst s as well:

 # $(call file-to-class-name, file-name) file-to-class-name := $(subst /,.,$(subst .java,,)) 

We can then use this function to invoke the Java class like this:

 CALIBRATE_ELEVATOR := com/wonka/CalibrateElevator.java calibrate:         $(JAVA) $(call file-to-class-name,$(CALIBRATE_ELEVATOR)) 

If there are more parent directory components in $(sources) above com , they can be removed with the following function by passing the root of the directory tree as the first argument: [3]

[3] In Java, it is suggested that all classes be declared within a package containing the developer's complete Internet domain name, reversed . Also, the directory structure typically mirrors the package structure. Therefore, many source trees look like root-dir /com/ company-name / dir .

 # $(call file-to-class-name, root-dir, file-name) file-to-class-name := $(subst /,.,          \                         $(subst .java,,     \                           $(subst /,,))) 

When reading functions such as this, it is typically easiest to try to understand them inside out. Beginning at the inner-most subst , the function removes the string $1/ , then removes the string .java , and finally converts all slashes to periods.

4.2.3 Filename Functions

Makefile writers spend a lot of time handling files. So it isn't surprising there are a lot of make functions to help with this task.


$wildcard pattern . . . )

Wildcards were covered in Chapter 2, in the context of targets, prerequisites, and command scripts. But what if we want this functionality in another context, say a variable definition? With the shell function, we could simply use the subshell to expand the pattern, but that would be terribly slow if we needed to do this very often. Instead, we can use the wildcard function:

 sources := $(wildcard *.c *.h) 

The wildcard function accepts a list of patterns and performs expansion on each one. [4] If a pattern does not match any files, the empty string is returned. As with wildcard expansion in targets and prerequisites, the normal shell globbing characters are supported: ~ , * , ? , [...] , and [^...] .

[4] The make 3.80 manual fails to mention that more than one pattern is allowed.

Another use of wildcard is to test for the existence of a file in conditionals. When used in conjunction with the if function (described shortly) you often see wildcard function calls whose argument contains no wildcard characters at all. For instance,

 dot-emacs-exists := $(wildcard ~/.emacs) 

will return the 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 . Here is an expression to return every subdirectory that contains C files:

 source-dirs := $(sort                          \                  $(dir                         \                    $(shell find . -name '*.c'))) 

The find returns all the source files, then the dir function strips off the file portion leaving the directory, and the sort removes duplicate directories. Notice that this variable definition uses a simple variable to avoid reexecuting the find each time the variable is used (since we assume source files will not spontaneously appear and disappear during the execution of the makefile ). Here's a function implementation that requires a recursive variable:

 # $(call source-dirs, dir-list) source-dirs = $(sort                             \                 $(dir                            \                   $(shell find  -name '*.c')))) 

This version accepts a space-separated directory list to search as its first parameter. 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. (A find feature I didn't know about for several decades!)


$(notdir name . . . )

The notdir function returns the filename portion of a file path. Here is an expression to return the Java class name from a Java source file:

 # $(call get-java-class-name, file-name) get-java-class-name = $(notdir $(subst .java,,)) 

There are many instances where dir and notdir can be used together to produce the desired output. For instance, 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 $@) 

The automatic variable, $@ , representing the target, can be decomposed to yield the target directory and file as separate values. In fact, if OUT is an absolute path, it isn't necessary to use the notdir function here, but doing so will make the output more readable.

In command scripts, another way to decompose a filename is through the use of $(@D) and $(@F) as mentioned in Section 2.2.1 in Chapter 2.

Here are functions for adding and removing file suffixes, etc.


$(suffix name . . . )

The suffix function returns the suffix of each word in its argument. Here is a function to test whether all the words in a list have the same suffix:

 # $(call same-suffix, file-list) same-suffix = $(filter 1 $(words $(sort $(suffix )))) 

A more common use of the suffix function is within conditionals in conjunction with findstring .


$(basename name . . . )

The basename function is the complement of suffix . It returns the filename without its suffix. Any leading path components remain intact after the basename call. Here are the earlier file-to-class-name and get-java-class-name functions re-written with basename :

 # $(call file-to-class-name, root-directory, file-name) file-to-class-name  := $(subst /,.,          \                          $(basename          \                            $(subst /,,))) # $(call get-java-class-name, file-name) get-java-class-name =  $(notdir $(basename )) 


$(addsuffix suffix , name . . . )

The addsuffix function appends the given suffix text to each word in name . The suffix text can be anything. Here is a function to find all the files in the PATH that match an expression:

 # $(call find-program, filter-pattern) find-program = $(filter ,                     \                  $(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 .

Although this may seem like an extremely slow function to run (and it may well be on many systems), on my 1.9 GHz P4 with 512 MB this function executes in 0.20 seconds and finds 4,335 programs. This performance can be improved by moving the $1 argument inside the call to wildcard . The following version eliminates the call to filter and changes addsuffix to use the caller's argument.

 # $(call find-program,wildcard-pattern) find-program = $(wildcard                       \                  $(addsuffix /,               \                    $(sort                       \                      $(subst :, ,               \                        $(subst ::,:.:,          \                          $(patsubst :%,.:%,     \                            $(patsubst %:,%:.,$(PATH)))))))) find:         @echo $(words $(call find-program,*)) 

This version runs in 0.17 seconds. It runs faster because wildcard no longer returns every file only to make the function discard them later with filter . A similar example occurs in the GNU make manual. Notice also that the first version uses filter -style globbing patterns (using % only) while the second version uses wildcard -style globbing patterns ( ~ , * , ? , [...] , and [^...] ).


$(addprefix prefix , name . . . )

The addprefix function is the complement of addsuffix . Here is an expression to test whether a set of files exists and is nonempty :

 # $(call valid-files, file-list) valid-files = test -s . $(addprefix -a -s ,) 

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 )

The join function is the complement of dir and notdir . It accepts two lists and concatenates the first element from prefix-list with the first element from suffix-list , then the second element from prefix-list with the second element from suffix-list and so on. It can be used to reconstruct lists decomposed with dir and notdir .

4.2.4 Flow Control

Because many of the functions we have seen so far are implemented to perform their operations on lists, they work well even without a looping construct. But without a true looping operator and conditional processing of some kind the make macro language would be very limited, indeed. Fortunately, make provides both of these language features. I have also thrown into this section the fatal error function, clearly a very extreme form of flow control!


$(if condition , then-part , else-part )

The if function (not to be confused with the conditional directives ifeq , ifneq , ifdef , and ifndef discussed in Chapter 3) 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). In this case, the then-part is expanded. Otherwise, if the expansion of condition is empty, it is false and the else-part is expanded. [5]

[5] In Chapter 3, I made a distinction between macro languages and other programming languages. Macro languages work by transforming source text into output text through defining and expanding macros. This distinction becomes clearer as we see how the if function works.

Here is 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),;,:) 

make evaluates the condition by first removing leading and trailing whitespace, then expanding the expression. If the expansion yields any characters (including whitespace), the expression is true. Now PATH_SEP contains the proper character to use in paths, whether the makefile is running on Windows or Unix.

In the last chapter, we mentioned checking the version of make if you use some of the newest features (like eval ). The if and filter functions are often used together to test the value of a string:

 $(if $(filter $(MAKE_VERSION),3.80),,\   $(error This makefile requires GNU make version 3.80.)) 

Now, as subsequent versions of make are released, the expression can be extended with more acceptable versions:

 $(if $(filter $(MAKE_VERSION),3.80 3.81 3.90 3.92),,\   $(error This makefile requires one of GNU make version ....)) 

This technique has the disadvantage that the code must be updated when a new version of make is installed. But that doesn't happen very often. (For instance, 3.80 has been the release version since October 2002.) The above test can be added to a makefile as a top-level expression since the if collapses to nothing if true and error terminates the make otherwise.


$(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. Here is an implementation of the common assert programming construct for make :

 # $(call assert,condition,message) define assert   $(if ,,$(error Assertion failed: )) endef # $(call assert-file-exists,wildcard-pattern) define assert-file-exists   $(call assert,$(wildcard ), does not exist) endef # $(call assert-not-null,make-variable) define assert-not-null   $(call assert,$(),The variable "" is null) endef error-exit:         $(call assert-not-null,NON_EXISTENT) 

The first function, assert , just tests its first argument and prints the user's error message if it is empty. The second function builds on the first and tests that a wildcard pattern yields an existing file. Note that the argument can include any number of globbing patterns.

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. But if a variable contains the name of another variable how can you access the value of that other variable? Well, very simply by expanding the variable twice:

 NO_SPACE_MSG := No space left on device. NO_FILE_MSG  := File not found. ...; STATUS_MSG   := NO_SPACE_MSG $(error $($(STATUS_MSG))) 

This example is slightly contrived to keep it simple, but here 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) ). In our assert-not-null function we assume the argument to the function is the name of a make variable. We first expand the argument, $1 , to access the variable name, then expand again, $($1 ), to determine if it has a value. If it is null, then we have the variable name right in $1 to use in the error message.

 $ make Makefile:14: *** Assertion failed: The variable "NON_EXISTENT" is null.  Stop. 

There is also a warning function (see the Section 4.2.5 later in this chapter) that prints a message in the same format as error , but does not terminate make .


$(foreach variable , list , body )

The foreach function provides a way to expand text repeatedly while substituting different values into each expansion. Notice that this is different from executing a function repeatedly with different arguments (although it can do that, too). For example:

 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.

Here is 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 .) This loop sets i to each word of VARIABLE_LIST . 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 the expression has a value, the then part does nothing; otherwise, the else part prints a warning. Note that if we omit the redirection from the echo, the output of the shell command will be substituted into the makefile , yielding a syntax error. As shown, the entire foreach loop expands to nothing.

As promised earlier, here is a function that gathers all the words that contain a substring from a list:

 # $(call grep-string, search-string, word-list) define grep-string $(strip                                         \   $(foreach w, ,                               \     $(if $(findstring , $w),                   \       $w))) endef words := count_words.c counter.c lexer.l lexer.h counter.h find-words:         @echo $(call grep-string,un,$(words)) 

Unfortunately, this function does not accept patterns, but it does find simple substrings:

 $ make count_words.c counter.c counter.h 

4.2.4.1 Style note concerning variables and parentheses

As noted earlier, parentheses are not required for make variables of one character. For instance, all of the basic automatic variables are one character. Automatic variables are universally written without parentheses even in the GNU make manual. However, the make manual uses parentheses for virtually all other variables, even single character variables, and strongly urges users to follow suit. This highlights the special nature of make variables since almost all other programs that have "dollar variables" (such as shells, perl , awk , yacc , etc.) don't require parentheses. One of the more common make programming errors is forgetting parentheses. Here is a common use of foreach containing the error:

 INCLUDE_DIRS := ... INCLUDES := $(foreach i,$INCLUDE_DIRS,-I $i) # INCLUDES now has the value "-I NCLUDE_DIRS" 

However, I find that reading macros can be much easier through the judicious use of single-character variables and omitting unnecessary parentheses. For instance, I think the has-duplicates function is easier to read without full parentheses:

 # $(call has-duplicates, word-list) has-duplicates = $(filter               \                    $(words )          \                    $(words $(sort )))) 

versus:

 # $(call has-duplicates, word-list) has-duplicates = $(filter                 \                    $(words $(1))          \                    $(words $(sort $(1))))) 

However, the kill-program function might be more readable with full parentheses since it would help distinguish make variables from shell variables or variables used in other programs:

 define kill-program   @ $(PS) $(PS_FLAGS)                                  \   $(AWK) 'BEGIN { FIELDWIDTHS = $(PS_FIELDS) }          \           /$(1)/{                                       \                   print "Killing " $;                 \                   system( "$(KILL) $(KILLFLAGS) " $ ) \                 }' endef 

The search string contains the first parameter to the macro, $(1) . $$3 and $$1 refer to awk variables.

I use single-character variables and omit the parentheses only when it seems to make the code more readable. I typically do this for the parameters to macros and the control variable in foreach loops . You should follow a style that suits your situation. If you have any doubts about the maintainability of your makefile s, follow the make manual's suggestion and use full parentheses. Remember, the make program is all about easing the problems associated with maintaining software. If you keep that in mind as you write your makefile s, you will most likely stay clear of trouble.

4.2.5 Less Important Miscellaneous Functions

Finally, we have some miscellaneous (but important) string functions. Although minor in comparison with foreach or call , you'll find yourself using these very often.


$(strip text )

The strip function removes all leading and trailing whitespace from text and replaces all internal whitespace with a single space. A common use for this function is to clean up variables used in conditional expressions.

I most often use this function to remove unwanted whitespace from variable and macro definitions I've formatted across multiple lines. But it can also be a good idea to wrap the function parameters $1 , $2 , etc., with strip if the function is sensitive to leading blanks. Often programmers unaware of the subtleties of make will add a space after commas in a call argument list.


$(origin variable )

The origin function returns a string describing the origin of a variable. This can be very useful in deciding how to use the value of a variable. For instance, you might want to ignore the value of a variable if it came from the environment, but not if it was set from the command line. For a more concrete example, here is a new assert function that tests if a variable is defined:

 # $(call assert-defined,variable-name) define assert-defined   $(call assert,                          \     $(filter-out undefined,$(origin )), \     '' 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. Like the error function, 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.)) 



Managing Projects with GNU make
Managing Projects with GNU Make (Nutshell Handbooks)
ISBN: 0596006101
EAN: 2147483647
Year: 2003
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net