How to define a rule with a recursive recipe in GNU make? - recursion

I want to call make files in subfolders from one top make file.
An example of my top make file that works looks like this, where buildPath is a parameter in to the make script:
.PHONY: testSystem
testSystem:
$(MAKE) all -C $(buildPath)/Test1Build
$(MAKE) all -C $(buildPath)/Test2Build
$(MAKE) all -C $(buildPath)/Test3Build
The problem with this solution is that I have to list all subfolders; Test1Build, Test2Build, Test3Build etc.
Is there a way (with make) to define this rule in such a way that the subfolders in the receipt are recursively found without having to list them all?
...or can I solve this problem in a totally different way?
All subfolders begins with Test and ends with Build as a pattern.

It's not hard at all. One simple way:
testSystem:
for d in $(buildPath)/*/.; do \
$(MAKE) all -C $$d; \
done
However, that has many problems. Much more reliable and robust will be this:
subdirs := $(wildcard $(buildPath)/*/.)
testSystem: $(subdirs)
$(subdirs):
$(MAKE) -C $# all
.PHONY: testSystem $(subdirs)
One caveat: if you use parallel make (-j) then you may run into problems with the second solution if the results of the subdirectories depend on each other. If they do then you'll have to declare these dependency relationships in your makefile:
$(buildPath)/foo/. : $(buildPath)/bar/.
etc.

Related

Make rule using `$<` only adding one object to archive library

I feel stupid, but I cannot find the solution myself:
I have a Makefile that correctly builds objects from C source, and I want to place $(OBJECTS) in an archive library.
I'm using GNU Make 4.0.
x.a: $(OBJECTS)
echo "$< ($(OBJECTS))"
$(AR) $(ARFLAGS) $# $<
(Lines are indented with TABs in original. The echo was added for debugging purposes only, The $(AR) $(ARFLAGS) $# $< was copied from make's internal rules)
Make outputs (at the end after the objects were built):
echo "log_thread/log_thread.o (log_thread/log_thread.o log_thread/thread.o)"
log_thread/log_thread.o (log_thread/log_thread.o log_thread/thread.o)
ar rv x.a log_thread/log_thread.o
ar: creating x.a
a - log_thread/log_thread.o
So I expect both objects modules to be added to the library x.a, but $< only contains one object module, as opposed to $(OBJECTS)).
My Make knowledge may be a little rusty, maybe that's why I don't get it.
Of course both object modules exist, and the output was created after having removed x.a (the original file is a bit more complex).
I'm not sure where the confusion lies:
The $< automatic variable contains the first prerequisite only, as per definition.
See the GNU make manual.
To get all prerequisites newer than the target, $? is the correct automatic variable.
So the correct rule probably is:
x.a: $(OBJECTS)
echo "$? ($(OBJECTS))"
$(AR) $(ARFLAGS) $# $?

writing a recursive make recipe with prerequisite on parent directory

