例如,我的 makefile 中有这样的内容:
all:
cd some_directory
但是当我输入时,make
我只看到“cd some_directory”,就像在echo
命令中一样。
它实际上是在执行命令,将目录更改为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_dir
并Makefile
在其中执行目标“全部”。作为最佳实践,使用$(MAKE)
而不是直接调用make
,因为它会小心调用正确的 make 实例(例如,如果您为构建环境使用特殊的 make 版本),并且在运行时提供稍微不同的行为使用某些开关,例如-t
.
作为记录,make总是回显它执行的命令(除非明确禁止),即使它没有输出,这就是您所看到的。
从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
标志可能也很有趣:
如果设置,则管道的返回值是以非零状态退出的最后一个(最右边)命令的值,如果管道中的所有命令成功退出,则返回为零。默认情况下禁用此选项。
这是处理目录和制作的一个可爱技巧。而不是使用多行字符串或“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
一旦它到达那里,你希望它做什么?每个命令都在一个子shell中执行,所以子shell改变了目录,但最终的结果是下一个命令仍然在当前目录中。
使用 GNU make,您可以执行以下操作:
BIN=/bin
foo:
$(shell cd $(BIN); ls)
更改目录
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
导致配方的所有行都被执行,即使前面的行之一以非零退出状态失败。像调用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
祝你好运!