recursive gmake in any subdir with Makefile - gnu-make

I have a two-level heirarchy: a code directory with a dozen or so sub-projects.
I want to be able to issue a gmake <target> in the parent directory, for any target, and have it recurse into all subdirectories with containing a Makefile and repeat the gmake there. Question 1 is: how?
I could manually manage the list of targets and list of subdirectories but I'm sure gmake can do this automatically, and I cannot think of why I would ever want a target or subdirectory excluded.
I used to use a shell loop to loop through subdirectories, but I'd guess there's a gmake way to do this.
The following is as far as I've gotten but it only works for the default target, not my wish. Also, it doesn't even go down into both directories I have, only one.
DIRS := ${dir ${wildcard */Makefile}}
.PHONY: $(DIRS)
$(DIRS):
echo $(DIRS)
cd $#; $(MAKE)
This produces the output:
> gmake
echo HelloWorld/ doc/
HelloWorld/ doc/
cd HelloWorld/; make
make[1]: Entering directory '/t/TimeLike/dev/TL3/code/HelloWorld'
g++ -fPIC -march=native -I. -I/l/demo/include -MT main.o -MMD -MP -MF .deps/main.d -c main.cpp
rm -rf ../../dist/Fedora31-64/HelloWorld
gcc -fPIC -march=native main.o -L. -L/l/demo/include -o ../../dist/Fedora31-64/HelloWorld
make[1]: Leaving directory '/t/TimeLike/dev/TL3/code/HelloWorld'
So question 2: why no Entering directory '/t/TimeLike/dev/TL3/code/doc'? I suppose I don't need to know as the actual solution probably involves a very different approach but still curious.

Let's expand the variable:
.PHONY: HelloWorld/ doc/
HelloWorld/ doc/:
echo HelloWorld/ doc/
cd $#; $(MAKE)
How does make treat a rule with multiple targets? This is identical in every way to this makefile:
.PHONY: HelloWorld/ doc/
HelloWorld/:
echo HelloWorld/ doc/
cd $#; $(MAKE)
doc/:
echo HelloWorld/ doc/
cd $#; $(MAKE)
So you can easily see why you always see both directories output regardless of what is being built.
And, since make always only builds the first explicit target in the makefile, you can see why it only builds HelloWorld/ and not doc/. Generally you would add an initial target that depended on everything you wanted to be built when no target is provided:
.PHONY: all $(DIR)
all: $(DIRS)
$(DIR):
cd $# && $(MAKE)

Many thanks to MadScientist for pointing me in the right direction.
Here is the entire top-level makefile:
SUBDIRS = ${dir ${wildcard */Makefile}}
all $(MAKECMDGOALS):: $(SUBDIRS)
.PHONY: $(SUBDIRS)
$(SUBDIRS)::
$(MAKE) --no-print-directory -C $# $(MAKECMDGOALS)
#echo -----------
#echo
clean veryclean::
rm -rf *~
The wildcard text function in gmake matches the name of all Makefile's in subdirectories, and dir strips the filename off leaving the directory.
Whatever the user specified to make (all, clean, etc.) is stored by gmake in $MAKECMDGOALS. I say: whatever the user just typed, or, if they typed nothing, by default, all depends on all those targets.
Then we tell gmake the targets aren't real files (or directories)(which they by definition are), but phony. They only exist for the purpose of firing off rules.
And in each of those subdirectories that had a Makefile, we recurse. The -C option basically does a cd to that directory first. The --no-print-directory option prevents some messages that annoy me about entering and leaving directories. Instead of those I prefer the separator and some blank space.
Finally, for a few other targets I also do some extra cleanup. The fact that there are multiple lines giving rules for these targets necessitates the double-colon here and on the $(MAKECMDGOALS) line.

Related

Why does gnumake remake the intermediate file?

