427

たとえば、makefileには次のようなものがあります。

all:
     cd some_directory

しかし、入力すると、コマンドmakeのように「cdsome_directory」しか表示されませんでした。echo

4

7 に答える 7

740

実際にはコマンドを実行し、ディレクトリをに変更します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は、出力がない場合でも、実行するコマンドを常にエコーします(明示的に抑制されていない場合)。これは、表示されているものです。

于 2009-11-24T11:58:37.627 に答える
127

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フラグも興味深いかもしれません。

設定されている場合、パイプラインの戻り値は、ゼロ以外のステータスで終了する最後の(右端の)コマンドの値です。パイプライン内のすべてのコマンドが正常に終了した場合はゼロになります。このオプションはデフォルトで無効になっています。

于 2015-06-02T07:44:43.980 に答える
20

これは、ディレクトリを処理して作成するためのかわいいトリックです。複数行の文字列、または「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
于 2011-08-10T22:53:37.547 に答える
9

そこに着いたら何をしたいですか?各コマンドはサブシェルで実行されるため、サブシェルはディレクトリを変更しますが、最終的には次のコマンドが現在のディレクトリに残ります。

GNU makeを使用すると、次のようなことができます。

BIN=/bin
foo:
    $(shell cd $(BIN); ls)
于 2009-11-24T11:59:28.747 に答える
2

dirを変更するには

foo: 
    $(MAKE) -C mydir

multi:
    $(MAKE) -C / -C my-custom-dir   ## Equivalent to /my-custom-dir
于 2018-06-26T15:24:23.003 に答える
2

これが私が使用したパターンです:

.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 .

このパターンの私の動機は次のとおりです。

  • 上記の解決策は単純で読みやすいです(冗長ではありますが)
  • 古典的な論文「RecursiveMakeConsideredHarmful」を読んだので、使用するのを思いとどまらせました。$(MAKE) -C some_dir all
  • 読みにくいため、1行のコード(セミコロンまたはで区切られている&&)だけを使用したくありませんでした。makeレシピを編集するときにタイプミスをするのではないかと心配しています。
  • .ONESHELL次の理由で、特別なターゲット を使用したくありませんでした。
    • これは、makefile内のすべてのレシピに影響するグローバルオプションです
    • 使用する.ONESHELLと、前の行の1つがゼロ以外の終了ステータスで失敗した場合でも、レシピのすべての行が実行されます。呼び出しset -eなどの回避策は可能ですが、そのような回避策は、makefile内のすべてのレシピに実装する必要があります。
于 2021-02-21T04:02:48.530 に答える
-8

このような:

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

幸運を!

于 2016-04-05T09:37:49.070 に答える