如何检查是否可以从Makefile调用程序?
(也就是说,该程序应该存在于路径中,否则可以被调用。)
例如,它可用于检查安装了哪个编译器。
例如类似这样的问题,但不假定底层shell与POSIX兼容。
automake
脚本检查各种先决条件并写出合适的脚本Makefile
。
如何检查是否可以从Makefile调用程序?
(也就是说,该程序应该存在于路径中,否则可以被调用。)
例如,它可用于检查安装了哪个编译器。
例如类似这样的问题,但不假定底层shell与POSIX兼容。
automake
脚本检查各种先决条件并写出合适的脚本Makefile
。
Answers:
有时,您需要一个Makefile才能在不同的目标OS上运行,并且如果所需的可执行文件不在文件中,并且希望构建在失败PATH
之前运行很长时间,则您希望构建早日失败。
Engineerchuan提供的出色解决方案需要确定目标。但是,如果要测试的可执行文件很多,并且Makefile有很多独立的目标,每个目标都需要测试,那么每个目标都需要测试目标作为依赖项。当您一次创建多个目标时,这会带来很多额外的输入以及处理时间。
0xf提供的解决方案可以测试可执行文件而无需创建目标。当可以单独或一起构建多个目标时,可以节省大量的键入和执行时间。
我对后一种解决方案的改进是使用which
可执行文件(where
在Windows中),而不是--version
直接在GNU Make ifeq
指令中依靠每个可执行文件中的选项,而不是定义一个新变量,而是使用GNU Make。error
如果所需的可执行文件不在的话,该函数停止构建${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.
where my_exe 2>NUL
代替which
。
我混合了@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变量。
command
是bash内置的,要获得更多的可移植性,请考虑使用which
?
command -v
需要类似POSIX的外壳环境。如果没有cygwin或类似的仿真,这将无法在Windows上运行。
command
经常不起作用的原因不是因为缺少它,而是因为GNU Make 进行了特殊的优化:如果命令“足够简单”,它将绕过shell;如果命令“足够简单”,它将被shell忽略。不幸的是,这意味着内置插件有时无法工作,除非您稍微修改一下命令。
使用该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的其他部分中很有用。
*** missing separator. Stop.
。如果我在所有行添加选项卡之后all:
,我得到的错误make: ifdef: Command not found
ifdef
,else
,endif
线。只要确保这些@echo
行以制表符开头即可。
ifdef
检查非空变量值。如果您的变量不为空,它的计算结果是什么?
ifdef
或ifndef
(如接受的答案)仅在被评估的变量立即设置为(:=
)而不是延迟设置(=
)时有效。但是,使用立即设置的变量的缺点是它们在声明时进行评估,而延迟设置的变量在调用时进行评估。这意味着:=
即使Make仅运行从未使用这些变量的规则,您也将使用这些变量执行命令!您可以通过=
与ifneq ($(MY_PROG),)
我的解决方案涉及一个小的帮助程序脚本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
技术的更多信息,请参见此处。
if
语句上得到“文件结尾意外” 。
对我来说,以上所有答案均基于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})
您可以使用bash构建的命令,例如type foo
或command -v foo
,如下所示:
SHELL := /bin/bash
all: check
check:
@type foo
foo
您的程序/命令在哪里。重定向到> /dev/null
是否要使其静音。
我个人定义的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
通过在另一个makefile目标中编译一个特殊的小程序来解决该问题,其唯一目的是检查我正在寻找的任何运行库内容。
然后,我在另一个makefile目标中调用了该程序。
如果我没记错的话,就是这样的:
real: checker real.c
cc -o real real.c `./checker`
checker: checker.c
cc -o checker checker.c
检查STDERR
输出的解决方案--version
不适用于将版本打印为STDOUT
而不是的程序STDERR
。而不是检查它们到STDERR
或的输出,而是检查STDOUT
程序的返回码。如果该程序不存在,则其退出代码将始终为非零。
#!/usr/bin/make -f
# /programming/7123241/makefile-as-an-executable-script-with-shebang
ECHOCMD:=/bin/echo -e
SHELL := /bin/bash
RESULT := $(shell python --version >/dev/null 2>&1 || (echo "Your command failed with $$?"))
ifeq (,${RESULT})
EXISTS := true
else
EXISTS := false
endif
all:
echo EXISTS: ${EXISTS}
echo RESULT: ${RESULT}