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

John Graham-Cumming's picture John Graham-Cumming

John Graham-Cumming is Co-Founder at Electric Cloud, Inc . Prior to joining Electric Cloud, John was a Venture Consultant with Accel Partners, VP of Internet Technology at Interwoven, Inc. (IWOV), VP of Engineering at Scriptics Corporation (acquired by Interwoven), and Chief Architect at Optimal Networks, Inc. John holds BA and MA degrees in Mathematics and Computation and a Doctorate in Computer Security from Oxford University. John is the creator of the highly acclaimed open source POPFile project. He also holds two patents in network analysis and has others pending.

AgileConnection is one of the growing communities of the TechWell network.

Featuring fresh, insightful stories, TechWell.com is the place to go for what is happening in software development and delivery.  Join the conversation now!