Aquí se explica cómo resolver el problema sin MAKECMDGOALS
introspección. El problema es básicamente que las reglas que especifiques en Makefile
constituyen un gráfico estático. Las asignaciones específicas de destino se utilizan durante la ejecución de cuerpos de regla, pero no durante su compilación .
La solución a esto es tomar el control sobre la compilación de reglas: use las construcciones tipo macro de GNU Make para generar las reglas. Entonces tenemos un control total: podemos incluir material variable en el objetivo, requisito previo o receta.
aquí está mi versión de su Makefile
all:
@echo specify configuration 'debug' or 'release'
OBJS := foo.o bar.o
# BUILDDIR is a macro
# $(call BUILDDIR,WORD) -> .build/WORD
BUILDDIR = .build/$(1)
# target is a macro
# $(call TARGET,WORD) -> ./build/WORD/foo.o ./build/WORD/bar.o
TARGET = $(addprefix $(call BUILDDIR,$(1))/,$(OBJS))
# BUILDRULE is a macro: it builds a release or debug rule
# or whatever word we pass as argument $(1)
define BUILDRULE
$(call BUILDDIR,$(1))/%.o: %.c
@echo [$(call BUILDDIR,$(1))/$$*.o] should be [[email protected]]
@mkdir -p $$(dir [email protected])
$$(CC) -c -DMODE=$(1) $$< -o [email protected]
endef
debug: $(call TARGET,debug)
release: $(call TARGET,release)
# generate two build rules from macro
$(eval $(call BUILDRULE,debug))
$(eval $(call BUILDRULE,release))
clean:
rm -rf .build
Ahora, observe la ventaja: puedo construir ambos debug
y release
objetivos de una sola vez, porque he instancia ambas reglas de la plantilla!
$ make clean ; make debug release
rm -rf .build
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c -DMODE=debug foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c -DMODE=debug bar.c -o .build/debug/bar.o
[.build/release/foo.o] should be [.build/release/foo.o]
cc -c -DMODE=release foo.c -o .build/release/foo.o
[.build/release/bar.o] should be [.build/release/bar.o]
cc -c -DMODE=release bar.c -o .build/release/bar.o
Además, he tomado la libertad de añadir el argumento de macro en la línea de comandos cc
también, de modo que los módulos reciben un MODE
macro que les dice cómo están siendo compilados.
Podemos utilizar la indirección variable para configurar diferentes CFLAGS
o lo que sea. Mira lo que sucede si parchear el anterior así:
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,9 @@
OBJS := foo.o bar.o
+CFLAGS_debug = -O0 -g
+CFLAGS_release = -O2
+
# BUILDDIR is a macro
# $(call BUILDDIR,WORD) -> .build/WORD
BUILDDIR = .build/$(1)
@@ -17,7 +20,7 @@ define BUILDRULE
$(call BUILDDIR,$(1))/%.o: %.c
@echo [$(call BUILDDIR,$(1))/$$*.o] should be [[email protected]]
@mkdir -p $$(dir [email protected])
- $$(CC) -c -DMODE=$(1) $$< -o [email protected]
+ $$(CC) -c $$(CFLAGS_$(1)) -DMODE=$(1) $$< -o [email protected]
endef
debug: $(call TARGET,debug)
Run:
$ make clean ; make debug release
rm -rf .build
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c -O0 -g -DMODE=debug foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c -O0 -g -DMODE=debug bar.c -o .build/debug/bar.o
[.build/release/foo.o] should be [.build/release/foo.o]
cc -c -O2 -DMODE=release foo.c -o .build/release/foo.o
[.build/release/bar.o] should be [.build/release/bar.o]
cc -c -O2 -DMODE=release bar.c -o .build/release/bar.o
Por último, podemos combinar eso con MAKECMDGOALS
. Podemos inspeccionar MAKECMDGOALS
y filtrar los modos de compilación que no están especificados allí. Si se llama al make release
, no necesitamos las reglas debug
para expandir. Parche:
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,11 @@
OBJS := foo.o bar.o
+# List of build types, but only those mentioned on command line
+BUILD_TYPES := $(filter $(MAKECMDGOALS),debug release)
+
+$(warning "generating rules for BUILD_TYPES := $(BUILD_TYPES)")
+
CFLAGS_debug = -O0 -g
CFLAGS_release = -O2
@@ -17,18 +22,15 @@ TARGET = $(addprefix $(call BUILDDIR,$(1))/,$(OBJS))
# BUILDRULE is a macro: it builds a release or debug rule
# or whatever word we pass as argument $(1)
define BUILDRULE
+$(1): $(call TARGET,$(1))
$(call BUILDDIR,$(1))/%.o: %.c
@echo [$(call BUILDDIR,$(1))/$$*.o] should be [[email protected]]
@mkdir -p $$(dir [email protected])
$$(CC) -c $$(CFLAGS_$(1)) -DMODE=$(1) $$< -o [email protected]
endef
-debug: $(call TARGET,debug)
-release: $(call TARGET,release)
-
-# generate two build rules from macro
-$(eval $(call BUILDRULE,debug))
-$(eval $(call BUILDRULE,release))
+$(foreach type,$(BUILD_TYPES),\
+ $(eval $(call BUILDRULE,$(type))))
clean:
rm -rf .build
Tenga en cuenta que he simplificado las cosas haciendo rodar los objetivos y debug:
release:
en el BUILDRULE
macro.
$ make clean ; make release
Makefile:9: "generating rules for BUILD_TYPES := "
rm -rf .build
Makefile:9: "generating rules for BUILD_TYPES := release"
[.build/release/foo.o] should be [.build/release/foo.o]
cc -c -O2 -DMODE=release foo.c -o .build/release/foo.o
[.build/release/bar.o] should be [.build/release/bar.o]
cc -c -O2 -DMODE=release bar.c -o .build/release/bar.o
$ make clean ; make release debug
Makefile:9: "generating rules for BUILD_TYPES := "
rm -rf .build
Makefile:9: "generating rules for BUILD_TYPES := debug release"
[.build/release/foo.o] should be [.build/release/foo.o]
cc -c -O2 -DMODE=release foo.c -o .build/release/foo.o
[.build/release/bar.o] should be [.build/release/bar.o]
cc -c -O2 -DMODE=release bar.c -o .build/release/bar.o
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c -O0 -g -DMODE=debug foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c -O0 -g -DMODE=debug bar.c -o .build/debug/bar.o
posible duplicado de [Variables Target-específicas como requisitos previos en un Makefile] (http://stackoverflow.com/questions/1340060/target-specific-variables-as-prerequisites-in-a-makefile) –