Как я могу заставить Makefile автоматически пересобирать исходные файлы, содержащие модифицированный заголовочный файл? (В C/С++)

У меня есть следующий make файл, который я использую для сборки программы (на самом деле ядро), над которой я работаю. Это с нуля, и я изучаю процесс, поэтому он не идеален, но я думаю, что его достаточно мощный на данный момент для моего уровня опыта написания make файлов.

AS  =   nasm
CC  =   gcc
LD  =   ld

TARGET      =   core
BUILD       =   build
SOURCES     =   source
INCLUDE     =   include
ASM         =   assembly

VPATH = $(SOURCES)

CFLAGS  =   -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
            -nostdinc -fno-builtin -I $(INCLUDE)
ASFLAGS =   -f elf

#CFILES     =   core.c consoleio.c system.c
CFILES      =   $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
SFILES      =   assembly/start.asm

SOBJS   =   $(SFILES:.asm=.o)
COBJS   =   $(CFILES:.c=.o)
OBJS    =   $(SOBJS) $(COBJS)

build : $(TARGET).img

$(TARGET).img : $(TARGET).elf
    c:/python26/python.exe concat.py stage1 stage2 pad.bin core.elf floppy.img

$(TARGET).elf : $(OBJS)
    $(LD) -T link.ld -o [email protected] $^

$(SOBJS) : $(SFILES)
    $(AS) $(ASFLAGS) $< -o [email protected]

%.o: %.c
    @echo Compiling $<...
    $(CC) $(CFLAGS) -c -o [email protected] $<

#Clean Script - Should clear out all .o files everywhere and all that.
clean:
    -del *.img
    -del *.o
    -del assembly\*.o
    -del core.elf

Моя основная проблема с этим make файлом заключается в том, что когда я изменяю заголовочный файл, содержащий один или несколько файлов C, файлы C не перестраиваются. Я могу исправить это довольно легко, имея все мои файлы заголовков зависимостями для всех моих файлов C, но это может привести к полной перестройке проекта в любое время, когда я изменил/добавил файл заголовка, что было бы не очень изящным.

То, что я хочу, это только для файлов C, которые включают файл заголовка, который я изменяю, чтобы быть перестроенным, и для того, чтобы весь проект был связан снова. Я могу сделать ссылку, заставив все файлы заголовков быть зависимыми от цели, но я не могу понять, как сделать файлы C недействительными, когда их включенные заголовочные файлы являются более новыми.

Я слышал, что GCC имеет некоторые команды, чтобы сделать это возможным (так что makefile может каким-то образом определить, какие файлы нужно перестраивать), но я не могу на всю жизнь найти реальный пример реализации. Может ли кто-нибудь опубликовать решение, которое позволит это поведение в make файле?

РЕДАКТИРОВАТЬ: Я должен уточнить, я знаком с концепцией включения отдельных целей и наличия каждого target.o, требующих файлы заголовков. Это требует, чтобы я редактировал make файл каждый раз, когда я включаю файл заголовка где-то, что немного больно. Я ищу решение, которое может самостоятельно определять зависимости заголовка файла, что я вполне уверен, что видел в других проектах.

Ответ 1

Как уже указывалось в другом месте на этом сайте, смотрите эту страницу: Генерация автозависимостей

Короче говоря, gcc может автоматически создавать файлы зависимостей .d, которые представляют собой мини-фрагменты make файла, содержащие зависимости скомпилированного файла .c. Каждый раз, когда вы изменяете файл .c и компилируете его, файл .d будет обновляться.

Помимо добавления флага -M в gcc, вам нужно будет включить файлы .d в make файл (как писал Крис выше). На странице есть несколько более сложных проблем, которые решаются с помощью sed, но вы можете их игнорировать и выполнить команду "make clean", чтобы убрать файлы .d, когда make жалуется на невозможность создать файл заголовка, который больше не существует. ,

Ответ 2

Вы могли бы добавить команду make make, как заявили другие, но почему бы не получить gcc для создания зависимостей и компиляции в одно и то же время:

DEPS := $(COBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) -c $(CFLAGS) -MM -MF $(patsubst %.o,%.d,[email protected]) -o [email protected] $<

Параметр '-MF' указывает файл для хранения зависимостей.

Черточка в начале '-include' сообщает Make продолжить, когда файл .d не существует (например, при первой компиляции).

Заметьте, что в gcc есть ошибка в отношении опции -o. Если вы укажете имя файла объекта obj/_file__c.o, то сгенерированный _file_.d будет по-прежнему содержать _file_.o, а не obj/_file_c.o.

