78

I have a C++ library built using a Makefile. Until recently, all the sources were in a single directory, and the Makefile did something like this

SOURCES = $(wildcard *.cpp)

which worked fine.

Now I've added some sources that are in a subdirectory, say subdir. I know I can do this

SOURCES = $(wildcard *.cpp) $(wildcard subdir/*.cpp)

but I'm looking for a way to avoid specifying subdir manually, that is, make wildcard look into subdirectories, or generating a list of subdirectories somehow and expanding it with several wildcard functions. At this point, having a non-recursive solution (that is, expanding only the first level) would be fine.

I haven't found anything - my best guess is using find -type d to list the subdirectories, but it feels like a hack. Is there any built-in way to do this?

Yukulélé
  • 15,644
  • 10
  • 70
  • 94
ggambetta
  • 3,312
  • 6
  • 32
  • 42
  • 1
    possible duplicate of [Recursive wildcards in GNU make?](http://stackoverflow.com/questions/2483182/recursive-wildcards-in-gnu-make) – Jeroen Nov 05 '13 at 19:41
  • 1
    @Jeroen should be the other way around as this question has a superior answer (using `**`). –  Mar 13 '16 at 10:28

6 Answers6

102

This should do it:

SOURCES = $(wildcard *.cpp) $(wildcard */*.cpp)

If you change you mind and want a recursive solution (i.e. to any depth), it can be done but it involves some of the more powerful Make functions. You know, the ones that allow you to do things you really shouldn't.

EDIT:
Jack Kelly points out that $(wildcard **/*.cpp) works to any depth, at least on some platforms, using GNUMake 3.81. (How he figured that out, I have no idea.)

Beta
  • 96,650
  • 16
  • 149
  • 150
  • 36
    To search at any depth, I think `$(wildcard **/*.cpp)` would work. – Jack Kelly Oct 27 '10 at 23:24
  • 4
    @Jack Kelly: I just tried that and it didn't work (GNU Make 3.81). Does it work with your version? – Beta Oct 28 '10 at 01:40
  • Yes. I'm also on GNU Make 3.81, so perhaps it's due to something else (glob?) behaving differently on our platforms? I'm on Ubuntu 10.10 amd64. – Jack Kelly Oct 28 '10 at 02:57
  • @Beta I need a recursive solution and **/*.cpp does not work in my GNU Make 3.81, what powerful Make functions should I look at? – ygram Nov 16 '12 at 19:56
  • 12
    `**/*` isn't working for me. OS X 10.8.2, GNU Make 3.81. It's behaving sort of weird actually... – RyanM Nov 16 '12 at 23:47
  • 1
    I hope this is won't be out of topic, but where I can find a full makefile example that compile sources in subfolders as in this answer? thanks – Manu Mar 30 '14 at 20:07
  • 19
    `SOURCES := $(shell find . -name "*.cpp")` also works. – Chnossos May 04 '14 at 14:19
  • 6
    `**/*.cpp` *does* work for me, but it does not include anything in the current directory (i.e. to get behavior similar to `*.cpp */*.cpp */*/*.cpp ...`, you have to do `*.cpp **/*.cpp`), which is rather unfortunate IMO. – Ponkadoodle Nov 05 '15 at 23:59
  • 1
    @JackKelly I would imagine `**/*` pattern to work in shells that have bash's `shopt -s globstar` functionality. From bash man; `globstar If set, the pattern ** used in a pathname expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a /, only directories and subdirectories match` – Dani_l Mar 15 '18 at 08:33
  • 2
    `$(wildcard **/*.cpp)` doesn't work for me. It only searches 1 level down. If I want 2 levels, I have to do `$(wildcard **/**/*.cpp)`. GNU Make 4.3 – 425nesp Apr 11 '20 at 06:50
  • 2
    As Yukulélé points out, `**/*` only works one level down as it is equivalent to `*/*`. Is that not what actually happened to the ones who believe they got it working? – nijoakim May 10 '20 at 14:06
  • Can confirm that `$(wildcard **/*.cpp)` indeed does *not* work, at least in 2023 it doesn't, using GNU make 4.4. – wheeler May 25 '23 at 17:02
33

Recursive wildcards can be done purely in Make, without calling the shell or the find command. Doing the search using only Make means that this solution works on Windows as well, not just *nix.

# Make does not offer a recursive wildcard function, so here's one:
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))

# How to recursively find all files with the same name in a given folder
ALL_INDEX_HTMLS := $(call rwildcard,foo/,index.html)

# How to recursively find all files that match a pattern
ALL_HTMLS := $(call rwildcard,foo/,*.html)

The trailing slash in the folder name is required. This rwildcard function does not support multiple wildcards the way that Make's built-in wildcard function does, but adding that support would be straightforward with a couple more uses of foreach.

LightStruk
  • 1,451
  • 14
  • 17
  • This doesn't work for me. It returns all subdirectories and files regardless of the provided pattern. – Antimony May 05 '14 at 22:25
  • 1
    @Antimony: (In case you did...) Do not add extra spaces within the recursive $(call ...) around commas separating arguments. Adding such spaces alters the result. – Petr Vepřek Dec 16 '17 at 09:31
20

If you don't want to use recursive makefiles, this might give you some ideas:

subdirs := $(wildcard */)
sources := $(wildcard $(addsuffix *.cpp,$(subdirs)))
objects := $(patsubst %.cpp,%.o,$(sources))

