Makefile, зависимости заголовка
Скажем, у меня есть make файл с правилом
%.o: %.c
gcc -Wall -Iinclude ...
Я хочу, чтобы *.o перестраивался всякий раз, когда файл заголовка менялся. Вместо того, чтобы разрабатывать список зависимостей, всякий раз, когда изменяется любой файл заголовка в /include
, все объекты в каталоге должны быть перестроены.
Я не могу придумать хороший способ изменить правило, чтобы разместить это, я открыт для предложений. Бонусные баллы, если список заголовков не должен быть жестко закодирован
Ответы
Ответ 1
Если вы используете компилятор GNU, компилятор может собрать список зависимостей для вас. Фрагмент файла:
depend: .depend
.depend: $(SRCS)
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^ -MF ./.depend;
include .depend
или
depend: .depend
.depend: $(SRCS)
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^ > ./.depend;
include .depend
где SRCS
- это переменная, указывающая на весь список исходных файлов.
Существует также инструмент makedepend
, но мне он никогда не нравился, как gcc -MM
Ответ 2
Большинство ответов удивительно сложны или ошибочны. Однако простые и надежные примеры были опубликованы в другом месте [ codereview ]. Следует признать, что параметры, предоставляемые препроцессором GNU, немного запутывают. Однако удаление всех каталогов из цели сборки с -MM
задокументировано, а не ошибка [ gpp ]:
По умолчанию CPP берет имя основного входного файла, удаляет все компоненты каталога и любой суффикс файла, такой как '.c, и добавляет обычный суффикс объекта платформы.
(Несколько новее) -MMD
вероятно, то, что вам нужно. Для полноты примера приведем make файл, который поддерживает несколько директорий src и создает директории с некоторыми комментариями. Для простой версии без сборки dir смотрите [ codereview ].
CXX = clang++
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow
# Final binary
BIN = mybin
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build
# List of all .cpp source files.
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)
# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)
# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)
# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
# Create build directories - same structure as sources.
mkdir -p $(@D)
# Just link all the object files.
$(CXX) $(CXX_FLAGS) $^ -o [email protected]
# Include all .d files
-include $(DEP)
# Build target for every single object file.
# The potential dependency on header files is covered
# by calling '-include $(DEP)'.
$(BUILD_DIR)/%.o : %.cpp
mkdir -p $(@D)
# The -MMD flags additionaly creates a .d file with
# the same name as the .o file.
$(CXX) $(CXX_FLAGS) -MMD -c $< -o [email protected]
.PHONY : clean
clean :
# This should remove all generated files.
-rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)
Этот метод работает, потому что если существует несколько строк зависимостей для одной цели, зависимости просто объединяются, например:
a.o: a.h
a.o: a.c
./cmd
эквивалентно:
a.o: a.c a.h
./cmd
как упомянуто в: Makefile несколько строк зависимостей для одной цели?
Ответ 3
Поскольку я разместил здесь, gcc может создавать зависимости и компилировать в одно и то же время:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,[email protected]) -o [email protected] $<
Параметр '-MF' указывает файл для хранения зависимостей.
Черточка в начале '-include' сообщает Make продолжить, когда файл .d не существует (например, при первой компиляции).
Заметьте, что в gcc есть ошибка в отношении опции -o. Если вы укажете имя файла объекта obj/_file__c.o, то сгенерированный файл .d все равно будет содержать file.o, а не obj/_file__c.o.
Ответ 4
Как насчет чего-то типа:
includes = $(wildcard include/*.h)
%.o: %.c ${includes}
gcc -Wall -Iinclude ...
Вы также можете использовать подстановочные знаки напрямую, но я, как правило, считаю, что они мне нужны в более чем одном месте.
Обратите внимание, что это работает только на небольших проектах, так как предполагается, что каждый объектный файл зависит от каждого файла заголовка.
Ответ 5
Решение Martin выше работает отлично, но не обрабатывает файлы .o, которые находятся в подкаталогах. Годрик указывает, что флаг -MT заботится об этой проблеме, но одновременно препятствует правильному написанию файла .o. Ниже перечислены следующие проблемы:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MT [email protected] -MF $(patsubst %.o,%.d,[email protected]) $<
$(CC) $(CFLAGS) -o [email protected] $<
Ответ 6
Это сделает работу просто прекрасной и даже обрабатывает поддиры:
$(CC) $(CFLAGS) -MD -o [email protected] $<
проверил его с помощью gcc 4.8.3
Ответ 7
Я предпочитаю это решение, в соответствии с принятым ответом Майкла Уильямсона, он ловит изменения в источниках + встроенные файлы, затем источники + заголовки и, наконец, источники. Преимущество здесь в том, что вся библиотека не перекомпилирована, если сделаны только несколько изменений. Не очень важно для проекта с несколькими файлами, если у вас есть 10 или 100 источников, вы заметите разницу.
COMMAND= gcc -Wall -Iinclude ...
%.o: %.cpp %.inl
$(COMMAND)
%.o: %.cpp %.hpp
$(COMMAND)
%.o: %.cpp
$(COMMAND)
Ответ 8
Для меня работает следующее:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.cpp
$(CXX) $(CFLAGS) -MMD -c -o [email protected] $<
Ответ 9
Вот два лайнера:
CPPFLAGS = -MMD
-include $(OBJS:.c=.d)
Это работает со стандартным рецептом make, если у вас есть список всех ваших объектных файлов в OBJS
.
Ответ 10
Немного измененная версия ответа Софи, которая позволяет выводить файлы *.d в другую папку (я вставлю только интересную часть, которая генерирует файлы зависимостей):
$(OBJDIR)/%.o: %.cpp
# Generate dependency file
mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT [email protected] $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o [email protected]
Обратите внимание, что параметр
-MT [email protected]
используется для обеспечения того, чтобы цели (то есть имена объектных файлов) в сгенерированных файлах *.d содержали полный путь к файлам *.o, а не только имя файла.
Я не знаю, почему этот параметр НЕ нужен при использовании -MMD в сочетании с -c (как в версии Софи). В этой комбинации он, кажется, записывает полный путь файлов *.o в файлы *.d. Может быть, кто-то знает, почему это отличается от использования -MMD без -c. Я не нашел никаких подсказок в справочной странице g++.