检查Makefile中是否存在程序


115

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

(也就是说,该程序应该存在于路径中,否则可以被调用。)

例如,它可用于检查安装了哪个编译器。

例如类似这样的问题,但不假定底层shell与POSIX兼容。


1
您可以调用 POSIX兼容的外壳程序吗?
reinierpost 2011年

可能不是,我想我可以要求一个人在那里,但是如果不需要的话,这会容易得多。
法尔肯教授

同时,我通过向该项目添加一个程序来解决该问题,该程序首先被构建,其唯一目的是检查该其他程序... :-)
Falken教授

2
传统的解决方法是使用automake脚本检查各种先决条件并写出合适的脚本Makefile
2012年

Answers:


83

有时,您需要一个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.

2
如果使EXECUTABLES所有变量(即LS CC LD),并使用$($(exec)),则可以将其传递以与环境或其他makefile无缝连接。交叉编译时很有用。
rickfoosusa

1
我在运行“ ifeq(,$(shell(lzop))$”)时在“,”处
感到窒息

2
在Windows上,使用where my_exe 2>NUL代替which
亚伦·坎贝尔

2
注意使用ifeq的TABS缩进是一种规则语法!它必须没有选项卡。很难过。stackoverflow.com/a/21226973/2118777
Pipo

2
如果您正在使用Bash,则无需外部调用which。请改用内置的command -v更多信息
kurczynski

48

我混合了@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变量。


5
至少在某些系统(例如Ubuntu 14.04)中,$(shell command -v dot)失败make: command: Command not found。为了解决这个问题,重定向stderr输出到/ dev / null的:$(shell command -v dot 2> /dev/null)说明
-J0HN

3
command是bash内置的,要获得更多的可移植性,请考虑使用which
朱利安·帕拉德

10
@JulienPalard我相信您误会了:posixcommand需要该实用程序(包括该选件)另一方面,该标准没有行为(我知道),有时甚至-vwhich
根本

3
command -v需要类似POSIX的外壳环境。如果没有cygwin或类似的仿真,这将无法在Windows上运行。
支石墓

10
command经常不起作用的原因不是因为缺少它,而是因为GNU Make 进行了特殊的优化:如果命令“足够简单”,它将绕过shell;如果命令“足够简单”,它将被shell忽略。不幸的是,这意味着内置插件有时无法工作,除非您稍微修改一下命令。
Rufflewind

33

这是你做的吗?

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

归功于我的同事。


不,我在一个目标中编译了一个程序,然后在另一个目标中运行了该程序。
法尔肯教授

21

使用该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的其他部分中很有用。


这给了错误*** missing separator. Stop.。如果我在所有行添加选项卡之后all:,我得到的错误make: ifdef: Command not found
马蒂亚斯009

同样:ifdef即使your_program不存在,也会评估为true gnu.org/software/make/manual/make.html#Conditional-Syntax
Matthias 009

1
不要添加标签为ifdefelseendif线。只要确保这些@echo行以制表符开头即可。
0xF 2014年

我在Windows和Linux上测试了我的解决方案。您链接的文档指出ifdef检查非空变量值。如果您的变量不为空,它的计算结果是什么?
0xF 2014年

1
当我测试GNU Make v4.2.1时,@ Matthias009使用ifdefifndef(如接受的答案)仅在被评估的变量立即设置为(:=)而不是延迟设置(=)时有效。但是,使用立即设置的变量的缺点是它们在声明时进行评估,而延迟设置的变量在调用时进行评估。这意味着:=即使Make仅运行从未使用这些变量的规则,您也将使用这些变量执行命令!您可以通过=ifneq ($(MY_PROG),)
Dennis

9

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

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 ...),如果你想这是更安静,你可以排除。

这将很快失败。不需要目标。


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技术的更多信息,请参见此处


2
我刚刚对其进行了测试,并且可以正常工作,并且由于您没有提供任何提示(不适用于bash脚本或Makefile代码)或任何错误消息,因此我无法帮助您找到问题。
流量

我在第一条if语句上得到“文件结尾意外” 。
亚当·格兰特

6

对我来说,以上所有答案均基于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})        

我从没想过有人会在可怕的cmd.exe ...“ shell”中使用GNU Make。
JohanBoulé19年

4

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

SHELL := /bin/bash
all: check

check:
        @type foo

foo您的程序/命令在哪里。重定向到> /dev/null是否要使其静音。


是的,但问题是,当我仅安装GNU make而未安装bash时,我希望它能够正常工作。
Falken教授2015年

3

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

例如:

make_tools := gcc md5sum gzip

$(make_tools):  
    @which $@ > /dev/null

file.txt.gz: file.txt gzip
    gzip -c file.txt > file.txt.gz 

3

我个人定义的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

1

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

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

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

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

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

谢谢。但这会导致失败,对吧?是否可以防止品牌中止?我试图跳过生成步骤,如果需要的工具不可用..
马克-基督教舒尔茨

@ coding.mof已编辑-更像是那样。该程序在运行时检查了一些内容,并相应地提供了信息给其余的编译过程。
法肯教授2012年

1

检查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}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.