如何写入“cd”? makefile 中的命令?

发布于 2024-08-12 05:54:52 字数 174 浏览 2 评论 0原文

例如,我的 makefile 中有类似这样的内容:

all:
     cd some_directory

但是当我输入 make 时,我只看到“cd some_directory”,就像在 echo 命令中一样。

For example, I have something like this in my makefile:

all:
     cd some_directory

But when I typed make I saw only 'cd some_directory', like in the echo command.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

扬花落满肩 2024-08-19 05:54:52

它实际上是在执行命令,将目录更改为 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 并执行 < code>Makefile 在该目录中,目标为“all”。最佳实践是使用 $(MAKE) 而不是直接调用 make,因为它会小心地调用正确的 make 实例(例如,如果您使用适合您的构建环境的特殊 make 版本),并在使用某些开关(例如 -t)运行时提供略有不同的行为。

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

It is actually executing the command, changing the directory to some_directory, however, this is performed in a sub-process shell, and affects neither make nor the shell you're working from.

If you're looking to perform more tasks within some_directory, you need to add a semi-colon and append the other commands as well. Note that you cannot use new lines as they are interpreted by make as the end of the rule, so any new lines you use for clarity need to be escaped by a backslash.

For example:

all:
        cd some_dir; echo "I'm in some_dir"; \
          gcc -Wall -o myTest myTest.c

Note also that the semicolon is necessary between every command even though you add a backslash and a newline. This is due to the fact that the entire string is parsed as a single line by the shell. As noted in the comments, you should use '&&' to join commands, which means they only get executed if the preceding command was successful.

all:
        cd some_dir && echo "I'm in some_dir" && \
          gcc -Wall -o myTest myTest.c

This is especially crucial when doing destructive work, such as clean-up, as you'll otherwise destroy the wrong stuff, should the cd fail for whatever reason.

A common usage, though, is to call make in the subdirectory, which you might want to look into. There's a command-line option for this, so you don't have to call cd yourself, so your rule would look like this

all:
        $(MAKE) -C some_dir all

which will change into some_dir and execute the Makefile in that directory, with the target "all". As a best practice, use $(MAKE) instead of calling make directly, as it'll take care to call the right make instance (if you, for example, use a special make version for your build environment), as well as provide slightly different behavior when running using certain switches, such as -t.

For the record, make always echos the command it executes (unless explicitly suppressed), even if it has no output, which is what you're seeing.

总以为 2024-08-19 05:54:52

GNU make 3.82(2010 年 7 月)开始,您可以使用 .ONESHELL< /a> 在 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"

命令不与 && 链接,因此如果您想在第一个失败的地方停止,您还应该添加 -e 标记到您的 .SHELLFLAGS

.SHELLFLAGS += -e

还有 -o pipelinefail 标志可能令人感兴趣:

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

Starting from GNU make 3.82 (July 2010), you can use the .ONESHELL special target to run all recipes in a single instantiation of the shell (bold emphasis mine):

  • New special target: .ONESHELL instructs make to invoke a single instance of the shell and provide it with the entire recipe, regardless of how many lines it contains.
.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

Note that this will be equivalent to manually running

$(SHELL) $(.SHELLFLAGS) "cd ~/some_dir; pwd"
# Which gets replaced to this, most of the time:
/bin/sh -c "cd ~/some_dir; pwd"

Commands are not linked with && so if you want to stop at the first one that fails, you should also add the -e flag to your .SHELLFLAGS:

.SHELLFLAGS += -e

Also the -o pipefail flag might be of interest:

If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands in the pipeline exit successfully. This option is disabled by default.

我为君王 2024-08-19 05:54:52

这是一个处理目录和 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

Here's a cute trick to deal with directories and make. Instead of using multiline strings, or "cd ;" on each command, define a simple chdir function as so:

CHDIR_SHELL := $(SHELL)
define chdir
   $(eval _D=$(firstword $(1) $(@D)))
   $(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL))
endef

Then all you have to do is call it in your rule as so:

all:
          $(call chdir,some_dir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c

You can even do the following:

some_dir/myTest:
          $(call chdir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c
尹雨沫 2024-08-19 05:54:52

一旦它到达那里,你希望它做什么?每个命令都是在子shell中执行的,因此子shell会改变目录,但最终结果是下一个命令仍然在当前目录中。

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

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

What do you want it to do once it gets there? Each command is executed in a subshell, so the subshell changes directory, but the end result is that the next command is still in the current directory.

With GNU make, you can do something like:

BIN=/bin
foo:
    $(shell cd $(BIN); ls)
明月松间行 2024-08-19 05:54:52

这是我使用的模式:

.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
  • 我不想只使用一行代码(用分号或 && 标点),因为它的可读性较差,而且我担心我会犯错编辑制作配方时。
  • 我不想使用 .ONESHELL 特殊目标,因为:
    • 这是一个全局选项,会影响 makefile 中的所有配方
    • 使用.ONESHELL 会导致执行配方的所有行,即使较早的行之一因非零退出状态而失败也是如此。可以使用诸如调用 set -e 之类的解决方法,但必须为 makefile 中的每个配方实现此类解决方法。

Here is the pattern I've used:

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

My motivations for this pattern are:

  • The above solution is simple and readable (albeit verbose)
  • I read the classic paper "Recursive Make Considered Harmful", which discouraged me from using $(MAKE) -C some_dir all
  • I didn't want to use just one line of code (punctuated by semicolons or &&) because it is less readable, and I fear that I will make a typo when editing the make recipe.
  • I didn't want to use the .ONESHELL special target because:
    • that is a global option that affects all recipes in the makefile
    • using .ONESHELL causes all lines of the recipe to be executed even if one of the earlier lines has failed with a nonzero exit status. Workarounds like calling set -e are possible, but such workarounds would have to be implemented for every recipe in the makefile.
穿透光 2024-08-19 05:54:52

更改目录

foo: 
    $(MAKE) -C mydir

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

To change dir

foo: 
    $(MAKE) -C mydir

multi:
    $(MAKE) -C / -C my-custom-dir   ## Equivalent to /my-custom-dir
寒尘 2024-08-19 05:54:52

PYTHON = python3

测试:
cd src/mainscripts; ${PYTHON} -m pytest

#将make文件保存在根目录中并从上面的源根目录运行测试#对我有用。

PYTHON = python3

test:
cd src/mainscripts; ${PYTHON} -m pytest

#to keep make file in root directory and run test from source root above #worked for me.

沦落红尘 2024-08-19 05:54:52

就像这样:

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

祝你好运!

Like this:

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

Good luck!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文