Makefile 生成文件中的级联通配符

Makefile 生成文件中的级联通配符,makefile,wildcard,Makefile,Wildcard,我必须构建一个c项目(实际上有点复杂)。这个项目有一些c文件需要用自制脚本预处理。这些文件的扩展名是.cd 所以我有这个文件结构: /project + Makefile + foo/ | + foo1.cd | + foo2.cd + bar/ + bar1.cd + bar2.cd 当然,我可以添加一个包含其他文件的新文件夹,如foobar。值得一提的是,我不能有两个同名的源文件 也就是说,make可以做到: foo/foo1.cd --> foo/foo1.c foo

我必须构建一个c项目(实际上有点复杂)。这个项目有一些c文件需要用自制脚本预处理。这些文件的扩展名是
.cd

所以我有这个文件结构:

/project
+ Makefile   
+ foo/
| + foo1.cd
| + foo2.cd
+ bar/
  + bar1.cd
  + bar2.cd
当然,我可以添加一个包含其他文件的新文件夹,如foobar。值得一提的是,我不能有两个同名的源文件

也就是说,make可以做到:

foo/foo1.cd --> foo/foo1.c
foo/foo1.c  --> obj/foo1.o
...
obj/foo1.o obj/bar1.o ... obj/bar2.o --> a.out
因为我有数百个文件。我使用通配符
%
vpath
告诉make应该在哪里找到它们

为了说明我的问题,我编写了以下Makefile:

