Reputation: 148
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
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
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