2008-10-23 17 views
50

Tengo origen en un grupo de subdirectorios como:¿Cómo generar un Makefile con fuente en subdirectorios utilizando un solo archivo MAKE

src/widgets/apple.cpp 
src/widgets/knob.cpp 
src/tests/blend.cpp 
src/ui/flash.cpp 

En la raíz del proyecto Quiero generar un solo uso de un Makefile gobernar como:

%.o: %.cpp 
    $(CC) -c $< 

build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o 
    $(LD) build/widgets/apple.o .... build/ui/flash.o -o build/test.exe 

Cuando intento esto no encuentra una regla de acumulación/widgets/apple.o. ¿Puedo cambiar algo para que se use% .o:% .cpp cuando se necesita crear build/widgets/apple.o?

Respuesta

64

La razón es que la regla

%.o: %.cpp 
     ... 

espera que el archivo .cpp para residir en el mismo directorio que el .o su edificio. Como test.exe en su caso depende de build/widgets/apple.o (etc), make espera que apple.cpp sea build/widgets/apple.cpp.

Se puede utilizar para resolver este VPATH:

VPATH = src/widgets 

BUILDDIR = build/widgets 

$(BUILDDIR)/%.o: %.cpp 
     ... 

Cuando se trata de construir "construir/widgets/apple.o", que va a buscar apple.cpp en VPATH. Tenga en cuenta que la regla de construcción tiene que utilizar variables especiales con el fin de acceder a la marca nombre de archivo real se encuentra:

$(BUILDDIR)/%.o: %.cpp 
     $(CC) $< -o [email protected] 

Donde "$ <" se expande a la ruta en la marca situada la primera dependencia.

También tenga en cuenta que esto construirá todos los archivos .o en compilación/widgets. Si usted quiere construir los binarios en diferentes directorios, puede hacer algo como

build/widgets/%.o: %.cpp 
     .... 

build/ui/%.o: %.cpp 
     .... 

build/tests/%.o: %.cpp 
     .... 

Yo recomendaría que se utiliza "canned command sequences" con el fin de evitar la repetición de la regla actual compilador de construcción:

define cc-command 
$(CC) $(CFLAGS) $< -o [email protected] 
endef 

a continuación, puede tener varias reglas como esta:

build1/foo.o build1/bar.o: %.o: %.cpp 
    $(cc-command) 

build2/frotz.o build2/fie.o: %.o: %.cpp 
    $(cc-command) 
+2

'VPATH' no le permite tener diferentes archivos de origen con el mismo nombre en directorios diferentes, lo que en primer lugar frustra el propósito de los directorios. –

+10

Eh, no, no creo que lo haga. Organizar su código fuente en diferentes directorios tiene más beneficios que permitir múltiples archivos fuente con el mismo nombre. – JesperE

+0

He intentado seguir su solución y he creado objetivos de la forma '.build/cwrapper /%. O:% .c% .cpp'. Sin embargo, cuando los uso como dependencia, p. Ej. 'cwrapper: .build/cwrapper /%. o',' make' se queja: "No hay regla para hacer objetivo' .build/cwrapper /%. o '". ¿Qué estoy haciendo mal? – Raphael

0

Por lo general, crea un Makefile en cada subdirectorio y escribe en el Makefile de nivel superior para llamar a make en los subdirectorios.

Esta página puede ayudar: http://www.gnu.org/software/make/

+1

Esto se hace comúnmente, pero está lleno de problemas. La principal es que nadie hace que el proceso sepa sobre todas las dependencias, por lo que cosas como -j2 en sistemas multinúcleo no funcionarán. Ver http://aegis.sourceforge.net/auug97.pdf – KeithB

+4

La referencia de Keith es a un excelente artículo llamado 'Recursive Make Harmatful'. Esta es una contribución a la serie de artículos que comienzan con la carta de Dijkstra "Ir a Considerado dañino", y que culminan en "Considerado perjudicial" Considerado perjudicial ". –

+4

Se hace generalmente porque las personas no entienden cómo escribir Makefiles. Un Makefile por directorio es una mierda. – mxcl

4

cosa es [email protected] incluirá toda la ruta (relativa) en el fichero fuente que está en t urna utiliza para construir el nombre del objeto (y por lo tanto su ruta relativa)

Utilizamos:

