
Make lain?
Banyak orang tidak puas dengan Make, jika tidak, tidak akan ada lusinan sistem build lain dan puluhan dialek Make saja. Sebuah mengulang satu ini alternatif lain? Di satu sisi, tentu saja, ya - hanya sangat sederhana, namun mampu memecahkan benar-benar semua tugas yang sama seperti Membuat. Di sisi lain, apakah kita memiliki beberapa Make yang umum dan seragam?
Kebanyakan sistem build "alternatif" lahir karena mereka tidak memiliki kemampuan Make asli, tidak memiliki fleksibilitas. Banyak sistem hanya mementingkan pembuatan Makefile, bukan membangunnya sendiri. Banyak yang disesuaikan dengan ekosistem bahasa pemrograman tertentu.
Di bawah ini saya akan mencoba menunjukkan pengulangan itu adalah sistem yang jauh lebih penting, bukan hanya solusi lain.
Make selalu ada
Secara pribadi, saya masih selalu memandang curiga pada keseluruhan alternatif ini, karena lebih kompleks, atau ekosistem / bahasa-spesifik, atau ketergantungan tambahan yang perlu diatur dan dipelajari bagaimana menggunakannya. Dan Make adalah hal yang, plus atau minus, semua orang sudah familiar dan tahu bagaimana menggunakan pada tingkat dasar. Oleh karena itu, selalu dan di mana pun saya mencoba menggunakan POSIX Make, dengan asumsi bahwa ini adalah sesuatu yang setiap orang miliki di sistem (POSIX) di luar kotak, seperti kompiler C. Dan tugas di Make untuk melakukan hanya yang dimaksudkan: eksekusi tujuan yang dapat diparalelkan (perintah ) dengan mempertimbangkan ketergantungan di antara mereka.
Apa masalahnya hanya dengan menulis di Make dan memastikannya berfungsi di sistem apa pun? Lagi pula, Anda dapat (harus!) Menulis di shell POSIX dan tidak memaksa pengguna untuk menginstal GNU Bash yang sangat besar. Satu-satunya masalah adalah bahwa hanya dialek POSIX Make yang akan berfungsi, yang cukup langka bahkan untuk banyak proyek kecil yang sederhana. Membuat sistem BSD modern lebih kompleks dan penuh fitur. Nah, dengan GNU Make, hanya sedikit yang dapat dibandingkan dengan siapa pun, meskipun hampir tidak ada yang menggunakan kemampuan penuhnya dan tidak tahu bagaimana menggunakannya. Tetapi GNU Make tidak mendukung dialek sistem BSD modern. Sistem BSD tidak memiliki GNU Make di dalamnya (dan dapat dimengerti!).
Menggunakan dialek BSD / GNU berarti berpotensi memaksa pengguna untuk menginstal perangkat lunak tambahan yang tidak dikeluarkan dari kotaknya. Dalam hal ini, keuntungan yang mungkin dari Make - keberadaannya di sistem, dibatalkan.
Dimungkinkan untuk menggunakan dan menulis di POSIX Make, tetapi sulit. Secara pribadi, saya langsung teringat dua kasus yang sangat mengganggu:
- Beberapa Membuat implementasi, saat menjalankan $ (MAKE) -C, "pergi" ke direktori tempat Make baru dijalankan, dan beberapa tidak. Apakah mungkin untuk menulis Makefile agar berfungsi sama di mana-mana? Tentu saja:
tgt: (cd subdir ; $(MAKE) -C ...)
Nyaman? Tentu saja tidak. Dan sungguh tidak menyenangkan bahwa seseorang harus selalu mengingat tentang hal-hal sepele seperti itu. - Dalam POSIX Make, tidak ada pernyataan yang mengeksekusi panggilan shell dan menyimpan hasilnya dalam variabel. Di GNU Make up to versi 4.x Anda dapat melakukan:
VAR = $(shell cat VERSION)
dan mulai dengan 4.x, serta dalam dialek BSD, Anda dapat melakukan:
VAR != cat VERSION
Tidak cukup tindakan yang sama dapat dilakukan:
VAR = `cat VERSION`
tetapi secara harfiah menggantikan ekspresi ini dalam perintah shell Anda yang dijelaskan dalam target. Pendekatan ini digunakan dalam suckless proyek, tetapi, tentu saja, kruk.
Secara pribadi, di tempat-tempat seperti itu, saya sering menulis Makefiles untuk tiga dialek sekaligus (GNU, BSD dan POSIX):
$ cat BSDmakefile
GOPATH != pwd
VERSION != cat VERSION
include common.mk
$ cat GNUmakefile
GOPATH = $(shell pwd)
VERSION = $(shell cat VERSION)
include common.mk
Nyaman? Jauh dari itu! Meskipun tugasnya sangat sederhana dan umum. Jadi ternyata:
- Tulis secara paralel untuk beberapa dialek Make. Trading waktu pengembang untuk kenyamanan pengguna.
- Mengingat banyak nuansa dan hal sepele, mungkin dengan substitusi yang tidak efisien ( `cmd ...` ), coba tulis di POSIX Make. Bagi saya pribadi, dengan pengalaman bertahun-tahun dengan GNU / BSD Make, opsi ini paling memakan waktu (lebih mudah untuk menulis dalam beberapa dialek).
- Tulis di salah satu dialek Make, yang memaksa pengguna untuk menginstal perangkat lunak pihak ketiga.
Buat masalah teknis
Tapi semuanya jauh lebih buruk karena Make mana pun tidak mengatakan bahwa itu (baik) mengatasi tugas yang diberikan padanya.
- mtime , Make mtime, . , , Make . mtime ! mtime , , ! mtime — , . FUSE mtime . mmap mtime… -, msync ( POSIX ). NFS? , Make : ( ), , FUSE/NFS/mmap/VCS.
- . ? Make . :
tgt-zstd: zstd -d < tgt-zstd.zst > tgt tgt-fetch: fetch -o tgt-fetch SOME://URL
, , Make , , , , Make, .
:
tgt-zstd: zstd -d < tgt-zstd.zst > tgt-zstd.tmp fsync tgt-zstd.tmp mv tgt-zstd.tmp tgt-zstd
tmp/fsync/mv ? , Make-, tgt.tmp. - . ( ) Makefile, Make ? . - $(CFLAGS)? .
Makefile! . Makefile , , . , , - , .
Makefile :
$ cat Makefile include tgt1.mk include tgt2.mk ...
. ? !
- , . Recursive Make Considered Harmful , Makefile-, Makefile- - , , Make , . Makefile — . ? , Makefile.
? , . FreeBSD , , , , .
- . , #include «tgt.h», .c tgt.h, .c - sed .
tgt.o: tgt.c `sed s/.../ tgt.c`
. .mk Makefile include. ? Make, : .mk , , Makefile- include-.
- Makefile- shell, , - , \\$, , .sh , Make. Make /, shell shell, . ?
Mari kita akui dengan jujur: seberapa sering dan seberapa banyak yang harus Anda lakukan untuk membersihkan atau membangun kembali tanpa paralelisasi, karena ada sesuatu yang tidak dikompilasi atau tidak dibangun ulang bertentangan dengan harapan? Dalam kasus umum, tentu saja, ini bukan karena Makefile yang ditulis dengan benar, benar dan lengkap, yang berbicara tentang kompleksitas tulisan mereka yang kompeten dan efisien. Alat itu akan membantu.
Ulangi persyaratan
Untuk melanjutkan ke deskripsi redo , pertama-tama saya akan memberi tahu Anda apa itu sebagai implementasi dan apa yang harus dipelajari "pengguna" (pengembang menjelaskan tujuan dan ketergantungan di antara mereka).
- redo, , - . redo . POSIX shell . Python . : , , .
- redo : POSIX shell, GNU bash, Python, Haskell, Go, C++, Inferno Shell. .
- C , SHA256, 27KB. POSIX shell 100 . , POSIX shell redo tarball- .
- Make-, ( ).
redo
Aturan build target adalah skrip shell POSIX reguler di target_name.do . Izinkan saya mengingatkan Anda untuk terakhir kalinya bahwa itu bisa berupa bahasa lain (jika Anda menambahkan shebang) atau hanya file biner yang dapat dieksekusi, tetapi secara default itu adalah shell POSIX. Skrip dijalankan dengan set -e dan tiga argumen:
- $1 —
$2 — ( )
$3 —
redo . stdout $3 . ? - , - stdout. redo:
$ cat tgt-zstd.do zstd -d < $1.zst $ cat tgt-fetch.do fetch -o $3 SOME://URL
, fetch stdout. stdout , $3. , fsync . ! , fsync — .
, (make) clean, , . redo , . , all .
default
. POSIX Make .c:
.c: $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
redo default.do , default.---.do. Make :
$ cat default.c.do $CC $CLFAGS $LDFLAGS -o $3 $1
— $2 , $1 «» redo . default- :
a.b.c.do -> $2=a.b.c default.do -> $2=a.b.c default.c.do -> $2=a.b default.b.c.do -> $2=a
, , . cd dir; redo tgt redo dir/tgt. .do . , .
-.do , default.do . , .do ../a/b/xtarget.y :
./../a/b/xtarget.y.do ./../a/b/default.y.do ./../a/b/default.do ./../a/default.y.do ./../a/default.do ./../default.y.do ./../default.do
2/3 redo .
redo-ifchange :
$ cat hello-world.do redo-ifchange hello-world.o ../config . ../config $CC $CFLAGS -o $3 hello-world.o $ cat hello-world.o.do redo-ifchange hw.c hw.h ../config . ../config $CC $CFLAGS -c -o $3 hw.c $ cat ../config CC=cc CFLAGS=-g $ cat ../all.do # , , <em>redo</em>, # hw/hello-world redo-ifchange hw/hello-world # $ cat ../clean.do redo hw/clean $ cat clean.do rm -f *.o hello-world
redo : state. . redo-ifchange , - , - , , , , . .do . , config hello-world .
state? . - TSV-like -.do.state, - , .redo , - SQLite3 .redo .
stderr - , - state, « - ».
state? redo : , FUSE/mmap/NFS/VCS, . ctime, inode number, — , .
state lock- Make — . ( ) state lock- . .
, redo-ifchange - , . — . redo-ifchange , :
redo-ifchange $2.c gcc -o $3 -c $2.c -MMD -MF $2.deps read deps < $2.deps redo-ifchange ${deps#*:}
, include-:
$ cat default.o.do deps=`sed -n 's/^#include "\(.*\)"$/\1/p' < $2.c` redo-ifchange ../config $deps [...]
*.c?
for f in *.c ; do echo ${f%.c}.o ; done | xargs redo-ifchange
.do (....do.do ) . .do $CC $CFLAGS..., « »:
$ cat tgt.do redo-ifchange $1.c cc ./cc $3 $1.c $ cat cc.do redo-ifchange ../config . ../config cat > $3 <<EOF #!/bin/sh -e $CC $CFLAGS $LDFLAGS -o \$1 \$@ $LDLIBS EOF chmod +x $3
compile_flags.txt Clang LSP ?
$ cat compile_flags.txt.do redo-ifchange ../config . ../config echo "$PCSC_CFLAGS $TASN1_CFLAGS $CRYPTO_CFLAGS $WHATEVER_FLAGS $CFLAGS" | tr " " "\n" | sed "/^$/d" | sort | uniq
$PCSC_CFLAGS, $TASN1_CFLAGS? , pkg-config, autotools!
$ cat config.do cat <<EOF [...] PKG_CONFIG="${PKG_CONFIG:-pkgconf}" PCSC_CFLAGS="${PCSC_CFLAGS:-`$PKG_CONFIG --cflags libpcsclite`}" PCSC_LDFLAGS="${PCSC_LDFLAGS:-`$PKG_CONFIG --libs-only-L libpcsclite`}" PCSC_LDLIBS="${PCSC_LDLIBS:-`$PKG_CONFIG --libs-only-l libpcsclite`}" TASN1_CFLAGS="${TASN1_CFLAGS:-`$PKG_CONFIG --cflags libtasn1`}" TASN1_LDFLAGS="${TASN1_LDFLAGS:-`$PKG_CONFIG --libs-only-L libtasn1`}" TASN1_LDLIBS="${TASN1_LDLIBS:-`$PKG_CONFIG --libs-only-l libtasn1`}" [...] EOF
- .do , Makefile:
foo: bar baz hello world .c: $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
:
$ cat default.do case $1 in foo) redo-ifchange bar baz hello world ;; *.c) $CC $CFLAGS $LDFLAGS -o $3 $1 ;; esac
, default.do . .o ? special.o.do, fallback default.o.do default.do .
redo , , « , !?» ( default ). , , , , . suckless ( , CMake, GCC, pure-C redo — ).
- - .
- (*BSD vs GNU) — POSIX shell , (Python, C, shell) redo .
- / Makefile-.
- .
- ( ) , , .
- — , , l **.do.
/?
- Make , .
- Butuh waktu lebih dari satu bulan untuk menghilangkan refleks melakukan redo clean , karena sudah menjadi kebiasaan setelah Make bahwa sesuatu tidak akan berkumpul (kembali).
Saya merekomendasikan dokumentasi implementasi apenwarr / redo , dengan banyak contoh dan penjelasan.
Sergey Matveev , cypherpunk , Python / Go / C-developer, kepala spesialis FSUE STC Atlas.