检查Makefile中是否存在程序

时间:2011-04-11 08:23:07

标签: makefile gnu-make

如何检查程序是否可以从Makefile中调用?

(也就是说,程序应存在于路径中或以其他方式可调用。)

例如,它可用于检查安装的编译器。

E.g。类似this question之类的东西,但不假设底层shell与POSIX兼容。

12 个答案:

答案 0 :(得分:45)

有时您需要一个Makefile才能在不同的目标操作系统上运行,如果所需的可执行文件不在PATH而不是运行很长时间,您希望构建早期失败在失败之前。

engineerchuan提供的出色解决方案需要制作目标。但是,如果要测试许多可执行文件并且Makefile有许多独立目标,每个目标都需要测试,那么每个目标都需要将测试目标作为依赖项。当你一次制作多个目标时,这会产生大量的额外输入和处理时间。

0xf提供的解决方案可以在不创建目标的情况下测试可执行文件。当有多个目标可以单独或一起构建时,这可以节省大量的打字和执行时间。

我对后一种解决方案的改进是使用which可执行文件(Windows中为where),而不是依赖每个可执行文件中的--version选项,直接在GNU Make ifeq指令,而不是定义一个新变量,并且如果所需的可执行文件不在error中,则使用GNU Make ${PATH}函数来停止构建。例如,要测试lzop可执行文件:

 ifeq (, $(shell which lzop))
 $(error "No lzop in $(PATH), consider doing apt-get install lzop")
 endif

如果要检查多个可执行文件,那么您可能希望将foreach函数与which可执行文件一起使用:

EXECUTABLES = ls dd dudu lxop
K := $(foreach exec,$(EXECUTABLES),\
        $(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))

请注意,为了强制立即评估RHS表达式,需要使用:=赋值运算符。如果您的Makefile更改了PATH,那么您将需要:

而不是上面的最后一行
        $(if $(shell PATH=$(PATH) which $(exec)),some string,$(error "No $(exec) in PATH")))

这应该给你类似的输出:

ads$ make
Makefile:5: *** "No dudu in PATH.  Stop.

答案 1 :(得分:37)

我混合了来自@kenorb和@ 0xF的解决方案,得到了这个:

DOT := $(shell command -v dot 2> /dev/null)

all:
ifndef DOT
    $(error "dot is not available please install graphviz")
endif
    dot -Tpdf -o pres.pdf pres.dot 

它工作得非常漂亮,因为如果可执行文件不可用,“command -v”不会打印任何内容,因此变量DOT永远不会被定义,您可以随时在代码中进行检查。在这个例子中,我抛出了一个错误,但如果你愿意,你可以做一些更有用的事情。

如果变量可用,“command -v”执行打印命令路径的低成本操作,定义DOT变量。

答案 2 :(得分:31)

这就是你做的吗?

check: PYTHON-exists
PYTHON-exists: ; @which python > /dev/null
mytarget: check
.PHONY: check PYTHON-exists

归功于我的同事。

答案 3 :(得分:17)

使用shell功能以打印标准输出的方式调用程序。例如,传递--version

GNU Make忽略传递给shell的命令的退出状态。为避免潜在的“未找到命令”消息,请将标准错误重定向到/dev/null

然后,您可以使用ifdefifndef$(if)等检查结果。

YOUR_PROGRAM_VERSION := $(shell your_program --version 2>/dev/null)

all:
ifdef YOUR_PROGRAM_VERSION
    @echo "Found version $(YOUR_PROGRAM_VERSION)"
else
    @echo Not found
endif

作为奖励,输出(例如程序版本)可能在Makefile的其他部分有用。

答案 4 :(得分:8)

我的解决方案涉及一个小帮助脚本 1 ,如果存在所有必需的命令,则会放置一个标记文件。这样做的好处是只检查所需命令一次,而不是每次make次调用。

<强> check_cmds.sh

#!/bin/bash

NEEDED_COMMANDS="jlex byaccj ant javac"

for cmd in ${NEEDED_COMMANDS} ; do
    if ! command -v ${cmd} &> /dev/null ; then
        echo Please install ${cmd}!
        exit 1
    fi
done

touch .cmd_ok

<强>生成文件

.cmd_ok:
    ./check_cmds.sh

build: .cmd_ok target1 target2

1 可以找到有关command -v技术的更多信息here

答案 5 :(得分:7)

在这里清理了一些现有的解决方案......

REQUIRED_BINS := composer npm node php npm-shrinkwrap
$(foreach bin,$(REQUIRED_BINS),\
    $(if $(shell command -v $(bin) 2> /dev/null),$(info Found `$(bin)`),$(error Please install `$(bin)`)))

如果您想让它更安静,可以排除$(info ...)

这将快速失败。无需任何目标。

答案 6 :(得分:4)

对我来说,以上所有答案都是基于Linux的,并不适用于Windows。我是新手,所以我的方法可能不太理想。但是在linux和windows上对我有用的完整示例是:

# detect what shell is used
ifeq ($(findstring cmd.exe,$(SHELL)),cmd.exe)
$(info "shell Windows cmd.exe")
DEVNUL := NUL
WHICH := where
else
$(info "shell Bash")
DEVNUL := /dev/null
WHICH := which
endif

# detect platform independently if gcc is installed
ifeq ($(shell ${WHICH} gcc 2>${DEVNUL}),)
$(error "gcc is not in your system PATH")
else
$(info "gcc found")
endif

当我需要检测更多可以使用的工具时,

EXECUTABLES = ls dd 
K := $(foreach myTestCommand,$(EXECUTABLES),\
        $(if $(shell ${WHICH} $(myTestCommand) 2>${DEVNUL} ),\
            $(myTestCommand) found,\
            $(error "No $(myTestCommand) in PATH)))
$(info ${K})        

答案 7 :(得分:3)

您可以使用bash内置命令,例如type foocommand -v foo,如下所示:

SHELL := /bin/bash
all: check

check:
        @type foo

foo是你的程序/命令。如果你想让它保持沉默,请重定向到> /dev/null

答案 8 :(得分:2)

假设您有不同的目标和构建器,每个目标和构建器都需要另一套工具。 设置此类工具的列表,并将其视为强制检查其可用性的目标

例如:

application/json

答案 9 :(得分:2)

我个人定义了一个require目标,该目标先于其他所有目标运行。此目标一次仅运行一次所有要求的版本命令,并在命令无效时显示适当的错误消息。

all: require validate test etc

require:
    @echo "Checking the programs required for the build are installed..."
    @shellcheck --version >/dev/null 2>&1 || (echo "ERROR: shellcheck is required."; exit 1)
    @derplerp --version >/dev/null 2>&1 || (echo "ERROR: derplerp is required."; exit 1) 

# And the rest of your makefile below.

以下脚本的输出是

Checking the programs required for the build are installed...
ERROR: derplerp is required.
makefile:X: recipe for target 'prerequisites' failed
make: *** [prerequisites] Error 1

答案 10 :(得分:1)

通过在另一个makefile目标中编译一个特殊的小程序来解决,其唯一目的是检查我正在寻找的运行时内容。

然后,我在另一个makefile目标中调用了这个程序。

如果我没记错的话,就是这样:

real: checker real.c
    cc -o real real.c `./checker`

checker: checker.c
    cc -o checker checker.c

答案 11 :(得分:1)

检查STDERR的{​​{1}}输出的解决方案不适用于将其版本打印到--version而不是STDOUT的程序。无需检查其输出到STDERRSTDERR,而是检查程序返回码。如果该程序不存在,则其退出代码将始终为非零。

STDOUT