Makefile (Generación de autodependencia)

solo para una terminología rápida:

#basic makefile rule target: dependencies recipe 

El problema: quiero generar las dependencias automáticamente.

Por ejemplo, espero convertir esto:

 #one of my targets file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h $(COMPILE) 

Dentro de esto:

 #one of my targets file.o: $(GENERATE) $(COMPILE) 

y no estoy muy seguro de si es posible …

Lo que sí sé

Puedo usar esta bandera del comstackdor:

 g++ -MM file.cpp 

y devolverá el objective y la dependencia adecuados.
por lo tanto, del ejemplo, volvería:

 file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h 

sin embargo, ‘hacer’ NO me permite escribir explícitamente código de shell en la sección de destino o dependencia de una regla 🙁
Sé que hay una función ‘make’ llamada shell

pero no puedo conectar esto como una dependencia y hacer un análisis de magia porque se basa en la macro $ @ que representa el objective … o al menos creo que ese es el problema

Incluso intenté simplemente reemplazar la dependencia “file.cpp” con esta función makefile y eso tampoco funcionará.

 #it's suppose to turn the $@ (file.o) into file.cpp THE_CPP := $(addsuffix $(.cpp),$(basename $@)) #one of my targets file.o: $(THE_CPP) 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h $(COMPILE) #this does not work 

Entonces, en todo Google, parece haber dos soluciones. los cuales no entiendo completamente.
De GNU Make Manual

Algunos sitios que dicen que el manual GNU Make uno está desactualizado

Entonces mi última pregunta es: ¿es posible hacerlo de la manera que quiero hacerlo,
y si no, ¿alguien puede desglosar el código de uno de estos sitios y explicarme en detalle cómo funcionan? Lo implementaré de alguna de estas formas si es necesario, pero estoy cansado de pegar un trozo de código en mi archivo MAKE antes de entenderlo

Las versiones más nuevas de GCC tienen una opción -MP que se puede usar con -MD. Simplemente agregué -MP y -MD a la variable CPPFLAGS para mi proyecto (no escribí una receta personalizada para comstackr C ++) y agregué una línea “-include $ (SRC: .cpp = .d)”.

El uso de -MD y -MP proporciona un archivo de dependencia que incluye tanto las dependencias (sin tener que usar algunos sed extraños) como los objectives ficticios (por lo que eliminar archivos de encabezado no causará errores).

Para manipular los nombres de archivo cuando ya sabe cuáles deberían ser las dependencias, puede usar una regla de patrón:

 file.o: %.o : %.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h $(COMPILE) 

Y puedes reutilizar la regla para otros objectives:

 # Note these two rules without recipes: file.o: 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h anotherFile.o: 4.h 9.h yetAnother.h file.o anotherFile.o: %.o : %.cpp $(COMPILE) 

Pero si desea que Make determine la lista de dependencias automáticamente, la mejor manera (que yo sepa) es la Generación avanzada de dependencia automática . Se parece a esto:

 %.o : %.cc @g++ -MD -c -o $@ $< @cp $*.d $*.P; \ sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \ rm -f $*.d -include *.P 

Básicamente, cuando construye file.o , también construye file.d Luego ejecuta file.d través de un desconcertante comando sed que convierte la lista de dependencias en una regla sin recetas. La última línea es una instrucción para include las reglas que existen. La lógica aquí es sutil e ingeniosa: en realidad no necesitas las dependencias la primera vez que foo.o , porque Make ya sabe que foo.o debe foo.o , porque no existe. La próxima vez que ejecute Make, usará la lista de dependencias que creó la última vez. Si cambia uno de los archivos para que realmente exista una nueva dependencia que no está en la lista, Make aún reconstruirá foo.o porque usted cambió un archivo que era una dependencia . Pruébalo, ¡realmente funciona!

Excelentes respuestas, pero en mi comstackción puse los archivos .obj en un subdirectorio en función del tipo de comstackción (es decir, depuración frente a versión). Entonces, por ejemplo, si estoy construyendo depuración, pongo todos los archivos de objeto en una carpeta de comstackción / depuración. Fue una tarea abrumadora tratar de hacer que el comando multiline sed de arriba utilizara la carpeta de destino correcta, pero después de un poco de experimentación, tropecé con una solución que funciona muy bien para mi comstackción. Con suerte también ayudará a alguien más.

