例如,我在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,目标为“all”。作为一种最佳实践,使用$(MAKE)而不是直接调用MAKE,因为它会注意调用正确的MAKE实例(例如,如果您为构建环境使用一个特殊的MAKE版本),并且在使用某些开关(如-t)运行时提供略微不同的行为。

为了记录,make总是回显它执行的命令(除非显式抑制),即使它没有输出,这就是您所看到的。

其他回答

这里有一个处理目录和制作的可爱技巧。不要在每个命令上使用多行字符串或“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

它实际上是在执行命令,将目录更改为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,目标为“all”。作为一种最佳实践,使用$(MAKE)而不是直接调用MAKE,因为它会注意调用正确的MAKE实例(例如,如果您为构建环境使用一个特殊的MAKE版本),并且在使用某些开关(如-t)运行时提供略微不同的行为。

为了记录,make总是回显它执行的命令(除非显式抑制),即使它没有输出,这就是您所看到的。

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

新的特殊目标:. oneshell指示make调用shell的单个实例,并为其提供整个配方,而不管它包含多少行。

.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"

命令没有&&链接,所以如果你想在第一个失败的命令上停下来,你还应该在你的.SHELLFLAGS中添加-e标志:

.SHELLFLAGS += -e

此外,-o pipefail标志可能也会感兴趣:

如果设置了该值,则管道的返回值为最后一个(最右边)以非零状态退出的命令的值,如果管道中的所有命令都成功退出,则为零。默认情况下,该选项是禁用的。

更改dir

foo: 
    $(MAKE) -C mydir

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

是这样的:

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

好运!