如果目标名称有目录部分,则生成一个目录

Generate a directory if a target name has a directory part

我有这个 makefile 代码:

$(DIRS):
    @echo   "   MKDIR   build/tmp/base/socket/$@"
    $(Q)mkdir -p        $@/


%.a.s:
    @echo   "   CC  build/tmp/base/socket/$@"
    $(Q)$(CC) $(CFLAGS_A) -S $< -o $@
%.so.s:
    @echo   "   CC  build/tmp/base/socket/$@"
    $(Q)$(CC) $(CFLAGS_SO) -S $< -o $@
%.o: %.s
    @echo   "   AS  build/tmp/base/socket/$@"
    $(Q)$(AS) $< -o $@


tcp/client.a.s: $(TCP_CLIENT_SRC) $(TCP_CLIENT_INC)
tcp/client.so.s: $(TCP_CLIENT_SRC) $(TCP_CLIENT_INC)

tcp/server.a.s: $(TCP_SERVER_SRC) $(TCP_SERVER_INC)
tcp/server.so.s: $(TCP_SERVER_SRC) $(TCP_SERVER_INC)

我想在名称中有前导 tcp/ 的所有目标的先决条件中添加一个 | tcp,但我希望能够在一行中完成.我不想将其手动附加到需要它的每一行。

我想添加这条新规则:

tcp/%.s: | tcp

但它什么也没做。

我也想到了更通用的,会更好,但结果相同:

%.s: | $(@D)

我应该怎么写?


一个解决方法是每次都调用 mkdir(将其包含在 %.a.s 和 %.so.s 规则中),但这会增加对 mkdir 的不必要调用,不是吗?

您不能只使用模式规则将先决条件添加到一组给定的目标。这不是模式规则的工作方式:它们是 规则 :它们必须有与之关联的配方。没有配方的模式规则实际上 删除 该模式规则(参见 Canceling Implicit Rules)。

您可以创建两组模式规则,一组用于所有目标,另一组仅用于以 tcp/ 开头且具有额外先决条件的目标,但您必须将整个模式规则编写两次,包括食谱,而不仅仅是图案线。

或者直接将 mkdir 放入食谱中。已经存在的目录上的 mkdir 甚至不会被注意到。

我不喜欢模式规则。它们对我的口味来说太武断了。 (实际发生的情况取决于您的硬盘上可能有哪些文件。)

You cannot just add a prerequisite to a given set of targets using a pattern rule

好吧,如果你使用 static pattern rules 就可以了。这是一个更好的成语。在这里,我们在模式规则前加上您希望应用模式规则的实际来源列表。这很好,您可以使用 make 的 noddy 模式匹配来描述依赖关系。

素描:

%.a: ; date >$@ # Pattern rule

tcp: ; mkdir -p $@ # Explicit rule

tcp/a.a: tcp/%.a: | tcp ; # Static pattern rule!

.PHONY: all
all: tcp/a.a
all: c.a
all: dir/b.a
all: ; : $@ Success

我们有:

$ make all
mkdir -p tcp
date >tcp/a.a
date >c.a
date >dir/b.a
/bin/sh: dir/b.a: No such file or directory
make: *** [Makefile:3: dir/b.a] Error 1

这里我们告诉 make 在构建之前(即 "runs the recipe for")tcp/a.a,它必须先构建 tcp。这样可行。我们没有告诉 make 关于 dir/b.a 的目录,所以失败了。请注意,.a 文件的配方仍处于普通模式规则中。这只是通过阐述的方式。我肯定会改变这个。

是的,在这种情况下,tcp/ 的模式规则过于夸张。考虑一下,在创建 tcp/a.a 之前,您可能首先需要创建一个 auto/a.src(例如)。

tcp/a.a: tcp/%.a: auto/%.src | tcp ; # Static pattern rule!

易于扩展。

targets := tcp/a.a tcp/b.a tcp/c.a
${targets}: tcp/%.a: auto/%.src | tcp ; # Static pattern rule!

