Comment puis-je utiliser des fichiers HTTP comme prérequis dans GNU make?

10

Je veux utiliser des fichiers du World Wide Web comme prérequis dans mes makefiles:

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

Je veux seulement «transmogrifier» si le fichier distant est plus récent que le fichier local, tout comme make fonctionne normalement.

Je ne pas vouloir garder une copie en cache de example.gz - les fichiers sont volumineux, et je ne ai pas besoin des données brutes. De préférence, je voudrais éviter de télécharger le fichier du tout. L'objectif est d'en traiter quelques-unes en parallèle en utilisant l' -jindicateur make.

Quelle est la meilleure façon de résoudre ce problème? Je peux penser à quelques façons de procéder:

  • Gardez un fichier factice vide caché, mis à jour chaque fois que la cible est recréée
  • Certains plugins utilisant le nouveau système de plugins de GNU make (dont je ne sais rien)
  • Un moyen de faire de l'agnostic qui monte des serveurs HTTP dans le système de fichiers local

Avant d'aller plus loin, j'aimerais avoir quelques conseils, de préférence des exemples précis!

tuyau
la source

Réponses:

15

Essayez quelque chose comme ça dans votre 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

(Remarque: il s'agit d'un Makefile, donc les retraits sont des tabulations, pas des espaces. Bien sûr. Il est également important qu'il n'y ait pas d'espaces après les \lignes de continuation - vous pouvez également vous débarrasser des échappements antislash et en faire un long, ligne presque illisible)

Cette makerecette GNU vérifie d'abord qu'un fichier appelé example.gzexiste (parce que nous allons l'utiliser avec -zin curl), et le crée avec touchsi ce n'est pas le cas. Le toucher le crée avec un horodatage de 00h00 (12h00 du jour actuel).

Ensuite, il utilise curll' option 's -z( --time-cond) pour télécharger uniquementexample.gz s'il a été modifié depuis le dernier téléchargement. -zpeut recevoir une expression de date réelle ou un nom de fichier. Si on lui donne un nom de fichier, il utilisera l'heure de modification du fichier comme condition temporelle.

Après cela, s'il local.datn'existe pas, il le crée avec touch, en utilisant un horodatage garanti comme étant plus ancien que celui de example.gz. Ceci est nécessaire car local.datdoit exister pour que la prochaine commande puisse être utiliséestat pour obtenir son horodatage mtime.

Ensuite, si example.gza un horodatage plus récent que local.dat, il les tuyaux example.gzdans transmogrifyet réoriente la sortielocal.dat .

Enfin, il fait la comptabilité et le nettoyage:

  • il tronque example.gz(car il vous suffit de garder un horodatage, et non tout le fichier)
  • touches example.gzpour qu'il ait le même horodatage quelocal.dat

La cible .PHONY garantit que la local.datcible est toujours exécutée, même si le fichier de ce nom existe déjà.

Merci à @Toby Speight d'avoir souligné dans les commentaires que ma version originale ne fonctionnerait pas, et pourquoi.

Alternativement, si vous souhaitez diriger le fichier directement transmogrifysans le télécharger d'abord dans le système de fichiers:

.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

REMARQUE: ceci n'est généralement pas testé et peut nécessiter quelques modifications mineures pour obtenir la syntaxe exactement correcte. L'important ici est la méthode, pas une solution culte du copier-coller.

J'utilise des variantes de cette méthode (c'est-à-dire touchun fichier d'horodatage) avec makedepuis des décennies. Cela fonctionne et me permet généralement d'éviter d'avoir à écrire mon propre code de résolution de dépendance dans sh (même si j'ai dû faire quelque chose de similaire avecstat --printf %Y ici).

Tout le monde sait que makec'est un excellent outil pour compiler des logiciels ... IMO c'est aussi un outil très sous-évalué pour les tâches d'administration système et de script.

cas
la source
1
Le -zdrapeau, bien sûr, suppose que le serveur distant utilise des en- If-Modified-Sincetêtes. Ce n'est pas nécessairement le cas. Selon la configuration du serveur, vous devrez peut-être plutôt faire quelque chose avec ETag, ou en vérifiant les en- Cache-Controltêtes, ou en vérifiant un fichier de somme de contrôle distinct (par exemple, si le serveur fournit un sha1sum).
Bob
Oui. mais sans cela, il n'y a aucun moyen de faire ce que l'OP veut (à moins qu'il ne veuille télécharger l'énorme fichier dans un fichier temporaire à chaque fois qu'il s'exécute make, utilise cmpou quelque chose pour comparer les anciens et les nouveaux fichiers, et mv newfile oldfiles'ils sont différents) . BTW, les en-têtes de contrôle du cache ne vous disent pas si le fichier est plus récent qu'une heure donnée. ils vous indiquent pendant combien de temps les administrateurs du serveur veulent que vous mettiez en cache un fichier donné - et sont souvent utilisés par les droïdes marketing comme une pratique de contournement du cache pour "améliorer" leurs statistiques Web.
cas
ETag est une autre façon de le faire, tout comme un fichier de somme de contrôle distinct. Tout dépend de la configuration du serveur. Par exemple, on peut récupérer cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA1SUMS et vérifier s'il a changé avant de décider de récupérer l'ISO complet. ETag fait la même chose, en utilisant un en-tête au lieu d'un fichier séparé (et, comme If-Modified-Since, s'appuie sur le serveur HTTP qui l'implémente). Cache-Controlserait une option de dernier recours avant de télécharger le fichier si aucune autre méthode n'est prise en charge - c'est certainement la moins précise car elle essaie de prédire l'avenir.
Bob
Sans doute, et ETag/ If-None-Matchet d'autres sommes de contrôle sont plus fiables que If-Modified-Since, aussi. Dans tous les cas, ces commentaires tentent simplement de présenter les hypothèses de la réponse (à savoir, qui -zsuppose la prise en charge du serveur) - la méthode de base devrait être assez facile à adapter à d'autres algorithmes de vérification des modifications.
Bob
1
n'hésitez pas à rédiger une réponse mettant en œuvre une solution basée sur ETag. Si c'est bon, je vais le voter. puis quelqu'un viendra et soulignera que tous les serveurs Web ne fournissent pas un en-tête Etag :).
cas
1

Une autre alternative consiste à utiliser un système de génération qui utilise des sommes de contrôle de dépendance pour déterminer s'il faut déclencher des reconstructions. J'ai beaucoup utilisé le "toucher" avec Gnu Make, mais c'est beaucoup plus simple lorsque vous pouvez spécifier des dépendances dynamiques et que les fichiers qui ne changent pas ne déclenchent pas de reconstructions. Voici un exemple d'utilisation de 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
la source
Au lieu de -X HEAD, la page de manuel de curl recommande d'utiliser -I: "(-X) ne modifie que le mot réel utilisé dans la requête HTTP, il ne modifie pas le comportement de curl. Par exemple, si vous souhaitez effectuer une requête HEAD appropriée, utilisez -X HEAD ne suffira pas. Vous devez utiliser l'option -I, - head. "
LightStruk