web-dev-qa-db-ja.com

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

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

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

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

例えば。 この質問 のようなものですが、基礎となるシェルがPOSIX互換であることを前提としません。

83
Prof. Falken

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

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

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

後者のソリューションに対する私の改善は、各実行可能ファイルに--versionオプションが存在することに依存するのではなく、which実行可能ファイル(Windowsではwhere)を使用することです。 GNU新しい変数を定義するのではなく、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.
52

@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変数を定義して、コマンドパスを出力する安価な操作を実行します。

40
mentatkgs

これはあなたがしたことですか?

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

同僚に感謝します。

32
engineerchuan

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の他の部分で役立つ場合があります。

20
0xF

私のソリューションには、小さなヘルパースクリプトが含まれます1 必要なコマンドがすべて存在する場合、フラグファイルを配置します。これには、すべてのmake呼び出しではなく、必要なコマンドのチェックが1回だけ行われるという利点があります。

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

1 command -vテクニックの詳細については、 こちら をご覧ください。

8
Flow

ここで既存のソリューションの一部をクリーンアップしました...

_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 ...)は、これをより静かにしたい場合は除外できます。

これはfail fastになります。ターゲットは不要です。

7
mpen

私にとって、上記の答えはすべて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})        
4
Vit Bernatik

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

Shell := /bin/bash
all: check

check:
        @type foo

fooはプログラム/コマンドです。サイレントにしたい場合は、> /dev/nullにリダイレクトします。

3
kenorb

異なるターゲットとビルダーがあり、それぞれに別のツールセットが必要であるとします。そのようなツールのリストを設定し、それらの可用性を強制的にチェックするためのターゲットとしてそれらを検討してください

例えば:

make_tools := gcc md5sum gzip

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

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

特別な小さなプログラムを別のmakefileターゲットでコンパイルすることで解決しました。その唯一の目的は、私が探していたランタイムのものをチェックすることです。

次に、このプログラムをさらに別のmakefileターゲットで呼び出しました。

正しく思い出せば、次のようなものでした。

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

checker: checker.c
    cc -o checker checker.c
1
Prof. Falken

--versionSTDERR出力をチェックするソリューションは、STDOUTではなくSTDERRにバージョンを出力するプログラムでは機能しません。 STDERRまたはSTDOUTへの出力を確認する代わりに、プログラムの戻りコードを確認します。プログラムが存在しない場合、その終了コードは常にゼロ以外になります。

#!/usr/bin/make -f
# https://stackoverflow.com/questions/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}
1
user