gcc - primer - makefile make
¿Cómo puedo hacer que un Makefile recompile automáticamente los archivos fuente que incluyen un archivo de encabezado modificado?(En C/C++) (9)
Tengo el siguiente archivo MAKE que utilizo para construir un programa (un kernel, en realidad) en el que estoy trabajando. Es desde cero y estoy aprendiendo sobre el proceso, por lo que no es perfecto, pero creo que es lo suficientemente poderoso en este momento para mi nivel de experiencia escribiendo makefiles.
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
Mi problema principal con este makefile es que cuando modifico un archivo de encabezado que incluye uno o más archivos C, los archivos C no se reconstruyen. Puedo arreglar esto bastante fácilmente teniendo todos mis archivos de encabezado como dependencias para todos mis archivos C, pero eso efectivamente causaría una reconstrucción completa del proyecto cada vez que cambiara / añadiera un archivo de encabezado, lo cual no sería muy elegante.
Lo que quiero es que solo los archivos C que incluyen el archivo de cabecera que modifico se reconstruyan y que todo el proyecto se vuelva a vincular. Puedo hacer el enlace haciendo que todos los archivos de encabezado sean dependencias del objetivo, pero no puedo imaginar cómo hacer que los archivos C se invaliden cuando los archivos de encabezado incluidos son más nuevos.
He escuchado que GCC tiene algunos comandos para que esto sea posible (para que el archivo MAKE pueda de alguna manera descubrir qué archivos deben ser reconstruidos) pero no puedo encontrar un ejemplo de implementación real para mirar. ¿Alguien puede publicar una solución que permita este comportamiento en un archivo MAKE?
EDITAR: Debería aclarar, estoy familiarizado con el concepto de poner los objetivos individuales y tener cada objetivo. O requieren los archivos de encabezado. Eso requiere que edite el archivo MAKE cada vez que incluya un archivo de encabezado en alguna parte, lo cual es un poco molesto. Estoy buscando una solución que pueda derivar las dependencias del archivo de encabezado por sí mismo, que estoy bastante seguro de haber visto en otros proyectos.
Básicamente, debe crear dinámicamente las reglas de makefile para reconstruir los archivos de objeto cuando los archivos de encabezado cambian. Si usa gcc y gnumake, esto es bastante fácil; solo ponga algo como:
$(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
en tu archivo MAKE
Como ya se señaló en otro lugar en este sitio, consulte esta página: http://make.paulandlesley.org/autodep.html
En resumen, gcc puede crear automáticamente archivos de dependencia .d para usted, que son fragmentos de mini archivos que contienen las dependencias del archivo .c que compiló. Cada vez que cambie el archivo .c y lo compile, se actualizará el archivo .d.
Además de agregar la bandera -M a gcc, necesitarás incluir los archivos .d en el archivo MAKE (como Chris escribió arriba). Hay algunos problemas más complicados en la página que se resuelven con sed, pero puede ignorarlos y hacer una "limpieza" para eliminar los archivos .d siempre que se queje de no poder crear un archivo de encabezado que ya no existe. .
Creo que el comando mkdep
es lo que quieres. En realidad escanea archivos .c para #include
lines y crea un árbol de dependencias para ellos. Creo que los proyectos de Automake / Autoconf usan esto por defecto.
Esto es equivalente a la respuesta de Chris Dodd , pero usa una convención de nomenclatura diferente (y casualmente no requiere la magia sed
. Copiado de un duplicado posterior) .
Si está utilizando un compilador de GNU, el compilador puede armar una lista de dependencias para usted. Fichero de Makefile:
depend: .depend
.depend: $(SOURCES)
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^>>./.depend;
include .depend
También está la herramienta makedepend
, pero nunca me gustó tanto como gcc -MM
Más allá de lo que dijo @mipadi, también puedes explorar el uso de la opción '' -M
'' para generar un registro de las dependencias. Incluso puedes generarlos en un archivo separado (quizás ''depend.mk'') que luego incluyas en el archivo MAKE. O puede encontrar una regla '' make depend
'' que edite el archivo MAKE con las dependencias correctas (términos de Google: "no elimine esta línea" y dependa).
Ninguna de las respuestas funcionó para mí. Por ejemplo, la respuesta de Martin Fido sugiere que gcc puede crear un archivo de dependencia, pero cuando intenté generar archivos de objetos vacíos (cero bytes) sin advertencias ni errores. Puede ser un error de gcc. Estoy en
$ gcc --version gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)
Así que aquí está mi Makefile completo que funciona para mí; es una combinación de soluciones + algo que no fue mencionado por nadie más (por ejemplo, "regla de reemplazo de sufijo" especificada como .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
Observe que utilicé .cc .. El archivo Makefile anterior es fácil de ajustar para archivos .c.
También es importante tener en cuenta la importancia de estas dos líneas:
$(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $<
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
entonces gcc se llama una vez para construir primero un archivo de dependencia, y luego compila un archivo .cc. Y así sucesivamente para cada archivo fuente.
Puede agregar un comando ''make depend'' como otros han indicado, pero ¿por qué no obtener gcc para crear dependencias y compilar al mismo tiempo:
DEPS := $(COBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) -c $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<
El parámetro ''-MF'' 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 (p. Ej., En la primera compilación).
Tenga en cuenta que parece haber 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 _file_.d
generado aún contendrá _file_.o
, no obj/_file_c.o
.
Solución más simple: simplemente use Makefile para que la regla de compilación .c a .o dependa de los archivos de encabezado y de cualquier otra cosa relevante en su proyecto como dependencia.
Por ejemplo, en el Makefile en alguna parte:
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 $@ $<
Tendrá que crear objetivos individuales para cada archivo C y, a continuación, enumerar el archivo de encabezado como una dependencia. Todavía puede usar sus objetivos genéricos y simplemente colocar las dependencias .h
después, de esta manera:
%.o: %.c
@echo Compiling $<...
$(CC) $(CFLAGS) -c -o $@ $<
foo.c: bar.h
# And so on...