Jak mogę sprawić, by Makefile automatycznie odbudował pliki źródłowe, które zawierają zmodyfikowany plik nagłówkowy? (W C / C ++)

92

Mam następujący plik makefile, którego używam do budowania programu (właściwie jądra), nad którym pracuję. To jest od zera i uczę się o tym procesie, więc nie jest doskonały, ale myślę, że w tym momencie jest wystarczająco potężny, jak na mój poziom doświadczenia w pisaniu makefile.

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

Moim głównym problemem związanym z tym plikiem makefile jest to, że kiedy modyfikuję plik nagłówkowy, który zawiera jeden lub więcej plików C, pliki C nie są odbudowywane. Mogę to dość łatwo naprawić, mając wszystkie moje pliki nagłówkowe jako zależności dla wszystkich moich plików C, ale to skutecznie spowodowałoby całkowitą przebudowę projektu za każdym razem, gdy zmieniłem / dodałem plik nagłówkowy, co nie byłoby zbyt wdzięczne.

To, czego chcę, to tylko pliki C. zawierają zmieniony przeze mnie plik nagłówkowy, zostały odbudowane, a cały projekt został ponownie połączony. Mogę wykonać linkowanie, powodując, że wszystkie pliki nagłówkowe są zależnościami celu, ale nie mogę dowiedzieć się, jak unieważnić pliki C, gdy dołączone pliki nagłówkowe są nowsze.

Słyszałem, że GCC ma kilka poleceń, które to umożliwiają (więc plik makefile może w jakiś sposób dowiedzieć się, które pliki należy odbudować), ale nie mogę za całe życie znaleźć rzeczywistego przykładu implementacji do obejrzenia. Czy ktoś może opublikować rozwiązanie, które umożliwi takie zachowanie w pliku makefile?

EDYCJA: Powinienem wyjaśnić, jestem zaznajomiony z koncepcją umieszczania poszczególnych celów i posiadania każdego celu. Aby wymagać plików nagłówkowych. To wymaga ode mnie edytowania pliku makefile za każdym razem, gdy umieszczam gdzieś plik nagłówkowy, co jest trochę uciążliwe. Szukam rozwiązania, które może samodzielnie wyprowadzić zależności pliku nagłówkowego, co jestem pewien, że widziałem w innych projektach.

Nicholas Flynt
źródło

Odpowiedzi:

30

Jak już wskazano w innym miejscu tej witryny, zobacz tę stronę: Generowanie automatycznej zależności

W skrócie, gcc może automatycznie tworzyć dla ciebie pliki zależności .d, które są fragmentami mini makefile zawierającymi zależności skompilowanego pliku .c. Za każdym razem, gdy zmienisz plik .c i skompilujesz go, plik .d zostanie zaktualizowany.

Oprócz dodania flagi -M do gcc, będziesz musiał dołączyć pliki .d do pliku makefile (tak jak Chris napisał powyżej). Na stronie jest kilka bardziej skomplikowanych problemów, które można rozwiązać za pomocą seda, ale możesz je zignorować i zrobić "wyczyść", aby wyczyścić pliki .d, gdy narzekasz na niemożność zbudowania pliku nagłówkowego, który już nie istnieje .

Udi Meiri
źródło
2
Mogę się mylić, ale myślę, że GCC faktycznie dodało funkcję, aby spróbować obejść ten problem z sedem. Sprawdź gcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.html, a konkretnie -MP .
Eugene Marcotte,
Tak, -MP istnieje od GCC 3, istnieje w clang i icc i eliminuje potrzebę sed. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail opłakuje resignees
Najnowsza wersja odsyłacza w odpowiedzi zawiera przykłady wykorzystujące flagi GCC.
MadScientist
20

Mógłbyś dodać polecenie `` make depend '', jak powiedzieli inni, ale dlaczego nie uzyskać gcc do tworzenia zależności i kompilacji w tym samym czasie:

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

-include $(DEPS)

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

Parametr „-MF” określa plik, w którym mają być przechowywane zależności.

Myślnik na początku „-include” mówi programowi Make, aby kontynuował, gdy plik .d nie istnieje (np. Przy pierwszej kompilacji).

Zauważ, że wydaje się, że w gcc jest błąd dotyczący opcji -o. Jeśli ustawisz nazwę pliku obiektu, aby powiedzieć, obj/_file__c.owygenerowany _file_.dnadal będzie zawierał _file_.o, a nie obj/_file_c.o.

Martin Fido
źródło
18
To nie zadziałało dla mnie. Np. g++ -c -Wall -Werror -MM -MF main.d -o main.o main.cpp Plik makefile wygenerował i uruchomił to: Otrzymałem plik main.d, ale main.o miał 0 bajtów. Jednak flaga -MMD wydaje się robić dokładnie to, co było wymagane. Tak więc moja robocza reguła plików stała się następująca:$(CC) -c $(CFLAGS) -MMD -o $@ $<
Darren Cook
17

