制作:如何自动生成规则以构建目标?

时间:2017-09-05 17:05:04

标签: makefile gnu-make

这个问题有点模糊,因为我并不完全确定在这么简短的总结中提出我想要达到的最好方法。

最好解释一下这就是我现在拥有的......

common.mk

DESTDIR = ../../install/

tools.mk

CC = gcc
CFLAGS = -fPIC -Wall -Wextra -O2 -g -I.
LDFLAGS = -shared
RM = rm -f
MAKEDIR = mkdir -p

生成文件

include ../../builder/tools.mk
include ../../builder/common.mk

TEST_RUNNERS = test_foo test_bar

test_foo_TESTS = tests/test_foo.c
test_foo_SOURCES = foo.c

test_bar_TESTS = tests/test_bar.c
test_bar_SOURCES = bar.c


# Gather lists of ALL sources and objects required to build test_foo
test_foo_ALL_SOURCES = $(test_foo_TESTS) $(test_foo_SOURCES)
test_foo_ALL_OBJECTS = $(test_foo_ALL_SOURCES:.c=.o)

# Compile All the sources required for test_foo
$(test_foo_ALL_SOURCES:.c=.d):%.d:%.c
    $(CC) $(CFLAGS) -MM $< >$@
include $(test_foo_ALL_SOURCES:.c=.d)

# Build test_foo and clean up temporary build files
test_foo: $(test_foo_ALL_OBJECTS)
    $(CC) -L$(DESTDIR) -o $(strip $(DESTDIR))$(strip $@) $^
    -${RM} ${test_foo_ALL_OBJECTS} ${test_foo_ALL_SOURCES:.c=.d}


# Gather lists of ALL sources and objects required to build test_bar
test_bar_ALL_SOURCES = $(test_bar_TESTS) $(test_bar_SOURCES)
test_bar_ALL_OBJECTS = $(test_bar_ALL_SOURCES:.c=.o)

# Compile All the sources required for test_bar
$(test_bar_ALL_SOURCES:.c=.d):%.d:%.c
    $(CC) $(CFLAGS) -MM $< >$@
include $(test_bar_ALL_SOURCES:.c=.d)

# Build test_bar and clean up temporary build files
test_bar: $(test_bar_ALL_OBJECTS)
    $(CC) -L$(DESTDIR) -o $(strip $(DESTDIR))$(strip $@) $^
    -${RM} ${test_bar_ALL_OBJECTS} ${test_bar_ALL_SOURCES:.c=.d}

我想要做的是消除必须为每个目标手动添加规则的所有复杂性,而不是#34;自动生成&#34;这些规则。它在我看来相当干净简单......

TEST_RUNNERS = test_foo test_bar

因此,对于列表中指定的每个TEST_RUNNER,必须提供SOURCES列表(测试中的代码)和TESTS列表(单元测试源)...

test_foo_TESTS
test_foo_SOURCES

我一直在玩foreach,但这不是正确的方法,而且我还不完全确定我需要做些什么来实现我的目标,所以在玩了几下之后几个小时我以为我试着问你们其中一些人,因为这里有一些相当聪明的家伙希望能够帮助我!

我正在玩的另一个想法是创建我可以调用的模板来生成这些规则:

$(foreach runner,$(TEST_RUNNERS),$(eval $(call COMPILE_ALL_TEST_RUNNER_SOURCES, runner)))
$(foreach runner,$(TEST_RUNNERS),$(eval $(call MAKE_TEST_RUNNER_TEMPLATE, runner)))

 define COMPILE_ALL_TEST_RUNNER_SOURCES
 $($(1)_ALL_SOURCES:.c=.d):%.d:%.c
     $(CC) $(CFLAGS) -MM $< >$@
 include $($(1)_ALL_SOURCES:.c=.d)
 endef


 define MAKE_TEST_RUNNER_TEMPLATE
 $(1): $($(1)_ALL_OBJECTS)
     $(CC) -L$(DESTDIR) -o $(strip $(DESTDIR))$(strip $@) $^
     -${RM} ${$(1)_ALL_OBJECTS} ${$(1)_ALL_SOURCES:.c=.d}
 endef

2 个答案:

答案 0 :(得分:0)

如果您愿意为每个目标再添加一行:

test_foo_TESTS = tests/test_foo.c
test_foo_SOURCES = foo.c
test_foo_ALL_OBJECTS := tests/test_foo.o foo.o

然后一些模式规则可以处理所有其余的事情:

%.o: %.c
    $(CC) $(CFLAGS) -MMD -c $< -o $@

-include *.d tests/*.d

.PHONY: $(TEST_RUNNERS)

$(TEST_RUNNERS): test_% : $(DESTDIR)test_%

$(DESTDIR)test_%:
    $(CC) -L$(DESTDIR) -o $@ $^

(相信我,这种依赖处理的方法比你的方法更好 。请参阅here以获得详细解释。)

如果您不想为每个目标编写三个行,请注意前两个实际上并未用于任何内容,可以省略。

如果你喜欢前两个并且真的不喜欢第三个,是的,你可以自动化构建对象列表的过程,但是这个问题并不值得。

答案 1 :(得分:0)

在花了一点时间阅读Make手册之后,我发现了这个非常有用的页面。

https://www.gnu.org/software/make/manual/html_node/Eval-Function.html

它有一些非常有用的信息,我可以准确地构建我的Makefile。如果有人有兴趣,我会用它作为我前进的基础......

TEST_RUNNERS = test_foo test_bar

test_foo_TESTS = tests/test_foo.c
test_foo_SOURCES = foo.c

test_bar_TESTS = tests/test_bar.c
test_bar_SOURCES = bar.c

.PHONY: test
test: unittest

#
# Template to create rules for each TEST_RUNNER defined within TEST_RUNNERS
#
# $(1) is the name of the test runner
#
define test_TEST_RUNNER_template
THE_$(1)_SOURCES = $$($(1)_TESTS)
.PHONY: unittest unittest_$(1)
unittest: unittest_$(1)
unittest_$(1):
    @echo "?(1)=" $(1) "?@=" $$@ " THE_$(1)_SOURCES="  $$(THE_$(1)_SOURCES)
endef

# Create a rule for all TEST_RUNNERS when "make test" is invoked...
$(foreach runner,$(TEST_RUNNERS),$(eval $(call test_TEST_RUNNER_template,$(runner))))