web-dev-qa-db-ja.com

Makefileにループを書く方法は?

次のコマンドを実行したい:

./a.out 1
./a.out 2
./a.out 3
./a.out 4
.
.
. and so on

このことをMakefileのループとして書く方法は?

190
avd

./a.outを使用することで想定しているように、UNIXタイプのプラットフォームを使用している場合は、次のようにします。

for number in 1 2 3 4 ; do \
    ./a.out $$number ; \
done

次のようにテストします。

target:
    for number in 1 2 3 4 ; do \
        echo $$number ; \
    done

生成するもの:

1
2
3
4

より大きな範囲については、次を使用します。

target:
    number=1 ; while [[ $$number -le 10 ]] ; do \
        echo $$number ; \
        ((number = number + 1)) ; \
    done

これは、1から10までを出力します。コメントで示されているように、while終了条件を10から1000に変更して、はるかに大きな範囲に変更します。

ネストループは次のように実行できます。

target:
    num1=1 ; while [[ $$num1 -le 4 ]] ; do \
        num2=1 ; while [[ $$num2 -le 3 ]] ; do \
            echo $$num1 $$num2 ; \
            ((num2 = num2 + 1)) ; \
        done ; \
        ((num1 = num1 + 1)) ; \
    done

生産:

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
4 1
4 2
4 3
238
paxdiablo

GNU makeを使用している場合は、試すことができます

 NUMBERS = 1 2 3 4 
 doit:
 $(foreach var、$(NUMBERS)、。/ a.out $(var);)

生成して実行します

 ./ a.out 1; ./a.out 2; ./a.out 3; ./a.out 4; 
241
Idelic

THE make IMHOを使用する主な理由は-jフラグです。 make -j5は、5つのシェルコマンドを一度に実行します。これは、CPUが4つある場合に有効であり、任意のメイクファイルの適切なテストです。

基本的に、次のようなものを確認する必要があります。

.PHONY: all
all: job1 job2 job3

.PHONY: job1
job1: ; ./a.out 1

.PHONY: job2
job2: ; ./a.out 2

.PHONY: job3
job3: ; ./a.out 3

これは-jに適しています(良い兆候)。ボイラープレートを見つけることができますか?私たちは書くことができます:

.PHONY: all job1 job2 job3
all: job1 job2 job3
job1 job2 job3: job%:
    ./a.out $*

同じ効果のために(はい、これはmakeに関する限り、前の定式化と同じで、もう少しコンパクトです)。

コマンドラインで制限を指定できるようにするためのパラメーター化のさらなるビット(makeには適切な算術マクロがないため、ここではごまかして$(Shell ...)を使用します)

LAST := 1000
NUMBERS := $(Shell seq 1 ${LAST})
JOBS := $(addprefix job,${NUMBERS})
.PHONY: all ${JOBS}
all: ${JOBS} ; echo "$@ success"
${JOBS}: job%: ; ./a.out $*

これをmake -j5 LAST=550で実行し、LASTのデフォルトは1000です。

99
bobbogo

質問は数年前のものですが、この投稿は上記とは異なるアプローチを示しており、シェル操作にも開発者がハードコーディングされたコードを当てにする必要もないため、まだ誰かにとって役に立つかもしれません数値の文字列。

$(eval ....)組み込みマクロはあなたの友達です。または少なくともできます。

define ITERATE
$(eval ITERATE_COUNT :=)\
$(if $(filter ${1},0),,\
  $(call ITERATE_DO,${1},${2})\
)
endef

define ITERATE_DO
$(if $(Word ${1}, ${ITERATE_COUNT}),,\
  $(eval ITERATE_COUNT+=.)\
  $(info ${2} $(words ${ITERATE_COUNT}))\
  $(call ITERATE_DO,${1},${2})\
)
endef