##################### 
# rules to build the object files 
$(OBJDIR_1)/%.o: %.c 
    @$(ECHO) "$< -> [email protected]" 
    @test -d $(OBJDIR_1) || mkdir -pm 775 $(OBJDIR_1) 
    @test -d $(@D) || mkdir -pm 775 $(@D) 
    @-$(RM) [email protected] 
    $(CC) $(CFLAGS) $(CFLAGS_1) $(ALL_FLAGS) $(ALL_DEFINES) $(ALL_INCLUDEDIRS:%=-I%) -c $< -o [email protected] 

Esto crea un directorio objeto con nombre especificado en $(OBJDIR_1) y subdirectorios de acuerdo con subdirectorios en fuente.

Por ejemplo (asumir objs como directorio de objetos de nivel superior), en Makefile:

widget/apple.cpp 
tests/blend.cpp 

resultados en directorio siguiente objeto:

objs/widget/apple.o 
objs/tests/blend.o 
3

Esto lo hará sin manipulación dolorosa o múltiples secuencias de comandos:

 
build/%.o: src/%.cpp 
src/%.o: src/%.cpp 
%.o: 
    $(CC) -c $< -o [email protected] 

build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o 
    $(LD) $^ -o [email protected] 

JasperE ha explicado por qué "% .o:% .cpp" no funcionará; esta versión tiene una regla de patrón (% .o :) con comandos y sin prerrequisitos, y dos reglas de patrones (build /%. o: y src /%. o :) con prereqs y sin comandos. (Tenga en cuenta que puse la regla src /% .o para tratar con src/ui/flash.o, suponiendo que no era un error tipográfico para build/ui/flash.o, así que si no lo necesita puede dejarlo fuera.)/necesidades test.exe

bUILD/widgets/apple.o,
acumulación/widgets/apple.o se parece a la acumulación /% o., por lo que necesita src /%. cpp (en este caso src/widgets/apple.cpp),
build/widgets/apple.o también se ve como% .o, por lo que ejecuta el comando CC y usa los prereqs que acaba de encontrar (a saber src/widgets/apple.cpp) para construir el objetivo (build/widgets/apple.o)

+0

Esto se rompe, porque su regla '% .o:' no tiene requisitos previos, pero se refiere a ellos a través de '$ <' (que, por lo tanto, está vacía). "No hay archivos de entrada", lo siento. – DevSolar

60

Esto hace el truco:

CC  := g++ 
LD  := g++ 

MODULES := widgets test ui 
SRC_DIR := $(addprefix src/,$(MODULES)) 
BUILD_DIR := $(addprefix build/,$(MODULES)) 

SRC  := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.cpp)) 
OBJ  := $(patsubst src/%.cpp,build/%.o,$(SRC)) 
INCLUDES := $(addprefix -I,$(SRC_DIR)) 

vpath %.cpp $(SRC_DIR) 

define make-goal 
$1/%.o: %.cpp 
    $(CC) $(INCLUDES) -c $$< -o [email protected] 
endef 

.PHONY: all checkdirs clean 

all: checkdirs build/test.exe 

build/test.exe: $(OBJ) 
    $(LD) $^ -o [email protected] 


checkdirs: $(BUILD_DIR) 

$(BUILD_DIR): 
    @mkdir -p [email protected] 

clean: 
    @rm -rf $(BUILD_DIR) 

$(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir)))) 

Este Makefile supone que tiene sus archivos de inclusión en los directorios fuente. También comprueba si existen los directorios de compilación y los crea si no existen.

La última línea es la más importante. Se crea las reglas implícitas para cada compilación usando la función make-goal, y no es necesario escribirlos uno por uno

También puede agregar la generación automática de dependencias, utilizando Tromey's way

+0

Muy bien, excatly lo que estaba buscando;) Gracias Manzill0 – Geoffroy

+0

Su uso de Foreach realmente me ayudó. Creo una larga lista de archivos C que genero un archivo de objeto individual. ¡Gracias! –

+0

@ Manzill0 la siguiente pregunta tiene el mismo conjunto de problemas cuando se trata de objetivos múltiples y sus dependencias? https://stackoverflow.com/questions/30043480/make-recipe-to-prevent-rebuilding-of-non-dependent-targets# –

3

Este es otro truco.

