2010-03-07 17 views
70

Digamos que tengo un makefile con la reglaMakefile, dependencias de cabecera

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

quiero * .o, para ser reconstruido siempre que un archivo de cabecera cambios. En lugar de elaborar una lista de dependencias, cada vez que cambie cualquier archivo de encabezado en /include, todos los objetos en el directorio deben reconstruirse.

No puedo pensar en una buena manera de cambiar la regla para acomodar esto, estoy abierto a sugerencias. Los puntos de bonificación si la lista de cabeceras no tiene que ser duro con código de

+0

Al haber escrito mi respuesta a continuación, miré en la lista relacionada y encontré: http://stackoverflow.com/questions/297514/how-can-i-have-a-makefile-automatically-rebuild-source-files-that -include-a-modif que parece ser un duplicado. La respuesta de Chris Dodd es equivalente a la mía, aunque usa una convención de nomenclatura diferente. – dmckee

Respuesta

104

Si está utilizando un compilador de GNU, el compilador puede armar una lista de dependencias para usted. fragmento de 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 

donde SRCS es una variable apuntando a toda su lista de archivos fuente.

También existe la herramienta makedepend, pero nunca me ha gustado tanto como gcc -MM

+2

Me gusta este truco, pero ¿cómo puedo hacer que 'dependen' se ejecute solo cuando los archivos de origen han cambiado? Parece que se ejecuta cada vez independientemente ... – chase

+2

@chase: Bueno, erróneamente he hecho la dependencia de los archivos objeto, cuando obviamente debería estar en las fuentes y tenía el orden de dependencia incorrecto para los dos objetivos, también. Eso es lo que obtengo por tipear de memoria. Pruebalo ahora. – dmckee

+4

¿Es forma de agregar antes de cada archivo algún prefijo para mostrar que está en otro directorio, por ejemplo 'build/file.o'? – RiaD

21

¿Qué tal algo como:

includes = $(wildcard include/*.h) 

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

También es posible usar los comodines directamente, pero tiendo a encontrar los necesito en más de un lugar

Tenga en cuenta que esto solo funciona bien en proyectos pequeños, ya que supone que cada archivo de objeto depende de cada archivo de encabezado.

+0

gracias, estaba haciendo esto mucho más complicado de lo que era necesario – Mike

+9

Esto funciona, sin embargo, el problema con esto es que cada archivo de objeto se vuelve a compilar, cada vez que se realiza un pequeño cambio, es decir, si tiene 100 fuente/archivos de encabezado, y realiza un pequeño cambio en solo uno, todos los 100 se vuelven a compilar. –

+0

Realmente debe actualizar su respuesta para decir que esta es una forma muy ineficiente de hacerlo porque reconstruye TODOS los archivos cada vez que se cambia CUALQUIER archivo de encabezado. Las otras respuestas son mucho mejores. – xaxxon

24

Como he publicado here gcc puede crear dependencias y compilar al mismo tiempo:

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

-include $(DEPS) 

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

El '-MF 'parámetro especifica un archivo para almacenar las dependencias.

El guión al inicio de' -include 'le dice a Make que continúe cuando el archivo .d no exista (por ejemplo, en la primera compilación).

Nota Parece que hay un error en gcc con respecto a la opción -o. Si configura el nombre del archivo del objeto para que diga obj/_file__c.o, el archivo generado .d seguirá conteniendo el archivo .o, no obj/_file__c.o.

+4

Cuando intento esto, todos mis archivos .o se crean como archivos vacíos. Tengo mis objetos en una subcarpeta de compilación (así que $ OBJECTS contiene build/main.o build/smbus.o build/etc ...) y eso ciertamente crea los archivos .d como describiste con el error aparente, pero ciertamente no está compilando los archivos .o en absoluto, mientras que si elimino -M -M y -MF. – bobpaul

+1

El uso de -MT resolverá la nota en las últimas líneas de su respuesta que actualiza el objetivo de cada lista de dependencia. –

+0

He intentado esto en varias combinaciones y no parece funcionar ... – g24l

0

Prefiero esta solución, sobre la respuesta aceptada por Michael Williamson, capta los cambios en fuentes + archivos en línea, luego fuentes + encabezados, y finalmente solo fuentes. La ventaja aquí es que no se vuelve a compilar toda la biblioteca si solo se realizan algunos cambios. No es una gran consideración para un proyecto con un par de archivos, pero si tiene 10 o 100 fuentes, notará la diferencia.

COMMAND= gcc -Wall -Iinclude ... 

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

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

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

Esto funciona solo si no tiene nada en sus archivos de encabezado que requiera la recompilación de cualquier archivo cpp distinto del correspondiente archivo de implementación . – matec

3

Esto va a hacer el trabajo muy bien, e incluso manejar subdirectorios Especificados:

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

probado con gcc 4.8.3

4

solución de Martin anterior funciona muy bien, pero no maneja .o archivos que residen en subdirectorios.Godric señala que la bandera -MT se encarga de ese problema, pero evita simultáneamente que el archivo .o se escriba correctamente. Lo siguiente se ocupará de ambos problemas:

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 mayoría de las respuestas son sorprendentemente complicadas o erróneas. Sin embargo, ejemplos simples y robustos han sido publicados en otra parte [codereview]. Es cierto que las opciones proporcionadas por el preprocesador gnu son un poco confusas. Sin embargo, la eliminación de todos los directorios del tipo de generación con -MM está documentado y no un error [gpp]:

Por defecto CPP lleva el nombre del archivo de entrada principal, borra cualquier componentes de directorio y cualquier sufijo de archivo como '.c', y agrega el sufijo de objeto habitual de la plataforma .

La opción (algo más reciente) -MMD es probablemente lo que usted desea. Para completar, un ejemplo de un archivo MAKE que admite varios directorios src y compilar directorios con algunos comentarios. Para una versión simple sin directorios de compilación ver [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) 

Este método funciona porque si hay varias líneas de dependencia para un único objetivo, las dependencias son simplemente unidas, por ejemplo:

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

es equivalente a:

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

como se ha mencionado en: Makefile multiple dependency lines for a single target?

+0

Me gusta esta solución. No quiero escribir el comando make depends. Útil !! – Robert

+1

Hay un error ortográfico en el valor de la variable OBJ: el 'CPP' debe leer' CPPS' – ctrucza

+0

Esta es mi respuesta preferida; +1 para ti. Este es el único en esta página que tiene sentido, y cubre (por lo que puedo ver) todas las situaciones en las que es necesaria una recompilación (evitando la compilación innecesaria, pero suficiente) – Joost

0

Me funciona lo siguiente:

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

-include $(DEPS) 

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

Aquí hay dos forro:

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

Esto funciona con la receta como predeterminado, siempre y cuando usted tiene una lista de todos los archivos de objetos en OBJS.