Reputation: 979
I'm sure this is a totally normal thing to do, but I can't figure out how to get make to do this.
I have a compiler that generates make dependencies of the usual form:
M/A.o : M/A.hs
M/B.o : M/A.o
So I write a rule to compile %.hs into %.o, add a rule to link the binary, include the dependencies file, and all is well. But I want to have several binary targets with different flags. E.g. I want build/test built with -DTESTING and build/profile built with -prof. So I need to keep the .o files in a separate tree, where they will be compiled with special flags.
The straightforward way I can think of would be to have dependencies that look something like this:
build/test/M/A.o : M/A.hs
build/test/M/B.o : build/test/M/A.o
build/profile/M/A.o : M/A.hs
... etc.
And then rules so that %.hs to build/test/%.o compiles with -DTESTING, etc. I think this would work, but it's clumsy, means preprocessing the deps file to add all that build/whatever/ prefix stuff, and would multiply its size by however many kinds of builds.
VPATH appears to be designed for this sort of thing and my idea was that I could set the VPATH and compiler flags depending on the target, and it almost works, but:
%.o: %.hs
@mkdir -p build/M
cp $< build/$@
VPATH = build
main: M/A.o M/B.o
cat $^ >$@
M/A.o : M/A.hs
M/B.o : M/B.hs
The first time the main target wants to run 'cat M/A.o M/B.o >main' which seems contrary to the gnu make documentation that says $^ should include the include the VPATH directory in which the dependency was found. Curiously, if I remove 'main' and make again, this time it uses the correct path. This is GNU make, 3.81.
What's going on here? Is there a better way to build with different flags? VPATH seems like a clumsy tool, surely there is a better way?
Upvotes: 0
Views: 218
Reputation: 36049
If you haven't solved your problem by now or are experiencing further problems, best give the autotools (automake and autoconf) a chance. They'll quickly build you a Makefile that supports more configurable and flexible out-of-tree builds.
Upvotes: 0
Reputation: 99084
Make is working correctly. It tries cat M/A.o M/B.o >main
the first time because it can't find the prerequisites it needs, but it knows a rule for M/A.o' and
M/B.o(<em>not</em>
build/M/A.o' and build/M/B.o
) and expects that that is what the rule will produce. If you remove main
and try again, it will find build/M/A.o' and
build/M/B.o` via VPATH.
Let's modify this makefile in stages. First we change the VPATH so that it can find the .hs
files (Make is good at using things there to build things here, not vise-versa, and that's what VPATH is good for), and change the rules slightly:
build/%.o: %.hs
cp $< $@
VPATH = M
main: build/A.o build/B.o
cat $^ > $@
Now for the different object directories.
build/test/%.o build/project/%.o: %.hs
cp $< $@
VPATH = M
test: build/test/A.o build/test/B.o
cat $^ > $@
project: build/project/A.o build/project/B.o
cat $^ > $@
Then we simplify those last two rules, so that it's easy to add more object files and binary targets:
OBJECTS = A.o B.o
test: $(addprefix build/test/,$(OBJECTS))
project: $(addprefix build/project/,$(OBJECTS))
test project:
cat $^ > $@
Now for the different compiler flags:
build/test/%.o: FLAGS += test_flags
build/project/%.o: FLAGS += proj_flags
build/test/%.o build/project/%.o: %.hs
@echo building $@ from $^ using flags $(FLAGS)
cp $< $@
Finally the dependencies. This is a little tricky. Suppose you want the dependency B.o : A.hs
to apply to however many object you have. This is one approach:
OBJECT_PATHS = build/test/ build/project/
# The following is from the included file generated by the compiler
$(addsuffix B.o,$(OBJECT_PATHS)) : A.hs
To generate lines like that, I'd pipe the raw lines (e.g. B.o: A.hs
) through sed 's/\(.*\):\(.*\)/\1:\2/'
, and note that if you want to put this in a makefile command, don't forget to double the $
signs to preserve them for the shell.
I know that's a lot to absorb. Take it one step at a time and let us know how it works out.
Upvotes: 1