在 makefile 中调用 bash 命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39479489/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Invoking bash commands in makefile
提问by Allen
Inside of a makefile
, I'm trying to check if fileA was modified more recently than fileB. Using a few prior posts (thisand this) as references, I've come up with this as an attempt to store the time since last file modification as a variable:
在 a 中makefile
,我试图检查 fileA 是否比 fileB 修改得更近。使用之前的一些帖子(this和this)作为参考,我想出了这个来尝试将自上次文件修改以来的时间存储为变量:
(I'd rather this be in a function outside of a make
recipe, but one step at a time.)
(我宁愿这是在make
配方之外的函数中,但一次一个。)
.PHONY: all clean
all : (stuff happens here)
radio :
BASE_MOD_TIME="$( expr $(date +%s) - $(date +%s -r src/radio_interface/profile_init.c) )"
@echo "$(BASE_MOD_TIME)"
I thought that I would be assigning the output of the expr
command to a variable, BASE_MOD_TIME
, but the output is:
我以为我会将expr
命令的输出分配给一个变量BASE_MOD_TIME
,但输出是:
bash-4.1$
BASE_MOD_TIME=""
echo ""
What am I doing wrong here? Simple attempts to save the output of ls -l
also didn't work like this.
我在这里做错了什么?保存输出的简单尝试ls -l
也没有像这样工作。
回答by rici
Make variables are normally global, and you don't normally set make variables in a recipe. A recipe is simply a list of commands to be executed by a shell, and so what looks like a variable assignment in a recipe is a shell variable assignment.
Make 变量通常是全局的,您通常不会在配方中设置 make 变量。配方只是一个由 shell 执行的命令列表,因此在配方中看起来像变量赋值的就是 shell 变量赋值。
However, each line in a make
recipe is run in its own shell subprocess. So a variable set in one line won't be visible in another line; they are not persistent. That makes setting shell variables in recipes less useful. [Note 1]
但是,make
配方中的每一行都在其自己的 shell 子进程中运行。因此,在一行中设置的变量在另一行中不可见;他们不执着。这使得在配方中设置 shell 变量不太有用。[注1]
But you can combine multiple lines of a recipe into a single shell command using the backslash escape at the end of the line, and remembering to terminate the individual commands with semicolons (or, better, link them with &&
), because the backslash-escaped newline will not be passed to the shell. Also, don't forget to escape the $
characters so they will be passed to the shell, rather than being interpreted by make
.
但是您可以使用行尾的反斜杠转义将一个配方的多行组合成一个 shell 命令,并记住用分号终止各个命令(或者,更好地,将它们与 链接&&
),因为反斜杠转义的换行符不会传递给shell。此外,不要忘记对$
字符进行转义,以便将它们传递给 shell,而不是被make
.
So you could do the following:
因此,您可以执行以下操作:
radio:
@BASE_MOD_TIME="$$( expr $$(date +%s) - $$(date +%s -r src/radio_interface/profile_init.c) )"; \
echo "$$BASE_MOD_TIME"; \
# More commands in the same subprocess
But that gets quite awkward if there are more than a couple of commands, and a better solution is usually to write a shell script and invoke it from the recipe (although that means that the Makefile is no longer self-contained.)
但是如果有多个命令,这会变得很尴尬,更好的解决方案通常是编写一个 shell 脚本并从配方中调用它(尽管这意味着 Makefile 不再是自包含的。)
Gnu make provides two ways to set make variables in a recipe:
Gnu make 提供了两种方法来设置配方中的 make 变量:
1. Target-specific variables.
1. 特定于目标的变量。
You can create a target-specific variable (which is not exactly local to the target) by adding a line like:
您可以通过添加如下一行来创建一个特定于目标的变量(它不完全是目标的本地变量):
target: var := value
To set the variable from a shell command, use the shell
function:
要从 shell 命令设置变量,请使用以下shell
函数:
target: var := $(shell ....)
This variable will be available in the target recipe and all dependencies triggered by the target. Note that a dependency is only evaluated once, so if it could be triggered by a different target, the target-specific variable might or might not be available in the dependency, depending on the order in which make
resolves dependencies.
此变量将在目标配方和由目标触发的所有依赖项中可用。请注意,依赖项仅被评估一次,因此如果它可以由不同的目标触发,则特定于目标的变量可能会或可能不会在依赖项中可用,具体取决于make
解析依赖项的顺序。
2. Using the eval
function
2. 使用eval
功能
Since the expansion of recipes is always deferred, you can use the eval
function inside a recipe to defer the assignment of a make variable. The eval
function can be placed pretty well anywhere in a recipe because its value is the empty string. However, eval
ing a variable assignment makes the variable assignment global; it will be visible throughout the makefile, but its value in other recipes will depend, again, on the order in which make evaluates recipes, which is not necessarily predictable.
由于配方的扩展总是延迟的,您可以使用eval
配方中的函数来延迟 make 变量的分配。该eval
函数可以很好地放置在配方中的任何位置,因为它的值是空字符串。但是,eval
对变量赋值会使变量赋值成为全局的;它在整个 makefile 中都是可见的,但它在其他配方中的价值将再次取决于 make 评估配方的顺序,这不一定是可预测的。
For example:
例如:
radio:
$(eval var = $(shell ...))
Notes:
笔记:
- You can change this behaviour using the
.ONESHELL:
pseudo-target, but that will apply to the entire Makefile; there is no way to mark a single recipe as being executed in a single subprocess. Since changing the behaviour can break recipes in unexpected ways, I don't usually recommend this feature.
- 您可以使用
.ONESHELL:
伪目标更改此行为,但这将适用于整个 Makefile;无法将单个配方标记为在单个子流程中执行。由于更改行为可能会以意想不到的方式破坏配方,因此我通常不推荐此功能。
回答by Beta
What's wrong with this?
这有什么问题?
fileB: fileA
@echo $< was modified more recently than $@
回答by Allen
Instead of forcing the makefile
to do all of the heavy lifting via some bash
commands, I just called a separate bash
script. This provided a lot more clarity for a newbie to bash
scripting like myself since I didn't have to worry about escaping the current shell being used by make
.
我没有makefile
通过一些bash
命令强制执行所有繁重的工作,而是调用了一个单独的bash
脚本。这为bash
像我这样的脚本新手提供了更多清晰度,因为我不必担心转义make
.
Final solution:
最终解决方案:
.PHONY: all clean
all : (stuff happens here)
radio :
./radio_init_check.sh
$(MKDIR_P) $(OBJDIR)
make $(radio_10)
with radio_init_check.sh
being my sew script.
与radio_init_check.sh
是我缝的脚本。