plexando
plexando

Reputation: 1261

GNU Make wildcard function and terminating slashes on directory names

I have some issues with the behavior of the wildcard function of GNU Make with respect to terminating slashes in the pattern and the output.

Consider the following simple directory structure:

dir
|
+-- file
|
+-- subdir

On Linux,

$(wildcard dir/*/)   # (1)

evaluates with GNU Make 4.1 to

dir/subdir/ dir/file

but with GNU Make 4.3 to

dir/subdir/

One could argue whether including the regular file filein the former case is a bug or a feature (names of directories but not those of regular files are terminated with a slash). However, both versions of GNU Make evaluate

$(wildcard $(addsuffix /,$(wildcard dir/*)))   # (2)

to

dir/file dir/subdir/

(subject to sorting). In particular, $(wildcard dir/file/) evaluates to dir/file. This is more in the spirit of the above GNU Make 4.1 feature but seems to be somewhat inconsistent with respect to GNU Make 4.3.

What can I assume from the wildcard function regarding a terminating slash in the pattern?

I would like to determine the content of a directory such that the names of subdirectories are terminated by a slash while the names of regular files are not. In GNU Make 4.1 I used approach 1 which broke my build with GNU Make 4.3. In both cases I could use approach 2. But is this feasible or do I rely on undefined behavior here? If so, what would be the correct (and efficient) way to do what I want?

Upvotes: 0

Views: 635

Answers (2)

Vroomfondel
Vroomfondel

Reputation: 2898

The function wildcard-rec in the GNUmake table toolkit does exactly what you want. It distinguishes between files and directories via a obvious feature: if the given glob ends in / then you want directories, if the / is absent you want files.

include gmtt.mk

$(info $(call wildcard-rec,**.c)) # all C source files in the tree

$(info $(call wildcard-rec,**.c **.h)) # a C source and header files

$(info $(call wildcard-rec,drivers/**.c)) # only sources for the `drivers` tree

$(info $(call wildcard-rec,drivers/**/test/)) # all test subdirectories in the `drivers` tree

$(info $(call wildcard-rec,drivers/**/test/*.cfg)) # config files in all test subdirectories in the `drivers` tree

Upvotes: 0

MadScientist
MadScientist

Reputation: 100866

The problem is not simple. The short answer is that the behavior of GNU make 4.3 is correct for the expansion of dir/*/ and the behavior of earlier versions of make that don't agree with that, are wrong.

As for the behavior of dir/file/ that seems to me to be wrong in all versions of GNU make; that is, it should return the empty string.

However, GNU make doesn't actually implement its own file globbing, at least not on systems that provide the GNU libc C runtime library, which is most Linux systems. It simply calls the system-provided glob(3) function. I wrote a small test program that simply calls GNU libc's glob(3) function directly and it gives the same behavior as GNU make 4.3:

  • dir/*/ -> dir/subdir/
  • dir/file/ -> dir/file/

In my opinion this is a bug in GNU libc's glob(3) but perhaps I'm missing some subtlety here.

In any event, if what you really want is just directories then the best/safest/works everywhere solution is to use this:

$(wildcard dir/*/.)

then you don't have to worry about magical behaviors related to trailing slashes.

Upvotes: 1

Related Questions