web-dev-qa-db-ja.com

コマンドの出力をMakefile変数に割り当てる方法

インストールされたPythonが特定のバージョン(たとえば2.5)よりも大きい場合にのみ、いくつかのmakeルールを条件付きで実行する必要があります。

私は実行するようなことをすることができると思った:

python -c 'import sys; print int(sys.version_info >= (2,5))'

ifeq makeステートメントで出力([OK]の場合は[1]、それ以外の場合は[0])を使用します。

単純なbashシェルスクリプトでは、次のようになります。

MY_VAR=`python -c 'import sys; print int(sys.version_info >= (2,5))'`

しかし、それはMakefileでは機能しません。

助言がありますか?他の賢明な回避策を使用してこれを達成することができます。

186
fortran

MY_VAR=$(Shell echo whatever)のようにMake Shellビルトインを使用します

me@Zack:~$make
MY_VAR IS whatever
me@Zack:~$ cat Makefile 
MY_VAR := $(Shell echo whatever)

all:
    @echo MY_VAR IS $(MY_VAR)
295
Arkaitz Jimenez

割り当てをevalにラップすることは、私にとっては有効です。

# dependency on .PHONY prevents Make from 
# thinking there's `nothing to be done`
set_opts: .PHONY
  $(eval DOCKER_OPTS = -v $(Shell mktemp -d -p /scratch):/output)
24
Dave

以下は、レシピ内のパイピングと変数の割り当てを使用したもう少し複雑な例です。

getpodname:
    # Getting pod name
    @eval $$(minikube docker-env) ;\
    $(eval PODNAME=$(Shell sh -c "kubectl get pods | grep profile-posts-api | grep Running" | awk '{print $$1}'))
    echo $(PODNAME)
13

私は、問題を解決する実際の構文の可視性を高めるための答えを書いています。残念ながら、些細なこととして誰かが見ているかもしれないことは、合理的な質問に対する簡単な答えを探している人にとって非常に大きな頭痛の種になります。

ファイル「Makefile」に以下を入力します。

MY_VAR := $(Shell python -c 'import sys; print int(sys.version_info >= (2,5))')

all:
    @echo MY_VAR IS $(MY_VAR)

確認したい動作は次のとおりです(最近のpythonがインストールされていると仮定)。

make
MY_VAR IS 1

上記のテキストをコピーしてMakefileに貼り付けると、これが得られますか?おそらくない。ここで報告されるようなエラーが表示される可能性があります。

makefile:4:***区切り記号がありません。停止

理由:私は個人的に本物のタブを使用しましたが、Stack Overflow(役立つことを試みている)が私のタブをいくつかのスペースに変換するためです。失望したインターネット市民は、これをコピーして、あなたが今使ったのと同じテキストを持っていると考えます。 makeコマンドは、スペースを読み取り、「all」コマンドが誤ってフォーマットされていることを検出します。したがって、上記のテキストをコピーして貼り付け、「@ echo」の前の空白をタブに変換すると、この例が最終的にうまくいくはずです。

13
AlanSE

GNU Makeを使用すると、Shellおよびevalを使用して、任意のコマンドライン呼び出しからの出力を保存、実行、および割り当てることができます。以下の例と:=を使用する例の違いは、:=の割り当てが1回(検出されたとき)に発生することです。 =で設定された再帰的に展開された変数は、もう少し「怠laz」です。他の変数への参照は、変数自体が参照されるまで残り、変数が参照されるたびに後続の再帰的展開が行われます。これは、一貫性のある呼び出し可能なスニペット」。詳細については、 変数の設定に関するマニュアル を参照してください。

# Generate a random number.
# This is not run initially.
GENERATE_ID = $(Shell od -vAn -N2 -tu2 < /dev/urandom)

# Generate a random number, and assign it to MY_ID
# This is not run initially.
SET_ID = $(eval MY_ID=$(GENERATE_ID))

# You can use .PHONY to tell make that we aren't building a target output file
.PHONY: mytarget
mytarget:
# This is empty when we begin
    @echo $(MY_ID)
# This recursively expands SET_ID, which calls the Shell command and sets MY_ID
    $(SET_ID)
# This will now be a random number
    @echo $(MY_ID)
# Recursively expand SET_ID again, which calls the Shell command (again) and sets MY_ID (again)
    $(SET_ID)
# This will now be a different random number
    @echo $(MY_ID)
4
Mitchell Tracy

このようなレシピに注意してください

target:
    MY_ID=$(GENERATE_ID);
    echo $MY_ID;

間違ったことが2つあります。レシピの最初の行は、2行目とは別のシェルインスタンスで実行されます。その間、変数は失われます。 2つ目の問題は、$がエスケープされていないことです。

target:
    MY_ID=$(GENERATE_ID); \
    echo $$MY_ID;

両方の問題が修正されており、変数は使用可能です。バックスラッシュは両方の行を結合して1つのシェルで実行するため、変数の設定と変数の後書きの読み取りが機能します。

元の投稿では、シェルコマンドの結果をMAKE変数に取得する方法が記載されていましたが、この回答はシェルコマンドに結果を取得する方法を示しています。しかし、他の読者には利益があるかもしれません。

最後の改善点として、消費者が「環境変数」の設定を期待している場合、それをエクスポートする必要があります。

my_Shell_script
    echo $MY_ID

メイクファイルでこれが必要になります

target:
    export MY_ID=$(GENERATE_ID); \
    ./my_Shell_script;

それが誰かを助けることを願っています。一般に、誰かが '--dry-run'オプションを指定してmakefileを使用すると、それが何をするのかを見るだけで、望ましくない副作用がないため、レシピ以外で実際の作業を行うことは避けてください。すべての$(Shell)呼び出しはコンパイル時に評価され、実際の作業が誤って実行される可能性があります。 IDの生成などの実際の作業は、可能な場合はレシピの内部に残す方が良いでしょう。

3
Juraj