Sources from subdirectories in Makefile

Makefile

Makefile Problem Overview


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?

Makefile Solutions


Solution 1 - Makefile

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.)

Solution 2 - Makefile

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.

Solution 3 - Makefile

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

Solution 4 - Makefile

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.

Solution 5 - Makefile

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:

SOURCES := $(shell find . -name "*.cpp")

Solution 6 - Makefile

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) \
        ...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionggambettView Question on Stackoverflow
Solution 1 - MakefileBetaView Answer on Stackoverflow
Solution 2 - MakefileLightStrukView Answer on Stackoverflow
Solution 3 - MakefileChristophView Answer on Stackoverflow
Solution 4 - MakefileFred FooView Answer on Stackoverflow
Solution 5 - MakefileYukuléléView Answer on Stackoverflow
Solution 6 - MakefileAchimnolView Answer on Stackoverflow