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++.