单元测试的 Makefile(通用文件)

Makefile for UnitTests (universal file)

我一直在研究 Makefile 以在 C 中自动化单元测试。主要目标是对于任何遵循源和测试目录结构的给定项目,make 编译函数和make test 编译并运行测试。

虽然 make 命令工作得很好,但 make test 却没有。在下面的配置中,test 的依赖调用 %.exe 目标,而不是 test_%.exe.

如何成功调用任何以“test_”开头的测试目标?

我试过为它创建一个通配符,但没有成功。这可能仍然缺乏知识,但我希望能在这里找到一些帮助:)

CC := gcc
CFLAGS := -Wall -Werror
CSYNTAX := -fsyntax-only -Wall

SRC = $(wildcard ./src/*.c)
DEP = $(SRC:.c=.exe)

SRCTEST = ./test/UnitTests

PATHI = ./test/UnitTests/includes

PATHU = ./test/UnitTests/Unity

INC := ./src/includes

TEST = test_$.exe


# Compilation of main program
all:    $(DEP)


syntax: *.c $(SRC) $(SRCTEST)/test_*.c
    $(CC) $(CSYNTAX) $^ $(CFLAGS) -I$(INC) -I $(PATHI)


%.exe:  *.c $(SRC)
    $(CC) $^ $(CFLAGS) -I$(INC) -o $@ 
    ./$@


# Compilation and execution of tests
test:   $(SRCTEST)/test_*.exe


test_%.exe: $(SRCTEST)/test_%.c $(SRC) $(PATHU)/unity.c
    $(CC) $(CFLAGS) $^ -I $(INC) -I $(PATHI) -I $(PATHU) -o $@
    ./$@

clean:
    del *.exe

我在 MinGW 8.1.0 上使用 GNU gcc,但也可以访问 gcc Ubunutu 7.5.0 (replit.com)

这里有很多微妙的问题。

首先,当你的测试目标依赖于一个通配符时,它的解析并不直观,因为它没有被 make 直接展开。如果匹配文件不存在,make 将尝试查找其中包含文字 * 的规则:

$ cat Makefile
SRC = $(wildcard ./src/*.c)
SRCTEST = ./test/UnitTests

%.exe: $(SRC)
        echo Making $@ from $^

test: $(SRCTEST)/test_*.exe

test_%.exe: $(SRCTEST)/test_%.c $(SRC)
        echo Making $@ from $^

$ make test -dr
...
Considering target file 'test'.
 Looking for an implicit rule for 'test'.
 No implicit rule found for 'test'.
  Considering target file 'test/UnitTests/test_*.exe'.
   File 'test/UnitTests/test_*.exe' does not exist.
   Looking for an implicit rule for 'test/UnitTests/test_*.exe'.
   Trying pattern rule with stem '*'.
   Trying implicit prerequisite 'test/UnitTests/test/UnitTests/test_*.c'.
   Trying pattern rule with stem 'test_*'.
   Trying rule prerequisite 'src/foo.c'.
   Found an implicit rule for 'test/UnitTests/test_*.exe'.
    Considering target file 'src/foo.c'.
     Looking for an implicit rule for 'src/foo.c'.
     No implicit rule found for 'src/foo.c'.
     Finished prerequisites of target file 'src/foo.c'.
    No need to remake target 'src/foo.c'.
   Finished prerequisites of target file 'test/UnitTests/test_*.exe'.
  Must remake target 'test/UnitTests/test_*.exe'.
echo Making test/UnitTests/test_*.exe from src/foo.c
...

假设您不想创建一个字面上名为 test_*.exe 的文件,测试二进制文件应该具有预期的名称,即:

$ cat Makefile
SRC = $(wildcard ./src/*.c)
SRCTEST = ./test/UnitTests

%.exe: $(SRC)
        echo Making $@ from $^

.PHONY: test
test: $(patsubst %.c,%.exe,$(wildcard $(SRCTEST)/test_*.c))

test_%.exe: $(SRCTEST)/test_%.c $(SRC)
        echo Making $@ from $^

这将从测试源文件派生测试二进制名称,方法是将它们的后缀从 .c 转换为 .exe。 (作为旁注,目标本身应该是假的,因为它不会创建真实文件)。

给定的文件结构:

$ ls -R
.:
Makefile  src  test

./src:
foo.c

./test:
UnitTests

./test/UnitTests:
test_foo.c

这导致尝试创建 test_foo.exe 而不是 test_*.exe:

$ make test -dr
...
Considering target file 'test'.
 File 'test' does not exist.
  Considering target file 'test/UnitTests/test_foo.exe'.
   File 'test/UnitTests/test_foo.exe' does not exist.
   Looking for an implicit rule for 'test/UnitTests/test_foo.exe'.
   Trying pattern rule with stem 'foo'.
   Trying implicit prerequisite 'test/UnitTests/test/UnitTests/test_foo.c'.
   Trying pattern rule with stem 'test_foo'.
   Trying rule prerequisite 'src/foo.c'.
   Found an implicit rule for 'test/UnitTests/test_foo.exe'.
    Considering target file 'src/foo.c'.
     Looking for an implicit rule for 'src/foo.c'.
     No implicit rule found for 'src/foo.c'.
     Finished prerequisites of target file 'src/foo.c'.
    No need to remake target 'src/foo.c'.
   Finished prerequisites of target file 'test/UnitTests/test_foo.exe'.
  Must remake target 'test/UnitTests/test_foo.exe'.
echo Making test/UnitTests/test_foo.exe from src/foo.c
...

仍然,它没有遵循测试规则,而是遵循一般源代码规则。这是为什么?好吧,那是因为在匹配词干(% 部分)时,目录是 prepended 到搜索到的目标。您可以在上面的输出中看到,当 makeConsidering target file 'test/UnitTests/test_foo.exe' 时,它将查找文件 test/UnitTests/test/UnitTests/test_foo.c,该文件来自 $(SRCTEST)/test_%.c,同时明确提及 $(SRCTEST)也会自动从词干添加到前缀。当然这样的文件不存在,因此规则被拒绝。

正确的修复很简单:

$ cat Makefile
SRC = $(wildcard ./src/*.c)
SRCTEST = ./test/UnitTests

%.exe: $(SRC)
        echo Making $@ from $^

.PHONY: test
test: $(patsubst %.c,%.exe,$(wildcard $(SRCTEST)/test_*.c))

test_%.exe: test_%.c $(SRC)
        echo Making $@ from $^

$ make -s test
Making test/UnitTests/test_foo.exe from test/UnitTests/test_foo.c src/foo.c

请注意,它现在已经按预期从测试源构建了测试二进制文件。