I have a bunch of .c test source file in directory tests/. Now I want to compile and link them respectively and output the executable *.out in tests/. So I wrote a makefile, which didn't work.

# ...
TestDir := tests
TestSourceFile := $(shell sh -c "ls tests/*.c")
TestTargetFile := $(subst .c,.out,$(TestSourceFile))

TestFrame := testframe.o

TestNeededObjectFile := $(TestFrame) \
    + util.o \
    + tokennames.o \
    + lex.yy.o \
    + hex.o \

.PHONY: test-%

test-%: $(TestDir)/%.out

.PHONY: test

test: $(TestTargetFile)
    @for t in $(TestTargetFile); do \
        $$t ; \

$(TestDir)/%.out: $(TestDir)/%.o $(TestNeededObjectFile)
    gcc -o $@ $^

%.o : %.c
    gcc -c $(CFLAGS) $^

    rm -rf *.o lextest *.yy.? *.tab.? *.output $(TokenNameFile) \

When I run make test-add(add.c is in tests/), I am expecting to see add.out in tests/ but instead there is an error:

> make test-add
make: *** No rule to make target 'tests/add.out', needed by 'test-add'.  Stop.

I want to know how to write this makefile correctly and why this makefile is wrong.

  • 2
    Did you add those plus signs next to the object files? It's not the only thing that's wrong, but they're definitely not helping – Jose Fernando Lopez Fernandez Mar 22 at 12:05
  • @Kusalananda the %.foo: bar.% pattern subst from GNU make doesn't care about .SUFFIXES; it does care about the existence of the requisites, though. – mosvy Mar 22 at 12:25
  • @mosvy Thanks, I wasn't sure how GNU make handles these types of pattern targets. – Kusalananda Mar 22 at 12:30
  • @JoseFernandoLopezFernandez thanks for pointing that out. – Charles Mar 22 at 14:18

The pattern rules in GNU make won't be considered if their prerequisites do not resolve to existing files, either directly or recursively via other pattern rules[1]:

$ make -f <(echo '%.foo: %.bar file.txt;') a.foo
make: *** No rule to make target 'a.foo'.  Stop.

$ touch a.bar
$ make -f <(echo '%.foo: %.bar file.txt;') a.foo
make: *** No rule to make target 'a.foo'.  Stop.

$ touch a.bar file.txt
$ make -f <(echo '%.foo: %.bar file.txt;') a.foo
make: 'a.foo' is up to date.

Since your TestNeededObjectFile macro includes spurious +s, and you probably don't have any file named that way, that will break the $(TestDir)/%.out: rule.

[1] according to the GNU make manual:

In order for the pattern rule to apply, its target pattern must match the file name under consideration and all of its prerequisites (after pattern substitution) must name files that exist or can be made. These files become prerequisites of the target.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.