如何使用来自HTTP的文件作为GNU make中的先决条件?


10

我想将万维网上的文件用作我的makefile中的先决条件:

local.dat: http://example.org/example.gz
    curl -s $< | gzip -d | transmogrify >$@

我只想在远程文件比本地文件更新的情况下“迁移”,就像make正常运行一样。

希望保留的高速缓存副本example.gz -文件都很大,而且我不需要原始数据。最好我完全避免下载文件。目的是使用-jmake标志并行处理其中的一些。

有什么干净的方法可以解决这个问题?我可以想到一些方法:

  • 保留一个空的虚拟文件,每次重新创建目标时更新
  • 一些使用GNU make的新插件系统插件(我对此一无所知)
  • 不可知的方式将HTTP服务器安装在本地文件系统中

在深入探讨之前,我想提出一些建议,最好是具体的例子!

Answers:


15

在您的Makefile中尝试以下操作:

.PHONY: local.dat

local.dat:
    [ -e example.gz ] || touch -d '00:00' example.gz
    curl -z example.gz -s http://example.org/example.gz -o example.gz
    [ -e $@ ] || touch -d 'yesterday 00:00' $@
    if [     "$(shell stat --printf '%Y' example.gz)" \
         -gt "$(shell stat --printf '%Y' $@)"         ] ; then \
      zcat example.gz | transmogrify >$@ ; \
    fi
    truncate -s 0 example.gz
    touch -r $@ example.gz

(请注意:这是一个Makefile,因此缩进是制表符,而不是空格。当然\,在续行上的空格后也没有空格是很重要的-否则,请删除反斜杠转义符并将其变长,几乎不可读的行)

这个GNU make配方首先检查一个名为example.gz存在的文件(因为我们将-z在in中使用它curl),并touch在不存在的情况下创建它。触摸会以00:00(当天的凌晨12点)的时间戳创建它。

然后,它使用curl-z--time-cond)选项仅example.gz在自上次下载以来对其进行了修改的情况下进行下载。 -z可以给出实际的日期表达式或文件名。如果给定文件名,它将使用文件的修改时间作为时间条件。

之后,如果local.dat不存在,则使用touch,确保使用于的时间戳创建它example.gz。这是必需的,因为local.dat必须存在下stat一条命令才能使用它的mtime时间戳。

然后,如果example.gz时间戳晚于local.dat,它将通过管道传递example.gztransmogrify并将输出重定向到local.dat

最后,它执行簿记和清理工作:

  • 它会截断example.gz(因为您只需要保留一个时间戳,而不是整个文件)
  • touches,example.gz以便它具有与local.dat

.PHONY目标可确保local.dat始终执行该目标,即使该名称的文件已经存在也是如此。

感谢@Toby Speight在评论中指出我的原始版本不起作用以及原因。

或者,如果您想直接将文件传输到其中transmogrify而无需先将其下载到文件系统中:

.PHONY: local.dat

local.dat:
    [ -e example.gz ] || touch -d '00:00' example.gz
    [ -e $@ ] || touch -d 'yesterday 00:00' $@
    if [     "$(shell stat --printf '%Y' example.gz)" \
         -gt "$(shell stat --printf '%Y' $@)"         ] ; then \
      curl -z example.gz -s http://example.org/example.gz | transmogrify >$@ ; \
    fi
    touch -r $@ example.gz

注意:这大部分未经测试,因此可能需要进行一些较小的更改才能使语法完全正确。这里重要的是方法,而不是复制粘贴的简单方法。

数十年来,我一直在使用这种方法的变体(即-添加touch时间戳文件)make。它可以正常工作,通常可以避免使用sh编写自己的依赖关系解析代码(尽管我必须在stat --printf %Y此处执行类似的操作)。

众所周知,这make是一个很棒的软件编译工具... IMO,它对于系统管理和脚本编写任务也是一个被低估的工具。


1
-z当然,该标志假定远程服务器使用If-Modified-Since标头。不一定是这种情况。根据服务器的设置,您可能需要使用ETag或通过检查Cache-Control标头或通过检查单独的校验和文件(例如,如果服务器提供sha1sum)来执行某些操作。
鲍勃

是的,它确实。但是如果没有它,就根本无法执行OP想要的操作(除非他每次运行make,使用cmp或比较旧文件和新文件时,mv newfile oldfile如果他不愿意,他愿意将大文件下载到临时文件中) 。顺便说一句,缓存控制标头不会告诉您文件是否比给定时间新。它们告诉您服务器管理员希望您为给定文件缓存多长时间-营销机器人通常将其用作消除缓存的做法,以“改善”他们的网络统计信息。
cas

ETag 这是另一种实现方法,还有一个单独的校验和文件。这完全取决于服务器的设置方式。例如,可能要获取cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA1SUMS,并在决定获取完整的ISO之前检查它是否已更改。ETag使用标头而不是单独的文件来做同样的事情(并且,像一样If-Modified-Since,依赖于实现它的HTTP服务器)。Cache-Control如果不支持其他方法,则将是最后一个无法下载文件的选项-它肯定是最不准确的方法,因为它试图预测未来。
鲍勃

可以说,ETag/ If-None-Match和其他校验和也比可靠If-Modified-Since。无论如何,这些注释只是试图列出答案的假设(即,-z假设服务器支持)-基本方法应相当容易适应其他变更检查算法。
鲍勃

1
随时写一个答案来实现基于ETag的解决方案。如果有什么好处,我会投票赞成。然后有人会指出并非所有的Web服务器都提供Etag标头:)。
cas

1

另一种选择是使用使用依赖项校验和确定是否触发重建的构建系统。我已经在Gnu Make中使用了“触摸”技巧,但是当您可以指定动态依赖项并且当不变的文件不触发重建时,它要简单得多。这是使用GoodMake的示例:

#! /usr/local/goodmake.py /bin/sh -se

#! *.date
    # Get the last-modified date
    curl -s -v -X HEAD http://${1%.date} 2>&1 | grep -i '^< Last-Modified:' >$1

#? local.dat
    site=http://example.org/example.gz
    $0 $site.date
    curl -s $site | gzip -d | transmogrify >$1

相反的-X HEAD,卷曲的手册页建议使用-I:“(X)只改变在HTTP请求中使用的实际词,它不会改变的方式卷曲的行为因此,举例来说,如果你想使一个适当的HEAD请求,使用-X HEAD。还不够。您需要使用-I,-head选项。”
LightStruk
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.