Wie kann ich Dateien von HTTP als Voraussetzungen in GNU make verwenden?

10

Ich möchte Dateien aus dem World Wide Web als Voraussetzungen für meine Makefiles verwenden:

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

Ich möchte nur "transmogrifizieren", wenn die entfernte Datei neuer als die lokale Datei ist, genau wie make normalerweise funktioniert.

Ich möchte keine zwischengespeicherte Kopie von example.gz behalten - die Dateien sind groß und ich benötige keine Rohdaten. Am liebsten möchte ich das Herunterladen der Datei überhaupt vermeiden. Ziel ist es, einige davon mit dem -jmake-Flag parallel zu verarbeiten .

Was ist ein sauberer Weg, um dies zu lösen? Ich kann mir ein paar Wege vorstellen:

  • Bewahren Sie eine leere Dummy-Datei auf, die bei jeder Neuerstellung des Ziels aktualisiert wird
  • Einige Plugins, die das neue Plugin-System von GNU verwenden (von dem ich nichts weiß)
  • Eine make-agnostische Methode, mit der HTTP-Server im lokalen Dateisystem bereitgestellt werden

Bevor ich weiter grabe, möchte ich einige Ratschläge, vorzugsweise spezifische Beispiele!

Rohr
quelle

Antworten:

15

Versuchen Sie so etwas in Ihrem 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

(Hinweis: Dies ist ein Makefile, daher sind die Einrückungen Tabulatoren und keine Leerzeichen. Es ist auch wichtig, dass nach \den Fortsetzungszeilen keine Leerzeichen mehr vorhanden sind. Alternativ können Sie die Backslash-Escapezeichen entfernen und es lang machen. fast unlesbare Zeile)

Dieses GNU- makeRezept überprüft zunächst, ob eine aufgerufene Datei example.gzvorhanden ist (da wir sie mit -zin verwenden werden curl), und erstellt sie mit, touchwenn dies nicht der Fall ist. Die Berührung erstellt es mit einem Zeitstempel von 00:00 (12 Uhr des aktuellen Tages).

Dann verwendet es curl‚s -z( --time-cond) Option , um nur herunterladen , example.gzwenn es seit dem letzten Mal geändert wurde es heruntergeladen wurde. -zkann ein tatsächlicher Datumsausdruck oder ein Dateiname angegeben werden. Wenn ein Dateiname angegeben wird, wird die Änderungszeit der Datei als Zeitbedingung verwendet.

Wenn local.datdies nicht der Fall ist, wird es anschließend mit toucheinem Zeitstempel erstellt, der garantiert älter als der von ist example.gz. Dies ist erforderlich, da local.datder nächste Befehl vorhanden sein muss stat, um seinen Zeitstempel mtime abzurufen.

Wenn dann example.gzhat einen Zeitstempel neuer ist als local.dates Leitungen example.gzin transmogrifyund leitet die Ausgabe zu local.dat.

Schließlich erledigt es die Buchhaltung und Bereinigung:

  • es wird abgeschnitten example.gz(weil Sie nur einen Zeitstempel und nicht die gesamte Datei behalten müssen)
  • touches example.gzso, dass es den gleichen Zeitstempel wie hatlocal.dat

Das .PHONY-Ziel stellt sicher, dass das local.datZiel immer ausgeführt wird, auch wenn die Datei mit diesem Namen bereits vorhanden ist.

Vielen Dank an @Toby Speight für den Hinweis in den Kommentaren, dass meine Originalversion nicht funktionieren würde und warum.

Alternativ, wenn Sie die Datei direkt transmogrifyweiterleiten möchten, ohne sie zuerst in das Dateisystem herunterzuladen:

.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

HINWEIS: Dies ist größtenteils ungetestet und erfordert möglicherweise einige geringfügige Änderungen, um die Syntax genau richtig zu machen. Das Wichtigste dabei ist die Methode, keine Copy-Paste-Cargo-Kult-Lösung.

