如何在makefile中编写'cd'命令?

时间:2009-11-24 11:53:52

标签: makefile gnu-make

例如,我的makefile中有这样的东西:

all:
     cd some_directory

但是当我输入make时,我只看到了'cd some_directory',就像echo命令一样。

7 个答案:

答案 0 :(得分:519)

它实际上正在执行命令,将目录更改为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并在其中执行目标为“all”的Makefile。作为最佳做法,请使用$(MAKE)而不是直接调用make,因为它会注意调用正确的make实例(例如,如果您为构建环境使用特殊的make版本) ,以及在使用某些开关(例如-t

)运行时提供略微不同的行为

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

答案 1 :(得分:70)

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

  
      
  • 新的特殊目标:.ONESHELL指示 make 调用 shell的单个实例,并为其提供整个配方,无论它有多少行包含的内容。 [...]
  •   
.ONESHELL:
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded

答案 2 :(得分: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

答案 3 :(得分:7)

一旦它到达那里你想要它做什么?每个命令都在子shell中执行,因此subshel​​l更改目录,但最终结果是下一个命令仍在当前目录中。

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

BIN=/bin
foo:
    $(shell cd $(BIN); ls)

答案 4 :(得分:0)

更改目录

foo: 
    $(MAKE) -C mydir

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

答案 5 :(得分:0)

这是我使用过的模式:

.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 中的所有配方
    • 使用 .ONESHELL 会导致执行配方的所有行,即使较早的行之一因非零退出状态而失败。调用 set -e 等变通方法是可能的,但必须为 makefile 中的每个配方实施此类变通方法。

答案 6 :(得分:-3)

像这样:

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
祝你好运!

相关问题