$(objects) : %.o : %.cpp
Christoph
  • 164,997
  • 36
  • 182
  • 240
  • 1
    That's a useful list of commands. `subdirs := $(wildcard */)` is what I was looking for! – Mike Mar 21 '13 at 18:55
15

You can use several rules in wildcard:

SOURCES := $(wildcard *.cpp */*.cpp)

if you need more depth:

SOURCES := $(wildcard *.cpp */*.cpp */*/*.cpp */*/*/*.cpp)

Unfortunately, and unlike what we sometimes read, glob (**) is not supported by makefile and will be interpreted as normal wildcard (*).

For example **/*.cpp match dir/file.cpp but neither file.cpp nor dir/sub/file.cpp.

If you need infinite depth use shell and find:

SOURCES := $(shell find . -name "*.cpp")
Yukulélé
  • 15,644
  • 10
  • 70
  • 94
12

Common practice is to put a Makefile in each subdir with sources, then

all: recursive
    $(MAKE) -C componentX
    # stuff for current dir

or

all: recursive
    cd componentX && $(MAKE)
    # stuff for current dir

recursive: true

It may be wise to put settings for each Makefile in a Makefile.inc in the root source directory. The recursive target forces make to go into the subdirectories. Make sure that it doesn't recompile anything in a target requiring recursive.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • Sure, but I'd still have to write "componentX" by hand, which I'm trying to avoid. – ggambetta Oct 27 '10 at 18:12
  • 2
    Please do it this way. Simply compiling and linking every source/object file in every subdir is going to break once you want to build a library, build one file with special compiler settings, write test programs, etc. I always list every single object file in my Makefiles and sometimes every single source file. Listing a few directories to loop over isn't much of a pain. – Fred Foo Oct 27 '10 at 18:25
  • 5
    Don't call `make -C` directly. You need to call `$(MAKE) -C` instead. The version of `make` being run could be different from the system `make`. In addition, aren't you going to set off an infinite loop by running `$(MAKE) $@`? Lastly, recursive make is considered harmful by some. See http://miller.emu.id.au/pmiller/books/rmch/ – Jack Kelly Oct 27 '10 at 23:22
  • 4
    Note: with recursive makefile approach, it's hard to get your dependencies right. Good read: [Resurvie Make Considered Harmfull](http://aegis.sourceforge.net/auug97.pdf) – harmv Feb 26 '14 at 12:36
2

If you can use find shell command, you may define a function to use it.

recurfind = $(shell find $(1) -name '$(2)')
SRCS := $(call recurfind,subdir1,*.c) $(call recurfind,subdir2,*.cc) $(call recurfind,subdir2,*.cu) \
        ...
Achimnol
  • 1,551
  • 3
  • 19
  • 31