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


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

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 $@ $^

$(SOBJS) : $(SFILES)
    $(AS) $(ASFLAGS) $< -o $@

%.o: %.c
    @echo Compiling $<...
    $(CC) $(CFLAGS) -c -o $@ $<

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

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

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

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

EDIT: я должен уточнить, я знаком с концепцией размещения отдельных целей и наличия каждой цели.o требовать заголовочный файл. Это требует от меня редактирования файла makefile каждый раз, когда я включаю файл заголовка где-то, что немного больно. Я ищу решение, которое может получить зависимости от файла заголовка самостоятельно, что я довольно уверен, что видел в других проектах.

9 83

9 ответов:

Как уже указывалось в другом месте на этом сайте, см. эту страницу: http://make.paulandlesley.org/autodep.html

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

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

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

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

-include $(DEPS)

%.o: %.c
    $(CC) -c $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<

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

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

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

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


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

depend: .depend

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

include .depend

есть также инструмент makedepend, но я никогда не любил его так же, как gcc -MM

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

%.o: %.c
        @echo Compiling $<...
        $(CC) $(CFLAGS) -c -o $@ $<

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

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

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

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

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

в вашем makefile.

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

$ gcc --версия gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)

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

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's from .cc's
#build dependency files first, second line actually compiles into .o
.cc.o:
    $(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<

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

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

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

$(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $<
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<

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

простое решение: просто используйте Makefile, чтобы иметь .С до .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 $@ $<

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