是否有一种可移植的方式在 Makefile 中制作可移植的可选依赖项?

Posted

技术标签:

【中文标题】是否有一种可移植的方式在 Makefile 中制作可移植的可选依赖项?【英文标题】:Is there a portable way to make portable optionnal dependency in Makefile? 【发布时间】:2016-11-23 19:37:45 【问题描述】:

在Lua-i18n 上工作,我正在尝试更改 Makefile,以便用户可以使用他们感兴趣的工具编译项目,即 Makefile 定义了一些将禁用相应代码的 C 宏。在 Makefile 中定义它是直截了当的,但是当以可移植性为目标时,摆脱与如此定义的特性相关的不必要的编译依赖关系就不那么简单了。事实上,该项目旨在尽可能少地修改原始代码,并尽可能少地添加依赖项(可能没有)。

所以这里是过程中找到的一些相关链接

http://pubs.opengroup.org/onlinepubs/009695399/utilities/make.html http://www.netbsd.org/docs/pkgsrc/makefile.html http://netbsd.gw.com/cgi-bin/man-cgi?make+1+NetBSD-5.0.1+i386 http://gallium.inria.fr/blog/portable-conditionals-in-makefiles/ https://docs.google.com/document/d/1oUR7iMnaNzkeT3TTOS-Gwul6_V3TE8caIDAd1FwPyNc/edit# https://www.gnu.org/software/make/manual/html_node/Conditional-Syntax.html https://www.mkssoftware.com/docs/man5/makefile.5.asp

这是一个似乎可以在带有 GNU Make 的 Fedora 24 桌面上运行的解决方案。但是,我想知道这样的解决方案是否适用于任何(相当现代的)实施方案。

cflag= -D CODE_SWITCH
resolved_conditional_cflag!= test -n "$(cflag)" && echo "$(cflag)"
conditional_dep= additional_dep.c
mandatory_dep= dep0.c dep1.c
resolved_conditional_dep!= test -n "$(cflag)" && echo "$(conditional_dep)"
all_dep= $(mandatory_dep) $(resolved_conditional_dep)
all: target
    @echo building code with flag $(resolved_conditional_cflag) 
    @echo $SHELL
target: $(all_dep)
    @echo building $@ which depends on $(all_dep)
dep0.c:
    @echo building dep0.c
dep1.c:
    @echo building dep1.c
additional_dep.c:
    @echo building $@

想法是用户注释行cflag= -D CODE_SWITCH,仅此而已,编译器不会处理相关的依赖项。那么,这样的解决方案是否可移植?它依赖于 shell 扩展 !=test 命令。我认为最后一个可能是POSIX,但我可能错了。更一般地说,它们可能是我在这里不知道的可移植性问题。

请注意,对于这个特定需求,“使用 CMake、autotools 或其他”不是理想的答案。

【问题讨论】:

【参考方案1】:

!= 仅在 GNU make 4.0 及更高版本中可用。它也不是 POSIX;它被添加到 GNU make 以便与 BSD make 一起移植,并被提议包含在 POSIX 规范中,但目前尚未被接受。

我不知道有任何方法仅使用纯可移植 make (POSIX) 来根据 make 宏的内容更改目标的先决条件。

如果您愿意使用 GNU make,那么(至少目前)使用$(shell ...) 比使用!= 更便携。虽然如果你假设 GNU make 有更好的方法来处理这个,根本不需要 shell。如果您想使用!= 获得 BSD make 的可移植性,请务必在您的文档中添加注释,说明您需要 GNU make 4.0 或更高版本。

注意,在 makefile 中不要在变量名和赋值运算符之间放置空格通常是一个非常糟糕的主意;例如最好写FOO != bar,而不是FOO!=barFOO!= bar

编辑

Chrono Kitsune 讨论的一个选项是使用一点递归;它看起来像这样:

cflag= -D CODE_SWITCH

conditional_dep= additional_dep.c
mandatory_dep= dep0.c dep1.c

all_dep= $(mandatory_dep)
all: target
        @echo building code with flag $(cflag) 
        @echo $SHELL
target: $(mandatory_dep)
        @all_dep='$^'; if test -n "$(cflag)"; then \
            all_dep='$^ $(conditional_dep)'; \
            $(MAKE) $(conditional_dep); \
         fi; \
        echo building $@ which depends on $$all_dep
dep0.c:
        @echo building dep0.c
dep1.c:
        @echo building dep1.c
additional_dep.c:
        @echo building $@

另一种选择,如果您不必让事情依赖于注释掉cflag 本身,那就是使用递归变量命名;看起来像这样:

SWITCH = enabled

enabled_cflag = -D CODE_SWITCH
enabled_dep = additional_dep.c

mandatory_dep = dep0.c dep1.c

cflag = $($(SWITCH)_cflag)
all_dep = $(mandatory_dep) $($(SWITCH)_dep)

all: target
        @echo building code with flag $(cflag) 
        @echo $SHELL
target: $(all_dep)
        echo building $@ which depends on $(all_dep)
dep0.c:
        @echo building dep0.c
dep1.c:
        @echo building dep1.c
additional_dep.c:
        @echo building $@

现在,如果您注释掉 SWITCH 设置,或在 make 命令行上将其设置为空,那么这些元素将被关闭(因为 $($(SWITCH)_cflag) 将扩展不存在/空变量 @987654334 @不是enabled_cflag)。大多数版本的 make(但不是全部)都支持递归变量命名。

【讨论】:

是否可以在需要的系统上手动使用一些 shell 代码和 $(MAKE) conditional-target?不理想,因为您仍在使用 shell 代码,但它至少比 != 构造更便携。 我不确定我是否理解您的建议。我想您可以将条件先决条件完全排除在all_dep 变量之外,然后在构建target 的配方中,您可以在实际运行链接命令之前先运行test -n "$(cflags)" && $(MAKE) $(conditional_dep)。你是这么想的吗?如果作者愿意使用完全不同的变量,而不是 cflags,作为包含事物的触发器,那么可以使用递归宏非常简单地完成。 是的,这就是我的想法。抱歉,我的意图不是很清楚。这样的解决方案会有什么问题吗?由于test 已经被使用,我认为这个想法是一种潜在的便携式解决方案,但我可能对可能出现的一些问题视而不见。 如果你的意思是“任何问题”,它是否是 POSIXly 正确的,我认为没有理由不这样做。您还必须设置一个本地 shell 变量以包含或不包含链接行中的目标文件,因此它不像我的评论中那么简单。 我明白你的意思......如果你不能根据需要链接目标文件,这个想法就没有意义。对于这么小的makefile,这是一个小障碍,但对于较大的makefile,我可以看到它是一个问题。感谢您指出了这一点。 :-)

以上是关于是否有一种可移植的方式在 Makefile 中制作可移植的可选依赖项?的主要内容,如果未能解决你的问题,请参考以下文章

是否有一种可移植的方式来定义 INT_MAX?

标准 C++ 中是不是有一种可移植的方式来检索主机名?

是否有一种可移植的(C++ 标准)方法来计算前一个对齐的指针?

有没有一种可移植的方式来用 Qt 给线程名称?

是否有一种可嵌入 SharePoint 的向导/指南?

使用 testthat 在单元测试中重用变量。是不是有一种可接受的做法来避免重新创建计算量大的变量