I have a "lib" directory in my applications main directory, which contains an arbitrary number of subdirectories, each having its own Makefile.
I would like to have a single Makefile in the main directory, that calls each subdirectory's Makefile. I know this is possible if I manually list the subdirs, but I would like to have it done automatically.
I was thinking of something like the following, but it obviously does not work. Note that I also have clean, test, etc. targets, so % is probably not a good idea at all.
LIBS=lib/* all: $(LIBS) %: (cd $@; $(MAKE))
Any help is appreciated!
The following will work with GNU make
:
LIBS=$(wildcard lib/*) all: $(LIBS) .PHONY: force $(LIBS): force cd $@ && pwd
If there might be something other than directories in lib
, you could alternatively use:
LIBS=$(shell find lib -type d)
To address the multiple targets issue, you can build special targets for each directory, then strip off the prefix for the sub-build:
LIBS=$(wildcard lib/*) clean_LIBS=$(addprefix clean_,$(LIBS)) all: $(LIBS) clean: $(clean_LIBS) .PHONY: force $(LIBS): force echo make -C $@ $(clean_LIBS): force echo make -C $(patsubst clean_%,%,$@) clean
There is also a way of listing sub-directories with gmake commands only, without using any shell commands:
test: @echo $(filter %/, $(wildcard lib/*/))
This will list all sub-directories with trailing '/'
. To remove it you can use the substitute pattern:
subdirs = $(filter %/, $(wildcard lib/*/)) test: @echo $(subdirs:%/=%)
Then to actually create rules executing makefiles in each sub-directory you can use a small trick - a phony target in a non-existent directory. I think in this case an example will tell more than any explanation:
FULL_DIRS =$(filter %/, $(wildcard lib/*/)) LIB_DIRS =$(FULL_DIRS:%/=%) DIRS_CMD =$(foreach subdir, $(LIB_DIRS), make-rule/$(subdir)) make-rule/%: cd $* && $(MAKE) all: DIRS_CMD
Basically, target 'all'
lists all sub-directories as prerequisites. For example, if LIB_DIRS
contained lib/folder1 lib/folder2
then the expansion would look like this:
all: make-rule/lib/folder1 make-rule/lib/folder2
Then 'make', in order to execute rule 'all'
, tries to match each prerequisite with an existing target. In this case the target is 'make-rule/%:'
, which uses '$*'
to extract the string after 'make-rule/'
and uses it as argument in the recipe. For example, the first prerequisite would be matched and expanded like this:
make-rule/lib/folder1: cd lib/folder1 && $(MAKE)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With