jos*_*444 6 makefile downloadfile
我想有一个用于编译项目的makefile,如果它们丢失,我需要下载一些文件(二进制文件),因为用git跟踪它们不是一个好主意.
我有一个URL列表和一个给定名称的列表(我希望能够选择名称来保存它们,因为我无法控制URL名称,而且大多数名称非常糟糕且不具描述性).假设文件将始终相同或者我们不关心它们是否在服务器中更改(我们不需要再次下载它们).
所以我的想法是在我的makefile中写这样的东西:
files = one two three four
main : $(files)
command to do when files are present
Run Code Online (Sandbox Code Playgroud)
但我希望每个人都可以下载,如果它们不存在,并且每个都来自它自己的URL,所以我需要像$(文件)元素的迭代.
我会在脑海里写下我想在混合python代码和makefile中知道的想法.我知道这是垃圾,但我不知道其他任何方式(问题是如何写好,基本上),我认为这很容易理解.
urls = url1 url2 url3 url4
for i in len(files):
$(files)[i] :
curl -Lo $(files)[i] $(urls)[i]
Run Code Online (Sandbox Code Playgroud)
因此问题是:我该怎么做?
我一直在查看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)))\
)
Run Code Online (Sandbox Code Playgroud)
为了更容易测试,我用 echo 替换了食谱:
$ 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
Run Code Online (Sandbox Code Playgroud)
说明:
DOWNLOAD_rule
多行变量是下载操作的模板规则,其中代表$(1)
文件名,$(2)
代表对应的URL。
$(1)
您可以执行和$(2)
in的替换DOWNLOAD_rule
,并将结果实例化为 make 语法:
$(eval $(call DOWNLOAD_rule,one,url1))
Run Code Online (Sandbox Code Playgroud)
这和你写的一样:
one:
@echo 'curl -Lo one url1'
Run Code Online (Sandbox Code Playgroud)
该foreach
函数允许循环单词列表。所以:
$(foreach f,$(files),$(eval $(call DOWNLOAD_rule,$(f),url1)))
Run Code Online (Sandbox Code Playgroud)
将为所有文件实例化规则...除了url1
所有文件的 URL 都是相同的 ( ) 之外。不是你想要的。
为了获取每个文件对应的 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
,但还有另一个方面需要考虑:配方在传递到 shell 之前由 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))))
Run Code Online (Sandbox Code Playgroud)
请注意 的$$
定义中的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))
Run Code Online (Sandbox Code Playgroud)
这将完全符合我们的要求。如果没有它,$$
它将是:
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
Run Code Online (Sandbox Code Playgroud)
请记住这一点:使用时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))))
Run Code Online (Sandbox Code Playgroud)
解决方案#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))))
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
417 次 |
最近记录: |