Make: Sorting and Searching


in it. That's because all GNU Make functions assume lists split on spaces and have no special escaping or handling of spaces in paths. The work around for this situation is to replace all spaces in the pattern with the ? globbing character and then call $(wildcard):

sp :=

sp += # add space

space-wildcard = $(wildcard $(subst $(sp),?,$1))

space-wildcard takes a single argument which is exactly the same format as $(wildcard) and replaces any spaces with ? before calling the native $(wildcard). Because of this you cannot call space-wildcard with a list of patterns to search.

Another way to search is to use the shell's find command (in a similar manner to the use of sort above). Since find has many more search options than $(wildcard) it's possible to perform much more flexible searches (including recursive descents).

Here's the definition of my-wildcard:

my-wildcard = $(shell find $1 -name '$2' $3 -print;)

It takes three arguments: the first is the directory to start searching in, the second is the pattern to search for, and the last optional argument are any argument to pass directly to find (for example, if you don't want find to recursive the third argument would be -maxdepth 0 ).

A simple example, shows how my-wildcard can be used to recursively search from the current directory for all .c files.

$(warning $(call my-wildcard,.,*.c))

If you don't need all the power of find then a simpler replacement function for $(wildcard) can be created just using echo:

simple-wildcard = $(shell echo $1)

It only takes one argument: the pattern to search for, but it overcomes the directory cache limit mentioned above and can be used as a direct replacement for $(wildcard):

Here's how simple-wildcard can be used to find all the .c files in the parent directory with exactly the same syntax as $(wildcard):

$(call simple-wildcard,../*.c)

Recursively searching for a file

Although you can use my-wildcard above (which used the shell find function to perform recursive searches) it would be nice to perform them using only built-in GNU Make functions to avoid the cost of starting a shell with $(shell).

It's possible to perform a recursive $(wildcard) using the following definition that only uses built-in GNU Make functions and never calls the shell:

search = $(foreach d,$(wildcard $1/*),$(call search,$d)$(filter $(subst *,%,$2),$d))

This search function has two arguments: the first is the directory to start the recursive descent in; the second is the pattern to search for. The pattern is not as flexible as $(wildcard) and only supports a single * wild card.

It works by first calling $(wildcard) to get a list of all files and directories (the $(wildcard $1/*) ) and then for each one it recurses and calls itself. After the recursion returns to the main function the list of files and directories is matched against the pattern using $(filter).

It's here that the 'single * wild card' restriction is created because $(filter) can only accept a single % wild card. Any * present in the second argument to search is converted to % for $(filter) by the $(subst *,%,$2) .

Searching a list of directories for a file

Another common type of file searching is 'in which of these directories can I find file X?' And the most common form of that is searching the path for an executable.

It's fairly easy to define a function, let's call it which-dir, that takes two arguments: a list of directories to search and the name of a file to look for. which-dir will search the list of directories and

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.