[顺便说一下,在您的原始 makefile 中,您的存档和共享对象应该依赖于 .o 文件,而不是源代码 (???)]

这个答案只是为了展示一个有效的 Makefile 实现@bobbogo 的答案。

这个Makefile是Makefile树的叶子,所以这里没有定义的所有变量都由上层Makefile导出。

Makefile:

#! /usr/bin/make -f

################################################################################
# *AUTHOR*
# FULL NAME     "Alejandro Colomar Andrés"
################################################################################

################################################################################

DIRS    =                               \
        $(CURDIR)/tcp

OBJ     =                                                               \
        $(CURDIR)/tcp/client.o                                          \
        $(CURDIR)/tcp/server.o                                        \
        $(CURDIR)/foo.o

SRC     =                                                               \
        $(SRC_DIR)/base/socket/tcp/client.c                             \
        $(SRC_DIR)/base/socket/tcp/server.c                             \
        $(SRC_DIR)/base/socket/foo.c

DEP             = $(OBJ:.o=.d)
BOTH_OBJ        = $(subst .a.o,.a.o ,$(join $(OBJ:.o=.a.o),$(OBJ:.o=.so.o)))
BOTH_ASM        = $(subst .a.s,.a.s ,$(join $(OBJ:.o=.a.s),$(OBJ:.o=.so.s)))
NEEDDIR         = $(DEP) $(BOTH_ASM)

################################################################################

PHONY := all
all: $(BOTH_OBJ)
        @:

$(DIRS): $(CURDIR)/%:
    @echo   "   MKDIR   build/tmp/base/socket/$*"
    $(Q)mkdir -p        $@

$(NEEDDIR): | $(DIRS)

$(CURDIR)/%.d: $(SRC_DIR)/base/socket/%.c
        @echo   "       CC -M   build/tmp/base/socket/$*.d"
        $(Q)$(CC) $(CFLAGS_A) -MG -MT"$@"                               \
                        -MT"$(CURDIR)/$*.a.s" -MT"$(CURDIR)/$*.so.s"    \
                        -M $< -MF $@
$(CURDIR)/%.a.s: $(SRC_DIR)/base/socket/%.c $(CURDIR)/%.d
        @echo   "       CC      build/tmp/base/socket/$*.a.s"
        $(Q)$(CC) $(CFLAGS_A) -S $< -o $@
$(CURDIR)/%.so.s: $(SRC_DIR)/base/socket/%.c $(CURDIR)/%.d
        @echo   "       CC      build/tmp/base/socket/$*.so.s"
        $(Q)$(CC) $(CFLAGS_SO) -S $< -o $@
$(CURDIR)/%.o: $(CURDIR)/%.s
        @echo   "       AS      build/tmp/base/socket/$*.o"
        $(Q)$(AS) $< -o $@

include $(DEP)


PHONY += clean
clean:
        $(Q)rm -rf *.o *.s *.d
################################################################################
# Declare the contents of the PHONY variable as phony.
.PHONY: $(PHONY)


################################################################################
######## End of file ###########################################################
################################################################################

输出:

    MKDIR   build/tmp/base/socket/tcp
    CC -M   build/tmp/base/socket/foo.d
    CC -M   build/tmp/base/socket/tcp/server.d
    CC -M   build/tmp/base/socket/tcp/client.d
    CC  build/tmp/base/socket/tcp/client.a.s
    AS  build/tmp/base/socket/tcp/client.a.o
    CC  build/tmp/base/socket/tcp/client.so.s
    AS  build/tmp/base/socket/tcp/client.so.o
    CC  build/tmp/base/socket/tcp/server.a.s
    AS  build/tmp/base/socket/tcp/server.a.o
    CC  build/tmp/base/socket/tcp/server.so.s
    AS  build/tmp/base/socket/tcp/server.so.o
    CC  build/tmp/base/socket/foo.a.s
    AS  build/tmp/base/socket/foo.a.o
    CC  build/tmp/base/socket/foo.so.s
    AS  build/tmp/base/socket/foo.so.o