427

例如,我的 makefile 中有这样的内容:

all:
     cd some_directory

但是当我输入时,make我只看到“cd some_directory”,就像在echo命令中一样。

4

7 回答 7

740

它实际上是在执行命令,将目录更改为some_directory,但是,这是在子进程 shell 中执行的,并且既不影响 make 也不影响您正在使用的 shell。

如果您希望在 中执行更多任务some_directory,则需要添加分号并附加其他命令。请注意,您不能使用换行符,因为它们被 make 解释为规则的结尾,因此您为清楚起见使用的任何换行符都需要用反斜杠转义。

例如:

all:
        cd some_dir; echo "I'm in some_dir"; \
          gcc -Wall -o myTest myTest.c

另请注意,即使您添加了反斜杠和换行符,每个命令之间也需要分号。这是因为整个字符串被 shell 解析为单行。如评论中所述,您应该使用 '&&' 来加入命令,这意味着它们只有在前面的命令成功时才会被执行。

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

它将变为some_dirMakefile在其中执行目标“全部”。作为最佳实践,使用$(MAKE)而不是直接调用make,因为它会小心调用正确的 make 实例(例如,如果您为构建环境使用特殊的 make 版本),并且在运行时提供稍微不同的行为使用某些开关,例如-t.

作为记录,make总是回显它执行的命令(除非明确禁止),即使它没有输出,这就是您所看到的。

于 2009-11-24T11:58:37.627 回答
127

GNU make 3.82(2010 年 7 月)开始,您可以使用.ONESHELL特殊目标在 shell 的单个实例中运行所有配方(粗体强调我的):

  • 新的特殊目标:.ONESHELL指示make调用shell 的单个实例并为其提供整个 recipe,而不管它包含多少行。
.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

一旦它到达那里,你希望它做什么?每个命令都在一个子shell中执行,所以子shell改变了目录,但最终的结果是下一个命令仍然在当前目录中。

使用 GNU make,您可以执行以下操作:

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

更改目录

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 .

我对这种模式的动机是:

  • 上面的解决方案简单易读(虽然很冗长)
  • 我阅读了经典论文“Recursive Make Considered Harmful”,这使我不鼓励使用$(MAKE) -C some_dir all
  • 我不想只使用一行代码(用分号或 标点符号&&),因为它的可读性较差,而且我担心在编辑 make 配方时会打错字。
  • 我不想使用.ONESHELL特殊目标,因为:
    • 这是一个全局选项,会影响 makefile 中的所有配方
    • using.ONESHELL导致配方的所有行都被执行,即使前面的行之一以非零退出状态失败。像调用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 回答