Makefile 如何编写';cd&x27;生成文件中的命令?

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会将它们解释为规则的结尾,因此为清晰起见使用的

例如,我的makefile中有如下内容:

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 .