2009-03-12 7 views
7

Ultimamente stiamo discutendo sul modo in cui gestiamo i file .d per le dipendenze nel nostro processo di build basato su make. È stato sollevato il problema che a volte i file .d possono essere danneggiati quando i build vengono interrotti.Getting make to delete additional files on error

Stiamo utilizzando la destinazione .DELETE_ON_ERROR per garantire che se una build viene interrotta o non sia riuscita, i file oggetto che era in fase di generazione vengono eliminati. Tuttavia, stiamo anche utilizzando GCC per generare file .d in fase di compilazione, che dovrebbero essere eliminati. Non sembra essere un modo semplice per dirlo su questo.

Quindi la domanda è, c'è un modo in cui possiamo fare in modo di eliminare sia il nostro oggetto che i nostri file di dipendenza in caso di errore? C'è un modo in cui possiamo impostare le regole in modo che sappia che entrambi i file .d e .o sono generati nello stesso momento e devono essere cancellati se c'è un errore?

In alternativa, c'è qualcos'altro che possiamo fare per risolvere il problema dei file .d corrotti? Un suggerimento lungo queste linee è quello di generare i file .d con un nome temporaneo e avere un passaggio post-compilazione separato per file che lo copia con il nome corretto.

risposta

6

In generale, GNU make non supporta gli obiettivi con più uscite. Tuttavia, c'è un'eccezione a questa regola: le regole del modello. Se riesci a strutturare il tuo makefile in modo tale che usi le regole del modello per generare i file oggetto, potresti riuscire a raggiungere i tuoi obiettivi. Per esempio:

.DELETE_ON_ERROR: 

all: foo.o 

%.o %.d: %.c 
    @touch $*.d 
    @touch $*.o 
    @exit 1 

Vedrai che con questo makefile quando viene rilevato l ' "errore" nella regola, sia la .de e il file .o vengono eliminati. Il vantaggio di questo approccio è che esprime in modo più preciso il grafico delle dipendenze descrivendo come deve essere generato il file .d e quale regola lo produrrà.

In alternativa, un paradigma comune in questo caso è proprio come suggerito: avere GCC generare il file .d in un nome di file temporaneo e spostarlo solo dopo che il comando GCC è stato completato correttamente. Di solito questo è compiuto con un trucco del guscio:

all: foo.o 

%.o: %.c 
    gcc -o [email protected] -MMD -MF $(basename [email protected]).d.tmp -c $< \ 
     && mv $(basename [email protected]).d.tmp $(basename [email protected]).d 

Ecco il trucco "magia" è l'uso delle bandiere del CCG -MMD, che genera il file di dipendenza come un effetto collaterale di compilazione, e -MF, che consente di specificare il nome di output per il file delle dipendenze; e l'uso della sintassi della shell cmd1 && cmd2, che fa sì che la shell esegua solo cmd2 se cmd1 si chiude correttamente.

+0

Il '.DELETE_ON_ERROR:' nella parte superiore del makefile permettimi di eliminare tutti i miei tempfile. Grazie! –

+0

Suggerirei di mettere il mv come seconda riga nella ricetta per la regola del modello "% .o:% .c".Non vedo alcun cambiamento nel comportamento o beneficio per l'utilizzo della shell '&&' per metterlo sulla prima riga. –

+0

@RichardPerrin la proposta produrrà un comportamento errato se gmake viene invocato con l'opzione '-i' (ignore errori). In tal caso, la tua versione eseguirà il comando 'mv' indipendentemente dal fatto che ci sia o meno un errore in' gcc', mentre il mio originale no. –

-1

Non ho nessuno degli esempi di Eric per funzionare correttamente. GCC (versione 4.4) non compila nulla quando si passa all'interruttore -MM in modo che non sembri che sia possibile compilare e scrivere il file .d in una volta sola. Ecco quello che ho fatto:

%.o: %.c 
    @rm -f [email protected] $(patsubst %.o,%.d,[email protected]) 
    gcc -c $< -o [email protected] 
    @$(CXX) -MM -MG > $(patsubst %.o,%.d,[email protected]) 

Si inizia cancellando un file .d esistente, la terza linea che genera quella nuova viene eseguita solo se il secondo comando, il passo compilazione vera e propria, riesce (& & trick di Eric non è necessario, make lo fa automaticamente). Per qualche motivo non capisco, un file .o esistente non viene automaticamente cancellato se la compilazione fallisce, ma è stato risolto facilmente aggiungendo rm al numero rm.

+0

Corretto, gcc in realtà non compila il file se si specifica solo '-MM'. Devi anche usare '-MF' come descritto nella mia risposta originale. –

+0

@Eric Ho aggiunto anche l'opzione -MF, ma il preprocessore sembra non inviare alcun output alla parte del compilatore di gcc, così finisco con un file oggetto a byte zero ... – Wim

+0

Scusa, ho fatto un errore: tu voglio '-MMD', non' -MM'. Ho corretto anche la risposta originale. –