当有人运行`make`命令时,如何检测是否使用GNU Make?

7
我有一个项目,其Makefile使用了仅适用于GNU Make的功能。遗憾的是,在运行make命令时,我们必须支持一些还不支持GNU make的平台。
我的一个同事曾因此受挫,当一个非GNU make实现未正确构建代码时(它将自动变量扩展为空字符串),他并没有发出任何声音。我想通过生成明确的错误消息来防止再次发生这种情况。
在Makefile中,我应该写些什么来区分GNU make和非GNU make,打印清晰的错误信息并退出呢?
我已经想出了一个解决方法,即将真正的Makefile重命名为GNUmakefile,并在Makefile中放置一个小型存根,但我更希望有更直接的方法。
Beta和Dan Moulding的答案看起来非常好且简单,但在AIX 6.1上,make实现无法处理它们中的任何一个。
$ cat testmake

foo:
        touch foo

ifeq ($(shell $(MAKE) -v | grep GNU),)
$(error this is not GNU Make)
endif

ifeq "${MAKE_VERSION}" ""
$(info GNU Make not detected)
$(error ${MIN_MAKE_VER_MSG})
endif


$ /usr/bin/make -f testmake
"testmake", line 5: make: 1254-055 Dependency line needs colon or double colon operator.
"testmake", line 6: make: 1254-055 Dependency line needs colon or double colon operator.
"testmake", line 7: make: 1254-055 Dependency line needs colon or double colon operator.
"testmake", line 8: make: 1254-055 Dependency line needs colon or double colon operator.
"testmake", line 11: make: 1254-055 Dependency line needs colon or double colon operator.
"testmake", line 12: make: 1254-055 Dependency line needs colon or double colon operator.
"testmake", line 13: make: 1254-055 Dependency line needs colon or double colon operator.
make: 1254-058 Fatal errors encountered -- cannot continue.

我在Sun的make工具的古老版本和现代版本(Solaris 8和10)上遇到了类似的问题。虽然这个问题不是很关键,但如果能解决就更好了。


你正在运行哪个版本的GNU make? - bta
你说得对。我采取的方法过于依赖GNUisms,因此可能根本无法与非GNU实现的make一起使用。我现在怀疑是否有一种可移植的方式仅使用Makefile(即不使用shell脚本或其他外部资源)来实现您的目标。我已经撤回了我的答案。 - Dan Moulding
2个回答

9

如前所述,GNU make 会在查找 makefileMakefile 之前先查找 GNUmakefile。我使用了一个简单的修复方法,就是创建一个默认 (诱饵) 的 Makefile ,导致出现错误/警告:

default:
    @echo "This requires GNU make, run gmake instead"
    exit 70

GNU make文档建议在Makefile是GNU make特定的情况下使用GNUmakefile名称,这是我的首选解决方案。
在本地make偏爱不同Makefile名称的平台上,您可以进行一些变化,例如在FreeBSD上,我在BSDmakefile中有上述诱饵,该文件优先于Makefile(从而防止系统make破坏我的构建)。据我所知,AIX或Solaris make没有可用于此方式的替代名称。
包装器Makefile的一个问题是尝试调用GNU make时传递所有参数。
看似可移植的测试(到目前为止,我已经发现它可以在古老的OSF1、BSD和Solaris系统混合中工作)可以使用SOMETHING=$(shell ...)来检测是否正在运行GNU make,非GNU版本将不设置SOMETHING。由于变量的延迟求值,您不能像预期的那样轻松使用它。这依赖于实现在展开$()时会默默处理带空格的宏名称(即将$(shell foo)视为变量/宏名称而不是函数,即使在该实现中对这种名称进行赋值也会导致错误)。
您唯一可以打印一个清晰错误的可移植方式是有一个始终运行的虚拟目标,使用上述技巧:
GNUMAKE=$(shell echo GNUMAKE)

default: gnumake all

gnumake:
        @[ "$(GNUMAKE)" = "GNUMAKE" ] || { echo GNU make required ; exit 70; }

假设您拥有一个 POSIX 的 sh shell。
(我已经看到过一些测试在检查 $(MAKE) -v 时失败,当系统和 GNU make 都被称为 "make" 时,系统的 make 会阻挠您并调用 GNU make ... 您需要小心检查环境变量 PATHMAKE 和可能的 SHELL 来处理每种情况。)

使用 $(eval ...)$(error ...) 有什么好处? - Caleb

1

我不知道GNU Make中有哪些内部特性是绝对独特的,但这里有一个折中的方法:调用“make -v”并解析输出以寻找“GNU”(因为非GNU Make似乎不太可能将MAKE设置为GNU Make):

ifeq ($(shell $(MAKE) -v | grep GNU),)
$(error this is not GNU Make)
endif

编辑:
像丹·莫尔丁一样,我开始看到这个问题的真正规模。按照现有的写法,它需要一个在所有版本的Make中都语法正确的Makefile。我没有访问Sun Make的权限(也找不到它的手册),所以我不知道这是否可能,如果可能的话该如何编写它,或者如果我编写了它该如何测试。

但是我可以建议一种可能有效的方法。也许可以制作类似于这样的通用程序:

default:
  ./runGNUMake.pl

就是这样,整个makefile都在这里了。然后用Perl(或bash,或其他你喜欢的语言)编写runGNUMake脚本,执行类似于我的“make -v”小技巧,然后要么打印错误消息,要么运行“make -f realMakefile”。


我曾考虑过在 make 命令之前运行一个外部脚本来完成这个任务,但我没有想到可以将其嵌入到 makefile 中。 - Phil Miller
Novelocrat: 真的吗?它失败了吗?是因为非GNU Make具有看似设置为GNU Make的“MAKE”,还是GNU Make看起来像非GNU Make?(我猜我们可以分别称之为“假阳性”和“假阴性”。) - Beta
我编辑了问题以显示失败。它不喜欢 ifeq - Phil Miller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接