Aquí hay un fragmento:

 # List my sources CPP_SOURCES := foo.cpp bar.cpp # If I'm debugging, change my output location ifeq (1,$(DEBUG)) OBJ_DIR:=./obj/debug CXXFLAGS+= -g -DDEBUG -O0 -std=c++0x else CXXFLAGS+= -s -O2 OBJ_DIR:=./obj/release endif # destination path macro we'll use below df = $(OBJ_DIR)/$(*F) # create a list of auto dependencies AUTODEPS:= $(patsubst %.cpp,$(OBJ_DIR)/%.d,$(CPP_SOURCES)) # include by auto dependencies -include $(AUTODEPS) .... other rules # and last but not least my generic compiler rule $(OBJ_DIR)/%.o: %.cpp @# Build the dependency file @$(CXX) -MM -MP -MT $(df).o -MT $(df).d $(CXXFLAGS) $< > $(df).d @# Compile the object file @echo " C++ : " $< " => " $@ @$(CXX) -c $< $(CXXFLAGS) -o $@ 

Ahora para los detalles: la primera ejecución de CXX en mi regla de comstackción genérica es la más interesante. Tenga en cuenta que no estoy usando ningún comando "sed". Las versiones más nuevas de gcc hacen todo lo que necesitaba (estoy usando gcc 4.7.2).

-MM crea la regla de dependencia principal que incluye los encabezados de proyecto pero no los encabezados del sistema. Si lo dejé así, mi archivo .obj NO tendría la ruta correcta. Entonces uso la opción -MT para especificar la ruta "real" a mi destino .obj. (usando la macro "df" que creé).
También uso una segunda opción -MT para asegurarme de que el archivo de dependencia resultante (es decir, el archivo .d) tenga la ruta correcta, y que esté incluido en la lista de destino y, por lo tanto, tenga las mismas dependencias que el archivo fuente.

Por último, pero no menos importante, es la inclusión de la opción -MP. Esto le dice a gcc que también haga que las reglas resueltas para cada encabezado resuelvan el problema que ocurre si elimino un encabezado que hace que make genere un error.

Sospecho que debido a que estoy usando gcc para toda la generación de dependencias en lugar de conectarme a sed, mi comstackción es más rápida (aunque todavía tengo que probar que mi comstackción es relativamente pequeña en este momento). Si ves formas de mejorar esto, siempre estoy abierto a sugerencias. Disfrutar

Primero, puedes tener THE_CPP=$(patsubst %.o,%.cpp,$@)

Luego puede ejecutar make -p para comprender las reglas integradas de make

Una forma habitual de hacerlo podría ser generar las dependencias del archivo MAKE en archivos *.md :

 %.o: %.c $(COMPILE.c) $(OUTPUT_OPTION) $< -MMD -MF $(patsubst %.c,%.md,$@) 

y luego en tu Makefile incluyéndolos con algo como

 -include $(wildcard *.md) 

Pero también puede considerar el uso de otros constructores como omake y muchos otros.

Para el registro, así es como genero dependencias automáticamente ahora:

 CPPFLAGS = -std=c++1y -MD -MP SRC = $(wildcard *.cpp) all: main main: $(SRC:%.cpp=%.o) g++ $(CPPFLAGS) -o $@ $^ -include $(SRC:%.cpp=%.d) 

Los indicadores del comstackdor -MD y -MP ayudan a hacer el truco.

WOOO! Me las arreglé para obtener el código en la publicación de Beta para trabajar en un pequeño proyecto de prueba.
Debo señalar, para cualquier otra persona que se encuentre con esto, si está utilizando el shell bash (que yo era), tendrá que agregar un carácter de escape delante del signo de libra para escapar de hacer el rest de la expresión un comentario. (ver la 4ª línea de código)

 %.o : %.cpp g++ -c -MD -o $@ $< cp $*.d $*.P; \ sed -e 's/\#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \ rm -f $*.d -include *.P 

