プログラムがMakefileから呼び出し可能かどうかを確認するにはどうすればよいですか?
(つまり、プログラムはパスに存在するか、そうでなければ呼び出し可能になります。)
たとえば、どのコンパイラがインストールされているかを確認するために使用できます。
例えば。 この質問 のようなものですが、基礎となるシェルがPOSIX互換であることを前提としません。
異なるターゲット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.
@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変数を定義して、コマンドパスを出力する安価な操作を実行します。
これはあなたがしたことですか?
check: PYTHON-exists
PYTHON-exists: ; @which python > /dev/null
mytarget: check
.PHONY: check PYTHON-exists
同僚に感謝します。
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の他の部分で役立つ場合があります。
私のソリューションには、小さなヘルパースクリプトが含まれます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
テクニックの詳細については、 こちら をご覧ください。
ここで既存のソリューションの一部をクリーンアップしました...
_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になります。ターゲットは不要です。
私にとって、上記の答えはすべて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})
以下のように、type foo
やcommand -v foo
などのbashビルドコマンドを使用できます。
Shell := /bin/bash
all: check
check:
@type foo
foo
はプログラム/コマンドです。サイレントにしたい場合は、> /dev/null
にリダイレクトします。
異なるターゲットとビルダーがあり、それぞれに別のツールセットが必要であるとします。そのようなツールのリストを設定し、それらの可用性を強制的にチェックするためのターゲットとしてそれらを検討してください
例えば:
make_tools := gcc md5sum gzip
$(make_tools):
@which $@ > /dev/null
file.txt.gz: file.txt gzip
gzip -c file.txt > file.txt.gz
特別な小さなプログラムを別のmakefileターゲットでコンパイルすることで解決しました。その唯一の目的は、私が探していたランタイムのものをチェックすることです。
次に、このプログラムをさらに別のmakefileターゲットで呼び出しました。
正しく思い出せば、次のようなものでした。
real: checker real.c
cc -o real real.c `./checker`
checker: checker.c
cc -o checker checker.c
--version
のSTDERR
出力をチェックするソリューションは、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}