En main 'Makefile', defina SRCDIR para cada directorio de origen e incluya 'makef.mk' para cada valor de SRCDIR. En cada directorio de origen, coloque el archivo 'files.mk' con una lista de archivos de origen y opciones de compilación para algunos de ellos. En el principal 'Makefile' uno puede definir opciones de compilación y excluir archivos para cada valor de SRCDIR.

Makefile:

PRG    := prog-name 

OPTIMIZE  := -O2 -fomit-frame-pointer 

CFLAGS += -finline-functions-called-once 
LDFLAGS += -Wl,--gc-section,--reduce-memory-overheads,--relax 


.DEFAULT_GOAL := hex 

OBJDIR   := obj 

MK_DIRS   := $(OBJDIR) 


SRCDIR   := . 
include   makef.mk 

SRCDIR := crc 
CFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE 
ASFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE 
include makef.mk 

################################################################ 

CC    := avr-gcc -mmcu=$(MCU_TARGET) -I. 
OBJCOPY   := avr-objcopy 
OBJDUMP   := avr-objdump 

C_FLAGS   := $(CFLAGS) $(REGS) $(OPTIMIZE) 
CPP_FLAGS  := $(CPPFLAGS) $(REGS) $(OPTIMIZE) 
AS_FLAGS  := $(ASFLAGS) 
LD_FLAGS  := $(LDFLAGS) -Wl,-Map,$(OBJDIR)/$(PRG).map 


C_OBJS   := $(C_SRC:%.c=$(OBJDIR)/%.o) 
CPP_OBJS  := $(CPP_SRC:%.cpp=$(OBJDIR)/%.o) 
AS_OBJS   := $(AS_SRC:%.S=$(OBJDIR)/%.o) 

C_DEPS   := $(C_OBJS:%=%.d) 
CPP_DEPS  := $(CPP_OBJS:%=%.d) 
AS_DEPS   := $(AS_OBJS:%=%.d) 

OBJS   := $(C_OBJS) $(CPP_OBJS) $(AS_OBJS) 
DEPS   := $(C_DEPS) $(CPP_DEPS) $(AS_DEPS) 


hex: $(PRG).hex 
lst: $(PRG).lst 


$(OBJDIR)/$(PRG).elf : $(OBJS) 
    $(CC) $(C_FLAGS) $(LD_FLAGS) $^ -o [email protected] 

%.lst: $(OBJDIR)/%.elf 
    [email protected] [email protected] 2> /dev/nul 
    $(OBJDUMP) -h -s -S $< > [email protected] 

%.hex: $(OBJDIR)/%.elf 
    [email protected] [email protected] 2> /dev/nul 
    $(OBJCOPY) -j .text -j .data -O ihex $< [email protected] 


$(C_OBJS) : $(OBJDIR)/%.o : %.c Makefile 
    $(CC) -MMD -MF [email protected] -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o [email protected] 
    @sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected] 
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected] 
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected] 
    [email protected] -f [email protected] 

$(CPP_OBJS) : $(OBJDIR)/%.o : %.cpp Makefile 
    $(CC) -MMD -MF [email protected] -c $(CPP_FLAGS) $(CPP_FLAGS_$(call clear_name,$<)) $< -o [email protected] 
    @sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected] 
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected] 
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected] 
    [email protected] -f [email protected] 

$(AS_OBJS) : $(OBJDIR)/%.o : %.S Makefile 
    $(CC) -MMD -MF [email protected] -c $(AS_FLAGS) $(AS_FLAGS_$(call clear_name,$<)) $< -o [email protected] 
    @sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected] 
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected] 
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected] 
    [email protected] -f [email protected] 


