【问题标题】:Generate a directory if a target name has a directory part如果目标名称具有目录部分,则生成目录
【发布时间】:2020-06-09 12:04:05
【问题描述】:

我有这个 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 的不必要调用,不是吗?

【问题讨论】:

    标签: makefile wildcard gnu-make


    【解决方案1】:

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

    您不能只使用模式规则为给定的一组目标添加先决条件

    如果你使用 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 在它构建(即“运行配方”)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 文件,而不是源 (???)]

    【讨论】:

    • 关于您的 BTW 问题:请参阅最后一个扩展名。这些是汇编文件。两者的区别在于CFLAGS_SO-fpic,而CFLAGS_A 没有。因此,我将从 .a.s 文件组装 .a.o 目标文件,这些文件将归档到 .a 库中。我将从 .so.s 文件组装 .so.o 目标文件,这些文件将被编译成 .so 库。
    • 如果我传递整个对象列表而不是只传递以 tcp 开头的对象,它是否仍然有效(忽略那些与模式不匹配的对象,即不以 tcp 开头的对象)?: $(OBJS): tcp/%.a: | tcp ; # Static pattern
    • @CacahueteFrito 并非不合理,每个源文件名都必须与目标模式匹配。 file.o: %.c: %.auto … 不行。
    • 好吧,静态模式规则和模式规则有同样的问题:静态模式规则是显式规则,它必须有一个配方,就像一个模式规则必须有一个配方一样。 OP 想要做的是使用模式规则向 已经存在的 目标添加先决条件。你不能这样做,你也不能用静态模式规则来做到这一点。您可能更喜欢其中一个,但除了偏好之外,这个问题并没有给静态模式规则带来任何优势。
    • 顺便说一下,看起来 make 支持任意数量的这些静态模式依赖行。这真的很有用。
    【解决方案2】:

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

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

    或者只是将mkdir 放入配方中。已经存在的目录上的mkdir 甚至不会被注意到。

    【讨论】:

    • 很明显,您应该使用mkdir -p 来确保在并行构建等过程中不会出现故障。
    【解决方案3】:

    此答案仅显示实现 @bobbogo 答案的有效 Makefile。

    这个 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
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-06
      • 2018-12-09
      相关资源
      最近更新 更多