¿Cómo puedo hacer que un Makefile reconstruya automáticamente los archivos de origen que incluyen un archivo de encabezado modificado? (En C / C ++)

92

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 punto para mi nivel de experiencia escribiendo archivos MAKE.

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 principal problema con este archivo MAKE es que cuando modifico un archivo de encabezado que incluyen uno o más archivos C, los archivos C no se reconstruyen. Puedo solucionar esto con bastante facilidad haciendo que todos mis archivos de encabezado sean dependencias para todos mis archivos C, pero eso causaría efectivamente una reconstrucción completa del proyecto cada vez que cambie / agregue un archivo de encabezado, lo que no sería muy elegante.

Lo que quiero es solo para los archivos C que incluyen se reconstruyan el archivo de encabezado que cambio 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 averiguar cómo hacer que los archivos C se invaliden cuando sus archivos de encabezado incluidos son más nuevos.

Escuché que GCC tiene algunos comandos para hacer esto posible (por lo que el archivo MAKE de alguna manera puede averiguar qué archivos deben reconstruirse) pero no puedo por mi vida encontrar un ejemplo de implementación real para mirar. ¿Alguien puede publicar una solución que habilite este comportamiento en un archivo MAKE?

EDITAR: Debo aclarar, estoy familiarizado con el concepto de poner los objetivos individuales y hacer que cada objetivo. O requiera los archivos de encabezado. Eso requiere que edite el archivo MAKE cada vez que incluyo 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í sola, lo cual estoy bastante seguro de haber visto en otros proyectos.

Nicholas Flynt
fuente

Respuestas:

30

Como ya se señaló en otra parte de este sitio, consulte esta página: Generación de dependencia automática

En resumen, gcc puede crear automáticamente archivos de dependencia .d para usted, que son pequeños fragmentos de archivos MAKE que contienen las dependencias del archivo .c que compiló. Cada vez que cambie el archivo .cy lo compile, el archivo .d se actualizará.

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

Udi Meiri
fuente
2
Puedo estar equivocado, pero creo que GCC ha agregado una función para tratar de solucionar ese problema de sed. Consulte gcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.html específicamente -MP.
Eugene Marcotte
Sí, -MP existe desde GCC 3, existe en clang y icc, y anula la necesidad de sed. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail llora resignatarios
La última versión del enlace en la respuesta contiene ejemplos que usan banderas GCC.
MadScientist
20

Puede agregar un comando 'make depend' como otros han dicho, pero ¿por qué no hacer que gcc cree dependencias y compile 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 comienzo de '-include' le dice a Make que continúe cuando el archivo .d no existe (por ejemplo, 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 de archivo del objeto para que diga obj/_file__c.o, el generado _file_.daún contendrá _file_.o, no obj/_file_c.o.

Martín Fido
fuente
18
Esto no funcionó para mí. Por ejemplo, el archivo MAKE generó y ejecutó 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 trabajo MAKE se convirtió en:$(CC) -c $(CFLAGS) -MMD -o $@ $<
Darren Cook
17

Esto es equivalente a la respuesta de Chris Dodd , pero usa una convención de nomenclatura diferente (y casualmente no requiere la sedmagia. Copiado de un duplicado posterior .


Si está utilizando un compilador GNU, el compilador puede ensamblar una lista de dependencias por usted. Fragmento 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 comogcc -MM

dmckee --- ex-gatito moderador
fuente
4
¿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.
Hola
@HelloGoodbye Un poco de estilo. Siempre he usado y siempre he visto SRCSy OBJS. Estoy de acuerdo la mayor parte del tiempo, pero todos deberían saber cuáles son.
sherrellbc
@sherrellbc Lo que la gente "debería" saber y lo que la gente realmente sabe son a menudo dos cosas diferentes: P
Hola
7

Tendrá que crear objetivos individuales para cada archivo C y luego enumerar el archivo de encabezado como una dependencia. Todavía puede usar sus objetivos genéricos y simplemente colocar las .hdependencias después, así:

%.o: %.c
        @echo Compiling $<...
        $(CC) $(CFLAGS) -c -o $@ $<

foo.c: bar.h
# And so on...
mipadi
fuente
4

Básicamente, necesita crear dinámicamente las reglas del archivo MAKE para reconstruir los archivos de objeto cuando cambian los archivos de encabezado. Si usa gcc y gnumake, esto es bastante fácil; solo pon 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 su archivo MAKE.

Chris Dodd
fuente
2
En cierto modo entiendo esto, excepto que (aparte de que las banderas -MM y -MG son nuevas) no entiendo para qué sirve la línea de texto críptico de búsqueda de expresiones regulares. Eso no va a hacer felices a mis compañeros de equipo ... ^ _ ^ Sin embargo, lo intentaré y veré si obtengo algún resultado.
Nicholas Flynt
sed es la abreviatura de "editor de flujo" que puede modificar un flujo de texto sin necesidad de utilizar un archivo. Es una herramienta estándar de Unix y más pequeña y rápida, por lo que se usa con más frecuencia que awk o perl.
Zan Lynx
Ah, ahí está el problema: estoy haciendo esto en Windows.
Nicholas Flynt
3

Además de lo que dijo @mipadi, también puede explorar el uso de la -Mopción ' ' para generar un registro de las dependencias. Incluso podría generarlos en un archivo separado (tal vez 'depend.mk') que luego incluiría en el archivo MAKE. O puede encontrar una make dependregla ' ' que edita el archivo MAKE con las dependencias correctas (términos de Google: "no elimine esta línea" y dependa).

Jonathan Leffler
fuente
1

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é que estaba generando archivos de objetos vacíos (cero bytes) para mí sin advertencias ni errores. Podría 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 nadie más mencionó (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 Makefile anterior es fácil de ajustar para archivos .c.

También note 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 un archivo de dependencia primero, y luego compila realmente un archivo .cc. Y así sucesivamente para cada archivo fuente.

Tagar
fuente
0

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

Por ejemplo, en el Makefile en algún lugar:

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 $@ $<
Richard Elkins
fuente
-1

Creo que el mkdepcomando es lo que quieres. De hecho, escanea archivos .c en busca de #includelíneas y crea un árbol de dependencia para ellos. Creo que los proyectos de Automake / Autoconf usan esto de forma predeterminada.

jdizzle
fuente