I have a Makefile
from which I run the command of another Makefile
if some objects file do not exist already.
The rule is the following:
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
where the variables are defined as follows in the same Makefile
:
COMMONDIR := ../common
SOURCESCOMMON := $(wildcard $(COMMONDIR)/*.c)
OBJDIRCOMMON := $(COMMONDIR)/obj
OBJECTSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.o, $(SOURCESCOMMON))
DEPENDSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.d, $(SOURCESCOMMON))
This rule works fine but at the end of the day the only real input required by the rule is the other Makefile
so I tried:
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
but this does not work, why is that?
For completeness here is the complete Makefile
CC = gcc
INC_PATH = -I../common/
SOURCEDIR := ./
SOURCES := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR :=./obj
OBJECTS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))
COMMONDIR := ../common
SOURCESCOMMON := $(wildcard $(COMMONDIR)/*.c)
OBJDIRCOMMON := $(COMMONDIR)/obj
OBJECTSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.o, $(SOURCESCOMMON))
DEPENDSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.d, $(SOURCESCOMMON))
# ADD MORE WARNINGS!
WARNING := -Wall -Wextra
# OBJS_LOC is in current working directory,
EXECUTABLE := ../server
# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean
# The first rule is the default, ie. "make",
# "make all" and "make parking" mean the same
all: $(EXECUTABLE)
clean:
$(RM) $(OBJECTS) $(DEPENDS) $(EXECUTABLE)
# Linking the executable from the object files
# $^ # "src.c src.h" (all prerequisites)
$(EXECUTABLE): $(OBJECTS) $(OBJECTSCOMMON)
$(CC) $(WARNING) $^ -o $@
-include $(DEPENDS) $(DEPENDSCOMMON)
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
$(CC) $(WARNING) -MMD -MP -c $(INC_PATH) $< -o $@
$(OBJDIRCOMMON):
mkdir -p $(OBJDIRCOMMON)
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
EDIT
The error I get is like this:
Entering directory '/home/user/Documents/UnixSystem/network/common'
gcc -Wall -Wextra -MMD -MP -c utilities.c -o obj/utilities.o
gcc -Wall -Wextra -MMD -MP -c error.c -o obj/error.o
make[2]: Leaving directory '/home/user/Documents/UnixSystem/network/common'
gcc ../common/obj/error.d.o -o ../common/obj/error.d
gcc: error: ../common/obj/error.d.o: No such file or directory
gcc: fatal error: no input files
compilation terminated.
From which I understand the execution of the other Makefile
was successful. However after that it is trying to execute this command gcc ../common/obj/error.d.o -o ../common/obj/error.d
which is wrong but I do not know which rule and why it's generating it.
Should there be && instead of pipe
Why what you did was wrong
Recipe A:
and recipe B:
have essentially different meanings and will certainly not yield the same behaviour.
Recipe A says:
$(OBJDIRCOMMON)/file.o
must be made up-to-date if does not exist or is older than either$(COMMONDIR)/file.c
or$(COMMONDIR)/Makefile
.$(OBJDIRCOMMON)/file.o
must be made up-to-date, then$(OBJDIRCOMMON)
must be made up-to-date first.$(OBJDIRCOMMON)/file.o
up-to-date, run the expansion of$(MAKE) -C $(COMMONDIR)
in a shell.Recipe B says:
$(OBJDIRCOMMON)/file.o
must be made up-to-date if does not exist or is older than$(COMMONDIR)/Makefile
.$(OBJDIRCOMMON)/file.o
must be made up-to-date, then$(OBJDIRCOMMON)
must be made up-to-date first.$(OBJDIRCOMMON)/file.o
up-to-date, execute the expansion of$(MAKE) -C $(COMMONDIR)
in a shell.Notice that criterion A.1 is different from criterion B.1. Recipe A will execute if
$(OBJDIRCOMMON)/file.o
is older than$(OBJDIRCOMMON)/file.c
. Recipe B will not. Recipe B discards the dependency of the object files on the corresponding source files, and tells Make that$(OBJDIRCOMMON)/file.o
is only ever to be remade if it is older than$(COMMONDIR)/Makefile
.What you mean by "the rule" here is actually the commandline (expanded from)
$(MAKE) -C $(COMMONDIR)
. The inputs of this command are one thing; the criteria for executing it are another.How what you did caused the error you see.
This is thornier. Let's reproduce it.
Here's a playpen:
Here,
./app/Makefile
is exactly your Makefile with recipe A../common/Makefile
, which you didn't post, is just:because that will do for illustration.
We build in
./app
:which is fine.
Now I change
./app/Makefile
as you did, to use recipe B, and rebuild.Still fine... But wait a minute! That one didn't invoke the
./common
make at all, which is the one that the change might affect. Betterclean
:and try again:
No difference? Ah, that's because this Makefile's
clean
fails to delete all the files thatmake
builds: it leaves out../common/obj/bar.o
. So I'll just:And have another go:
which is your mystery.
When I zapped the
../common/obj
files, I deleted not only all the object files therein but also the dependency file../common/obj/bar.d
. And now Make is trying to remake it by running:How come? To answer that, we'll first change
./app/Makefile
back to use recipe A - consider it done - and then do:which dumps in
out.txt
all the information that Make gleans from reading all the makefiles (Makefile
and all the makefiles that it recursivelyinclude
-s, in this case just the auto-generated.d
files).Let's see what the database has to say about
../common/obj/bar.d
. It says:Certainly we don't want
../common/obj/bar.d
to be a target, and it isn't a target because, having read all the makefiles, and considered all its builtin rules, and all of the files it can actually find, Make can't see any way in which../common/obj/bar.d
has to be made up-to-date with respect to any of those files. Good.Now let's revert to recipe B in
./app/Makefile
again - consider it done - and again do:and again look in
out.txt
concerning../common/obj/bar.d
. This time we find:So this time
../common/obj/bar.d
is a target! And it depends on../common/obj/bar.d.o
! And the recipe to make it is:which will expand, of course, to:
How was Make able to work that out, thanks to recipe B?
Well first it considered whether any of the rules in the makefiles or any of the builtin rules gave it direct way to make
../common/obj/bar.d
from any existing files, and drew a blank.Next it considered whether any of those rules gave it a way to make
../common/obj/bar.d
from an intermediate file. An intermediate file being a file that doesn't exist but itself can be made from existing files, by any of the rules it has read or its builtin-rules. This time it saw a way.One of Make's builtin pattern rules is:
You can find it in right there in
out.txt
. And you can see this is the pattern rule that it matches with:The recipe there is a recipe to link a program called
../common/obj/bar.d
given an object file../common/obj/bar.d.o
.There is no object file
../common/obj/bar.d.o
, but can it be an intermediate file? If Make can find a rule for making../common/obj/bar.d.o
from files that do exist, then it can also make../common/obj/bar.d
with this%: %.o
rule.And it can find a recipe for making
../common/obj/bar.d.o
from existing files because we just gave it one! - recipe B:That tells Make that if any target matching
$(OBJDIRCOMMON)/%.o
(like../common/obj/bar.d.o
) does not exist, but$(COMMONDIR)/Makefile
does exist (which it does), then that target is made up-to-date by running:Make believed us. It ran
$(MAKE) -C $(COMMONDIR)
:and then considered
../common/obj/bar.d.o
up-to-date. So it moved on to:and ran:
which failed because we lied:
does not make
../common/obj/bar.d.o
at all.This does not arise with recipe A, because
does not offer Make a way to make
$(OBJDIRCOMMON)/bar.d.o
from existing files, because$(OBJDIRCOMMON)/bar.d.c
does not exist. So../common/obj/bar.d
is not a target.Stick with recipe A, because it's right, and recipe B is wrong. Also review and fix the makefiles so that
make clean
always deletes all the non-.PHONY
targets that might have been built, and nothing else. Lastly avoid writing recipes with non-.PHONY
targets where the recipe does not mention the target.