Речь идет о следующем Makefile:
#
# Compiler flags
#
CC = gcc
CFLAGS = -Wall -Werror -Wextra
#
# Project files
# example -
# SRCS = hash_table.c linked_list.c utils.c common.c business_logic.c user_interface.c
# SRCS = test/gc_test.c src/gc.c
SRCS := $(shell find . -name "*")
OBJS = $(SRCS:.c=.o)
EXE = output.out
FILENAMES := $(shell find . -type f -name "*.c" -printf "%f\n")
FILENAMES_OUT = $(FILENAMES:.c=.o)
#
# Default build settings
#
BUILDDIR = build
BUILDCFLAGS = -g -O0
BUILDEXE = $(BUILDDIR)/$(EXE)
BUILDOBJS = $(addprefix $(BUILDDIR)/, $(FILENAMES_OUT))
# Rules for default build
all: clean build
build: $(BUILDEXE)
$(BUILDEXE): $(BUILDOBJS)
$(CC) $(CFLAGS) $(BUILDCFLAGS) -o $(BUILDEXE) $^ -lcunit
$(BUILDDIR)/%.o: %.c
$(CC) $(CFLAGS) $(BUILDCFLAGS) -c $< -o $@
memtest: $(BUILDEXE)
valgrind --leak-check=full ./$<
#
# Other rules
#
clean:
mkdir -p $(BUILDDIR)
ПРОБЛЕМА -> make: *** No rule to make target `build/gc_test.o', needed by `build/output.out'. Stop.
ДЕРЕВО ПРОЕКТА
.
├── build
├── doc
│ └── design.md
├── Makefile
├── proj
│ ├── code_quality_report.md
│ ├── deviations.md
│ ├── individual_reflection.md
│ ├── team_reflection.md
│ └── test_report.md
├── README.md
├── src
│ ├── gc.c
│ └── headers
│ └── gc.h
└── tests
└── gc_test.c
Сама проблема возникает в $(BUILDEXE): $(BUILDOBJS)
, где находятся зависимости gc_test.c gc.c
. Эти зависимости ДОЛЖНЫ попасть в функцию под ней, потому что входными данными являются все файлы .c в каталоге build
. Эти файлы ДОЛЖНЫ быть правильно сопоставлены, а затем скомпилированы в файлы .o, которые затем должны подняться вверх по дереву и создать исполняемый файл. Я в замешательстве, потому что $(BUILDOBJS)
должно быть таким же, как $(BUILDDIR)/%.o
.
Я новичок в создании файлов Makefile, но хочу стать лучше. Пожалуйста, укажите лучшие соглашения об именах или терминологию, которые можно было бы лучше использовать для этого поста. Спасибо!
Пробовал, не получилось. Показывает ту же ошибку. Это очень странно.
Вы заявляете "...because it's input is all the .c files in the build directory"
о правиле $(BUILDDIR)/%.o: %.c
. Это неправильно. Зависимость (в данном случае gc_test.c
) будет искаться в текущем каталоге (или в любых соответствующих vpath
местах), поскольку %.c
расширяется до gc_test.c
.
... и gc_test.c
отсутствует в текущем каталоге. Если вы хотите продолжить создание объектов в специальном отдельном каталоге, а не в том же каталоге, что и их исходники, или в параллельном дереве сборки, вам потребуется отдельное правило для каждой папки, содержащей исходники, которые вы хотите скомпилировать. Честно говоря, я никогда не понимал, почему так много людей настаивают на этом. Он более хрупок и требует больше работы для обслуживания, и я не вижу, чего он достигает, чего нельзя достичь с помощью сборки VPATH или даже хорошего «чистого» правила.
vpath
является хорошим решением этой проблемы. (Я бы ответил, но @G.M. упомянул об этом за 40 минут до того, как я пришел.)
Проблема не в создании объектов в другом каталоге. это легко поддерживать и очень удобно по многим причинам: например, вы можете иметь разные каталоги объектов для кода, созданного с разными флагами (отладка, оптимизация, SAN и т. д.), а также упрощает очистку объектов. Проблема заключается в попытке использовать для объектов другую структуру каталогов, чем для источников; часто люди пытаются поместить все объектные файлы в один каталог, даже если исходные тексты находятся в нескольких каталогах. ЭТО раздражает и сложно поддерживать.
Если вы сделаете структуру каталогов объектов идентичной структуре исходных каталогов, только с префиксом OBJDIR
или подобным, то это будет очень просто реализовать.
Проблема здесь:
FILENAMES := $(shell find . -type f -name "*.c" -printf "%f\n")
Это неправильно, потому что -printf "%f\n"
печатает только имена файлов без пути. Вы теряете всю информацию о пути, по которому находятся файлы, так как же их найти?
Вы должны изменить это на просто -print
, тогда это сработает.
Будет ли это работать, если вы сделаете эту строку явно (то есть с именами файлов, жестко заданными вручную) вместо того, чтобы пытаться волшебным образом найти все файлы .c?
FILENAMES := $(shell find . -type f -name "*.c" -printf "%f\n")
. Насколько я знаю, такое подстановка часто является проблемой для make-настроек.