Bagaimana saya bisa menggunakan file dari HTTP sebagai prasyarat dalam pembuatan GNU?

10

Saya ingin menggunakan file dari World Wide Web sebagai prasyarat di makefile saya:

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

Saya hanya ingin "transmogrify" jika file jarak jauh lebih baru daripada file lokal, sama seperti make yang biasanya beroperasi.

Saya tidak ingin menyimpan salinan yang di-cache contoh.gz - file berukuran besar, dan saya tidak memerlukan data mentah. Lebih disukai saya ingin menghindari mengunduh file sama sekali. Tujuannya adalah untuk memproses beberapa dari ini secara paralel menggunakan -jflag make.

Apa cara bersih untuk menyelesaikan ini? Saya dapat memikirkan beberapa cara untuk pergi:

  • Simpan file dummy kosong yang disembunyikan, diperbarui setiap kali target dibuat kembali
  • Beberapa plugin menggunakan sistem plugin baru GNU make (yang saya tidak tahu tentang)
  • Cara make-agnostik yang me-mount server HTTP di sistem file lokal

Sebelum menggali lebih jauh, saya ingin beberapa saran, lebih disukai contoh spesifik!

pipa
sumber

Jawaban:

15

Coba sesuatu seperti ini di Makefile Anda:

.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

(catatan: ini adalah Makefile, jadi indentasi adalah tab, bukan spasi. Tentu saja juga penting bahwa tidak ada spasi setelah \pada garis kelanjutan - atau singkirkan dari garis miring terbalik dan buat satu panjang, baris yang hampir tidak dapat dibaca)

makeResep GNU ini pertama-tama memeriksa apakah suatu file yang dipanggil example.gzada (karena kita akan menggunakannya dengan -zin curl), dan membuatnya dengan touchjika tidak. Sentuhan menciptakannya dengan cap waktu 00:00 (12:00 hari ini).

Maka menggunakan curl's -z( --time-cond) pilihan untuk hanya men-download example.gzjika telah dimodifikasi sejak terakhir kali itu didownload. -zdapat diberikan ekspresi tanggal aktual, atau nama file. Jika diberi nama file, itu akan menggunakan waktu modifikasi file sebagai kondisi waktu.

Setelah itu, jika local.dattidak ada, ia membuatnya dengan touchmenggunakan cap waktu yang dijamin lebih tua dari itu example.gz. Ini diperlukan karena local.datharus ada untuk perintah berikutnya yang digunakan statuntuk mendapatkan cap waktu mtime-nya.

Kemudian, jika example.gzmemiliki timestamp yang lebih baru dari local.dat, itu menyalurkan example.gzke transmogrifydan mengarahkan output ke local.dat.

Akhirnya, ia melakukan pembukuan & pembersihan:

  • itu memotong example.gz(karena Anda hanya perlu menyimpan stempel waktu, dan bukan seluruh file)
  • touches example.gzsehingga memiliki cap waktu yang sama sepertilocal.dat

Target .PHONY memastikan bahwa local.dattarget selalu dijalankan, bahkan jika file dari nama itu sudah ada.

Terima kasih kepada @Toby Speight karena menunjukkan di komentar bahwa versi asli saya tidak akan berfungsi, dan mengapa.

Sebagai alternatif, jika Anda ingin menyalurkan file secara langsung transmogrifytanpa mengunduhnya ke sistem file:

.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

CATATAN: ini sebagian besar belum diuji sehingga mungkin memerlukan beberapa perubahan kecil untuk mendapatkan sintaks yang tepat. Yang penting di sini adalah metode, bukan solusi copy-paste kargo-kultus.

Saya telah menggunakan variasi dari metode ini (yaitu touchfileing timestamp) dengan makeselama beberapa dekade. Ini berfungsi, dan biasanya memungkinkan saya untuk menghindari harus menulis kode resolusi ketergantungan saya sendiri di sh (walaupun saya harus melakukan sesuatu yang mirip dengan di stat --printf %Ysini).

Semua orang tahu makeadalah alat yang hebat untuk mengkompilasi perangkat lunak ... IMO itu juga alat yang sangat rendah untuk admin sistem dan tugas skrip.

cas
sumber
1
The -zbendera, tentu saja, mengasumsikan bahwa server remote menggunakan If-Modified-Sinceheader. Ini mungkin belum tentu demikian. Bergantung pada pengaturan server, Anda mungkin perlu melakukan sesuatu dengan ETag, atau dengan memeriksa Cache-Controlheader, atau dengan memeriksa file checksum yang terpisah (misalnya jika server menyediakan a sha1sum).
Bob
ya, benar. tetapi tanpa itu, sama sekali tidak mungkin melakukan apa yang diinginkan OP (kecuali dia mau mengunduh file besar ke file temp setiap kali dia menjalankan make, menggunakan cmpatau sesuatu untuk membandingkan file lama dan baru, dan mv newfile oldfilejika mereka berbeda) . BTW, header-kontrol cache tidak memberi tahu Anda jika file lebih baru dari waktu yang ditentukan. mereka memberi tahu Anda berapa lama admin server ingin Anda membuat cache file yang diberikan untuk - dan sering digunakan oleh droid pemasaran sebagai praktik penghilang cache untuk "meningkatkan" statistik web mereka.
cas
ETag adalah cara lain untuk melakukannya, seperti halnya file checksum yang terpisah. Itu semua tergantung pada bagaimana server diatur. Sebagai contoh, seseorang dapat mengambil cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA1SUMS dan memeriksa apakah sudah berubah sebelum memutuskan untuk mengambil ISO penuh. ETag melakukan hal yang sama, menggunakan header bukan file terpisah (dan, seperti If-Modified-Since, bergantung pada server HTTP yang mengimplementasikannya). Cache-Controlakan menjadi pilihan terakhir untuk mengunduh file jika tidak ada metode lain yang didukung - itu pasti yang paling tidak akurat karena mencoba untuk memprediksi masa depan.
Bob
Boleh dibilang, ETag/ If-None-Matchdan checksum lainnya lebih dapat diandalkan daripada If-Modified-Sincejuga. Bagaimanapun, komentar ini hanya mencoba untuk meletakkan asumsi jawaban (yaitu, yang -zmengasumsikan dukungan server) - metode dasar harus cukup mudah untuk beradaptasi dengan algoritma pemeriksaan perubahan lainnya.
Bob
1
jangan ragu untuk menulis jawaban yang mengimplementasikan solusi berdasarkan ETag. Jika ada gunanya, saya akan membatalkannya. dan kemudian seseorang akan datang dan menunjukkan bahwa tidak semua server web menyediakan header Etag :).
cas
1

Alternatif lain adalah menggunakan sistem pembangunan yang menggunakan checksum dependensi untuk menentukan apakah akan memicu pembangunan kembali. Saya telah menggunakan trik "sentuh" ​​dengan Gnu Make a lot, tetapi jauh lebih mudah ketika Anda dapat menentukan dependensi dinamis dan ketika file yang tidak berubah tidak memicu pembangunan kembali. Berikut ini contoh menggunakan 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
pengguna5484700
sumber
Alih-alih -X HEAD, manual curl merekomendasikan menggunakan -I: "(-X) hanya mengubah kata aktual yang digunakan dalam permintaan HTTP, itu tidak mengubah cara curl berperilaku. Jadi misalnya jika Anda ingin membuat permintaan HEAD yang tepat, menggunakan -X HEAD tidak akan cukup. Anda perlu menggunakan opsi -I, - head. "
LightStruk