instalar - Cómo generar un archivo Makefile con fuente en subdirectorios utilizando solo un archivo MAKE
makefile linux c (7)
Tengo una fuente en un grupo de subdirectorios como:
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 archivo Makefile usando una regla 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 para build / widgets / apple.o. ¿Puedo cambiar algo para que se use% .o:% .cpp cuando se necesita crear build / widgets / apple.o?
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í está 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 $@
.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 debería ser muy fácil de adaptar para cualquier proceso de compilación.
Este es otro truco.
En el ''Makefile'' principal, 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 $@
%.lst: $(OBJDIR)/%.elf
-@rm $@ 2> /dev/nul
$(OBJDUMP) -h -s -S $< > $@
%.hex: $(OBJDIR)/%.elf
-@rm $@ 2> /dev/nul
$(OBJCOPY) -j .text -j .data -O ihex $< $@
$(C_OBJS) : $(OBJDIR)/%.o : %.c Makefile
$(CC) -MMD -MF [email protected] -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o $@
@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]
-@rm -f [email protected]
$(CPP_OBJS) : $(OBJDIR)/%.o : %.cpp Makefile
$(CC) -MMD -MF [email protected] -c $(CPP_FLAGS) $(CPP_FLAGS_$(call clear_name,$<)) $< -o $@
@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]
-@rm -f [email protected]
$(AS_OBJS) : $(OBJDIR)/%.o : %.S Makefile
$(CC) -MMD -MF [email protected] -c $(AS_FLAGS) $(AS_FLAGS_$(call clear_name,$<)) $< -o $@
@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]
-@rm -f [email protected]
clean:
-@rm -rf $(OBJDIR)/$(PRG).elf
-@rm -rf $(PRG).lst $(OBJDIR)/$(PRG).map
-@rm -rf $(PRG).hex $(PRG).bin $(PRG).srec
-@rm -rf $(PRG)_eeprom.hex $(PRG)_eeprom.bin $(PRG)_eeprom.srec
-@rm -rf $(MK_DIRS:%=%/*.o) $(MK_DIRS:%=%/*.o.d)
-@rm -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
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 $$@
endef
.PHONY: all checkdirs clean
all: checkdirs build/test.exe
build/test.exe: $(OBJ)
$(LD) $^ -o $@
checkdirs: $(BUILD_DIR)
$(BUILD_DIR):
@mkdir -p $@
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 de origen. 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. Crea las reglas implícitas para cada construcción con la función make-goal
, y no es necesario escribirlas una a una
También puede agregar generación automática de dependencias, usando la forma de Tromey
Esto lo hará sin manipulación dolorosa o secuencias de comandos múltiples:
build/%.o: src/%.cpp src/%.o: src/%.cpp %.o: $(CC) -c $< -o $@ build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o $(LD) $^ -o $@
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 déjalo afuera.)
build / test.exe necesita build / widgets / apple.o,
build / widgets / apple.o se ve como build /%. 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 destino (build / widgets / apple.o)
La cosa es $@
incluirá la ruta completa (relativa) al archivo fuente que a su vez se usa para construir el nombre del objeto (y por lo tanto su ruta relativa)
Usamos:
#####################
# rules to build the object files
$(OBJDIR_1)/%.o: %.c
@$(ECHO) "$< -> $@"
@test -d $(OBJDIR_1) || mkdir -pm 775 $(OBJDIR_1)
@test -d $(@D) || mkdir -pm 775 $(@D)
@-$(RM) $@
$(CC) $(CFLAGS) $(CFLAGS_1) $(ALL_FLAGS) $(ALL_DEFINES) $(ALL_INCLUDEDIRS:%=-I%) -c $< -o $@
Esto crea un directorio de objetos con el nombre especificado en $(OBJDIR_1)
y subdirectorios de acuerdo con los subdirectorios en la fuente.
Por ejemplo (suponga objs como el directorio de objetos de nivel superior), en Makefile:
widget/apple.cpp
tests/blend.cpp
resultados en el siguiente directorio de objetos:
objs/widget/apple.o
objs/tests/blend.o
La razón es que tu regla
%.o: %.cpp
...
espera que el archivo .cpp resida en el mismo directorio que 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.
Puede usar VPATH para resolver esto:
VPATH = src/widgets
BUILDDIR = build/widgets
$(BUILDDIR)/%.o: %.cpp
...
Al intentar construir "build / widgets / apple.o", make buscará apple.cpp en VPATH . Tenga en cuenta que la regla de compilación debe usar variables especiales para acceder al nombre de archivo real make finds:
$(BUILDDIR)/%.o: %.cpp
$(CC) $< -o $@
Donde "$ <" se expande a la ruta donde se ubica la primera dependencia.
También tenga en cuenta que esto construirá todos los archivos .o en compilación / widgets. Si quieres construir los binarios en diferentes directorios, puedes hacer algo como
build/widgets/%.o: %.cpp
....
build/ui/%.o: %.cpp
....
build/tests/%.o: %.cpp
....
Yo recomendaría que use " secuencias de comandos enlatados " para evitar repetir la regla de compilación del compilador real:
define cc-command
$(CC) $(CFLAGS) $< -o $@
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)
Usualmente, usted crea un Makefile en cada subdirectorio y escribe en el Makefile de nivel superior para hacer make en los subdirectorios.
Esta página puede ayudar: http://www.gnu.org/software/make/