I am trying to write a recursive make recipe. In this recipe, each target is dependent on a file with an equal name on the parent directory. A minimal (non-working) example:
foo/.dirstamp:
mkdir $(dir $#)
touch $#
.SECONDEXPANSION:
%/.dirstamp: $$(dir $$*).dirstamp
mkdir $(dir $#)
touch $#
With this example, I would expect make foo/bar/qux/lol/.dirstamp to generate the whole directory tree (if it does not exist), touching all .dirstamp files along the way. However, it does not work:
$ ls # note that there is nothing, make is meant to create the dir tree
Makefile
$ make --debug=v foo/bar/qux/lol/.dirstamp
GNU Make 4.0
[...]
Reading makefiles...
Reading makefile 'Makefile'...
Updating goal targets....
Considering target file 'foo/bar/qux/lol/.dirstamp'.
File 'foo/bar/qux/lol/.dirstamp' does not exist.
Finished prerequisites of target file 'foo/bar/qux/lol/.dirstamp'.
Must remake target 'foo/bar/qux/lol/.dirstamp'.
make: *** No rule to make target 'foo/bar/qux/lol/.dirstamp'. Stop.
It works fine as long as the recursive recipe only needs to be expanded twice, e.g., make foo/bar/.dirstamp works fine.
How can this work for an arbitrary number of levels? How can I handle a recursive expansion for the target and prerequisites names?
Note: my real problem is that the prerequisites of my recipes are in a root
directory different from the target so I am using the recipe above to duplicate the directory tree. I know about mkdir -p which seems to work fine in GNU systems. I am still interested on knowing how I would solve the recursion problem for arbitrary levels. which no longer works because part of the team is using Mac and mounting this directories over smb.
More details on the actual problem: prerequisites are in data/x/y/z while targets go into results/x/y/z. However, the results directory tree does not exist and needs to be created as needed. To solve this, I made the creation of parent directories an order-only prerequisite (via the .dirstamp files on my minimal example above).
can't copy data into results, that's several TB of data;
can't have the targets created in data, that's read-only;
can't use mkdir -p because the results directory will not be local, mounted over smb, and others may use non-GNU systems;
After an hint from #EtanReisner on the question:
make won't apply a rule more than once. That's a built-in (intentional) limitation. Without working around that with manual recursion or manually building the set of targets and using a static pattern rule (which may or may not actually work I'm not sure) there's not much you can do about this.
I worked up this solution:
RESULT_DIRS := $(patsubst data/%, results/%, $(shell find data/* -type d -print))
DIRSTAMPS := $(addsuffix /.dirstamp, $(RESULT_DIRS))
results/.dirstamp:
mkdir $(dir $#)
touch $#
.SECONDEXPANSION:
$(DIRSTAMPS): $$(dir $$(patsubst %/.dirstamp, %, $$#)).dirstamp
mkdir $(dir $#)
touch $#
It will duplicate the data directory tree in results as the dirstamp files are required. They are required by making them prerequisites of the other recipes (note the | which makes them order-only prerequisites):
results/%/foo.analysis: data/%/foo.data | results/%/.dirstamp
$(SOME_ANALYSIS_PROGRAM) $^ > $#

Passing rules to make recursively

It's probably trivial to do this but I can't see how.
I want to have a parent Makefile to decide which Makefile to call recursively based on the value of a variable passed in the command line.
I.e., I want to be able to call my main Makefile with:
make some_rule TARGET=a
or
make some_rule TARGET=b
and have my main Makefile decide based on the value of TARGET which makefile to invoke to run make some_rule. (For example, decide whether to call sub_directory_a/Makefile or sub_directory_b/Makefile to execute rule some_rule.)
Note: I have many different rules, so I do not want my main Makefile to list all the possible rules and for each of them call recursively the correct Makefile. I am hoping my main Makefile can only be a few lines long and not have to be updated whenever I create new rules.
You could do what you describe with
default_target:
%:
$(MAKE) -C some_directory_$(TARGET) $#
The %: rule is a pattern rule in which the pattern matches all rules (called a match-anything rule by the GNU make manual); $# is the current target. Note that the default_target: rule doesn't have a recipe, so calling make without a target will use the recipe of the match-anything rule (the only one that applies and has a recipe) to try to build default_target.
The caveat of this approach is that targets cannot be declared phony. If you want to have phony targets, you'll have to specify the recipe for those targets again, for example
PHONY_TARGETS = all clean distclean
.PHONY: $(PHONY_TARGETS)
$(PHONY_TARGETS):
$(MAKE) -C some_directory_$(TARGET) $#
%:
$(MAKE) -C some_directory_$(TARGET) $#
Unfortunately, I do not know a trick to declare all targets phony, which is what you'd really want to do.
Note that you can use ifeq etc. with the variables you set at the command line if you want to allow more fancy values for TARGET than parts of directory names, such as
%:
ifeq ($(TARGET),gibson)
echo 'Planet $# was successfully hacked.'
else
$(MAKE) -C some_directory_$(TARGET) $#
endif
Also note that a more common way to set common variables for many Makefiles is to put them into a file, often common.mk, and include it from the other Makefiles:
include ../common.mk # to include common.mk from some_directory_a/Makefile
But you'll have to decide yourself which approach is a better fit for your project.

Running make from Outside the Tree

Let's say I have a makefile like the following:
CXXFLAGS := -I./Include
Foo:
$(CXX) $(CXXFLAGS) -o Foo
If the user cds into the source tree and runs make, everything is fine and dandy. However, if make is invoked from somewhere outside the source tree, the include directory will be incorrect.
Using full paths instead of relative paths works, but that destroys the portability of the makefile.
Should I just rely on users invoking make "properly?" Or is there an easy way to get around this?
If you want to have CXXFLAGS be -Isome_dir/Include
when the make is invoked asmake -f some_dir/Makefile,
MAKEFILE_LIST
might meet the purpose.
For example:
MAKEFILE_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
CXXFLAGS := -I$(MAKEFILE_DIR)Include
If your make's version is 3.80 or lower, lastword might not work.
In that case,
$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) will work instead.
EDIT: This answer is for GNU-make.
Use the -C flag.
make -C my_dir
You could present your users a compile command that looks like (cd /to/proper/directory && make -k)

gnu make copy many files to a single location

This question is similar in spirit to question 2543127.
I have a gnu makefile with a list of header files. Each header file may be located in a different directory, e.g.,
HEADERS = $(wildcard *.h) $(wildcard foo/*.h) $(wildcard bar/*.h)
and I want to have the makefile copy all headers to an include directory
INCDIR = ../include
and when a dummy target, e.g., ALL is invoked, it will update the header files in the include directory appropriately, i.e.,
.PHONY: ALL
ALL : $(addprefix $(INCDIR)/,$(notdir $(HEADERS)))
Obviously, I could accomplish what I want quite easily if I knew what the lists of directories were. If I did, then I could write some rules (something) like so (not entirely correct, but you get the jist):
$(addprefix $(INCDIR)/,$(notdir $(filter foo/%.h,$(HEADERS)))) : %.h : foo/%.h
#cp -f $< $#
$(addprefix $(INCDIR)/,$(notdir $(filter bar/%.h,$(HEADERS)))) : %.h : bar/%.h
#cp -f $< $#
$(addprefix $(INCDIR)/,$(notdir $(filter-out bar/%.h,$(filter-out foo/%.h,$(HEADERS))))) : %.h : %.h
#cp -f $< $#
There are two problems with this approach, (1) It becomes tedious as the number of directories increases and (2) I am writing this in a makefile include, which doesn't know directories, all it knows are the variables INCDIR and HEADERS; it does not directly know the directories foo/, bar/, and ./ other than through $(sort $(dir $(HEADERS)))
Question: How can I write a rule to achieve the desired effect under the constraints of only being provided the INCDIR and HEADERS variables.
This should do it:
HFILES = $(notdir $(HEADERS))
DIRS = $(dir $(HEADERS))
TARGETS = $(addprefix $(INCDIR)/, $(HFILES))
all: $(TARGETS)
$(INCDIR)/%.h: %.h
cp $< $#
vpath %.h $(DIRS)
OK. The answer is pretty "easy", although it requires usage of some gnu make that I haven't previously used. My solution, creates a subroutine that requires 2 arguments: (1) the name of the file (sans directory) and (2) the name of the directory in which it resided.
The "subroutine" is a template for a rule. When one evaluates the call to the subroutine, one initiates another rules, just as if one had written it explicitly.
define COPY_HEADER
$$(INCDIR)/$(2) : $(1)$(2)
#cp -f $$< $$#
endef
One then evaluates this subroutine for every header file and passes in the directory part and the file part of each header file.
$(foreach file,$(HEADERS),$(eval $(call COPY_HEADER,$(dir $(file)),$(notdir $(file)))))

Resources