我想要一个用于编译项目的makefile,如果缺少某些文件(二进制文件),则需要下载这些文件(因为使用git跟踪它们不是一个好主意)。
我有一个URL列表和一个给定名称的列表(并且我希望能够选择名称来保存它们,因为我无法控制URL名称,并且大多数名称很糟糕且没有描述性)。假设文件将始终相同,或者我们不在乎服务器中的文件是否更改(我们无需再次下载它们)。
所以我的想法是在我的makefile文件中写这样的东西:
files = one two three four
main : $(files)
command to do when files are present
但是我希望每个都可以下载(如果它们还不存在的话),并且每个都可以从其自己的URL下载,所以我需要对$(files)元素进行迭代之类的操作。
我将把混合python代码和我从makefiles知道的想法写下来。我知道这是垃圾,但是我不知道有什么其他方式可以做到(问题是基本上如何正确编写),而且我认为这很容易理解。
urls = url1 url2 url3 url4
for i in len(files):
$(files)[i] :
curl -Lo $(files)[i] $(urls)[i]
因此,问题是:我该怎么做?
我已经看了一段时间的make文档,没有多次写文件名就找不到正确的方法(我认为应该避免使用文件名,而应该使用变量)。也许罐装食谱就是这样,但我看不出怎么做。
有几种方法可以做您想要的。警告:它们有些棘手,因为它们使用GNU make的高级功能:
解决方案1:
files := one two three four
urls := url1 url2 url3 url4
main: $(files)
@echo 'command to do when files are present'
# $(1): file name
# $(2): url
define DOWNLOAD_rule
$(1):
@echo 'curl -Lo $(1) $(2)'
endef
$(foreach f,$(files),\
$(eval $(call DOWNLOAD_rule,$(f),$(firstword $(urls))))\
$(eval urls := $(wordlist 2,$(words $(urls)),$(urls)))\
)
我用回声代替了配方,以便于测试:
$ make -j
curl -Lo one url1
curl -Lo four url4
curl -Lo three url3
curl -Lo two url2
command to do when files are present
说明:
在DOWNLOAD_rule
多行变量是哪里下载操作模板规则$(1)
表示文件名和$(2)
代表相应的URL。
您可以执行$(1)
和$(2)
中的替换,并使用DOWNLOAD_rule
make实例化结果作为make语法:
$(eval $(call DOWNLOAD_rule,one,url1))
这就像您写的一样:
one:
@echo 'curl -Lo one url1'
该foreach
功能允许循环单词列表。所以:
$(foreach f,$(files),$(eval $(call DOWNLOAD_rule,$(f),url1)))
将实例化所有文件的规则...除了所有URL的URL相同(url1
)外。不是你想要的。
为了获得对应于每个文件的URL,我们可以eval
在foreach
循环中对函数进行两次不同的调用:
$(urls)
,$(urls)
然后将结果重新分配给urls
。请注意:=
分配,它们是必不可少的。默认值=
(递归扩展)在这里不起作用。
$(wordlist 2,$(words $(urls)),$(urls))
可能看起来很复杂,但事实并非如此:
$(wordlist s,e,l)
作为字的数目扩展s
到e
在列表l
(字被编号从1到的长度l
)$(words l)
扩展为列表中的单词数 l
所以,如果$(urls)
是,例如url2 url3 url4
:
$(words $(urls))
扩展为3$(wordlist 2,$(words $(urls)),$(urls))
扩展为,url3 url4
因为它与$(wordlist 2,3,url2 url3 url4)
解决方案2:
也可以eval
在DOWNLOAD_rule
变量中包装第二个变量,但是要考虑的另一个方面是:在传递给外壳之前,使用make扩展配方。针对目标的变量(url
)在分析的第一遍过程中得到了扩展,可以解决以下问题:
files := one two three four
urls := url1 url2 url3 url4
main: $(files)
@echo 'command to do when files are present'
# $(1): file name
define DOWNLOAD_rule
$(1): url := $$(firstword $$(urls))
$(1):
@echo 'curl -Lo $(1) $$(url)'
urls := $$(wordlist 2,$$(words $$(urls)),$$(urls))
endef
$(foreach f,$(files),$(eval $(call DOWNLOAD_rule,$(f))))
请注意,$$
在的定义中DOWNLOAD_rule
,它们是必需的,因为eval
扩展其参数并将make作为常规make语法分析结果时会再次对其进行扩展。这$$
是一种保护变量引用不受第一次扩展影响的方法,这样eval
在的四个迭代期间实例化的foreach
是:
one: url := $(firstword $(urls))
one:
@echo 'curl -Lo one $(url)'
urls := $(wordlist 2,$(words $(urls)),$(urls))
two: url := $(firstword $(urls))
two:
@echo 'curl -Lo two $(url)'
urls := $(wordlist 2,$(words $(urls)),$(urls))
three: url := $(firstword $(urls))
three:
@echo 'curl -Lo three $(url)'
urls := $(wordlist 2,$(words $(urls)),$(urls))
four: url := $(firstword $(urls))
four:
@echo 'curl -Lo four $(url)'
urls := $(wordlist 2,$(words $(urls)),$(urls))
它将完全满足我们的要求。如果没有,$$
它将是:
one: url := url1
one:
@echo 'curl -Lo one '
urls := url2 url3 url4
two: url := url1
two:
@echo 'curl -Lo two '
urls := url2 url3 url4
three: url := url1
three:
@echo 'curl -Lo three '
urls := url2 url3 url4
four: url := url1
four:
@echo 'curl -Lo four '
urls := url2 url3 url4
请记住这一点:使用时eval
有两个扩展,$$
通常需要使用它们来逃避第一个扩展。
解决方案3:
我们还可以为每个文件(<file>-url
)声明一个变量来存储URL,并为设置这些变量和文件列表(files
)声明一个宏:
# Set file's URL and update files' list
# Syntax: $(call set_url,<file>,<url>)
set_url = $(eval $(1)-url := $(2))$(eval files := $$(files) $(1))
$(call set_url,one,url1)
$(call set_url,two,url2)
$(call set_url,three,url3)
$(call set_url,four,url4)
main: $(files)
@echo 'command to do when files are present'
# $(1): file name
define DOWNLOAD_rule
$(1):
@echo 'curl -Lo $(1) $$($(1)-url)'
endef
$(foreach f,$(files),$(eval $(call DOWNLOAD_rule,$(f))))
解决方案4:
最后,我们可以使用John Graham-Cumming出色的GNU Make标准库(gmsl
)及其关联数组来完成与解决方案#3中相同的操作。例如,我们可以定义一个关联数组,该数组以文件名作为键,URL作为值:urls
include gmsl
$(call set,urls,one,url1)
$(call set,urls,two,url2)
$(call set,urls,three,url3)
$(call set,urls,four,url4)
files := $(call keys,urls)
main: $(files)
@echo 'command to do when files are present'
# $(1): file name
define DOWNLOAD_rule
$(1):
@echo 'curl -Lo $(1) $$(call get,urls,$(1))'
endef
$(foreach f,$(files),$(eval $(call DOWNLOAD_rule,$(f))))
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句