How to make two different source directories in a Makefile output to one bin directory?
I have the following Makefile to build my erlang project:
.SUFFIXES: .erl .beam .yrl
ERL_SRC := $(wildcard src/*.erl)
ERL_OBJ := $(patsubst src/%.erl,ebin/%.beam,${ERL_SRC})
all: main
main: ${ERL_OBJ}
ebin/%.beam: src/%.erl
erlc +debug_info -W -o ebin $<
clean:
rm -fr ebin/*.beam
I'm trying to update this to also build my eunit tests in the test/eunit folder and have the output go to the same ebin folder as the src like this:
.SUFFIXES: .erl .beam .yrl
ERL_SRC := $(wildcard src/*.erl)
ERL_OBJ := $(patsubst src/%.erl,eb开发者_高级运维in/%.beam,${ERL_SRC})
EUNIT_SRC := $(wildcard test/eunit/*.erl)
EUNIT_OBJ := $(patsubst test/eunit/%.erl,ebin/%.beam,${EUNIT_SRC})
all: main
main: ${ERL_OBJ}
ebin/%.beam: src/%.erl test/eunit/%.erl
erlc +debug_info -W -o ebin $<
clean:
rm -fr ebin/*.beam
eunit: ${EUNIT_OBJ}
test: main eunit
Making main works fine, but if I try make test it fails with:
make: *** No rule to make target `ebin/data_eunit.beam', needed by `eunit'. Stop.
The test module data_eunit.erl is located in test/eunit. The problem seems to be with the ebin/%.beam target. If I swap src/%.erl with test/eunit/%.erl then I can build the tests but not the src. How can I do a build from two source folders and have the output go to one output folder?
You can use the vpath/VPATH in your Makefile
.SUFFIXES: .erl .beam .yrl
# use vpath to tell make where to search for %.erl files
vpath %.erl src eunit
# or use VPATH to tell make where to search for any prerequisite
# VPATH=src:eunit
ERL_OBJ = $(patsubst src/%.erl,ebin/%.beam, $(wildcard src/*erl))
ERL_OBJ += $(patsubst eunit/%.erl,ebin/%.beam, $(wildcard eunit/*erl))
all: main
main: ${ERL_OBJ}
ebin/%.beam: %.erl
erlc +debug_info -W -o ebin $<
clean:
rm -fr ebin/*.beam
Perhaps you should greatly simplify your build. My erlang build systems just invoke erl -make
with an Emakefile that looks like this:
{"src/*", [debug_info, {outdir, "ebin"}, {i, "include"}]}.
You could, of course, have more than one src loc, but just mix the tests and the regular code -- you're already mixing them in ebin. Don't make it harder on yourself than it needs to be.
This wont really answer your question, but I dont like to have the risk of polluting my ebin/ with test-enabled code. So this is how I organize my toplevel Makefile:
all:
(cd src && erl -make)
test:
(cd test && erl -make && \
erl -noinput -eval 'eunit:test({dir, "."}, [verbose]), init:stop()')
Then I put the following into src/Emakefile
:
{['*'],
[{outdir,"../ebin"}]}.
And into test/Emakefile
I put
{['../src/*'],
[debug_info, {d, 'TEST'}]}.
{['*'],
[debug_info, {d, 'TEST'}]}.
So if I run make all
then src/*.erl is compiled into ebin/, but if I run make test
I compile src/*.erl and test/*.erl into test/ and run all beam files there with unit tests.
When compiling the tests the TEST macro is enabled so that unit-tests are disabled if surrounded with ifdefs as the eunit guide suggest:
-ifdef(TEST).
test_code_() -> ...
-endif.
Its a setup that I'm quite pleased with.
You should make another target that compiles that tree into ebin, make it depend on the original build target.
This is doing what I want:
.SUFFIXES: .erl .beam .yrl
ERL_SRC := $(wildcard src/*.erl)
ERL_OBJ := $(patsubst src/%.erl,ebin/%.beam,${ERL_SRC})
EUNIT_SRC := $(wildcard test/eunit/*.erl)
EUNIT_OBJ := $(patsubst test/eunit/%.erl,ebin/%.beam,${EUNIT_SRC})
all: main
main: ${ERL_OBJ}
${ERL_OBJ}: ${ERL_SRC}
erlc +debug_info -W -o ebin $<
clean:
rm -fr ebin/*.beam
${EUNIT_OBJ}: ${EUNIT_SRC}
erlc +debug_info -W -o ebin $<
eunit: ${EUNIT_OBJ}
test: main eunit
Any other way I can improve this? Maybe move similar compile lines in ${ERL_OBJ} and ${EUNIT_OBJ} to a variable.
Instead of "ebin/%.beam: src/%.erl test/eunit/%.erl" have you tried just:
%.beam: %.erl
精彩评论