Як мені зробити так, щоб Makefile автоматично відновлював вихідні файли, які містять змінений файл заголовка? (У C / C ++)


92

У мене є такий 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?

EDIT: Я повинен уточнити, я знайомий з концепцією введення окремих цілей і наявності кожної цілі. Потрібні файли заголовків. Це вимагає від мене редагування make-файлу кожного разу, коли я де-небудь включаю файл заголовка, що трохи болить. Я шукаю рішення, яке може самостійно вивести залежності файлу заголовка, що я впевнений, що бачив в інших проектах.

Відповіді:


30

Як вже зазначалося в інших місцях на цьому сайті, див. Цю сторінку: Автогенерація залежностей

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

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


2
Я можу помилятися, але я думаю, що GCC насправді додав функцію, щоб спробувати обійти цю проблему sed. Перевірте gcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.html, зокрема -MP.
Eugene Marcotte

Так, -MP існує з GCC 3, існує в clang і icc і скасовує потребу в sed. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail оплакує відставних

Остання версія посилання у відповіді містить приклади використання прапорів GCC.
MadScientist

20

Ви можете додати команду 'make depend', як сказали інші, але чому б не отримати 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.


18
У мене це не спрацювало. Наприклад, згенерований і g++ -c -Wall -Werror -MM -MF main.d -o main.o main.cpp запущений файл make: я отримав файл main.d, але main.o - 0 байт. Однак прапор -MMD, схоже, робить саме те, що потрібно. Тож моє робоче правило створення файлів стало:$(CC) -c $(CFLAGS) -MMD -o $@ $<
Даррен Кук

17

Це еквівалентно відповіді Кріса Додда , але використовується інша конвенція щодо іменування (і за збігом обставин не вимагає sedмагії. Скопійовано з пізнішого дубліката .


Якщо ви використовуєте компілятор GNU, компілятор може зібрати для вас список залежностей. Фрагмент Makefile:

depend: .depend

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

include .depend

Є також інструмент makedepend, але мені він ніколи не подобався так сильно, якgcc -MM


4
Чому б вам не прописати ДЖЕРЕЛА? Не вимагає особливо багато набагато більше символів і не такий затуманений, як "SRCS", що може виглядати як абревіатура.
HelloGoodbye

@HelloGoodbye Трохи стилю. Я завжди користувався, і завжди бачив, SRCSі OBJS. Я б погодився більшу частину часу, але кожен повинен знати, що це таке.
sherrellbc

@sherrellbc Те, що люди "повинні" знати, і те, що люди насправді знають, часто є двома різними речами: P
HelloGoodbye

7

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

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

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

4

В основному, вам потрібно динамічно створювати правила makefile для відновлення об’єктних файлів, коли файли заголовків змінюються. Якщо ви використовуєте 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.


2
Я начебто це розумію, за винятком того, що (крім того, що він не є прапорами -MM та -MG), я не розумію, для чого призначений рядок загальнодоступного виразного тексту. Це не буде радувати мене по товаришах по команді ... ^ _ ^ Хоч я спробую і подивлюся, чи я маю результати.
Ніколас Флінт,

sed - це скорочення від "редактор потоку", який може модифікувати потік тексту без необхідності використовувати файл. Це стандартний інструмент Unix, менший і швидший, тому він використовується частіше, ніж awk або perl.
Zan Lynx

А, тут проблема: я роблю це під Windows.
Ніколас Флінт,

3

Крім того, що сказав @mipadi, ви також можете дослідити використання -Mопції ' ' для створення запису залежностей. Ви можете навіть створити їх в окремому файлі (можливо, 'depend.mk'), який потім включите в файл make. Або ви можете знайти make dependправило ' ', яке редагує файл make з правильними залежностями (умови Google: "не видаляти цей рядок" і залежати).


1

Жодна з відповідей у ​​мене не спрацювала. Наприклад, відповідь Мартіна Фідо передбачає, що 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

Більш просте рішення: Просто використовуйте 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 $@ $<

-1

Я вважаю, що mkdepкоманда - це те, що ти хочеш. Він фактично сканує файли .c на наявність #includeрядків і створює для них дерево залежностей. Я вважаю, що проекти Automake / Autoconf використовують це за замовчуванням.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.