Makefileからプログラムが存在するかどうかを確認する


115

プログラムがMakefileから呼び出し可能かどうかを確認するにはどうすればよいですか?

(つまり、プログラムはパスに存在するか、呼び出し可能でなければなりません。)

たとえば、インストールされているコンパイラを確認するために使用できます。

たとえば、この質問のようなものですが、基礎となるシェルがPOSIX互換であるとは想定していません。


1
POSIX互換シェルを起動できますか?
reinierpost

おそらくそうではないでしょう。そこにいるように要求することはできると思いますが、そうする必要がない場合ははるかに簡単です。
ファルケン教授、2011

それまでの間、私は最初にビルドされたプロジェクトにプログラムを追加することで解決しました。そのプログラムの唯一の目的は、他のプログラムを確認することです... :-)
教授Falken

2
従来の回避策は、automakeさまざまな前提条件をチェックして適切なものを書き出すスクリプトを用意することMakefileです。
tripleee

回答:


83

Makefileを別のターゲットOSで実行できるようにする必要があり、必要な実行可能ファイルが存在しない場合は、失敗するPATH前に長時間実行するのではなく、ビルドを早期に失敗させたい場合があります。

engineerchuanが提供する優れたソリューションは、ターゲットを作成する必要があります。ただし、テストする実行可能ファイルが多く、Makefileに多数の独立したターゲットがあり、それぞれにテストが必要な場合、各ターゲットには依存関係としてテストターゲットが必要です。これにより、一度に複数のターゲットを作成するときに、余分なタイピングと処理時間が大幅に増加します。

0xfが提供するソリューションは、ターゲットを作成せずに実行可能ファイルをテストできます。これにより、個別または一緒にビルドできるターゲットが複数ある場合に、入力と実行にかかる時間を大幅に節約できます。

後者の解決策に対する私の改善は、新しい変数を定義するのではなく、GNU Make ディレクティブで直接、各実行可能ファイルにオプションがあることに依存するのではなく、which実行可能ファイル(whereWindows)を使用して、GNU Makeを使用することです。必要な実行可能ファイルがにない場合にビルドを停止する関数。たとえば、実行可能ファイルをテストするには:--versionifeqerror${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))を使用する場合、それらを渡して、環境または他のメイクファイルからシームレスに作成できます。クロスコンパイル時に役立ちます。
rickfoosusa

1
"ifeq(、$(shell which lzop))"を実行すると、 "、"でチョークが作成されます= /
mentatkgs

2
Windowsでは、のwhere my_exe 2>NUL代わりにを使用しますwhich
アーロンキャンベル

2
ルール構文であるifeqによるTABSインデントに注意してください!タブなしである必要があります。それで苦労しました stackoverflow.com/a/21226973/2118777
Pipo

2
を使用している場合Bash、への外部呼び出しを行う必要はありませんwhichcommand -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です。移植性を高めるために、使用を検討してください。
ジュリアンパラード16年

10
@JulienPalard私はあなたが間違っていると思います:commandユーティリティはposix-vオプションを含む)で必要です一方which、標準化された動作(私が知っている)がなく、
まったく

3
command -vPOSIXのようなシェル環境が必要です。これは、cygwinまたは同様のエミュレーションを使用しないWindowsでは機能しません。
ドルメン

10
その理由はcommand、多くの場合、仕事があるしないため、その不在のではないが、理由の独特の最適化 GNUのメイクがないこと:コマンドが「シンプル十分」である場合には、シェルバイパスします。これは残念ながら、コマンドを少し変更しないと組み込みが機能しない場合があることを意味します。
Rufflewind 2017年

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
Matthias 009

また:ifdef場合でも真と評価されますyour_programが存在しませんgnu.org/software/make/manual/make.html#Conditional-Syntax
マティアス009

1
タブを追加しないでくださいifdefelseendifライン。@echo行がタブで始まることを確認してください。
0xF 2014年

WindowsとLinuxでソリューションをテストしました。リンクしたドキュメントには、ifdef空でない変数値をチェックすることが記載されています。変数が空でない場合、それは何に評価されますか?
0xF 2014年

1
@ Matthias009は、GNU Make v4.2.1をテストするときに、ifdefor ifndef(受け入れられた回答のように)を使用すると、評価される変数が:=遅延セット(=)ではなくすぐにセット()される場合にのみ機能します。ただし、即時に設定された変数を使用する場合の欠点は、宣言時に評価されるのに対し、遅延して設定された変数は呼び出されたときに評価されることです。これは:=、Makeが変数を使用しないルールのみを実行している場合でも、変数に対してコマンドを実行することを意味します。これを回避するには、=withifneq ($(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が含まれています。これには、必要なコマンドのチェックが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

Makefile

.cmd_ok:
    ./check_cmds.sh

build: .cmd_ok target1 target2

1command -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

以下のように、type fooまたはなどのbashビルドコマンドを使用できcommand -v fooます。

SHELL := /bin/bash
all: check

check:
        @type foo

fooプログラム/コマンドはどこにありますか。> /dev/nullサイレントにする場合は、にリダイレクトします。


はい、しかし、私はそれが私がGNU makeだけを持っていてbashがインストールされていなかったときにそれを動かしたかったのです。
Falken教授

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他のすべての前に実行するターゲットを個人的に定義しています。このターゲットは、すべての要件のバージョンコマンドを一度に1つずつ実行し、コマンドが無効な場合は適切なエラーメッセージを出力します。

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

ありがとうございました。しかし、これはmakeを中止しますよね?makeが異常終了するのを防ぐことはできますか?必要なツールが利用できない場合、私は、ビルドステップをスキップしようとしている...
マルク・クリスチャン・シュルツ

@ coding.mofが編集されました-もっと似ていました。プログラムはランタイムで何かをチェックし、残りのコンパイルに応じて情報を提供しました。
Falken教授、2012年

1

STDERR出力をチェックするソリューション--versionは、バージョンをのSTDOUT代わりに出力するプログラムでは機能しませんSTDERRSTDERRまたはへの出力を確認する代わりに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}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.