Ahora quiero compartir información que encontré en Managing Projects with GNU Make, 3rd Edition. porque señala algunos problemas importantes en este asunto y proporciona un código que aún no entiendo completamente.
Aparece un método en el libro que es similar al método que se encuentra en la página Hacer manual .
Se parece a esto:

 include $(subst .c,.d,$(SOURCES)) %.d: %.c $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\).o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ 

Esto es lo que creo que está sucediendo.
Inmediatamente, ‘make’ desea incluir un archivo “.d” para cada archivo fuente.
Como inicialmente no existen archivos .d, el fragmento de código se ejecuta una y otra vez para crear todos los archivos .d faltantes.
Esto significa que make comenzará una y otra vez hasta que cada archivo .d se cree e incluya en el archivo MAKE.
Cada archivo “.d” es lo que dijo Beta: un objective con un conjunto de dependencias y SIN receta.

Si alguna vez se cambia un archivo de encabezado, las reglas que se incluyen en, necesitarán las dependencias actualizadas primero. Esto es lo que me desanima un poco, ¿cómo es que el trozo de código puede volver a llamarse? Se usa para actualizar archivos .d, por lo que si un archivo .h cambia, ¿cómo se llama? Aparte de esto, me doy cuenta de que la regla predeterminada se utiliza para comstackr el objeto. Cualquier aclaración / concepto erróneo de esta explicación es apreciada.


Más adelante en el libro, señala los problemas con este método y los problemas que, en mi opinión, también existen en la implementación de Advanced Auto-Dependency Generation.
Problema 1: es ineficiente. ‘make’ debe reiniciarse cada vez que crea un archivo .d
Problema 2: make genera mensajes de advertencia para todos los archivos .d faltantes. Lo cual es más que nada una molestia y se puede ocultar agregando un “-” delante de la statement de inclusión.
Problema 3: si elimina un archivo src porque ya no es necesario, ‘make’ se bloqueará la próxima vez que intente comstackrse porque algún archivo .d tiene el src que falta como dependencia, y porque no hay una regla para volver a crear ese src , make se negará a ir más allá.

Dicen que una solución a estos problemas es el método de Tromey, pero el código se ve muy diferente del código en el sitio web. Tal vez sea solo porque usaron algunas macros, la convirtieron en una llamada a función y la escribieron ligeramente diferente. Todavía estoy investigando, pero quería compartir algunos descubrimientos que he hecho hasta ahora. Con suerte, esto abre un poco más de discusión y me acerca al fondo de todo esto.

Prefiero usar la función $ (shell …) con find. Aquí hay una muestra de uno de mis Makefiles:

 SRCDIR = src OBJDIR = obj LIBDIR = lib DOCDIR = doc # Get Only the Internal Structure of Directories from SRCDIR STRUCTURE := $(shell find $(SRCDIR) -type d) #Filter-out hidden directories STRUCTURE := $(filter-out $(shell find $(SRCDIR)/.* -type d),$(STRUCTURE)) # Get All Files From STRUCTURE CODEFILES := $(addsuffix /*,$(STRUCTURE)) CODEFILES := $(wildcard $(CODEFILES)) ## Filter Only Specific Files SRCFILES := $(filter %.c,$(CODEFILES)) HDRFILES := $(filter %.h,$(CODEFILES)) OBJFILES := $(subst $(SRCDIR),$(OBJDIR),$(SRCFILES:%.c=%.o)) DOCFILES := $(addprefix $(DOCDIR)/, \ $(addsuffix .md, \ $(basename $(SRCFILES)))) # Filter Out Function main for Libraries LIBDEPS := $(filter-out $(OBJDIR)/main.o,$(OBJFILES)) 

En este enfoque, primero obtengo toda la estructura del directorio interno, con cualquier profundidad. Luego obtengo todos los archivos dentro de la Estructura. En este momento, puedo usar filter, filter-out, addsuffix, etc., para obtener exactamente lo que necesito en cada momento.

Este ejemplo cubre archivos * .c, pero también puede cambiarlo a * .cpp.