Ответ 3

Это эквивалентно ответ Криса Додда, но использует другое соглашение об именах (и по совпадению не требует магии sed. Скопировано из более поздний дубликат.


Если вы используете компилятор GNU, компилятор может собрать список зависимостей для вас. Фрагмент файла:

depend: .depend

.depend: $(SOURCES)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^>>./.depend;

include .depend

Существует также инструмент makedepend, но мне он никогда не нравился, как gcc -MM

Ответ 4

Вам нужно будет сделать отдельные цели для каждого файла C, а затем перечислить заголовочный файл как зависимость. Вы все равно можете использовать свои общие цели и просто поместите зависимости .h впоследствии, например:

%.o: %.c
        @echo Compiling $<...
        $(CC) $(CFLAGS) -c -o [email protected] $<

foo.c: bar.h
# And so on...

Ответ 5

В принципе, вам необходимо динамически создавать правила makefile для восстановления файлов объектов при изменении файлов заголовков. Если вы используете gcc и gnumake, это довольно легко; просто поместите что-то вроде:

$(OBJDIR)/%.d: %.c
        $(CC) -MM -MG $(CPPFLAGS) $< | sed -e 's,^\([^:]*\)\.o[ ]*:,$(@D)/\1.o $(@D)/\1.d:,' >[email protected]

ifneq ($(MAKECMDGOALS),clean)
include $(SRCS:%.c=$(OBJDIR)/%.d)
endif

в вашем файле.

Ответ 6

Помимо того, что сказал @mipadi, вы также можете изучить использование опции "-M" для создания записи зависимостей. Вы даже можете сгенерировать их в отдельный файл (возможно, "depend.mk" ), который затем включаете в make файл. Или вы можете найти правило "make depend", которое редактирует make файл с правильными зависимостями (условия Google: "не удалять эту строку" и зависеть).

Ответ 7

Ни один из ответов не работал у меня. Например. Ответ Мартина Фидо предполагает, что gcc может создать файл зависимостей, но когда я попытался создать для него пустые (нулевые байты) объектные файлы без каких-либо предупреждений или ошибок. Это может быть ошибка gcc. Я нахожусь на

$gcc --version gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)

Итак, вот мой полный Makefile, который работает для меня; это комбинация решений + то, что никто не упоминал (например, "правило замены суффикса", указанное как .cc.o:):

CC = g++
CFLAGS = -Wall -g -std=c++0x
INCLUDES = -I./includes/

# LFLAGS = -L../lib
# LIBS = -lmylib -lm

# List of all source files
SRCS = main.cc cache.cc

# Object files defined from source files
OBJS = $(SRCS:.cc=.o)

# # define the executable file 
MAIN = cache_test

#List of non-file based targets:
.PHONY: depend clean all

##  .DEFAULT_GOAL := all

# List of dependencies defined from list of object files
DEPS := $(OBJS:.o=.d)

all: $(MAIN)

-include $(DEPS)

$(MAIN): $(OBJS)
    $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS)

#suffix replacement rule for building .o from .cc's
#build dependency files first, second line actually compiles into .o
.cc.o:
    $(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,[email protected]) $<
    $(CC) $(CFLAGS) $(INCLUDES) -c -o [email protected] $<

clean:
    $(RM) *.o *~ $(MAIN) *.d

Заметьте, что я использовал .cc.. Вышеуказанный Makefile легко настраивается для файлов .c.

Также важно отметить важность этих двух строк:

$(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,[email protected]) $<
$(CC) $(CFLAGS) $(INCLUDES) -c -o [email protected] $<

поэтому gcc вызывается один раз, чтобы сначала создать файл зависимостей, а затем фактически компилирует файл .cc. И так далее для каждого исходного файла.

Ответ 8

Упрощенное решение: просто используйте Makefile, чтобы правило компиляции .c в .o зависело от файлов заголовков и любого другого значения в вашем проекте как зависимости.

Например, в Makefile где-нибудь:

DEPENDENCIES=mydefs.h yourdefs.h Makefile GameOfThrones.S07E01.mkv

::: (your other Makefile statements like rules 
:::  for constructing executables or libraries)

# Compile any .c to the corresponding .o file:
%.o: %.c $(DEPENDENCIES)
        $(CC) $(CFLAGS) -c -o [email protected] $<

Ответ 9

Я считаю, что команда mkdep - это то, что вы хотите. Он фактически сканирует файлы .c для строк #include и создает для них дерево зависимостей. Я считаю, что проекты Automake/Autoconf используют это по умолчанию.