makefileに「cd」コマンドを書き込むにはどうすればよいですか?


354

たとえば、メイクファイルに次のようなものが含まれています。

all:
     cd some_directory

しかし、タイプするmakeと、echoコマンドのように「cd some_directory」しか表示されませんでした。


5
何をしたいのかは不明ですが、での私の経験ではmake、このようにディレクトリを変更したくありませんでした。多分あなたはあなたの解決策に別のアプローチを試すべきですか?
Pは、

2
ディレクトリが重要であると考えるのは、初心者によくある間違いです。ほとんどの場合、そうではありません。cd dir; cmd fileほとんどの場合、はより便利にとして表すことができcmd dir/fileます。
tripleee 2013年

34
現在の作業ディレクトリは重要ではないと考えるのは、初心者によくある間違いです。多くのプログラム、特にシェルスクリプトは、特定の価値を.念頭に置いて記述されています。ほとんどのツールは、パスワードを変更する必要がないように設計されているのは事実です。しかし、これは常に正しいとは限りません。ディレクトリが重要であると考えるために「間違い」と呼ぶのは良い考えではないと思います。
Evelyn Kokemoor

ヒント:cdコマンドで「そのようなファイルまたはディレクトリはありません」と表示された場合は、(相対)ディレクトリ存在する場合でも、CDPATH環境変数が空であるか、「。」が含まれていることを確認してください。makeは "sh"を使用してコマンドを実行します。これにより、CDPATHが設定されている場合にのみ相対パスが検索されます。これは、試行するbashとは対照的です。CDPATHを調べる前。
Denis Howe

回答:


621

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


8
まあ、いつもではありませ。エコーを抑制するには、行の先頭に@を付けます。
ベータ版、

2
@ベータ:ええ、そうです、ダッシュ接頭辞はエラーステータスも無視します。多分私は少し夢中になりました、私はそれがどんな種類のコマンドであるかに関係なく、コマンドがコマンドをエコーするという事実を指摘したかったです。この場合、それは出力のないコマンドなので、makeに慣れていない人には、エコーがさらに奇妙に見えます。
falstro 2009年

46
二つのニット:1.コマンドが本当ににより参加しなければならない&&とするので、;ディレクトリが存在していない場合はcd失敗し、シェルは神秘的な「ファイルのようなものをない引き起こすことが、現在のディレクトリ内の残りのコマンドを実行し続けますコンパイルのメッセージ、makeの呼び出し時の無限ループ、またはなどのルールの障害clean:: cd dir; rm -rf *。2.サブメイクを呼び出すときは、$(MAKE)代わりにを呼び出しmakeて、オプションが正しく渡されるようにします
andrewdotn

2
@perreal、私は通常、次のようにパターンルールを定義します:%-recursive:本体:(@T="$@";$(MAKE) -C some_dir $${T%-*}サブループのリストをループするために、通常はforループもあります。$${T%-*}これは-recursive、ターゲット名の一部を削除するbash拡張です) 、、のようall: all-recursivecheck: check-recursive、それぞれに明示的な省略形(および.PHONY)ターゲットを定義しますclean: clean-recursive
falstro

1
@ ChristianStewart、true、コメント2と3で述べたとおり
falstro

95

始まるGNUメーク3.82(2010年7月)は、使用することができます.ONESHELLシェル(太字強調鉱山)の単一のインスタンス内のすべてのレシピ行を実行するために、特別なターゲットを:

  • 新しい特別なターゲット:make、シェルの単一のインスタンスを呼び出して含まれる行数に関係なくレシピ全体を提供する.ONESHELLように指示します。[...]
.ONESHELL: # Only applies to all target
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded

6
ただ、ノートそれpwd自体は同様に、動作します`pwd`(バッククォートと)が、$(shell pwd)そして$(PWD)、まだやって前にディレクトリを返します。cdあなたがそれらを直接使用することはできませんので、コマンドを。
アルコール2016

3
はい。変数と関数の展開はmakeによってコマンドを実行する前に行われますが、pwdおよび`pwd`はシェル自体によって実行されるためです。
Chnossos

2
誰もが従来のメイクファイルに取り組んでいるわけではなく、それでも、この答えはこの可能性が存在することを知ることです。
クノッソス2017年

1
ターゲットの最後のコマンドのみが失敗の原因となり、以前のコマンドの失敗は無視されるため、これは煩わしい/危険なオプションになる可能性があり、おそらく誰もそれを期待していないでしょう。
tobii 2018

1
に設定SHELLFLAGSする-e -cと、シェルは失敗した最初のコマンドで終了します。
ティモシーボールドウィン

21

ここでは、ディレクトリを処理して作成するためのかわいいトリックを紹介します。複数行の文字列または「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

41
"可愛い"?足で自分を撃つのに十分なロープのように。
tripleee 2013年

1
この現在のディレクトリは、そのルールのコマンドだけに設定されていますか、それともその後に実行されるすべてのルールに設定されていますか?また、このバリエーションはWindowsでも機能しますか?
user117529 2014年

8
もちろん-jn、これは並列実行()の場合は中断します。これは本当にmakeの全体的なポイントです。
bobbogo 14年

5
これは悪いハックです。あなたがそのようなことに頼らなければならないならば、あなたはそれらが何のためにあるかのためにMakefileを使っていません。
gatopeich 14年

4
確かにそれが悪いハックであることには反対しません。しかし、あなたができる邪悪なことのいくつかを示しています。
JoeS 2014

9

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

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

BIN=/bin
foo:
    $(shell cd $(BIN); ls)

5
なぜ$(shell ...)ときcd $(BIN); lscd $(BIN) && ls(@andrewdotnが指摘したように)十分だろう。
蒸気2013年

1

ディレクトリを変更するには

foo: 
    $(MAKE) -C mydir

multi:
    $(MAKE) -C / -C my-custom-dir   ## Equivalent to /my-custom-dir

-6

このような:

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

幸運を!


1
いいえ、これは間違っています。具体的には$(shell cd ....)、この特定のレシピが実行されたときではなく、Makefileが最初に解析されたときにが実行されます。
tripleee 2018

@triplee違います— maketargetをビルドすることを決定した$(shell)場合にのみ展開されます。makeがレシピを必要としない場合、それは拡張されません。
bobbogo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.