make变量的第二次扩展

时间:2018-08-23 18:07:41

标签: makefile gnu-make expansion makefile-project

我有一个如下的makefile。当前目录中有一些文件x.cx.h。另外,我正在从目标code_gen下的python脚本生成一些源/头文件。生成后,我将它们构建。

一旦我生成了文件,我就不想再用make all再次生成它们。仅在缺少一个或多个生成文件的情况下才会生成。

#predefine list of generated src files.
GEN_SRC_FILES := a.c b.c d.c
GEN_HEADER_FILES := $(patsubst %.c,%.h,$(GEN_SRC_FILES))
GEN_FILES_LIST += $(GEN_SRC_FILES) $(GEN_HEADER_FILES)

#Get all source and header currently in dir.
SRC := $(wildcard *.c)
HEADER := $(wildcard *.h)

#Get list of generated source and header files currently existing in dir.
GEN_SRC_FILES_TEMP := $(filter $(GEN_SRC_FILES), $(SRC))
GEN_HEADER_FILES_TEMP := $(filter $(GEN_HEADER_FILES), $(HEADER))
GEN_FILES_LIST_TEMP += $(GEN_SRC_FILES_TEMP) $(GEN_HEADER_FILES_TEMP)

OBJECT_FILES :=$(patsubst %.c, %.o, $(SRC))
OBJ := $(patsubst %, $(BUILD_DIR)/%, $(OBJECT_FILES))
CLEAN_OBJECTS := $(OBJ)

ifneq ($(GEN_FILES_LIST_TEMP),$(GEN_FILES_LIST))
all:code_gen lib
else
all:lib
endif
$(BUILD_DIR)/%.o: %.c
        $(CC) -c $^ -o $@ $(CFLAGS) -I$(INCLUDE_PATH)

lib: $(BUILD_DIR)/lib_x.a

$(BUILD_DIR)/lib_x.a: $(OBJ)
        $(AR) $(ARCHIVE_OPTIONS) $(BUILD_DIR)/lib_x.a $(OBJ)

code_gen:
        python ../../my_script.py

问题是我第一次生成文件时:

SRC := $(wildcard *.c)
HEADER := $(wildcard *.h)

这仅具有现有的.c / .h文件-x.c /x.h的首次调用期间。因此,make目标完成后

code_gen

这仅指向OBJECT_FILES :=$(patsubst %.c, %.o, $(SRC)) OBJ := $(patsubst %, $(BUILD_DIR)/%, $(OBJECT_FILES)) ,但是现在有新文件x.oa.c,b.c,d.c在理想情况下应为obj。 如何强制x.o a.o b.o d.oSRC进行第二次扩展?
我尝试了OBJ,但语法不正确。还有其他方法吗?
有什么建议吗?

1 个答案:

答案 0 :(得分:1)

您可以自然地完成它的工作,即发现某些C文件丢失并告诉它如何生成它们(但也请阅读下面的重要说明):

GEN_SRC_FILES    := a.c b.c d.c
GEN_HEADER_FILES := $(patsubst %.c,%.h,$(GEN_SRC_FILES))
SRC              := $(sort $(wildcard *.c) $(GEN_SRC_FILES))
OBJ              := $(patsubst %.c,$(BUILD_DIR)/%.o,$(SRC))

.PRECIOUS: $(GEN_SRC_FILES) $(GEN_HEADER_FILES)

all: lib

$(BUILD_DIR)/%.o: %.c %.h
    $(CC) -c $< -o $@ $(CFLAGS) -I$(INCLUDE_PATH)

lib: $(BUILD_DIR)/lib_x.a

$(BUILD_DIR)/lib_x.a: $(OBJ)
    $(AR) $(ARCHIVE_OPTIONS) $@ $^

$(GEN_SRC_FILES) $(GEN_HEADER_FILES):
    python ../../my_script.py

