Make: Sorting and Searching


I use GNU Make's $(warning) function to print out the result of calling my-sort):

NUMBERS := 1 12 4 6 8 3 4 5 9 10

$(warning $(call my-sort,,$(NUMBERS))

which will output (the Makefile:2: part was generated by $(warning); the rest is the actual sorted list):

Makefile:2: 1 10 12 3 4 4 5 6 8 9

Or you could sort numerically with the -n option:

NUMBERS := 1 12 4 6 8 3 4 5 9 10

$(warning $(call my-sort,-n,$(NUMBERS))

which will output

Makefile:2: 1 3 4 4 5 6 8 9 10 12

Or even numerically, but in reverse order:

NUMBERS := 1 12 4 6 8 3 4 5 9 10

$(warning $(call my-sort,-n -r,$(NUMBERS))

which will output:

Makefile:2: 12 10 9 8 6 5 4 4 3 1

Dealing with duplicates

Since $(sort) always removes duplicates it's unsuitable for use if you need to sort a list but preserve duplicated entries. However, my-sort above works just fine for that purpose. If no options are specified in the first argument of my-sort then duplicates are not removed.

For example, you can sort the list a b a a c with my-sort like this:

DUPS := a b a a c

$(warning $(call my-sort,,$(DUPS)))

and the output is:

Makefile:2: a a a b c

In fact, the only way my-sort will remove duplicates is if the -u option is specified:

DUPS := a b a a c

$(warning $(call my-sort,-u,$(DUPS)))

and the output is:

Makefile:2: a b c

Another way to handle list deduplication is with the GNU Make Standard Library. It includes a function called uniq which removes duplicates from a list without sorting it. The first occurrence of each duplicated element is preserved.

For example, with the GNU Make Standard Library included (see for more details) using include gmsl , the list b a c a a d can be deduplicated while preserving order:

include gmsl

DUPS := b a c a a d

$(warning $(call uniq,$(DUPS)))

which will output:

Makefile:4: b a c d

Searching for a file

Enough sorting, let's look at searching. First, searching for a file. As with sorting there are two options: use a built-in function and spawn a shell and use a shell program.

The built-in file searching function is called $(wildcard). Its argument is a pattern to search for on the file system and it returns a list of files and directories that match the pattern. It uses the same wild card patterns as the Bourne shell and so it's possible to use *, ? and ranges and character classes.

For example, to return a list of all files ending .c in the current directory you would write $(wildcard *.c) . To search all subdirectories of the current directory for .c files you can write $(wildcard */*.c) and so on.

$(wildcard) can also be used to find directories. If the pattern ends with / then the function only returns directories that match the pattern, and each directory will be suffixed with /. For example, to get a list of all the directories in the current directory do $(wildcard */) .

There are a few of $(wildcard) gotchas though:

1. $(wildcard) does not provide any way to recursively search a tree of directories.

2. $(wildcard) interacts in a surprising way with GNU Make's built-in directory cache mechanism which can mean that $(wildcard) doesn't reflect the actual state of the file system (for more on this see my article The Trouble with $(wildcard)

3. $(wildcard) doesn't work correctly if the pattern has a space

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.