Reputation: 143
I am trying to wrap my head around why Make is using a certain rule to compile my code. Here is a minimal example:
Makefile
CC := gcc
CFLAGS := -O2 -Wno-implicit-function-declaration
targets := main1 main2
obj_targets := $(patsubst %,%.o,$(targets))
src_lib := add.c
obj_lib := $(patsubst %.c,%.o,$(src_lib))
all: $(targets)
%: %.o $(obj_lib)
$(CC) $(CFLAGS) -static -o $@ $^
.PHONY: all
add.c: int add(int a, int b) { return a + b; }
main1.c: int main(void) { return add(1,2); }
main2.c: int main(void) { return add(3,4); }
When running make
I get the following:
$ make
gcc -O2 -Wno-implicit-function-declaration main1.c -o main1
/usr/bin/ld: /tmp/ccBsxlMV.o: in function `main':
main1.c:(.text.startup+0x11): undefined reference to `add'
collect2: error: ld returned 1 exit status
make: *** [<builtin>: main1] Error 1
My intent is to indicate through %: %.o $(obj_lib)
that each target depends on all of the object files, so that the target's .o
file will be linked with add.o
. However, what I am observing instead is that Make is using the implicit rule for compiling a .c
file to an executable, which fails since add.o
isn't being linked in.
Why is Make using the implicit rule instead of the rule that I defined? How can I get Make to compile my targets using the command I specified there?
Upvotes: 0
Views: 718
Reputation: 101081
The reason is that make always uses the shortest chain of implicit rules. So, if make can find a rule %.x -> %.y
and another rule %.x -> %.z -> %.y
, it will always choose the first one.
Make doesn't treat built-in and user-defined rules differently from each other, except that user-defined rules are searched first before built-in rules.
In your situation, you have a built-in rule that knows how to create a binary from a source file with the same name, and a rule you defined that knows how to create a binary from an object file and another chained rule that builds the object file from the source file. So make chooses the first one.
You can remove that built-in rule with:
%: %.c
as you did (you don't have to remove the %.o : %.c
rule). Alternatively you can use a static pattern rule, which creates an explicit rule:
all: $(targets)
$(targets): %: %.o $(obj_lib)
...
Because this is a form of explicit rule, no implicit rule (like pattern rules) will be used.
Upvotes: 2
Reputation: 143
The other implicit rules are being used because they can. The .c
file exists and so it can be used to build the executable.
There are multiple ways to fix this:
Add the $(obj_lib)
as a dependency of $(targets)
. This will cause Make to ensure that the library .o
files are linked into the main executable, solving that problem. However, this can still use the implicit rule, so it does not address the 2nd part of the question.
Prevent Make from using the implicit rules. This is the solution that I found that works best for my use-case. This can be done by adding the following to the end of the Makefile.
# Delete implicit rules that could be used to create the executable.
%: %.c
%: %.o
# Indicate that Make doesn't need to delete any intermediate objects. (Optional)
.SECONDARY:
Here is a demonstration of this example before and after the change:
=============================================================
$ ls
Makefile add.c main1.c main2.c make.log
=============================================================
$ cat Makefile
CC := gcc
CFLAGS := -O2 -Wno-implicit-function-declaration
targets := main1 main2
obj_targets := $(patsubst %,%.o,$(targets))
src_lib := add.c
obj_lib := $(patsubst %.c,%.o,$(src_lib))
all: $(targets)
%: %.o $(obj_lib)
$(CC) $(CFLAGS) -static -o $@ $^
# .PHONY: all
# # Delete implicit rules that could be used to create the executable.
# %: %.c
# %: %.o
# # Indicate that Make doesn't need to delete any intermediate objects.
# .SECONDARY:
=============================================================
$ make
gcc -O2 -Wno-implicit-function-declaration main1.c -o main1
/usr/bin/ld: /tmp/ccBsxlMV.o: in function `main':
main1.c:(.text.startup+0x11): undefined reference to `add'
collect2: error: ld returned 1 exit status
make: *** [<builtin>: main1] Error 1
=============================================================
$ code Makefile
=============================================================
$ cat Makefile
CC := gcc
CFLAGS := -O2 -Wno-implicit-function-declaration
targets := main1 main2
obj_targets := $(patsubst %,%.o,$(targets))
src_lib := add.c
obj_lib := $(patsubst %.c,%.o,$(src_lib))
all: $(targets)
%: %.o $(obj_lib)
$(CC) $(CFLAGS) -static -o $@ $^
.PHONY: all
# Delete implicit rules that could be used to create the executable.
%: %.c
%: %.o
# Indicate that Make doesn't need to delete any intermediate objects.
.SECONDARY:
=============================================================
$ make
gcc -O2 -Wno-implicit-function-declaration -c -o main1.o main1.c
gcc -O2 -Wno-implicit-function-declaration -c -o add.o add.c
gcc -O2 -Wno-implicit-function-declaration -static -o main1 main1.o add.o
gcc -O2 -Wno-implicit-function-declaration -c -o main2.o main2.c
gcc -O2 -Wno-implicit-function-declaration -static -o main2 main2.o add.o
=============================================================
$
Upvotes: 0