Как мне сделать так, чтобы Makefile автоматически перестраивал исходные файлы, которые содержат измененный заголовочный файл? (В C / C ++) - PullRequest
83 голосов
/ 18 ноября 2008

У меня есть следующий 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 $@ $^

$(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

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

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

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

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

Ответы [ 9 ]

29 голосов
/ 09 марта 2009

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

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

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

20 голосов
/ 23 марта 2010

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

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

-include $(DEPS)

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

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

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

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

16 голосов
/ 07 марта 2010

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


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

depend: .depend

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

include .depend

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

7 голосов
/ 18 ноября 2008

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

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

foo.c: bar.h
# And so on...
4 голосов
/ 18 ноября 2008

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

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

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

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

3 голосов
/ 18 ноября 2008

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

0 голосов
/ 02 сентября 2016

Более простое решение: просто используйте Makefile, чтобы правило компиляции .c to .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 $@ $<
0 голосов
/ 02 июля 2016

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

$ gcc - версия 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'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. И так для каждого исходного файла.

0 голосов
/ 07 марта 2010

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

...