OBJDIR = obj
SRCDIR = $(patsubst %/,%,$(sort $(dir $(wildcard ./*/))))

vpath %.c  $(SRCDIR)/
vpath %.cd $(SRCDIR)/
vpath %.o  $(OBJDIR)/

DEFS = $(wildcard ./*/*.cd)   # To be preprocessed
SRCS = $(patsubst %.cd,%.c,$(DEFS)) # Source files
OBJS = $(notdir $(SRCS:.c=.o))      # Objects files

all: a.out

preproc: $(SRCS)

a.out: $(OBJS)
    echo "$^" >> a.out

%.c: %.cd
    echo "$<" >> $@

%.o:%.c
    @if [ ! -f $< ]; then \
        echo "File $< not found" && false;\
    else\
        echo "File $< found ";\
        touch $(OBJDIR)/$@;\
    fi;

$(OBJS): | $(OBJDIR)

$(OBJDIR):
    -mkdir $(OBJDIR)

clean:
    -rm -f $(SRCS) a.out *.c *.o
    -rm -rf $(OBJDIR)

init: clean
    mkdir -p foo
    mkdir -p bar
    touch foo/foo1.cd
    touch foo/foo2.cd
    touch bar/bar1.cd
    touch bar/bar2.cd
然后我需要预处理我的文件:

$ make preproc
echo "bar/bar2.cd" >> bar/bar2.c
echo "bar/bar1.cd" >> bar/bar1.c
echo "foo/foo2.cd" >> foo/foo2.c
echo "foo/foo1.cd" >> foo/foo1.c
最后,我构建项目:

$ make
mkdir obj
File ./bar/bar2.c found
File ./bar/bar1.c found
File ./foo/foo2.c found
File ./foo/foo1.c found
echo "bar2.o bar1.o foo2.o foo1.o" >> a.out
问题是我需要运行make两次。一次做预处理器,然后另一次构建项目。如果我尝试在
makeinit
之后直接
makeall
,我将得到:

$ make
echo "./bar/bar2.cd" >> bar2.c
mkdir obj
File bar2.c found
echo "./bar/bar1.cd" >> bar1.c
File bar1.c found
echo "./foo/foo2.cd" >> foo2.c
File foo2.c found
echo "./foo/foo1.cd" >> foo1.c
File foo1.c found
echo "bar2.o bar1.o foo2.o foo1.o" >> a.out
请注意,这次源文件放在根目录中,而不是它们假定的位置


我想找到一个不需要运行make两次的解决方案。

中的两种模式

%.o:%.c
仅匹配Makefile目录中的文件。我认为你应该能够使用

%.o: $(SRCDIR)/%.c

这是解决办法。我还向代码中添加了一些其他“最佳实践”

OBJDIR := obj
SRCDIRS := foo bar

vpath %.cd $(SRCDIRS)

DEFS := $(wildcard $(addsuffix  /*.cd, $(SRCDIRS)))     # To be preprocessed
OBJS := $(addprefix $(OBJDIR)/, $(notdir $(DEFS:cd=o)))     # Objects files

.PHONY: all
all: a.out 

a.out: $(OBJS) Makefile
    echo $(OBJS) > $@

.PRECIOUS: %.c %/.

%.c: %.cd Makefile
    echo $< > $@

%/.:
    mkdir -p $@


define OBJ_DIR_RECIPE
$1/%.o: $2/%.c Makefile | $1/.
    echo File $$< found
    touch $$@
endef

$(foreach dir, $(SRCDIRS), $(eval $(call OBJ_DIR_RECIPE,$(OBJDIR),$(dir))))
OBJDIR:=obj
SRCDIRS:=foo-bar
vpath%.cd$(SRCDIRS)
DEFS:=$(通配符$(addsuffix/*.cd,$(SRCDIRS)))#要预处理
OBJS:=$(addprefix$(OBJDIR)/,$(notdir$(DEFS:cd=o))#对象文件
冒牌货:全部
全部:a
a、 out:$(OBJS)Makefile
echo$(OBJS)>$@
.珍贵:%.c%/。
%.c:%.cd生成文件
echo$<>$@
%/.:
mkdir-p$@
定义OBJ_DIR_配方
$1/%.o:$2/%.c生成文件|$1/。
找到echo文件$$<
触碰$$@
恩德夫
$(foreach dir,$(SRCDIRS),$(eval$(调用OBJ_dir_RECIPE,$(OBJDIR),$(dir)))

第二个问题是因为make有许多常见编译步骤的内置规则。有关详细信息,请参见(以及来自
make-qp-f/dev/null的输出)。使用
vpath
查找生成文件(如上面的
%.c
%.o
将不起作用)。您只能正确地使用
vpath
查找源文件(如上面的
%.cp
)@madscitist我用一个新的makefile修改了我的问题。也许我们可以通过这个新的示例找到一个好的解决方案。一个棘手的解决方法是让
preproc
目标创建一个文件,您可以
将其包含到主makefile中,这样make会自动运行它,然后“重新启动”以正确包含它。这可能足以让我看到生成的文件,但我不确定@MadScientist可能会马上知道,但测试应该足够简单。中间文件(即post-
preproc
)需要位于源目录中,还是位于当前或输出目录中?不幸的是,我无法做到这一点。我有不同的SRCDIR,我让make找到合适的文件夹来找到正确的文件。我特别喜欢define recipe。我一直在找这样的东西。干得好!谢谢顺便说一句,你知道我在哪里可以读到更多关于“最佳实践”的内容吗?@coin是的,请看文档/白皮书。另外,请查看Make手册中的示例,尤其是整个Make文件
OBJDIR := obj
SRCDIRS := foo bar

vpath %.cd $(SRCDIRS)

DEFS := $(wildcard $(addsuffix  /*.cd, $(SRCDIRS)))     # To be preprocessed
OBJS := $(addprefix $(OBJDIR)/, $(notdir $(DEFS:cd=o)))     # Objects files

.PHONY: all
all: a.out 

a.out: $(OBJS) Makefile
    echo $(OBJS) > $@

.PRECIOUS: %.c %/.

%.c: %.cd Makefile
    echo $< > $@

%/.:
    mkdir -p $@


define OBJ_DIR_RECIPE
$1/%.o: $2/%.c Makefile | $1/.
    echo File $$< found
    touch $$@
endef

$(foreach dir, $(SRCDIRS), $(eval $(call OBJ_DIR_RECIPE,$(OBJDIR),$(dir))))