The Trouble with $(wildcard)

[article]
Summary:

$(wildcard) explained can be used anywhere in a Makefile or rule to get a list of files matching one or more glob style patterns.

$(wildcard) explained can be used anywhere in a Makefile or rule to get a list of files matching one or more glob style patterns.

For example, $(wildcard *.foo) returns a list of files ending in .foo. Recall that a list is a string where list elements are separated by spaces, so $(wildcard *.foo) might return a.foo b.foo c.foo. (If a filename itself contains a space then the returned list may appear incorrect because there's no way to spot the different between the list separator (a space) and then space in a filename.)

$(wildcard) can be called with a list of patterns, so $(wildcard *.foo *.bar) returns all the files ending in .foo or .bar. The $(wildcard) operator supports the following 'globbing' operators: * (match 0 or more characters), ? (match 1 character), and [...] (matches characters [123] or a range of characters [a-z]).

Another useful feature of $(wildcard) is that if the filename passed to it does not contain a pattern then that file is simply checked for existence. If the file exists then its name is returned, otherwise $(wildcard) returns an empty string. Thus $(wildcard) can be combined with $(if) to create an if-exists function:

if-exists = $(if ($wildcard $1),$2,$3)

if-exists has three parameters: the name of the filename to check for, what to do if the file exists, and what to do if it does not. Here's a simple example of its use:

$(warning a.foo is $(call if-exists,a.foo,there,not there))

which will print a.foo is there if a.foo exists, or a.foo is not there if not.
Some unexpected resultsEach of these examples use two variables for obtaining a list of files ending in .foo in a particular directory: WILDCARD_LIST and LS_LIST return the list of files ending in .foo by calling $(wildcard) and $(shell ls). The variable DIRECTORY holds the directory in which the examples look for files; for the current directory DIRECTORY is left empty.

The starting Makefile looks like this:

WILDCARD_LIST = wildcard returned \'$(wildcard $(DIRECTORY)*.foo)\'
LS_LIST = ls returned \'$(shell ls $(DIRECTORY)*.foo)\'

.PHONY: all
all:
@echo $(WILDCARD_LIST)
@echo $(LS_LIST)

With a single file a.foo in the current directory running make results in:

wildcard returned 'a.foo'
ls returned 'a.foo'

Now extend the Makefile so that it makes a file called b.foo using touch (I've skipped the declarations of WILDCARD_LIST and LS_LIST here since they are identical):

.PHONY: all
all: b.foo
@echo $(WILDCARD_LIST)
@echo $(LS_LIST)

b.foo:
@touch $@

Running this Makefile through make (with just the preexisting a.foo file) results in the following surprising output:

wildcard returned 'a.foo'
ls returned 'a.foo b.foo'

The ls is returning the correct list (since b.foo has been created by the time the all rule runs), but $(wildcard) is not; $(wildcard) appears to be showing the state before b.foo was created. Working with the .foo files in a subdirectory (not in the current working directory) results in different output. Here I've updated the Makefile so that it uses the $(DIRECTORY) variable to specify the subdirectory subdir. Once again there's a single preexisting file subdir/a.foo and the Makefile will create subdir/b.foo.

DIRECTORY=subdir/

.PHONY: all
all: $(DIRECTORY)b.foo
@echo $(WILDCARD_LIST)
@echo $(LS_LIST)

$(DIRECTORY)b.foo:
@touch $@

Running this Makefile results in:

wildcard returned 'subdir/a.foo subdir/b.foo'
ls returned 'subdir/a.foo subdir/b.foo'

Here both $(wildcard) and ls return the same results and both show the presence of the two .foo files: subdir/a.foo which existed before make was run and subdir/b.foo that was created by the Makefile. One final Makefile before getting down to an explanation of what's happening. In this Makefile $(warning) is used to print out a list of the .foo files that already exist in the subdirectory:

DIRECTORY=subdir/

About the author

AgileConnection is a TechWell community.

Through conferences, training, consulting, and online resources, TechWell helps you develop and deliver great software every day.