我正在尝试使用 Criterion 框架编译我的项目以进行单元测试。这是我的项目的组织方式:
.
├── include
│ └── mysh.h
├── Makefile
├── obj
├── src
│ ├── arrays_handler.c
│ ├── builtin_env.c
│ ├── delete_from_env.c
│ ├── display_env.c
│ ├── display_prompt.c
│ ├── doubly_linked_list.c
│ ├── env_handler.c
│ ├── execute_command.c
│ ├── mysh.c
│ ├── my_strcmp.c
│ ├── my_strcpy.c
│ ├── my_strdup.c
│ ├── my_strlen.c
│ ├── my_strncmp.c
│ ├── my_strndup.c
│ ├── my_strrchr.c
│ ├── my_str_to_word_array.c
│ ├── name_slice.c
│ ├── parse_command.c
│ ├── sanitize.c
│ ├── shell_handler.c
│ └── strbind.c
└── tests
├── Makefile
└── sanitize_test.c
为了编译我的项目,我使用第一个 Makefile(位于根目录的 Makefile),效果非常好。其内容如下:
INCLUDE_DIR := include
OBJ_DIR := obj
SRC_DIR := src
NAME := mysh
CC := gcc
CFLAGS := -Wall -Wextra -Werror -ggdb3
CPPFLAGS := -I$(INCLUDE_DIR)
SRC_FILES := $(wildcard $(SRC_DIR)/*.c)
OBJ_FILES := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC_FILES))
all: $(NAME)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
@$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
$(NAME): $(OBJ_FILES)
@$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)
clean:
@$(MAKE) -C ./tests tests_clean
@rm -rf $(OBJ_DIR)/*.o
fclean: clean
@$(MAKE) -C ./tests tests_fclean
@rm -rf $(NAME)
tests_run: fclean
@$(MAKE) -C ./tests tests_run
re: fclean all
.PHONY: all clean fclean tests_run re
问题似乎出在我的第二个Makefile(在测试文件夹中),它无法编译
tests_run
规则。其内容如下:
CC := gcc
CFLAGS := -Wall -Wextra -Werror -ggdb3
CPPFLAGS := -I../include
LDFLAGS := -Lcriterion/lib
LDLIBS := -lcriterion
NAME = ../unit-tests
OBJ_DIR = ../obj
SRC = $(shell find ../src -name '*.c' ! -name 'mysh.c')
TEST_SRC = $(shell find . -name '*.c')
OBJ = $(patsubst ../src/%.c,$(OBJ_DIR)/%.o,$(SRC))
TEST_OBJ = $(patsubst ./%.c,$(OBJ_DIR)/%.o,$(TEST_SRC))
$(OBJ_DIR)/%.o: %.c
@mkdir -p $(OBJ_DIR)
@$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
tests_run: $(OBJ) $(TEST_OBJ)
@$(CC) $(CFLAGS) $(LDFLAGS) $(filter-out ../obj/mysh.o,$(TEST_SRC) $(SRC)) $(LDLIBS) -o $(NAME)
@./$(NAME)
tests_clean:
@rm -rf $(OBJ)
@rm -f ../*.gcda
@rm -f ../*.gcno
tests_fclean: tests_clean
@rm -f $(NAME)
.PHONY: tests_run tests_clean tests_fclean
我在调用
tests_run
时收到以下错误:
make[1]: enter the "[...]/tests" directory
make[1]: exit the "[...]/tests" directory
make[1]: enter the "[...]/tests" directory
make[1]: exit the "[...]/tests" directory
make[1]: enter the "[...]/tests" directory
make[1]: *** No rule to make target "../obj/display_prompt.o", required for "tests_run". Stop.
make[1]: exit directory "[...]/tests".
make: *** [Makefile:29: tests_run] Error 2
你能帮我改正这个Makefile吗?
这条规则:
$(OBJ_DIR)/%.o: %.c
告诉 make,“如果你想构建一个文件
../obj/X.o
,你可以从文件创建它 X.c
”。
然后你要求 make 构建一个文件
../obj/display_prompt.o
,所以 make 会查找一个文件 display_prompt.c
。但是,该文件不存在。有一个源文件 ../src/display_prompt.c
但这不是您告诉 make 可以使用的文件,因此它不会查找该文件。
有多种方法可以做到这一点。一种方法是添加新规则:
$(OBJ_DIR)/%.o: ../src/%.c
...
现在 make 知道如何从
../src
以及本地目录中的源文件构建目标文件。
或者,您可以假设其他人为您创建了目标文件,并且此 makefile 不需要构建它们:在这种情况下,将目标文件从先决条件列表中取出,然后将它们放入链接行中:
tests_run: $(NAME)
./$(NAME)
$(NAME): $(TEST_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@
(让你的目标依赖于目标文件然后将源文件添加到链接行是错误的......为什么这样做?它会再次重新编译所有文件!)
您还可以使用VPATH来查找源文件。我个人认为第二种选择是最好的。使用两个包含构建相同目标文件的规则的不同 makefile 总是有问题的。