推迟评估配方中的自动变量

时间:2017-05-12 11:17:47

标签: makefile gnu-make

我有以下makefile:

CC ?= gcc
LD := gcc

CFLAGS := -Wall -Wextra -Werror -Wfatal-errors
LDFLAGS :=
LIBRARIES := m c
INCLUDEDIRS := .
LIBS = $(addprefix -l,$(LIBRARIES))
INCLUDES = $(addprefix -I,$(INCLUDEDIRS))

SRC := $(wildcard *.c)
TARGET = $(TARGETDIR)/test
OBJDIR = $(TARGETDIR)/obj/
OBJ = $(addprefix $(OBJDIR),$(SRC:%.c=%.c.o))

.SUFFIXES:
.SUFFIXES: .c.o

.PHONY: all debug i7avx i7avx-debug

all: TARGETDIR := generic
all: CFLAGS += -O3
all: LDFLAGS += -s
all: $(TARGET)

debug: CFLAGS += -Og
debug: TARGETDIR := generic/dbg
debug: $(TARGET)


$(OBJDIR):
    @mkdir -p $(OBJDIR)

$(OBJ): | $(OBJDIR)

$(OBJDIR)%.c.o : %.c
    $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@

$(TARGET) : $(OBJ)
    $(LD) $(LDFLAGS) -o $@ $^ $(LIBS)

这里的特殊之处在于输出目录取决于目标。 目前,只定义了所有和调试,但这个想法是支持一大堆架构,并为每个目标定义一个outputdir。

问题:这不起作用。如果我运行这个,我得到:

cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o /obj/main.c.o
Assembler messages:
Fatal error: can't create /obj/main.c.o: No such file or directory
make: *** [Makefile:37: /obj/main.c.o] Error 1

这意味着TARGETDIR变量扩展太晚了。 如果我用自动变量替换自动变量,它确实有效:

$(OBJ): | $(OBJDIR)

$(OBJDIR)%.c.o : %.c
    $(CC) $(CFLAGS) $(INCLUDES) -c $(SRC) -o $(OBJ)

$(TARGET) : $(OBJ)
    $(LD) $(LDFLAGS) -o $(TARGET) $(OBJ) $(LIBS)

运行这个:

cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o generic/obj/main.c.o
gcc -s -o generic/test generic/obj/main.c.o -lm -lc

Sooo,如何在定义TARGETDIR后扩展自动变量?

2 个答案:

答案 0 :(得分:0)

Make会非常巧妙地处理通配符,或者这将是一个更容易解决的问题。

实际上,我认为最好的解决方案是使用recursive Make。只需改变一下:

all: $(TARGET)

debug: $(TARGET)

到此:

all debug:                                       
    $(MAKE) $(TARGET) TARGETDIR=$(TARGETDIR) CFLAGS+='$(CFLAGS)' LDFLAGS=$(LDFLAGS)

答案 1 :(得分:0)

首先,它不起作用的原因:

您正在使用特定于目标的变量,但这些变量仅在目标配方的上下文中可用(我在此引用手册),而不是在规则评估期间: Make将首先阅读您的Makefile,评估您的$(OBJDIR)$(TARGET)规则(此时$(TARGETDIR)尚未定义)然后,它会尝试更新all,并且此点将$(TARGETDIR)设置为all的特定于目标的值(这解释了为什么您是第二个示例工作,但它应该每次重建)。

我可能会有一些提示来实现你想要做的事情(我实际上计划尽快做类似的事情):

您可以使用eval函数为每个构建/ archi生成一个规则,如下所示:

#define TARGET_RULE
$(TARGET) : $(OBJ)
    $$(RECIPE)
#endif
$(foreach TARGETDIR, $(BUILD_LIST), $(eval $(TARGET_RULE))

(配方需要$$以避免在规则评估期间将其展开)

您还应该只能为您当前构建的目标定义规则(不确定是否会产生明显的性能差异)。