Spearman
Spearman

Reputation: 148

Pattern only matches once per invocation and make is deleting intermediate files

I have the following rules in a Makefile to build an executable in 3 stages:

all: build/myexe

build/myexe: output/main_dats.o output/foo_dats.o | build/
    gcc $^ -o $@
output/%.o: output/%.c
    patscc -c $< -o $@
output/%_dats.c: src/%.dats | output/
    patsopt -cc -o $@ -d $<
build/:
    mkdir -p build/
output/:
    mkdir -p output/

An src/%.dats source file is used to generate an output/%_dats.c source file which is compiled to an output/%.o object file and finally they are linked into the executable build/myexe.

Running make the first time will only successfully build the first of the two .o files:

$ make
mkdir -p output/
patsopt -cc -o output/main_dats.c -d src/main.dats
patscc -c output/main_dats.c -o output/main_dats.o
make: *** No rule to make target `output/foo_dats.o', needed by `build/myexe'.  Stop.
rm output/main_dats.c

But running again will build the second .o file and successfully link the executable:

$ make
patsopt -cc -o output/foo_dats.c -d src/foo.dats
patscc -c output/foo_dats.c -o output/foo_dats.o
mkdir -p build/
gcc output/main_dats.o output/foo_dats.o -o build/myexe
rm output/foo_dats.c

and note that at the end of each invocation the command rm output/..._dats.c is deleting the generated .c source file.

Here is a Makefile written without pattern matching:

all: build/myexe

build/myexe: output/main_dats.o output/foo_dats.o | build/
    gcc $^ -o $@
output/foo_dats.o: output/foo_dats.c
    patscc -c $< -o $@
output/main_dats.o: output/main_dats.c
    patscc -c $< -o $@
output/foo_dats.c: src/foo.dats | output/
    patsopt -cc -o $@ -d $<
output/main_dats.c: src/main.dats | output/
    patsopt -cc -o $@ -d $<
build/:
    mkdir -p build/
output/:
    mkdir -p output/

which works more predictably:

$ make
mkdir -p output/
patsopt -cc -o output/main_dats.c -d src/main.dats
patscc -c output/main_dats.c -o output/main_dats.o
patsopt -cc -o output/foo_dats.c -d src/foo.dats
patscc -c output/foo_dats.c -o output/foo_dats.o
mkdir -p build/
gcc output/main_dats.o output/foo_dats.o -o build/myexe

and note that the generated .c files are not being removed any more.

Apparently I am misusing the pattern matching mechanism. I know there is some kind of wildcard function but I believe it is intended for file globbing.

Upvotes: 0

Views: 154

Answers (2)

MadScientist
MadScientist

Reputation: 100856

To avoid removing intermediate files, you just need to list them as actual targets somewhere. For example you could write a separate rule:

make_srcs: output/main_dats.c output/foo_dats.c

You don't have to list this target make_srcs as a prerequisite, or provide it a recipe, etc. Just listing the _dats.c files as actual targets or prerequisites in the makefile is enough to keep them from being deleted.

As for your "only building some output" behavior, I don't know: it works fine for me:

$ make --version | head -n1
GNU Make 4.2.1

$ cat Makefile 
all: build/myexe

build/myexe: output/main_dats.o output/foo_dats.o | build/
        touch $@
output/%.o: output/%.c
        touch $@
output/%_dats.c: src/%.dats | output/
        touch $@
build/:
        mkdir -p build/
output/:
        mkdir -p output/

make_srcs: output/main_dats.c output/foo_dats.c


$ rm -rf output build && make
mkdir -p output/
touch output/main_dats.c
touch output/main_dats.o
touch output/foo_dats.c
touch output/foo_dats.o
mkdir -p build/
touch build/myexe

So there's something about your setup which hasn't been made clear in your question. As the comment suggested you need to run make -d (I would leave off the -R option, I don't know why you'd add that) and figure out why make throws that error.

Upvotes: 2

Michael Livshin
Michael Livshin

Reputation: 401

Pattern rules should ideally be deprecated. They are prone to over-matching (because, well, patterns), they can be hard to get working, they bring with them the whole "intermediate target" issue (that's the deletion of output/*.c files that you are observing), they need another dubious feature ("secondary expansion") to make them usable in some more involved scenarios, etc.

In short: using pattern rules is not advised, and using multi-level pattern rules is definitely not advised. Just more trouble than it's worth. IMHO, anyway.

(end rant)

So I suggest that you write a simple macro instead, so your makefile ends up looking like this:

all: build/myexe

# $(call dats,basename)
define dats
output/$1_dats.o: output/$1_dats.c
    patscc -c $$< -o $$@
output/$1_dats.c: src/$1.c | output
    patcc -cc -o $$@ -d $$<
endif

build/myexe: output/main_dats.o output/foo_dats.o | build
    gcc $^ -o $@
$(eval $(call dats,foo))
$(eval $(call dats,main))

build:
    mkdir -p build
output:
    mkdir -p output

Upvotes: 0

Related Questions