2008-11-18 6 views
80

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.¿Cómo puedo hacer que un Makefile vuelva a generar automáticamente los archivos fuente que incluyen un archivo de encabezado modificado? (En C/C++)

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 [email protected] $^ 

$(SOBJS) : $(SFILES) 
    $(AS) $(ASFLAGS) $< -o [email protected] 

%.o: %.c 
    @echo Compiling $<... 
    $(CC) $(CFLAGS) -c -o [email protected] $< 

#Clean Script - Should clear out all .o files everywhere and all that. 
clean: 
    -del *.img 
    -del *.o 
    -del assembly\*.o 
    -del core.elf 

Mi principal problema con este archivo MAKE es que cuando modifico un archivo de cabecera que uno o más archivos de C incluyen, los archivos de 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 solo para los archivos C que incluyen el archivo de encabezado que cambio para reconstruirlo, y para 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 oído 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?

EDIT: 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.

Respuesta

25

Como ya se ha señalado en otra parte de 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 makefile que contienen las dependencias del archivo .c compilado Cada vez que cambie el archivo .c y lo compile, se actualizará el archivo .d.

Además de agregar el indicador -M a gcc, deberá incluir los archivos .d en el archivo MAKE (como escribió Chris anteriormente). Hay algunos problemas más complicados en la página que se resuelven con sed, pero puede ignorarlos y hacer un "make clean" para eliminar los archivos .d cada vez que se queja de no poder crear un archivo de encabezado que ya no existe existe

+2

Puedo estar equivocado, pero creo que GCC realmente ha agregado una función para tratar de evitar ese problema. Consulte http://gcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.html específicamente -MP. –

+0

Sí, -MP existe desde GCC 3, existe en clang y icc, y anula la necesidad de sed. http://bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/#comment-50775 – hmijail

6

Deberá establecer objetivos individuales para cada archivo C y, a continuación, enumerar el archivo de encabezado como una dependencia. Puede seguir utilizando sus objetivos genéricos, y sólo tiene que colocar las dependencias .h después, de este modo:

%.o: %.c 
     @echo Compiling $<... 
     $(CC) $(CFLAGS) -c -o [email protected] $< 

foo.c: bar.h 
# And so on... 
3

Más allá de lo que @mipadi Dicho esto, también se puede 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 edita el archivo MAKE con las dependencias correctas (términos de Google: "no elimine esta línea" y dependa).

3

Básicamente, necesita crear dinámicamente las reglas de makefile para reconstruir los archivos de objeto cuando cambian los archivos de encabezado. Si usa gcc y gnumake, esto es bastante fácil; simplemente ponga algo como:

$(OBJDIR)/%.d: %.c 
     $(CC) -MM -MG $(CPPFLAGS) $< | sed -e 's,^\([^:]*\)\.o[ ]*:,$(@D)/\1.o $(@D)/\1.d:,' >[email protected] 

ifneq ($(MAKECMDGOALS),clean) 
include $(SRCS:%.c=$(OBJDIR)/%.d) 
endif 

en su archivo MAKE.

+2

en cierto modo me entiendo esto, excepto que (a un lado enfrente él-MM y banderas Mg ser nuevo) No entiendo por qué la expresión regular Lookin' línea de texto críptico es para. Eso no me hará feliz a mis compañeros de equipo ...^_^Lo intentaré y veré si tengo algún resultado. –

+0

sed es la abreviatura de "editor de flujo" que puede modificar una secuencia de texto sin necesidad de utilizar un archivo. Es una herramienta estándar de Unix y más pequeña y más rápida, por lo que se usa con más frecuencia que awk o perl. –

+0

Ah, ahí está el problema: estoy haciendo esto en Windows. –

16

Esto es equivalente a Chris Dodd's answer, pero utiliza una convención de nomenclatura diferente (y casualmente no requiere la sed magia. Copiado de a later duplicate.


Si está utilizando un compilador de GNU, el compilador puede montar . una lista de dependencias para usted fragmento Makefile:

depend: .depend 

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

include .depend 

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

+3

¿Por qué no deletreas FUENTES? No requiere especialmente muchos más caracteres y no está tan ofuscado como "SRCS", que puede parecer un acrónimo. – HelloGoodbye

-1

Creo que el comando mkdep es lo que desea. En realidad escanea archivos .c para líneas #include y crea un árbol de dependencias para ellos. Creo que los proyectos de Automake/Autoconf usan esto por defecto.

19

Se podría añadir un 'make depend' comando como otros han dicho, 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,[email protected]) -o [email protected] $< 

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

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 _file_.d generado contendrá _file_.o, no obj/_file_c.o.

+13

Esto no funcionó para mí. P.ej. el archivo MAKE generado y ejecutado esto: 'g ++ -c -Wall -Werror -MM -MF main.d -o main.o main.cpp' Obtuve el archivo main.d, pero main.o tenía 0 bytes. Sin embargo, la bandera -MMD parece hacer exactamente lo que se requería. Así que mi regla de makefile se convirtió en '$ (CC) -c $ (CFLAGS) -MMD -o $ @ $ <' –

0

Ninguna de las respuestas funcionó para mí. P.ej. 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 gcc --version (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)

Así que aquí es mi completa Makefile que funciona para mí; que es una combinación de soluciones + algo que no fue mencionado por nadie más (por ejemplo, "regla de sustitución de sufijo" especifica 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,[email protected]) $< 
    $(CC) $(CFLAGS) $(INCLUDES) -c -o [email protected] $< 

clean: 
    $(RM) *.o *~ $(MAIN) *.d 

Aviso Solía ​​.cc .. El Makefile anterior es fácil para ajustar los archivos .c

También es importante destacar la importancia de estas dos líneas:

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

por lo gcc que se llama una vez para crear un archivo de dependencia primero, y luego en realidad compila un archivo .cc. Y así sucesivamente para cada archivo fuente.

0

Solución más simple: simplemente use el archivo Makefile para que la regla de compilación .c a .o dependa de los archivos de encabezado y cualquier otra cosa relevante en su proyecto como dependencia.

E.g., 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 [email protected] $< 
Cuestiones relacionadas