I have a very simple makefile as below:
.PHONY: clean all
CC = /home/utils/gcc-5.2.0/bin/g++
CFLAGS = -Wall -Werror -fPIC
SRC = $(wildcard *.c)
OBJ = $(subst .c,.o,$(SRC))
.INTERMEDIATE: $(OBJ)
all: test.so
%.o: %.c
$(CC) $(CFLAGS) -o $# -c $<
test.so: $(OBJ)
$(CC) -shared $^ -o $#
clean:
#rm -f *.o *~ *.so
I have only two files in the same directory: a.c and b.c
When I execute "make all", I got the following which is perfect.
/home/utils/gcc-5.2.0/bin/g++ -Wall -Werror -fPIC -o a.o -c a.c
/home/utils/gcc-5.2.0/bin/g++ -Wall -Werror -fPIC -o b.o -c b.c
/home/utils/gcc-5.2.0/bin/g++ -shared a.o b.o -o test.so
rm a.o b.o
However, if I do:
touch a.c; make all
I got the same make execution sequence as above which is not what I expected. There is no dependency between a.c and b.c. What I expect is:
/home/utils/gcc-5.2.0/bin/g++ -Wall -Werror -fPIC -o a.o -c a.c
/home/utils/gcc-5.2.0/bin/g++ -shared a.o b.o -o test.so
rm a.o
I don't understand why b.c is compiled again. According to gnumake manual:
The first difference is what happens if the intermediate file does not exist. If an ordinary file b does not exist, and make considers a target that depends on b, it invariably creates b and then updates the target from b. But if b is an intermediate file, then make can leave well enough alone. It won’t bother updating b, or the ultimate target, unless some prerequisite of b is newer than that target or there is some other reason to update that target.
b.o is an intermediate file, it should not be compiled again since b.c has not changed.
What did I miss?
If b.o is not recompiled, then test.so cannot be created because it depends on b.o. You can't create the shared library from a.o and b.o if b.o was deleted.
If you were trying to create a static library then you could use make's special archive syntax to replace a.o without needing to recompile b.o, but that's not possible unless you use the archive special syntax, and it can't be done for shared libraries such as you're trying to build here.
There's a reason these .o files are not considered intermediate by default, and forcing them to be by adding the .INTERMEDIATE target doesn't mean you can avoid rebuilding them.
Your difficulty stems from the fact that what you mean by an intermediate file
is not what GNU Make means; but the .INTERMEDIATE special target allows you to insist
that a.o and b.o, which in your makefile are not intermediate files in GNU Make's
sense, shall be treated as if they are intermediate in GNU Make's sense, and
then you're taken aback by the consequences.
What you mean by F is an intermediate file is: F is a prerequisite of target T and
F itself has prerequisites. Thus a.o and b.o are intermediate with respect to
test.so because they are prerequisites of that target and themselves have the
respective prerequisites a.c and b.c`.
In your sense of intermediate, making the target test.so does not call for
(a.o|b.o) to be remade unless it is older than (a.c|b.c). So you are taken aback by the fact
that when a.o and b.o are made .INTERMEDIATE, GNU Make always deletes them
when it makes test.so:
b.o is an intermediate file, it should not be compiled again since b.c has not changed. What did I miss?
Chiefly you missed 10.4 Chains of Implicit Rules
Ordinarily, a file cannot be intermediate if it is mentioned in the makefile as a target or prerequisite.
However, you can explicitly mark a file as intermediate by listing it as a prerequisite of the special target .INTERMEDIATE.
This takes effect even if the file is mentioned explicitly in some other way.
So, a.o and b.o would ordinarily not be intermediate in GNU Make's sense,
because they are mentioned in your makefile as prequisites of test.so. But you can
make them be treated as intermediate, in GNU Make's sense, by listing them as prerequisites of .INTERMEDIATE - as you did.
What GNU Make means by intermediate is more technical than what you mean and
is explained in the same section of the manual:
Sometimes a file can be made by a sequence of implicit rules. For example, a file n.o
could be made from n.y by running first Yacc and then cc. Such a sequence is called a chain.
If the file n.c exists, or is mentioned in the makefile, no special searching is required:
make finds that the object file can be made by C compilation from n.c; later on, when
considering how to make n.c, the rule for running Yacc is used. Ultimately both n.c and n.o are updated.
However, even if n.c does not exist and is not mentioned, make knows how to envision it as the
missing link between n.o and n.y! In this case, n.c is called an intermediate file.
Once make has decided to use the intermediate file, it is entered in the data base as
if it had been mentioned in the makefile, along with the implicit rule that says how to create it.
(My emphasis). More briefly, if Make is required to make target output, and discovers a file input such
that:
It has no explicit rule for making output from input, but -
It knows a sequence of implicit (a.k.a builtin) rules by
which a file stage that is not a target or prerequisite in the makefile can
be made from input, and from which output can be made, then it will adopt this
sequence of rules to make output from input via stage, and stage will be
an intermediate file.
Crucially, since stage is not one of your targets, or a prerequisite of any of them, Make
knows that it is merely a disposable by-product of making output from input,
and therefore can be deleted when it has served that purpose.
Here is a simple example project involving files that are really intermediate in GNU Make's sense.
We have some yacc source yacc.y and lex source lex.l, in the project directory,
and we want to build a parser from them, parse, with the following makefile:
Makefile
YFLAGS := -d
OBJS := yacc.o lex.o
.PHONY: all clean
all: parse
parse: $(OBJS)
$(CC) $^ -o $#
clean:
$(RM) parse $(OBJS)
It runs like:
$ make
yacc -d yacc.y
mv -f y.tab.c yacc.c
cc -c -o yacc.o yacc.c
lex -t lex.l > lex.c
cc -c -o lex.o lex.c
cc yacc.o lex.o -o parse
rm lex.c yacc.c
Now you see that Make, using its catalogue of implicit rules, figured out
that yacc.o could be made by compiling the non-existent C source file
yacc.c, and that yacc.c could be produced from the existing source file
yacc.y by running yacc -d yacc.y; mv -f y.tab.c yacc.c. Likewise, it figured out that lex.o
could be made by compiling the nonexistent C source lex.c, and that lex.c
could be made from lex.l by running lex -t lex.l > lex.c. Then parse is
made from yacc.o and lex.o by the routine linkage that the makefile
specifies.
But your makefile says nothing whatever about yacc.c and lex.c. As far as
you have told Make, it does not matter to you whether or not such files ever exist.
They are intermediate files, merely stages in the .y -> .o and .l -> .o productions. So
when Make is done with them:
rm lex.c yacc.c
Then if you:
$ touch yacc.y
and again:
$ make
yacc -d yacc.y
mv -f y.tab.c yacc.c
cc -c -o yacc.o yacc.c
cc yacc.o lex.o -o parse
rm yacc.c
only yacc.o gets remade for the linkage of parse, and the one
intermediate file yacc.c that was generated this time is deleted.
Bottom line
Your object files are not intermediate in GNU Make's sense and you don't
want them to be treated as if they are. This is normal for object files.
So remove .INTERMEDIATE: $(OBJ).

How to write a target in GNUmake to only create *.o files

Is there a way to write a target to
1) only create object files?
2) only to link object files and create the binary file?
I would like to be able create my binary file in 2 steps.
There is an implicit rule for that. Let's say you have the following Makefile:
CC=cc -g
all: client
client: client.c
$(CC) client.c -o client
clean:
-rm -f client
If you only want the object file, then you just need to run:
$ make client.o
And you will get the object file. However, you can also write an explicit rule, such as:
%.o: %.c
$(CC) -c $<
The previous rule is a rule to build from any .c file to an object (.o) file. $< helps to get the name of file where the rule depends on.
If you have several objects files, you might want to define variables then:
objects = client.o foo.o bar.o
client: $(objects)
$(cc) -o $# $(objects)
$(objects): config.h
clean:
-rm -f client $(objects)
In this case, objects is a variable associated with the object files you want to compile. Which is used in the rule client as a dependency and as argument to link them, it is also used to define rules that depends on header files (config.h in this example), and finalle is used in the clean rule to delete them to start all over again.
$# is a replacement for the name of the rule. In the last case it would be client.
The manual of GNU Make contains a lot of examples that should enlighten your learn process.

How can I get make to put the binaries in a different location?

Short and easy question, but I seem to have a writer's block here:
Suppose I have a source code file in the same directory as the makefile I use to build the program:
confus#confusion:~/prog$ ls
binaries includes main.c Makefile
How do I get make to put the binaries for my main.c in the binaries dir? Afterwards on a second run make should see if the binary file there is up to date (and don't compile it again) just like normal.
My thought was something like this:
# Makefile
.PHONY: all
SOURCES := $(wildcard *.c)
TARGETS := $(subst %.c,binaries/%.o,$(SOURCES))
all:$(TARGETS)
$(TARGETS):$(SOURCES)
./compile "$(subst .o,.c,$(#F))" -o "$#"
Don't say all targets depend on all sources, instead have a pattern rule
binaries/%.o: %.c
./compile ... -o $# -c $<
you may also need to use a vpath
Revised:
You also had a problem with your subst ...
this test worked (just for compiling individual .o files, you still need to link them, which would be a very simple rule)
# Makefile
.PHONY: all
SOURCES := $(wildcard *.c)
TARGETS := $(patsubst %.c,binaries/%.o,$(SOURCES))
all:$(TARGETS)
binaries/%.o: %.c
$(CC) -o $# -c $<

Out of tree builds with makefiles and static pattern rules

I'm working on some bare-metal embedded code that runs on ARM, and thus has to deal with the whole ARM vs. THUMB mode distinction. The current build system uses static pattern rules to determine whether to compile files in ARM or THUMB mode.
$(ACOBJS) : %.o : %.c
#echo
$(CC) -c $(CFLAGS) $(AOPT) -I . $(IINCDIR) $< -o $#
$(TCOBJS) : %.o : %.c
#echo
$(CC) -c $(CFLAGS) $(TOPT) -I . $(IINCDIR) $< -o $#
Where ACOBJS is a list of output objects that should be in ARM mode and the same for TCOBJS and Thumb mode. These lists are created from the list of sources in the usual manner of
ACOBJS = $(ACSRC:.c=.o)
TCOBJS = $(TCSRC:.c=.o)
Currently this results in the object files from the build being strewn about the source tree, which I don't particularly desire. I've been trying to set this up for out of tree builds but haven't been able to get this to work. I don't necessarily need to get full out of tree builds working, but I would like to at least be able to use an output directory under which all the intermediate files end up going. What is the best strategy to achieve this under these constraints?
One option I'm considering is using either automake or the whole autotools toolchain to build a makefile. This would seem to support creating the type of makefile I want, but seems like overkill. It also seems like there would be an inherent impedance mismatch between autotools, which is designed for portable builds, and bare-metal embedded systems, where things like host tuple are dictated by the target micro.
This is a bit old but I was just trying to do the same thing this was the first google hit. I thought it was worth sharing another approach since neither answer is convenient if you're not using autotools and want to be able to build in any directory with a single command and later just blow away that directory.
Here's an example of a Makefile that refers to files relative to the directory containing the Makefile.
MAKEFILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
MFD := $(MAKEFILE_DIR)
CXX=g++
CXXFLAGS=-std=c++14 -Wall -Wextra -pedantic -c
test: test.o adjacency_pointers_graph.o
$(CXX) $^ -o $#
%.o: $(MFD)/%.cpp $(MFD)/adjacency_pointers_graph.h
$(CXX) $(CXXFLAGS) $< -o $#
Then to do an sort of source build:
mkdir build
cd build
make -f ../Makefile
Considering/assuming you don't care about portability and are using GNU make, you can use the VPATH feature:
Create the directory where you want to do your build.
Create a 'Makefile' in that directory with (approximately) the following contents:
path_to_source = ..
VPATH = $(path_to_source)
include $(path_to_source)/Makefile
Change the path_to_source variable to point to the root of your source tree.
Additionally you probably need to tweak your original Makefile to make sure that it supports the out of source build. For example, you can't reference to prerequisites from your build rules and instead must use $^ and $<. (See GNU make - Writing Recipes with Directory Search) You might also need to modify the vpath-makefile. For example: adding CFLAGS+=-I$(path_to_source) might be useful.
Also note that if a file is in both your source and build directory, make will use the file in your build directory.
On automake
If you use automake, you're pretty much using the entire autotools. automake cannot work without autoconf.
The Makefiles generated by automake support out-of-source builds and cross-compilation, so you should be able to create subdirectories arm/ and thumb/ and run ../configure --host=arm-host-prefix in arm/ and run ../configure --host=thumb-host-prefix in thumb/. (I don't know the actual host tuples that you'd use for each compiler.)
Using GNU make
Since you're using GNUMake, you could do something like this:
ACOBJS := $(addprefix arm/,$(ACSRC:.c=.o))
TCOBJS := $(addprefix thumb/,$(TCSRC:.c=.o))
Use something like this answer to ensure that the arm/ and thumb/ directories (and any subdirectories) exist.

Makefile pattern rule fails?

While using GNU-make, my Makefile has some pattern rule as:
%.o:%.c
gcc $< -o:$#
This rule is added by me.
But when I do make it gives an error saying No rule to make target %.o and doesn't build the targets.
At times, there is this other behaviour as well. It does not build the target when I say make first time(It gives error saying No rule to make target), but when i say make again immediately, it does build correctly.
So when i explicity specify each source file separately, then it builds the targets fine first time itself.
EDIT: I am using GNU-make on a Centos (v6.3 i guess, not sure). Could this be some permission/user id /group id issue?
Any pointers to understand what might be happening and solution for this?
thank you,
-AD.
Make only uses a pattern rule as a sort of fallback. For example:
$ ls
Makefile
$ cat Makefile
%.o: %.c
gcc $< -o $#
$ make
make: *** No targets. Stop.
This is fine. Make does not consider the pattern rule as you have not asked it to make anything, and there is no default target in the makefile. Let's ask it to make something that might use the pattern rule.
$ make 1.o
make: *** No rule to make target `1.o'. Stop.
Expected. 1.c does not exist, so the pattern rule is not considered. Let's try again.
$ touch 1.c
$ make 1.o
gcc 1.c -o 1.o
(and then some error about main being missing).
Personally I dislike these fallback rules intensely. I much prefer listing the targets explicitly. Something like
file.o file2.o f.o: %.o: %.c
...
gives you a Target Specific Pattern Rule. These are quite different (see the manual for Static Pattern Rule). [Oh, and the pattern gives you no advantage in this noddy example.]
You might need spaces around the : in the first line of your rule. Also, gcc does not take a colon before the output file name; just use -o $#.
Just ran through this problem.
If this is your rule:
%.o:%.c
gcc $< -o:$#
Make sure that you have a tab and not spaces on the second line before the gcc. Took me a couple of hours to figure out.
Also no ':' after the -o flag, as somebody else pointed out.
If you put only this in a Makefile and call make:
%.o: %.cpp
g++ -g -o $# -c $<
It tells: make: *** No targets. Stop.
Because it's not a target. It's a rule.
It will work if your another target needs a .o file.
main.exe: main.o add.o
g++ -g -o $# $^
%.o: %.cpp
g++ -g -o $# -c $<
Are you sure the .c file exists? I think using -d as suggested earlier should help debug this.

Resources