2010-03-07 7 views
70

Diciamo che ho un makefile con la regolaMakefile, le dipendenze di intestazione

%.o: %.c 
gcc -Wall -Iinclude ... 

voglio * .o per essere ricostruito ogni volta che un file di intestazione modifiche. Invece di elaborare un elenco di dipendenze, ogni volta che viene modificato un file di intestazione in /include, è necessario ricreare tutti gli oggetti nella directory.

Non riesco a pensare a un bel modo per cambiare la regola per accogliere questo, sono aperto ai suggerimenti. I punti di bonus se l'elenco delle intestazioni non devono essere hard-coded

+0

Dopo aver scritto la mia risposta di seguito ho cercato nell'elenco correlato e ho trovato: http://stackoverflow.com/questions/297514/how-can-i-have-a-makefile-automatically-rebuild-source-files-that -include-a-modif che sembra essere un duplicato. La risposta di Chris Dodd è equivalente alla mia, sebbene usi una convenzione di denominazione diversa. – dmckee

risposta

104

Se si utilizza un compilatore GNU, il compilatore può assemblare un elenco di dipendenze per l'utente. frammento Makefile:

depend: .depend 

.depend: $(SRCS) 
     rm -f ./.depend 
     $(CC) $(CFLAGS) -MM $^ -MF ./.depend; 

include .depend 

o

depend: .depend 

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

include .depend 

dove SRCS è una punta variabile per l'intero elenco dei file di origine.

C'è anche lo strumento makedepend, ma mi è mai piaciuto tanto quanto gcc -MM

+2

Mi piace questo trucco, ma come posso ottenere dipendere da eseguire solo quando i file di origine sono cambiati? Sembra funzionare sempre a prescindere ... – chase

+2

@chase: Beh, ho erroneamente creato la dipendenza dai file oggetto, quando dovrebbe essere ovviamente sui sorgenti e l'ordine di dipendenza è sbagliato anche per i due obiettivi. Questo è quello che ottengo per la digitazione dalla memoria. Provalo ora. – dmckee

+4

È possibile aggiungere prima di ogni file un prefisso per mostrare che si trova in un'altra directory, ad esempio 'build/file.o'? – RiaD

21

ne dite qualcosa di simile:

