たとえば、makefileには次のようなものがあります。
all:
cd some_directory
しかし、入力すると、コマンドmake
のように「cdsome_directory」しか表示されませんでした。echo
実際にはコマンドを実行し、ディレクトリをに変更しますsome_directory
が、これはサブプロセスシェルで実行され、makeにも作業中のシェルにも影響しません。
内でさらにタスクを実行する場合はsome_directory
、セミコロンを追加し、他のコマンドも追加する必要があります。改行はルールの終わりとしてmakeによって解釈されるため、使用できないことに注意してください。したがって、明確にするために使用する改行は、バックスラッシュでエスケープする必要があります。
例えば:
all:
cd some_dir; echo "I'm in some_dir"; \
gcc -Wall -o myTest myTest.c
また、円記号と改行を追加した場合でも、すべてのコマンドの間にセミコロンが必要であることに注意してください。これは、文字列全体がシェルによって1行として解析されるためです。コメントに記載されているように、コマンドを結合するには「&&」を使用する必要があります。つまり、コマンドは、前のコマンドが成功した場合にのみ実行されます。
all:
cd some_dir && echo "I'm in some_dir" && \
gcc -Wall -o myTest myTest.c
これは、クリーンアップなどの破壊的な作業を行う場合に特に重要です。そうしcd
ないと、何らかの理由で失敗した場合に間違ったものが破壊されるためです。
ただし、一般的な使用法は、サブディレクトリでmakeを呼び出すことです。これは、調べたい場合があります。cd
これにはコマンドラインオプションがあるので、自分で呼び出す必要はないので、ルールは次のようになります。
all:
$(MAKE) -C some_dir all
これは、ターゲット「all」に変更され、そこでsome_dir
実行されます。Makefile
ベストプラクティスとして、直接$(MAKE)
呼び出す代わりにを使用make
します。これは、適切なmakeインスタンスを呼び出すように注意するため(たとえば、ビルド環境に特別なmakeバージョンを使用する場合)、実行時にわずかに異なる動作を提供します。などの特定のスイッチを使用します-t
。
記録のために、makeは、出力がない場合でも、実行するコマンドを常にエコーします(明示的に抑制されていない場合)。これは、表示されているものです。
GNU make 3.82 (2010年7月)以降、.ONESHELL
特別なターゲットを使用して、シェルの1回のインスタンス化ですべてのレシピを実行できます(太字の強調鉱山)。
- 新しい特別なターゲット:シェルの単一のインスタンスを呼び出して、含まれている行数に関係なく、レシピ全体を提供するようにmake
.ONESHELL
に指示します。
.ONESHELL: # Applies to every targets in the file!
all:
cd ~/some_dir
pwd # Prints ~/some_dir if cd succeeded
another_rule:
cd ~/some_dir
pwd # Prints ~/some_dir if cd succeeded
これは手動で実行するのと同じであることに注意してください
$(SHELL) $(.SHELLFLAGS) "cd ~/some_dir; pwd"
# Which gets replaced to this, most of the time:
/bin/sh -c "cd ~/some_dir; pwd"
コマンドはリンクされていない&&
ため、最初に失敗したコマンドで停止する場合は、-e
フラグを.SHELLFLAGS
:に追加する必要があります。
.SHELLFLAGS += -e
また、-o pipefail
フラグも興味深いかもしれません。
設定されている場合、パイプラインの戻り値は、ゼロ以外のステータスで終了する最後の(右端の)コマンドの値です。パイプライン内のすべてのコマンドが正常に終了した場合はゼロになります。このオプションはデフォルトで無効になっています。
これは、ディレクトリを処理して作成するためのかわいいトリックです。複数行の文字列、または「cd;」を使用する代わりに 各コマンドで、次のように単純なchdir関数を定義します。
CHDIR_SHELL := $(SHELL)
define chdir
$(eval _D=$(firstword $(1) $(@D)))
$(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL))
endef
次に、あなたがしなければならないのは、あなたのルールでそれを次のように呼ぶことです:
all:
$(call chdir,some_dir)
echo "I'm now always in some_dir"
gcc -Wall -o myTest myTest.c
次のこともできます。
some_dir/myTest:
$(call chdir)
echo "I'm now always in some_dir"
gcc -Wall -o myTest myTest.c
そこに着いたら何をしたいですか?各コマンドはサブシェルで実行されるため、サブシェルはディレクトリを変更しますが、最終的には次のコマンドが現在のディレクトリに残ります。
GNU makeを使用すると、次のようなことができます。
BIN=/bin
foo:
$(shell cd $(BIN); ls)
dirを変更するには
foo:
$(MAKE) -C mydir
multi:
$(MAKE) -C / -C my-custom-dir ## Equivalent to /my-custom-dir
これが私が使用したパターンです:
.PHONY: test_py_utils
PY_UTILS_DIR = py_utils
test_py_utils:
cd $(PY_UTILS_DIR) && black .
cd $(PY_UTILS_DIR) && isort .
cd $(PY_UTILS_DIR) && mypy .
cd $(PY_UTILS_DIR) && pytest -sl .
cd $(PY_UTILS_DIR) && flake8 .
このパターンの私の動機は次のとおりです。
$(MAKE) -C some_dir all
&&
)だけを使用したくありませんでした。makeレシピを編集するときにタイプミスをするのではないかと心配しています。.ONESHELL
次の理由で、特別なターゲット
を使用したくありませんでした。.ONESHELL
と、前の行の1つがゼロ以外の終了ステータスで失敗した場合でも、レシピのすべての行が実行されます。呼び出しset -e
などの回避策は可能ですが、そのような回避策は、makefile内のすべてのレシピに実装する必要があります。このような:
target:
$(shell cd ....); \
# ... commands execution in this directory
# ... no need to go back (using "cd -" or so)
# ... next target will be automatically in prev dir
幸運を!