根据文件的存在时间在生成文件中运行不同的代码



我正在尝试为共享的、继承的基本 makefile 代码创建一个自动更新机制,我只希望它每天检查一次更新。在解决了我对 Makefiles 中ifeq如何工作的误解后,我认为我需要弄清楚的最后一件事是为什么我正在比较的值没有按预期运行。

我正在尝试使用文件.makefile-update-ts上次修改的时间戳来指示上次运行更新的时间,并使用find . -mtime +24h -name '.makefile-update-ts'来指示它是否足够旧。我没有收到任何语法错误,而且我从各种find命令中返回的值似乎是正确的,所以我不明白为什么,但我的逻辑不起作用......

因此,让我们从最基本的开始,然后逐步提高:

从一个漂亮的香草Makefile开始:

work:
ifeq (a,a)
@echo "A"
else
@echo "not A"
endif
$ make work
A

✅ 按预期工作

如果我毕业使用变量来存储"a"值:

thing=a
compare=a
work:
ifeq ($(thing),$(compare))
@echo "A"
else
@echo "not A"
endif
$ make work
A

✅ 按预期工作

因此,让我们切换到使用find来获取文件是否存在以及它是否>= 24 小时前的一些指示。

ts-filename=.makefile-update-ts
exists=`find . -name '$(ts-filename)'`
old=`find . -mtime +24h -name '$(ts-filename)'`
match=./$(ts-filename)
work:
@echo "$(match) (match)"
@echo "$(exists) (exists)"
@echo "$(old) (old)"
$touch .makefile-update-ts
$make work
./.makefile-update-ts (match)
./.makefile-update-ts (exists)
(old)

✅ 似乎find命令正在获取我想要的值,并且我能够将它们回显出来。由于我刚刚创建了文件,因此"旧"搜索找不到任何内容,这是意料之中的。

是时候毕业到最终剧本了...或者我是这么想的。在下面的 makefile 中,我添加了一些额外的echo来说明比较时的状态,当与执行的各种命令的输出一起时,这是没有意义的。

ts-filename=.makefile-update-ts
exists=`find . -name '$(ts-filename)'`
old=`find . -mtime +24h -name '$(ts-filename)'`
match=./$(ts-filename)
work: .check-for-update
@echo "working..."
.check-for-update:
# is the file > 24 hours old?
ifeq ($(old),$(match))
@echo checking for Makefile updates...
@make .update
else
@echo $(match) - "match"
@echo $(old) - "old"
# if the file doesn't exist at all yet, pull the update
ifeq ($(exists),$(match))
@echo "last update was recent, not updating..."
else
@echo $(exists) - "exists"
@echo Getting base Makefile...
@make .update
endif
endif
.update:
touch $(ts-filename)
$ make work
./.makefile-update-ts - match
- old
./.makefile-update-ts - exists
Getting base Makefile...
touch .makefile-update-ts
working...

❌ 为什么,如果文件存在,并且它不是"旧的"($(old)是空的),它会打印"获取基本 Makefile..."???我希望它改为打印,"上次更新是最近的,没有更新......">

问题又回到了最初的误解。 生成文件不是 shell 脚本。 考虑一下:

exists=`find . -name '$(ts-filename)'`
old=`find . -mtime +24h -name '$(ts-filename)'`

反引号是一个外壳功能。 Make对他们一无所知。 您在此处声明了两个变量,其中包含包含反引号和命令的文本字符串,而不是像在 shell 中那样运行这些命令的输出

所以为此:

ifeq ($(old),$(match))

它扩展以比较文本字符串

ifeq (`find . -mtime +24h -name '$(ts-filename)'`,./.makefile-update-ts)

显然,这些字符串是不相等的。

如果你想运行一个 shell 脚本并将其输出分配给 make 变量,你必须使用 make 的shell函数,如下所示:

exists := $(shell find . -name '$(ts-filename)')
old := $(shell find . -mtime +24h -name '$(ts-filename)')

这里有几点:

我使用:=而不是=来提高效率。

我强烈建议您在处理make变量时在赋值运算符周围使用空格,而不是像使用 shell变量那样将它们一起运行而没有空格。 也许这种认知失调将有助于加强这种差异。 另外,它只是更好。

哦,你可能会问,为什么你的第三个例子有效? 很简单,这是因为您将文字字符串传递给shell,而shell正在为您进行扩展。 如果您删除 shell 脚本行上的@前缀,则可以轻松看到这一点。 另一个提示是,永远不要向任何makefile配方添加@,直到您100%确定它运行良好(即使这样,您也可以考虑不这样做)。

如果您删除它们,您将看到:

work:
echo "$(old) (old)"

这将打印:

echo "`find . -mtime +24h -name '$(ts-filename)'` `find . -mtime +24h -name '$(ts-filename)'`"
./.makefile-update-ts ./.makefile-update-ts

而不是您所期望的 make 变量包含已扩展的内容,这将是:

echo "./.makefile-update-ts ./.makefile-update-ts"
./.makefile-update-ts ./.makefile-update-ts

另一个提示是,当您尝试在 shell 脚本中使用MAKE变量时,您应该始终(除非该值可能包含单引号)单引号,以便 shell 不会弄乱值:

work:
echo '$(old) (old)'

将打印:

echo '`find . -mtime +24h -name '$(ts-filename)'` `find . -mtime +24h -name '$(ts-filename)'`'
`find . -mtime +24h -name '$(ts-filename)'` `find . -mtime +24h -name '$(ts-filename)'`

最新更新