default:
  $(call ITERATE,5,somecmd)
  $(call ITERATE,0,nocmd)
  $(info $(call ITERATE,8,someothercmd)

これは単純な例です。大きな値に対してはあまりスケールしません-動作しますが、ITERATE_COUNT文字列は各反復で2文字(スペースとドット)増加するため、数千に達すると単語をカウントするのに徐々に時間がかかります。書かれているように、ネストされた反復を処理しません(そうするには、別個の反復関数とカウンターが必要です)。これは純粋にgnu makeであり、シェルの要件はありません(明らかにOPは毎回プログラムを実行しようとしていましたが、ここではメッセージを表示するだけです)。 ITERATE内のifは、値0をキャッチすることを目的としています。それ以外の場合は$(Word ...)がエラーになります。

$(words ...)ビルトインはアラビア語のカウントを提供できるため、カウンターとして機能する成長文字列が使用されますが、それ以外の場合はmakeは数学演算をサポートしません(1 + 1を何かに割り当てて2を取得することはできません)シェルから何かを呼び出してそれを達成する場合、または同様に複雑なマクロ操作を使用する場合を除きます)。これはインクリメンタルカウンタには最適ですが、デクリメントカウンタにはあまり適していません。

私はこれを自分では使用しませんが、最近、いくつかのライブラリを含めるときにOTHERライブラリを取り込む必要があるマルチバイナリ、マルチライブラリビルド環境全体でライブラリの依存関係を評価する再帰関数を作成する必要がありましたそれ自体には他の依存関係があり(その一部はビルドパラメータによって異なります)、上記と同様の$(eval)およびカウンタメソッドを使用します(私の場合、カウンタは何らかの方法で無限にならないようにするために使用されますループ、および反復が必要な量を報告する診断として)。

OPのQにとって重要ではないものの、価値のない他の何か:$(eval ...)は、循環参照に対するmakeの内部嫌悪を回避する方法を提供します。これは、変数がマクロ型( =)、対即時割り当て(:=で初期化)。独自の割り当て内で変数を使用できるようにしたい場合があります。$(eval ...)を使用すると、それを実行できます。ここで考慮すべき重要なことは、evalを実行すると、変数が解決され、解決された部分がマクロとして扱われなくなることです。自分が何をしているかを知っていて、自分自身への割り当てのRHSで変数を使用しようとしている場合、これは通常、とにかく起こりたいことです。

  SOMESTRING = foo

  # will error.  Comment out and re-run
  SOMESTRING = pre-${SOMESTRING}

  # works
  $(eval SOMESTRING = pre${SOMESTRING}

default:
  @echo ${SOMESTRING}

ハッピーメイク。

20
s.straw

クロスプラットフォームサポートの場合、コマンドセパレーター(同じ行で複数のコマンドを実行するため)を構成可能にします。

たとえば、WindowsプラットフォームでMinGWを使用している場合、コマンド区切り記号は&です。

NUMBERS = 1 2 3 4
CMDSEP = &
doit:
    $(foreach number,$(NUMBERS),./a.out $(number) $(CMDSEP))

これにより、連結されたコマンドが1行で実行されます。

./a.out 1 & ./a.out 2 & ./a.out 3 & ./a.out 4 &

別の場所で述べたように、* nixプラットフォームではCMDSEP = ;を使用します。

17
cherno

これは実際には質問に対する純粋な答えではなく、そのような問題を回避するためのインテリジェントな方法です。

複雑なファイルを作成する代わりに、たとえば次のようなbashスクリプトに制御を委任するだけです。

foo : bar.cpp baz.h
    bash script.sh

script.shは次のようになります。

for number in 1 2 3 4
do
    ./a.out $number
done
5

Forループのプレフィックスとしてset -eを使用できます。例:

all:
    set -e; for a in 1 2 3; do /bin/false; echo $$a; done

makeは、終了コード<> 0ですぐに終了します。

3

使用できるかもしれません:

xxx:
    for i in `seq 1 4`; do ./a.out $$i; done;
2
Donwellus