user1648090
user1648090

Reputation: 9

Makefile does not see my implicit rule

I am trying to make worlds simplest (relatively generic) Makefile. I can use to drive a small series of tests to learn C. For some reason, Makefile refuses to understand my implicit rule to make object files form c: %.o: %.c, I get no rule to make target %.o needed by test1.exe. Below is my Makefile in entirety:

INC     = sglib
CC      = gcc
LD      = gcc
LDFLAGS =
# On MS-Windows, say "make os=win" to set proper extensions
os  = 
SO  = 
EXE =  

ifeq ($(os), win)
SO  = dll
EXE = .exe
# -fPIC is a no-op on Windows, but causes a compiler warning
CFLAGS  = -std=gnu99 -ggdb3 -Wall
else
SO  = so
CFLAGS  = -std=gnu99 -ggdb3 -Wall -fPIC
endif


all: test1$(EXE)

test1$(EXE): %.o
    $(LD) $< -o $@

lib-test.$(SO): %.o
    $(LD) -shared $(LDFLAGS) -o $@ $<

%.o: %.c
    $(CC) $(CFLAGS) -I$(INC) -c $< -o $@

check:
    echo Nothing yet!

.PHONY: all clean 

clean:
    rm -f *.o *.$(SO) test1$(EXE)

I am using msys on windows 20 with gnu make 4.2.1 and gcc 6.2.0. I am puzzled what is wrong there. it is probably something simple but I seem to be blind at the moment.

Upvotes: 0

Views: 1374

Answers (2)

Barry
Barry

Reputation: 303780

This:

test1$(EXE): %.o

defines a rule which states that the prerequisite of the target test1 is the file %.o. Not some wildcarded list of object files. Specifically the file named %.o. You have no other rule in your makefile to create that file, and it doesn't currently exist, hence the error that you get.

% only acts as a placeholder in Pattern Rules (and similar functions like patsubst), which are rules where % appears in the target. So your:

%.o : %.cpp
    $(CC) $(CFLAGS) -I$(INC) -c $< -o $@

is a pattern rule, because of the % in %.o. This does not create a rule building the target %.o, but rather any file with suffix .o. This would apply when trying to build target foo.o - we'd create an implicit rule with preqrequiste foo.cpp, with the stem % matching foo.

What you actually want is to wildcard your object files. There's a function for that:

test1$(EXE) : $(wildcard *.o)

But this doesn't actually work either. When you try to build the first time, there aren't any object files that exist yet. Clearly. So $(wildcard *.o) would return an empty string, and you end up with nonsense. So you can't wildcard them. You need to explicitly provide that string:

test1$(EXE) : $(object_files)
    $(LD) $< -o $@

Now we just need to come up with that list:

source_files := $(wildcard *.c)
object_files := $(source_files:.c=.o)

Now if you have source files like foo.c, bar.c, and baz.c, the $(wildcard) function will find them and set source_files to foo.c bar.c baz.c. The substitution on the next line will set object_files to foo.o bar.o baz.o. So we end up with test depending on those three object files, and you already have a pattern rule to build those object files.

Upvotes: 3

user2100815
user2100815

Reputation:

You seem to be thinking that in rules like this:

 test1$(EXE): %.o

the % sign is a wildcard character which cause filename expansion - it isn't. If you want to create a list of .o files, you should use the built-in wildcard function.

I have a couple of blog articles on generic makefiles starting at https://latedev.wordpress.com/2014/11/08/generic-makefiles-with-gcc-and-gnu-make/ which may (or may not) be useful.

Upvotes: 0

Related Questions