我有一个如下的makefile。当前目录中有一些文件x.c
,x.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.o
。
a.c,b.c,d.c
在理想情况下应为obj
。
如何强制x.o a.o b.o d.o
,SRC
进行第二次扩展?
我尝试了OBJ
,但语法不正确。还有其他方法吗?
有什么建议吗?
答案 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解决方案结合起来很棘手(并且留作练习...)