Jest to odpowiednik odpowiedzi Chrisa Dodda , ale używa innej konwencji nazewnictwa (i przypadkowo nie wymaga sedmagii. Skopiowano z późniejszego duplikatu) .


Jeśli używasz kompilatora GNU, kompilator może utworzyć dla Ciebie listę zależności. Fragment pliku Makefile:

depend: .depend

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

include .depend

Jest też narzędzie makedepend, ale nigdy nie podobało mi się tak bardzogcc -MM

dmckee --- kociak byłego moderatora
źródło
4
Dlaczego nie przeliterujesz ŹRÓDŁA? Nie wymaga szczególnie wielu znaków i nie jest tak zaciemniony jak „SRCS”, który może wyglądać jak akronim.
HelloGoodbye
@HelloGoodbye Trochę stylu. Zawsze używałem i zawsze widziałem SRCSi OBJS. Zwykle się z tym zgadzam, ale każdy powinien wiedzieć, co to jest.
sherrellbc
@sherrellbc To, co ludzie „powinni” wiedzieć, a co tak naprawdę wiedzą, to często dwie różne rzeczy: P
HelloGoodbye
7

Będziesz musiał określić indywidualne cele dla każdego pliku C, a następnie wyświetlić plik nagłówkowy jako zależność. Nadal możesz używać swoich ogólnych celów, a .hpotem po prostu umieścić zależności, na przykład:

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

foo.c: bar.h
# And so on...
mipadi
źródło
4

Zasadniczo musisz dynamicznie tworzyć reguły makefile, aby odbudować pliki obiektowe, gdy zmienią się pliki nagłówkowe. Jeśli używasz gcc i gnumake, jest to dość łatwe; po prostu wpisz coś takiego:

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

w swoim pliku makefile.

Chris Dodd
źródło
2
W pewnym sensie to rozumiem, z wyjątkiem tego, że (poza tym, że flagi -MM i -MG są nowe) nie rozumiem, do czego służy ta linia wyrażenia regularnego w tajemniczym tekście. To nie uszczęśliwi moich kolegów z drużyny ... ^ _ ^ Spróbuję tego jednak i zobaczę, czy mam jakieś rezultaty.
Nicholas Flynt
sed to skrót od „stream editor”, który może modyfikować strumień tekstu bez konieczności używania pliku. Jest to standardowe narzędzie uniksowe, mniejsze i szybsze, więc jest używane częściej niż awk czy perl.
Zan Lynx,
Ach, jest problem: robię to pod Windows.
Nicholas Flynt
3

Oprócz tego, co powiedział @mipadi, możesz również zbadać użycie opcji „ -M” w celu wygenerowania rekordu zależności. Możesz nawet wygenerować je w osobnym pliku (np. „Depend.mk”), który następnie umieścisz w pliku makefile. Możesz też znaleźć make dependregułę „ ”, która edytuje plik makefile z poprawnymi zależnościami (hasła Google: „nie usuwaj tej linii” i zależą).

Jonathan Leffler
źródło
1

Żadna z odpowiedzi nie pomogła. Np. Odpowiedź Martina Fido sugeruje, że gcc może utworzyć plik zależności, ale kiedy próbowałem, generował dla mnie puste (zero bajtów) pliki obiektowe bez żadnych ostrzeżeń ani błędów. To może być błąd gcc. jestem na

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

Oto mój kompletny plik Makefile, który działa dla mnie; jest to kombinacja rozwiązań + coś, o czym nikt inny nie wspominał (np. „reguła zastępowania sufiksów” określona jako .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

Zauważ, że użyłem .cc .. Powyższy plik Makefile można łatwo dostosować do plików .c.

Zwróć także uwagę na znaczenie tych dwóch linii:

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

tak więc gcc jest wywoływane raz, aby najpierw zbudować plik zależności, a następnie faktycznie kompiluje plik .cc. I tak dalej dla każdego pliku źródłowego.

Tagar
źródło
0

Prostsze rozwiązanie: po prostu użyj Makefile, aby reguła kompilacji .c do .o była zależna od plików nagłówkowych i wszystkiego innego, co jest istotne w twoim projekcie jako zależność.

Np. W pliku Makefile gdzieś:

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 $@ $<
Richarda Elkinsa
źródło
-1

Wierzę, że mkdeppolecenie jest tym, czego chcesz. W rzeczywistości skanuje pliki .c w poszukiwaniu #includewierszy i tworzy dla nich drzewo zależności. Uważam, że projekty Automake / Autoconf używają tego domyślnie.

jdizzle
źródło