我需要有条件地执行一些 make 规则,只有当安装的 Python 大于某个版本(比如 2.5)。
我以为我可以执行以下操作:
python -c 'import sys; print int(sys.version_info >= (2,5))'
然后在 ifeq
make 语句中使用输出(如果可以,则为“1”,否则为“0”)。
在一个简单的 bash shell 脚本中,它只是:
MY_VAR=`python -c 'import sys; print int(sys.version_info >= (2,5))'`
但这在 Makefile 中不起作用。
有什么建议么?我可以使用任何其他合理的解决方法来实现这一点。
像在 MY_VAR=$(shell echo whatever)
中一样使用 Make shell
me@Zack:~$make
MY_VAR IS whatever
me@Zack:~$ cat Makefile
MY_VAR := $(shell echo whatever)
all:
@echo MY_VAR IS $(MY_VAR)
小心这样的食谱
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)留在食谱内部。
将作业包装在 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)
.PHONY always make these targets
的用途。
使用 GNU Make,您可以使用 shell
和 eval
来存储、运行和分配来自任意命令行调用的输出。下面的示例与使用 :=
的示例之间的区别在于 :=
分配发生一次(遇到时)并且全部发生。使用 =
设置的递归扩展变量有点“懒惰”;对其他变量的引用一直存在,直到变量本身被引用,并且每次引用该变量时都会发生后续的递归扩展,这对于制作“一致、可调用的片段”是可取的。有关详细信息,请参阅 the manual on setting variables。
# 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)
$(SET_ID)
位于 if
子句 that is false 中,那么 它仍会被调用。
这是一个更复杂的示例,其中包含配方中的管道和变量赋值:
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)
PODNAME
:$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='{.items[0].metadata.name}'"))
我正在写一个答案,以提高对解决问题的实际语法的可见性。不幸的是,对于那些寻找一个合理问题的简单答案的人来说,一些人可能认为微不足道的事情可能会成为一个非常令人头疼的问题。
将以下内容放入文件“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: *** missing separator. Stop
原因:因为虽然我个人使用的是正版制表符,但 Stack Overflow(试图提供帮助)会将我的制表符转换为多个空格。你,沮丧的互联网公民,现在复制这个,认为你现在拥有我使用的相同文本。 make 命令现在读取空格并发现“all”命令的格式不正确。所以复制上面的文本,粘贴它,然后将“@echo”之前的空格转换为制表符,这个例子最终应该对你有用。
在下面的示例中,我将 Makefile 文件夹路径存储到 LOCAL_PKG_DIR
,然后在目标中使用 LOCAL_PKG_DIR
变量。
生成文件:
LOCAL_PKG_DIR := $(shell eval pwd)
.PHONY: print
print:
@echo $(LOCAL_PKG_DIR)
终端输出:
$ make print
/home/amrit/folder
从制作手册
shell 赋值运算符 '!=' 可用于执行 shell 脚本并将 > 变量设置为其输出。该运算符首先计算右侧,然后将 > 该结果传递给 shell 以执行。如果执行结果以 >newline 结尾,则删除该换行符;所有其他换行符都被空格替换。然后将 >resulting 字符串放入命名的递归扩展变量中。例如: hash != printf '\043' file_list != find 。 -name '*.c'
$
的重要说明。shell echo whatever
替换为python -c 'import sys; print int(sys.version_info >= (2,5))'
。您会收到“意外标记附近的语法错误”。我不明白有人怎么认为这回答了这个问题。谁能解释我错过了什么?