Makefile 如何编写';cd&x27;生成文件中的命令?
例如,我的makefile中有如下内容:Makefile 如何编写';cd&x27;生成文件中的命令?,makefile,gnu-make,Makefile,Gnu Make,例如,我的makefile中有如下内容: all: cd some_directory 但是,当我键入make时,我只看到了“cd some_directory”,就像在echo命令中一样。它实际上是在执行命令,将目录更改为some_directory,但是,这是在子进程shell中执行的,既不影响make,也不影响正在使用的shell 如果希望在某个目录中执行更多任务,则需要添加分号并附加其他命令。请注意,您不能使用换行符,因为make会将它们解释为规则的结尾,因此为清晰起见使用的
all:
cd some_directory
但是,当我键入
make
时,我只看到了“cd some_directory”,就像在echo
命令中一样。它实际上是在执行命令,将目录更改为some_directory
,但是,这是在子进程shell中执行的,既不影响make,也不影响正在使用的shell
如果希望在某个目录中执行更多任务,则需要添加分号并附加其他命令。请注意,您不能使用换行符,因为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
,目标为“all”。作为最佳实践,使用$(MAKE)
而不是直接调用MAKE
,因为它会注意调用正确的MAKE实例(例如,如果您为构建环境使用特殊的MAKE版本),并且在使用某些开关(例如-t
)运行时提供稍微不同的行为
对于记录,make总是回显它执行的命令(除非明确禁止),即使它没有输出,这就是您看到的。一旦它到达那里,您希望它做什么?每个命令都在子shell中执行,因此子shell会更改目录,但最终结果是下一个命令仍在当前目录中
使用GNU make,您可以执行以下操作:
BIN=/bin
foo:
$(shell cd $(BIN); ls)
这里有一个处理目录和制作的可爱技巧。在每个命令上定义一个简单的chdir函数,而不是使用多行字符串或“cd;”:
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
从(2010年7月)开始,您可以使用special target在shell的单个实例化中运行所有配方(粗体强调):
- 新的特殊目标:
.ONESHELL
指示make调用shell的单个实例,并向其提供整个配方,而不管它包含多少行
请注意,这相当于手动运行
$(SHELL)$(.SHELLFLAGS)“cd~/some_dir;pwd”
#大多数情况下会被替换为:
/bin/sh-c“cd~/some_dir;pwd”
指令未与&&
链接,因此如果您想在第一个出现故障时停止,还应将-e
标志添加到.SHELLFLAGS
:
.SHELLFLAGS += -e
像这样:
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
祝你好运 更改目录
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\u dir all
- 我不想只使用一行代码(用分号或
&&
标点),因为它可读性较差,而且我担心在编辑make配方时会输入错误
- 我不想使用
.ONESHELL
特殊目标,因为:
- 这是一个影响makefile中所有配方的全局选项
- 使用
.ONESHELL
会导致执行配方的所有行,即使前面的一行失败且退出状态为非零。像调用set-e
这样的变通方法是可能的,但是这样的变通方法必须为makefile中的每个配方实现
嗯,不总是这样。要抑制回音,只需将@放在行首。@Beta:是的,破折号前缀也会忽略错误状态。也许我有点忘乎所以,我想指出一个事实,make确实响应命令,不管它是什么类型的命令。在本例中,这是一个没有输出的命令,这使得对make不熟悉的人来说,回声显得更加陌生。这些命令实际上应该由&&
连接,因为使用
如果目录不存在,并且cd
失败,shell将继续运行当前目录中的其余命令,这可能会导致编译时出现神秘的“未找到文件”消息、调用make时出现无限循环,或者clean::cd dir等规则的灾难;rm-rf*
。2.调用sub MAKE时,调用$(MAKE)
而不是MAKE
,这样@perreal,我通常定义一个模式规则,如:%-递归:
,主体为:@T=“$”$(MAKE)-C some_dir$$${T%-*}
(我通常也有一个for循环,用于循环子目录列表,${T%-*}
是一个bash扩展,它删除了目标名称的-recursive
部分),然后为每个子目录定义显式的速记(和.PHONY)目标,如all:all recursive
,check:check recursive
,clean:clean recursive
@ChristianStewart,如注释2和3所述。您想做什么还不清楚,但根据我在make
方面的经验,我从来都不想像t
.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 .