Ich verwende seit Jahrzehnten Variationen dieser Methode (dh toucheine Zeitstempeldatei) mit make. Es funktioniert und ermöglicht mir normalerweise zu vermeiden, dass ich meinen eigenen Code für die Auflösung von Abhängigkeiten in sh schreiben muss (obwohl ich stat --printf %Yhier etwas Ähnliches tun musste ).

Jeder weiß, dass makees ein großartiges Tool zum Kompilieren von Software ist ... IMO ist es auch ein sehr unterbewertetes Tool für Systemadministrations- und Skriptaufgaben.

cas
quelle
1
Das -zFlag setzt natürlich voraus, dass der Remote-Server If-Modified-SinceHeader verwendet. Dies muss nicht unbedingt der Fall sein. Abhängig von der Serverkonfiguration müssen Sie möglicherweise stattdessen etwas tun ETag, indem Sie Cache-ControlHeader überprüfen oder eine separate Prüfsummendatei überprüfen (z. B. wenn der Server eine bereitstellt sha1sum).
Bob
ja tut es. Aber ohne das gibt es überhaupt keine Möglichkeit, das zu tun, was das OP will (es sei denn, er ist bereit, die riesige Datei jedes Mal in eine temporäre Datei herunterzuladen, wenn er alte und neue Dateien ausführt make, verwendet cmpoder etwas anderes, und mv newfile oldfilewenn sie unterschiedlich sind). . Übrigens sagen Cache-Control-Header nicht, ob die Datei neuer als eine bestimmte Zeit ist. Sie geben an, wie lange die Serveradministratoren möchten, dass Sie eine bestimmte Datei zwischenspeichern - und werden häufig von Marketing-Droiden als Cache-Busting-Methode verwendet, um ihre Webstatistiken zu "verbessern".
Cas
ETag ist eine andere Möglichkeit, ebenso wie eine separate Prüfsummendatei. Es hängt alles davon ab, wie der Server eingerichtet ist. Beispielsweise kann man cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA1SUMS abrufen und prüfen, ob es sich geändert hat, bevor man sich entscheidet, die vollständige ISO abzurufen. ETag macht dasselbe, indem es einen Header anstelle einer separaten Datei verwendet (und sich darauf If-Modified-Sinceverlässt, dass der HTTP-Server ihn implementiert). Cache-ControlDies wäre ein letzter Ausweg, wenn die Datei nicht heruntergeladen würde, wenn keine anderen Methoden unterstützt würden. Dies ist sicherlich die ungenaueste Option, da sie versucht, die Zukunft vorherzusagen.
Bob
Wahrscheinlich sind ETag/ If-None-Matchund andere Prüfsummen zuverlässiger als If-Modified-Sinceauch. In jedem Fall versuchen diese Kommentare nur, die Annahmen der Antwort darzulegen (nämlich die -zServerunterstützung) - die grundlegende Methode sollte relativ einfach an andere Änderungsprüfungsalgorithmen anzupassen sein.
Bob
1
Sie können gerne eine Antwort schreiben, in der eine auf ETag basierende Lösung implementiert wird. Wenn es etwas Gutes ist, werde ich es positiv bewerten. und dann wird jemand kommen und darauf hinweisen, dass nicht alle Webserver einen Etag-Header bereitstellen :).
Cas
1

Eine andere Alternative besteht darin, ein Build-System zu verwenden, das Abhängigkeitsprüfsummen verwendet, um zu bestimmen, ob Neuerstellungen ausgelöst werden sollen. Ich habe den "Touch" -Trick mit Gnu Make viel verwendet, aber es ist viel einfacher, wenn Sie dynamische Abhängigkeiten angeben können und wenn Dateien, die sich nicht ändern, keine Neuerstellungen auslösen. Hier ist ein Beispiel mit 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
quelle
Stattdessen -X HEADempfiehlt die Manpage von curl die Verwendung von -I: "(-X) ändert nur das tatsächlich in der HTTP-Anforderung verwendete Wort, ändert jedoch nicht das Verhalten von curl. Wenn Sie beispielsweise eine ordnungsgemäße HEAD-Anforderung mit -X HEAD erstellen möchten wird nicht ausreichen. Sie müssen die Option -I, - head verwenden. "
LightStruk