Как я могу использовать файлы из HTTP в качестве предварительных условий в GNU make?

10

Я хочу использовать файлы из World Wide Web в качестве предварительных условий в моих make-файлах:

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

Я хочу «преобразовать», только если удаленный файл новее, чем локальный файл, как обычно работает make .

Я не хочу хранить кэшированную копию example.gz - файлы большие, и мне не нужны необработанные данные. Желательно, чтобы я вообще не хотел скачивать файл. Цель состоит в том, чтобы обработать несколько из них параллельно, используя -jфлаг make.

Как правильно решить эту проблему? Я могу придумать несколько способов пойти:

  • Храните пустой фиктивный файл в тайне, обновляя его каждый раз при воссоздании цели
  • Некоторые плагины, использующие новую систему плагинов GNU make (о которой я ничего не знаю)
  • Способ, независимый от make, который монтирует HTTP-серверы в локальной файловой системе

Прежде чем копать дальше, я хотел бы получить несколько советов, желательно конкретные примеры!

труба
источник

Ответы:

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, поэтому отступы - это табуляции, а не пробелы. Конечно. Также важно, чтобы после \строк продолжения не было пробелов - альтернативно избавьтесь от escape-косых черт и сделайте его одним длинным, почти нечитаемая строка)

Этот makeрецепт GNU сначала проверяет, существует ли файл с именем example.gz(потому что мы собираемся использовать его с -zin curl), и создает его с помощью, touchесли это не так. Прикосновение создает его с отметкой времени 00:00 (12:00 текущего дня).

Затем он использует опцию curls -z( --time-cond) для загрузки, только example.gzесли он был изменен с момента последней загрузки. -zможет быть дано фактическое выражение даты или имя файла. Если задано имя файла, оно будет использовать время модификации файла в качестве условия времени.

После этого, если local.datего не существует, он создает его с touchиспользованием временной метки, которая гарантированно будет старше, чем у example.gz. Это необходимо, потому local.datчто должна существовать следующая команда, чтобы использовать statее метку времени mtime.

Затем, если example.gzесть отметка времени новее local.dat, это трубы example.gzв transmogrifyи перенаправляет вывод local.dat.

И, наконец, он занимается бухгалтерией и уборкой:

  • он усекается example.gz(потому что вам нужна только временная метка, а не весь файл)
  • touchЕсли 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

ПРИМЕЧАНИЕ: это в основном не проверено, поэтому могут потребоваться некоторые незначительные изменения, чтобы получить правильный синтаксис. Здесь важен метод, а не решение копи-пасты Cargo-Cult.

Я использую варианты этого метода (например, touchфайл временной метки) в makeтечение десятилетий. Это работает, и, как правило, позволяет мне избежать написания собственного кода разрешения зависимостей в sh (хотя я должен был сделать что-то подобное stat --printf %Yздесь).

Все знают, makeчто это отличный инструмент для компиляции программного обеспечения ... IMO, он также очень недооцененный инструмент для задач системного администратора и сценариев.

саз
источник
1
-zФлаг, конечно, предполагает , что удаленный сервер использует If-Modified-Sinceзаголовки. Это не обязательно так. В зависимости от настроек сервера, вам может потребоваться что-то сделать ETag, либо путем проверки Cache-Controlзаголовков, либо путем проверки отдельного файла контрольной суммы (например, если сервер предоставляет a 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. Если это будет хорошо, я буду голосовать. и тогда кто-нибудь придет и укажет, что не все веб-серверы предоставляют заголовок 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
user5484700
источник
Вместо этого -X HEAD, man-страница curl рекомендует использовать -I: "(-X) изменяет только фактическое слово, используемое в HTTP-запросе, но не изменяет поведение curl. Так, например, если вы хотите сделать правильный запрос HEAD, используя -X HEAD не будет достаточно. Вам нужно использовать опцию -I, - head. "
LightStruk