2010-08-03 60 views
5

我有一个项目,其makefile使用GNU Make独有的功能。不幸的是,在运行make时,我们必须支持GNU make所依赖的平台。当某人运行`make`时检测(非)GNU Make

我的一个同事被这个咬了,当一个非GNU make的实现静静地无法正确地构建我们的代码(它将自动变量扩展为空字符串)。我想通过生成明确的错误消息来防止再次发生。

我可以在Makefile中写什么来区分GNU make和non-GNU make,打印出明确的错误并退出?

我已经想出了一个解决方法,将我的真实生成文件重命名为GNUmakefile,并在Makefile中放置一个小存根,但我宁愿更直接一些。

的Beta版和丹塑造的答案看起来真的不错,很简单,但在AIX 6.1,厂名实现不能处理任何人:

$ 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的产品版本(Solaris 8 & 10)。那个不那么重要,但会很好管理。

+0

您运行的是哪个版本的GNU make? – bta 2010-08-04 18:00:28

+0

你是对的。我所采用的方法过于依赖GNUisms,因此可能无法在非GNU make的实现*上运行。我现在怀疑是否有一种使用Makefile(即不使用shell脚本或其他外部资源)来实现您的目标的便携式方法。我已经取消了我的答案。 – 2010-08-04 18:46:36

回答

2

我不知道任何GNUMake特有的内部特性,但这里有一个kludge:调用“make -v”并解析“GNU”的输出(因为非GNU Make本来MAKE集到GNU MAKE):

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

编辑:
像丹成型,我开始看到这个问题的真正规模。正如所写,它需要一个Makefile,它在所有版本的Make中在语法上都是正确的。我无法访问Sun Make(我无法找到它的手册),所以我不知道这是否可行,或者如果是这样写的话,或者如果测试它,如何测试。

但我可以建议一种可能的方法。也许像这样的东西可以通用:

default: 
    ./runGNUMake.pl 

就是这样,这就是整个makefile。然后用Perl(或bash,或其他任何你喜欢的)编写runGNUMake脚本,这些脚本可以像我的“make -v”kludge那样做,然后打印错误信息或运行“make -f realMakefile”。

+0

我曾想过在make之前运行的外部脚本中执行此操作,但我没想过将它嵌入makefile本身。 – Novelocrat 2010-08-04 17:14:32

+0

在AIX 6.1上不起作用:-( – Novelocrat 2010-08-04 17:32:20

+0

Novelocrat:真的吗?它是如何失败的,非GNU Make有一个MAKE似乎被设置为GNU Make,或者GNU Make似乎是非GNU?(我想我们可以分别称这些为“false positive”和“false negative”)。 – Beta 2010-08-04 17:46:02

6

如上所述,GNU make检查GNUmakefilemakefileMakefile或之前,我已经如你描述中使用的琐碎修复程序,默认(诱饵)Makefile会导致一个错误/警告:

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

的GNU make documentation建议在Makefile是GNU特定的时候使用GNUmakefile名称,所以这是我的首选解决方案。

在原生make更喜欢不同Makefile名称的平台上,您可以对其进行变更,例如,在FreeBSD上,我在BSDmakefile中有上面的诱饵,它优先于Makefile(这样可以防止系统make妨碍我的构建)。 AFAICT AIX或Solaris make没有可以以此方式使用的备用名称。

尝试调用GNU make的包装器Makefile的一个问题是传递所有参数。

一个看似便携式测试(到目前为止,我发现它在古代OSF1的混合工作,BSD和Solaris系统),可以使用SOMETHING=$(shell ...)检测是否GNU make运行,非GNU版本将不设置SOMETHING 。由于deferred evaluation of variables,你不能像预期的那样容易地使用它。这依赖于以$()(即将$(shell foo)作为变量/宏名称而不是function进行扩展,即使在该实现中对此类名称的分配会导致错误)执行时使用空格处理静默处理宏的方式。

可以打印明确错误的唯一可移植的方法是有一个虚拟的目标是始终运行,使用上述伎俩:

GNUMAKE=$(shell echo GNUMAKE) 

default: gnumake all 

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

这里假设你有一个POSIX sh壳。

(我已经看到它检查$(MAKE) -v失败当两个系统和GNU make被称为“make”,系统make密谋反对你和调用GNU make ...你会需要一些仔细检查环境变量的PATH测试, MAKE以及可能SHELL来处理每种情况。)