includes = $(wildcard include/*.h) 

%.o: %.c ${includes} 
    gcc -Wall -Iinclude ... 

Si potrebbe anche usare i caratteri jolly direttamente, ma io tendo a trovarli ho bisogno in più di un posto.

Si noti che questo funziona bene solo su piccoli progetti, dal momento che presuppone che ogni file oggetto dipende da ogni file di intestazione.

+0

grazie, lo stavo rendendo molto più complicato del necessario – Mike

+9

Questo funziona, tuttavia, il problema con questo è che ogni file oggetto viene ricompilato ogni volta che viene apportata una piccola modifica, ad esempio se si dispone di 100 sorgenti/file di intestazione, e si effettua una piccola modifica a uno solo, tutti i 100 vengono ricompilati. –

+0

Dovresti davvero aggiornare la tua risposta per dire che questo è un modo molto inefficiente di farlo perché ricostruisce TUTTI i file ogni volta che viene modificato un qualsiasi file di intestazione. Le altre risposte sono molto migliori. – xaxxon

24

Come ho scritto here gcc può creare dipendenze e compilare al tempo stesso:

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

-include $(DEPS) 

%.o: %.c 
    $(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,[email protected]) -o [email protected] $< 

Il '-MF 'parametro specifica un file per memorizzare le dipendenze in.

Il trattino all'inizio di "-include" indica a Make per continuare quando il file .d non esiste (ad esempio nella prima compilazione).

Nota sembra che ci sia un errore in gcc riguardo all'opzione -o. Se si imposta il nome file dell'oggetto per dire obj/_file__c.o, il file .d generato contiene file .o, non obj/_file__c.o.

+4

Quando provo questo risultato tutti i miei file .o vengono creati come file vuoti. Ho i miei oggetti in una sottocartella build (quindi $ OBJECTS contiene build/main.o build/smbus.o build/etc ...) e sicuramente crea i file .d come descritto con il bug apparente, ma sicuramente non sta creando i file .o, mentre lo fa se rimuovo -MM e -MF. – bobpaul

+1

L'utilizzo di -MT risolverà la nota nelle ultime righe della risposta che aggiorna la destinazione di ciascun elenco di dipendenze. –

+0

Ho provato questo in varie combinazioni e non sembra funzionare ... – g24l

0

Preferisco questa soluzione, oltre la risposta accettata da Michael Williamson, rileva le modifiche a sorgenti + file inline, quindi sorgenti + intestazioni e infine solo fonti. Il vantaggio è che l'intera libreria non viene ricompilata se vengono apportate solo alcune modifiche. Non è una considerazione enorme per un progetto con un paio di file, se si hanno 10 o 100 fonti, noterete la differenza.

COMMAND= gcc -Wall -Iinclude ... 

%.o: %.cpp %.inl 
    $(COMMAND) 

%.o: %.cpp %.hpp 
    $(COMMAND) 

%.o: %.cpp 
    $(COMMAND) 
+1

Funziona solo se non hai nulla nei tuoi file di intestazione che richiederebbe la ricompilazione di qualsiasi file cpp diverso dal corrispondente file di implementazione . – matec

3

Questo farà il lavoro bene, e anche gestire subdirs essere specificato:

$(CC) $(CFLAGS) -MD -o [email protected] $< 

provato con gcc 4.8.3

4

soluzione di Martin sopra grandi opere, ma non gestisce .o file che risiedono in sottodirectory.Godric sottolinea che il flag -MT si occupa di questo problema, ma impedisce al tempo stesso che il file .o venga scritto correttamente. Quanto segue si occuperà di entrambi questi problemi:

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

-include $(DEPS) 

%.o: %.c 
    $(CC) $(CFLAGS) -MM -MT [email protected] -MF $(patsubst %.o,%.d,[email protected]) $< 
    $(CC) $(CFLAGS) -o [email protected] $< 
26

La maggior parte delle risposte è sorprendentemente complicata o errata. Tuttavia, esempi semplici e robusti sono stati pubblicati altrove [codereview]. Ammetto che le opzioni fornite dal preprocessore di Gnu sono un po 'confuse. Tuttavia, la rimozione di tutte le directory dal bersaglio di compilazione con -MM è documentato e non un bug [gpp]:

Per impostazione predefinita CPP prende il nome del file di input principale, elimina tutte componenti di directory e qualsiasi suffisso di file come '.c' e aggiunge il suffisso dell'oggetto normale della piattaforma .

L'opzione (un po 'più recente) -MMD è probabilmente quello che desideri. Per completezza un esempio di makefile che supporta più dirs di src e crea dir con alcuni commenti. Per una versione semplice senza dirs di build vedi [codereview].

CXX = clang++ 
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow 

# Final binary 
BIN = mybin 
# Put all auto generated stuff to this build dir. 
BUILD_DIR = ./build 

# List of all .cpp source files. 
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp) 

# All .o files go to build dir. 
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o) 
# Gcc/Clang will create these .d files containing dependencies. 
DEP = $(OBJ:%.o=%.d) 

# Default target named after the binary. 
$(BIN) : $(BUILD_DIR)/$(BIN) 

# Actual target of the binary - depends on all .o files. 
$(BUILD_DIR)/$(BIN) : $(OBJ) 
    # Create build directories - same structure as sources. 
    mkdir -p $(@D) 
    # Just link all the object files. 
    $(CXX) $(CXX_FLAGS) $^ -o [email protected] 

# Include all .d files 
-include $(DEP) 

# Build target for every single object file. 
# The potential dependency on header files is covered 
# by calling `-include $(DEP)`. 
$(BUILD_DIR)/%.o : %.cpp 
    mkdir -p $(@D) 
    # The -MMD flags additionaly creates a .d file with 
    # the same name as the .o file. 
    $(CXX) $(CXX_FLAGS) -MMD -c $< -o [email protected] 

.PHONY : clean 
clean : 
    # This should remove all generated files. 
    -rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP) 

Questo metodo funziona perché se sono presenti più linee di dipendenza per un singolo bersaglio, le dipendenze vengono semplicemente uniti, ad esempio:

a.o: a.h 
a.o: a.c 
    ./cmd 

è equivalente a:

a.o: a.c a.h 
    ./cmd 

come menzionato a: Makefile multiple dependency lines for a single target?

+0

Mi piace questa soluzione. Non voglio digitare make comando dipendente. Utile !! – Robert

+1

C'è un errore di ortografia nel valore della variabile OBJ: il 'CPP' dovrebbe leggere' CPPS' – ctrucza

+0

Questa è la mia risposta preferita; +1 per te. Questo è l'unico in questa pagina che ha senso e copre (per quello che posso vedere) tutte le situazioni in cui è necessaria la ricompilazione (evitando compilazioni non necessarie, ma sufficienti) – Joost

0

Le seguenti opere per me:

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

-include $(DEPS) 

%.o: %.cpp 
    $(CXX) $(CFLAGS) -MMD -c -o [email protected] $< 
0

Ecco un due-liner:

CPPFLAGS = -MMD 
-include $(OBJS:.c=.d) 

Questo funziona con la ricetta di default marca, fino a quando si dispone di un elenco di tutti i file oggetto in OBJS.