clean: 
    [email protected] -rf $(OBJDIR)/$(PRG).elf 
    [email protected] -rf $(PRG).lst $(OBJDIR)/$(PRG).map 
    [email protected] -rf $(PRG).hex $(PRG).bin $(PRG).srec 
    [email protected] -rf $(PRG)_eeprom.hex $(PRG)_eeprom.bin $(PRG)_eeprom.srec 
    [email protected] -rf $(MK_DIRS:%=%/*.o) $(MK_DIRS:%=%/*.o.d) 
    [email protected] -f tags cscope.out 

# -rm -rf $(OBJDIR)/* 
# -rm -rf $(OBJDIR) 
# -rm $(PRG) 


tag: tags 
tags: $(SRC_FILES) 
    if [ -e tags ] ; then ctags -u $? ; else ctags $^ ; fi 
    cscope -U -b $^ 


# include dep. files 
ifneq "$(MAKECMDGOALS)" "clean" 
-include $(DEPS) 
endif 


# Create directory 
$(shell mkdir $(MK_DIRS) 2>/dev/null) 

makef.mk

SAVE_C_SRC := $(C_SRC) 
SAVE_CPP_SRC := $(CPP_SRC) 
SAVE_AS_SRC := $(AS_SRC) 

C_SRC := 
CPP_SRC := 
AS_SRC := 


include $(SRCDIR)/files.mk 
MK_DIRS += $(OBJDIR)/$(SRCDIR) 


clear_name = $(subst /,_,$(1)) 


define rename_var 
$(2)_$(call clear_name,$(SRCDIR))_$(call clear_name,$(1)) := \ 
    $($(subst _,,$(2))_$(call clear_name,$(SRCDIR))) $($(call clear_name,$(1))) 
$(call clear_name,$(1)) := 
endef 


define proc_lang 

ORIGIN_SRC_FILES := $($(1)_SRC) 

ifneq ($(strip $($(1)_ONLY_FILES)),) 
$(1)_SRC := $(filter $($(1)_ONLY_FILES),$($(1)_SRC)) 
else 

ifneq ($(strip $(ONLY_FILES)),) 
$(1)_SRC := $(filter $(ONLY_FILES),$($(1)_SRC)) 
else 
$(1)_SRC := $(filter-out $(EXCLUDE_FILES),$($(1)_SRC)) 
endif 

endif 

$(1)_ONLY_FILES := 
$(foreach name,$($(1)_SRC),$(eval $(call rename_var,$(name),$(1)_FLAGS))) 
$(foreach name,$(ORIGIN_SRC_FILES),$(eval $(call clear_name,$(name)) :=)) 

endef 


$(foreach lang,C CPP AS, $(eval $(call proc_lang,$(lang)))) 


EXCLUDE_FILES := 
ONLY_FILES := 


SAVE_C_SRC += $(C_SRC:%=$(SRCDIR)/%) 
SAVE_CPP_SRC += $(CPP_SRC:%=$(SRCDIR)/%) 
SAVE_AS_SRC += $(AS_SRC:%=$(SRCDIR)/%) 

C_SRC := $(SAVE_C_SRC) 
CPP_SRC := $(SAVE_CPP_SRC) 
AS_SRC := $(SAVE_AS_SRC) 

./files.mk

C_SRC := main.c 
CPP_SRC := 
AS_SRC := timer.S 

main.c += -DDEBUG 

./crc/files.mk

C_SRC := byte-modbus-crc.c byte-crc8.c 
AS_SRC := modbus-crc.S crc8.S modbus-crc-table.S crc8-table.S 

byte-modbus-crc.c += --std=gnu99 
byte-crc8.c  += --std=gnu99 
2

Aquí está mi solución, inspirada en la respuesta de Beta. Es más simple que las otras soluciones propuestas

Tengo un proyecto con varios archivos C, almacenados en muchos subdirectorios. Por ejemplo:

src/lib.c 
src/aa/a1.c 
src/aa/a2.c 
src/bb/b1.c 
src/cc/c1.c 

Aquí es mi Makefile (en el directorio src/):

# make  -> compile the shared library "libfoo.so" 
# make clean -> remove the library file and all object files (.o) 
# make all -> clean and compile 
SONAME = libfoo.so 
SRC  = lib.c \ 
      aa/a1.c \ 
      aa/a2.c \ 
      bb/b1.c \ 
      cc/c1.c 
# compilation options 
CFLAGS = -O2 -g -W -Wall -Wno-unused-parameter -Wbad-function-cast -fPIC 
# linking options 
LDFLAGS = -shared -Wl,-soname,$(SONAME) 

# how to compile individual object files 
OBJS = $(SRC:.c=.o) 
.c.o: 
    $(CC) $(CFLAGS) -c $< -o [email protected] 

.PHONY: all clean 

# library compilation 
$(SONAME): $(OBJS) $(SRC) 
    $(CC) $(OBJS) $(LDFLAGS) -o $(SONAME) 

# cleaning rule 
clean: 
    rm -f $(OBJS) $(SONAME) *~ 

# additional rule 
all: clean lib 

Este ejemplo funciona bien para una biblioteca compartida, y debe ser muy fácil de adaptar para cualquier proceso de compilación .

Cuestiones relacionadas