如何将命令的输出分配给Makefile变量

我需要有条件地执行一些make规则,只有当安装的Python大于某个版本(比如2.5)。

我想我可以做一些类似执行的事情:

python -c 'import sys; print int(sys.version_info >= (2,5))'

然后在ifeq make语句中使用输出(如果ok则为'1',否则为'0')。

在一个简单的bash shell脚本中,它只是:

MY_VAR=`python -c 'import sys; print int(sys.version_info >= (2,5))'`

但这在Makefile中不起作用。

有什么建议吗?我可以使用任何其他明智的解决办法来实现这一点。

263830 次浏览

MY_VAR=$(shell echo whatever)一样使用内置的Make shell

me@Zack:~$make
MY_VAR IS whatever

< p >

me@Zack:~$ cat Makefile
MY_VAR := $(shell echo whatever)


all:
@echo MY_VAR IS $(MY_VAR)

eval中包装赋值对我来说是有效的。

# dependency on .PHONY prevents Make from
# thinking there's `nothing to be done`
set_opts: .PHONY
$(eval DOCKER_OPTS = -v $(shell mktemp -d -p /scratch):/output)

我正在编写一个答案,以增加解决问题的实际语法的可见性。不幸的是,对于那些为合理问题寻找简单答案的人来说,一些在别人看来微不足道的东西可能会成为非常令人头痛的问题。

将以下内容放入“Makefile”文件中。

MY_VAR := $(shell python -c 'import sys; print int(sys.version_info >= (2,5))')


all:
@echo MY_VAR IS $(MY_VAR)

您希望看到的行为如下(假设您最近安装了python)。

make
MY_VAR IS 1

如果将上面的文本复制并粘贴到Makefile中,会得到这个结果吗?可能不会。你可能会得到如下所示的错误:

makefile:4: ***缺少分隔符。停止< / >

原因:因为虽然我个人使用了一个真正的选项卡,Stack Overflow(试图提供帮助)将我的选项卡转换为一些空格。你,沮丧的网民,现在复制这个,认为你现在有了我使用的相同的文本。make命令现在读取空格并发现“all”命令的格式不正确。因此,复制上面的文本,粘贴它,然后将“@echo”前面的空白转换为一个制表符,这个例子应该,最后,希望对你有用。

下面是一个稍微复杂一点的例子,在recipe中使用管道和变量赋值:

getpodname:
# Getting pod name
@eval $$(minikube docker-env) ;\
$(eval PODNAME=$(shell sh -c "kubectl get pods | grep profile-posts-api | grep Running" | awk '{print $$1}'))
echo $(PODNAME)

在GNU Make中,你可以使用shelleval来存储、运行和分配任意命令行调用的输出。下面的例子和那些使用:=的例子之间的区别是,:=赋值只发生一次(当它遇到时),并且是全部发生。用=设置的递归扩展变量有点“懒惰”;对其他变量的引用一直保留到变量本身被引用,以及每次引用变量时都进行后续的递归展开,这对于创建“一致的、可调用的代码段”是可取的。更多信息见关于设置变量的手册

# Generate a random number.
# This is not run initially.
GENERATE_ID = $(shell od -vAn -N2 -tu2 < /dev/urandom)


# Generate a random number, and assign it to MY_ID
# This is not run initially.
SET_ID = $(eval MY_ID=$(GENERATE_ID))


# You can use .PHONY to tell make that we aren't building a target output file
.PHONY: mytarget
mytarget:
# This is empty when we begin
@echo $(MY_ID)
# This recursively expands SET_ID, which calls the shell command and sets MY_ID
$(SET_ID)
# This will now be a random number
@echo $(MY_ID)
# Recursively expand SET_ID again, which calls the shell command (again) and sets MY_ID (again)
$(SET_ID)
# This will now be a different random number
@echo $(MY_ID)

小心这样的食谱

target:
MY_ID=$(GENERATE_ID);
echo $MY_ID;

它做错了两件事。配方中的第一行在与第二行不同的shell实例中执行。在此期间,变量将丢失。第二个错误是$没有转义。

target:
MY_ID=$(GENERATE_ID); \
echo $$MY_ID;

这两个问题都已修复,变量可用。反斜杠将这两行合并在一个shell中运行,因此变量的设置和变量后记的读取是有效的。

我意识到原来的帖子说了如何将shell命令的结果转化为MAKE变量,而这个答案展示了如何将其转化为shell变量。但其他读者可能会受益。

最后一个改进是,如果使用者希望设置一个“环境变量”,那么您必须导出它。

my_shell_script
echo $MY_ID

在makefile中需要这个吗

target:
export MY_ID=$(GENERATE_ID); \
./my_shell_script;

希望这能帮助到别人。一般来说,应该避免做任何食谱之外的实际工作,因为如果有人使用带有“——dry-run”选项的makefile,只看看它会做什么,它不会有任何不良的副作用。每个$(shell)调用都在编译时计算,一些实际工作可能会意外地完成。如果可能的话,最好将实际工作(如生成id)留在食谱内部。

在下面的例子中,我已经将Makefile文件夹路径存储到LOCAL_PKG_DIR,然后在目标中使用LOCAL_PKG_DIR变量。

Makefile:

LOCAL_PKG_DIR := $(shell eval pwd)


.PHONY: print
print:
@echo $(LOCAL_PKG_DIR)

终端输出:

$ make print
/home/amrit/folder

从制作手册

shell赋值操作符` != '可以用来执行shell脚本,并将一个>变量设置为其输出。这个操作符首先计算右边的值,然后将结果传递给shell执行。如果执行结果以>换行符结束,则删除该换行符;所有其他换行符都替换为空格。然后将生成的字符串放入命名的递归展开变量中。在示例:

hash != printf '\043'

file_list != find . -name '*.c'

source