Dưới đây là cách giải quyết sự cố mà không cần MAKECMDGOALS
nội tâm. Tha vấn đề về cơ bản là các quy tắc bạn chỉ định trong Makefile
là một biểu đồ tĩnh. Các bài tập cụ thể theo mục tiêu được sử dụng trong khi thực thi của các cơ quan quy tắc, nhưng không được sử dụng trong quá trình biên soạn của chúng.
Giải pháp cho điều này là nắm quyền kiểm soát việc biên soạn quy tắc: sử dụng cấu trúc giống như macro của GNU Make để tạo quy tắc. Sau đó, chúng ta có toàn quyền kiểm soát: chúng ta có thể gắn vật liệu biến đổi vào mục tiêu, điều kiện tiên quyết hoặc công thức.
Đây là phiên bản của tôi về bạn 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
Bây giờ, chú ý đến lợi thế: Tôi có thể xây dựng cả debug
và release
mục tiêu trong một đi, bởi vì tôi đã instantiated cả hai quy tắc từ mẫu!
$ 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
Hơn nữa, tôi đã lấy tự do để thêm đối số vĩ mô vào dòng lệnh cc
cũng có, do đó các module nhận được một MODE
vĩ mô mà nói với họ như thế nào họ đang được biên soạn.
Chúng tôi có thể sử dụng biến hướng khác nhau để thiết lập CFLAGS
hoặc bất kỳ thứ gì khác. Xem những gì sẽ xảy ra nếu chúng ta vá ở trên như thế này:
--- 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
Cuối cùng, chúng ta có thể kết hợp điều đó với MAKECMDGOALS
. Chúng tôi có thể kiểm tra MAKECMDGOALS
và lọc ra các chế độ xây dựng không được chỉ định ở đó. Nếu được gọi là make release
, chúng tôi không cần mở rộng quy tắc debug
. Patch:
--- 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
Lưu ý rằng tôi đơn giản hóa mọi thứ bằng cách cuộn các mục tiêu debug:
và release:
vào BUILDRULE
vĩ mô.
$ 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
thể trùng lặp của [Biến mục tiêu cụ thể như điều kiện tiên quyết trong một Makefile] (http://stackoverflow.com/questions/1340060/target-specific-variables-as-prerequisites-in-a-makefile) –