Как я могу заставить 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 используют это по умолчанию.