如何检查程序是否可以从Makefile中调用?
(也就是说,程序应存在于路径中或以其他方式可调用。)
例如,它可用于检查安装的编译器。
E.g。类似this question之类的东西,但不假设底层shell与POSIX兼容。
答案 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
。
然后,您可以使用ifdef
,ifndef
,$(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 foo
或command -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
的程序。无需检查其输出到STDERR
或STDERR
,而是检查程序返回码。如果该程序不存在,则其退出代码将始终为非零。
STDOUT