({sort不仅对作为参数传递的单词进行排序,还删除重复项)。请注意,我将标题添加到了编译的先决条件列表中,并且在相应的配方中将$^自动变量更改为$<(第一个先决条件)。还要注意,我在可能的所有地方都使用了自动变量。最后,我添加了.PRECIOUS规则以保护生成的文件不被自动删除。适应您的需求。

重要提示:此解决方案有一个缺点。如果您并行运行make(例如make -j4),则可能会看到python脚本运行了几次。这是因为make不知道一次运行会创建所有丢失的源文件。这很烦人,不仅因为性能,还因为它可能在python脚本和编译器之间创建竞争条件。不幸的是,仍然没有直接的方法来告诉一个配方产生多个文件。但是这里可以使用模式规则的特殊性:对于具有多个目标的模式规则,请考虑所有目标都是通过一次执行配方来产生的。从GNU制作手册:

  

模式规则可能有多个目标。与普通规则不同,这不会像具有相同先决条件和配方的许多不同规则一样起作用。如果阵列规则有多个目标,请让该规则的配方负责所有目标。配方仅执行一次即可构成所有目标。当搜索与目标匹配的模式规则时,除与需要规则的目标匹配的规则以外,其他规则的目标模式都是偶然的:仅担心为当前存在问题的文件提供配方和前提条件。但是,运行该文件的配方时,其他目标将被标记为自己已更新。

因此,如果您可以使用以下模式规则来表示源文件的生成:

foo%.c bar%.c baz%.c foo%.h bar%.h baz%.h:
    ...

%通配符必须至少匹配一个字符)已完成。例如,您可以重命名生成的文件{a,b,d}_gen.c

GEN_SRC_FILES := a_gen.c b_gen.c d_gen.c)
...
a_g%n.c b_g%n.c d_g%n.c a_g%n.h b_g%n.h d_g%n.h:
    python ../../my_script.py

当然,自动创建目标列表会更好。为此,我们将利用make patsubst函数仅替换在替换字符串中找到的第一个%的事实:

$(patsubst %_gen.c,%_g%n.c,$(GEN_SRC_FILES)) $(patsubst %_gen.h,%_g%n.h,$(GEN_HEADER_FILES)):
    python ../../my_script.py

如果这种重命名不是一种选择,并且您找不到能够完成这项工作的模式规则,则最后一种可能性是:递归make(make调用make):

GEN_SRC_FILES    := a.c b.c d.c
GEN_HEADER_FILES := $(patsubst %.c,%.h,$(GEN_SRC_FILES))
GEN_FILES        := $(GEN_SRC_FILES) $(GEN_HEADER_FILES)
SRC              := $(sort $(wildcard *.c) $(GEN_SRC_FILES))
OBJ              := $(patsubst %.c,$(BUILD_DIR)/%.o,$(SRC))

ifneq ($(sort $(wildcard $(GEN_FILES))),$(sort $(GEN_FILES)))
all:
    python ../../my_script.py
    $(MAKE)
else
all: lib

$(BUILD_DIR)/%.o: %.c %.h
    $(CC) -c $< -o $@ $(CFLAGS) -I$(INCLUDE_PATH)

lib: $(BUILD_DIR)/lib_x.a

$(BUILD_DIR)/lib_x.a: $(OBJ)
    $(AR) $(ARCHIVE_OPTIONS) $@ $^
endif

最后一点:告诉生成的源文件取决于它们的先决条件(可能是python脚本以及一些其他数据文件),这样甚至更好:

$(GEN_SRC_FILES) $(GEN_HEADER_FILES): ../../my_script.py ../../my_data_file.txt
    python $<

或:

$(patsubst %_gen.c,%_g%n.c,$(GEN_SRC_FILES)) $(patsubst %_gen.h,%_g%n.h,$(GEN_HEADER_FILES)): ../../my_script.py ../../my_data_file.txt
    python $<

但是将最后的改进与递归的make解决方案结合起来很棘手(并且留作练习...)