Makefile'ı seviyorum
(switowski.com)- Yeni derleme araçları sürekli ortaya çıksa da Makefile, bir projenin tekrarlayan işlerini bir araya getiren hafif bir varsayılan araç olarak hâlâ kullanışlı
make,make build,make install,make devgibi alışılagelmiş komutlar sayesinde ilk kez görülen bir projede bile derleme, kurulum ve geliştirme sunucusunu çalıştırma hızlıca denenebilir- Jekyll, Hugo, 11ty, Python web framework'leri, Docker, gulp gibi stack farklı olsa da Makefile ortak çalıştırma arayüzü rolü görür
- Kişisel projelerde koşul ifadeleri veya flag'ler yerine çoğu zaman
dev,build,deploygibi birkaç basit hedef yeterli olur - GNU Make yaygındır; olmasa bile Makefile içindeki shell komutları doğrudan çalıştırılabildiği için kısıtlı ortamlarda da yükü azdır
Projeden projeye değişen komutları tek bir giriş noktasında toplamak
- Makefile, derleme, çalıştırma ve dağıtım biçimleri farklı olan projeleri tanıdık bir komut kümesiyle sarmalamak için kullanılır
- İlk kez gördüğünüz bir projede
Makefilevarsa genelde şu komutlarla başlamayı deneyebilirsinizmakemake buildmake install
- Eski bir kişisel projeyi yeniden açtığınızda da yalnızca
make devçalıştırarak gerekli derleme adımlarını ve geliştirme sunucusunun başlatılmasını tek seferde halledecek şekilde yapılandırılabilir - Jekyll, Hugo, 11ty ve çeşitli Python web framework'leri gibi teknoloji stack'i farklı olsa da aynı giriş noktası korunabilir
- Docker veya gulp kullanıldığında da Makefile bu araçların yerini almaktan çok bir orkestrasyon katmanı olarak çalışır
- Örnek:
make build, projeye özgü ek argümanları geçirerek gerekli Docker imajını oluşturur
- Örnek:
Kişisel projelerde basit kullanım
- Makefile kullanımı karmaşık özelliklerden çok shell komutlarını gruplamaya odaklanır
- Koşul ifadeleri veya flag'ler gibi özellikler kullanılmaz
- make'in iş birimi teknik olarak hedef (target) olsa da pratikte görev (task) gibi ele alınır
- bash script fonksiyonlarıyla da yapılabilir, ancak Makefile'ın daha kolay ve hızlı olduğu düşünülür
- Kişisel projelerde sık kullanılan işler genelde 2-3 tanedir
dev: geliştirme sunucusunu başlatırbuild: gerektiğinde projeyi derlerdeploy: projeyi dağıtır veya yayımlarwatch: kaynak dosyalar değiştiğinde derleme işini otomatik olarak yeniden çalıştıran ek görev olarak kullanılır
- Mevcut blogun Makefile'ında yalnızca bir
devhedefi vardev:altındanpm run devçalıştırılır
- Daha karmaşık kişisel projelerde şu işler Makefile ile gruplanır
dev:bundle exec jekyll serveile geliştirme sunucusunu çalıştırırbuild:npm run gulp buildile asset'leri derlerwatch:npm run gulp watchile belirli klasörleri izlerdeploy: prodüksiyon ortamında Jekyll derlemesinden sonramake encrypt,netlify deploy --prodçalıştırırencrypt:npx staticryptile_siteklasöründeki HTML dosyalarını şifreler
Bilinmesi gereken sınırlamalar ve avantajlar
- Örneklerde phony targets atlanmış olsa da
dev,build,watch,deploy,encryptgibi adlara sahip dosyalar varsa Makefile beklendiği gibi çalışmayabilir- Hacker News okurları bu durumda phony target eklenmesini öneriyor
- GNU Make, makefile çalıştıran yazılımdır; Linux'ta zaten kurulu olma olasılığı yüksektir ve MacBook'ta da ayrıca kurulduğunu hatırlamayacak kadar yaygındır
- Make çok sayıda ek bağımlılık gerektirmediği için güvenlik nedeniyle paket kurmanın zor veya imkânsız olduğu kısıtlı ortamlarda kullanışlı olabilir
- Make olmasa bile Makefile içindeki komutlar shell'e doğrudan kopyalanıp çalıştırılabilir
- Buna karşılık, sunucuda gulp yoksa JavaScript kodunu terminale aynen yapıştırıp çalıştırmanın zor olması gibi bir fark vardır
- Daha iyi ve hızlı yeni bir derleme aracı bulunsa bile Make, farklı araçları ve ayarları yöneten ortak komut katmanı olarak kullanılmaya devam edebilir
2 yorum
Bağımlılıkları tanımlamayan
makefile, daha iyi bir kullanım deneyimi içinjustfileile değiştirilebilir.Hacker News yorumları
Bu başlıkta “make’i yanlış kullanıyorsun” denmesine moralinizi bozmanıza gerek yok. Make’in iyi bir araç olmasının nedenlerinden biri, göründüğünden daha basit şekilde başlanabilmesi.
.PHONYkullanmazsanız sorun çıkabilir, ama küçük bir projede bu, belki ancak 1 yıl sonra basacağınız bir tuzak olur; o zaman da muhtemelen bir saat kadar kafanızı kaşıyıp çözersiniz. Vakaların %99’unda “doğru usulü” dert etmeden hemen başlayabilirsiniz; Make de yıkılmayacağı kadar bir karmaşıklık isterMake, girdi dosyaları ve bağımlılıkları alıp tam olarak tek bir çıktı dosyası üretir.
install,all,cleangibi çıktı üretmeyen kurallar ya da yazıdaki tüm hedefler.PHONYgibi özel sihirli hedeflere bağlı olmalıdır;.PHONY2017 POSIX’te (IEEE Std 1003.1-2017, https://pubs.opengroup.org/onlinepubs/9699919799/utilities/m...) yoktu, ancak mevcut standarda (IEEE Std 1003.1-2024, https://pubs.opengroup.org/onlinepubs/9799919799/utilities/m...) girdi. Nesne dosyaları, modüller, önceden derlenmiş başlıklar gibi birden fazla dosya üretmek istiyorsanız kırılgan hileleri kendiniz yazmanız gerekir. Her Make, GNU Make değildir; BSD, Solaris/Illumos gibi nix türevleri de hâlâ var.Bu, Make’in işe yaramaz olduğu anlamına gelmiyor. Yeterince karmaşık, ama henüz “daha iyi” bir build sistemine ihtiyaç duyacak kadar karmaşık olmayan projeler için işe yarar. Sorun, bu tür projelerin kod büyüdükçe daha karmaşık hale gelmesi ve sonunda Makefile’ın tamamını ya da bir kısmını üreten script/programlara sahip olup geçici bir meta build sistemi oluşturmasıdır.
Daha büyük sorun, onu kullanmak değil, “herkese” çözüm olarak önermektir. Ayrıca proje kökünde
buildveyadevgibi bir dizin ya da dosya oluştuğu anda o Makefile çalışmayı bırakırBugünlerde pnpm’e geçiyorum gerçi, ama yine de bunun çok karmaşık olduğunu düşünmüyorum. Sözdizimini bir miktar bilmek gerekse de, diğer araçları en baştan düzgün çalışır hale getirmekten daha az zor
.PHONYişleriyle hiç sorun yaşamadım.Basit, okunabilir, değiştirilebilir, birleştirilebilir ve zaten her yerde kurulu. Yüzeyde yazan işi yapar, ondan fazlasını da pek yapmaz.
Bu arada başka projelerde moda build sistemleriyle ya da kâbus gibi build sistemleriyle uğraşmak zorunda kaldığımda da onları make ile sarmalayıp kullanıyorum
Herkes bir yerden başlamak zorunda; bir kez çalışmaya başladı mı, kademeli iyileştirmeleri eklemek kolaydır
$@üretmelidir. Makefile’ın tamamı, kurallar başka kurallara bağımlı olabildiği için keyfi sayıda çıktı üretebilir.Buradan phony hedefler için temiz bir pratik kural çıkar.
$@dışındaki şeylere dokunan bir tarifse,$@phony olarak işaretlenmelidir. phony hedefleri liste halinde yönetmek bunu çok daha kolaylaştırır.phonies :=phonies += somethingsomething:./do-somethingphonies += something-elsesomething-else: something./do-something-else# touches $@ and thus does not need to be phonycreate-file:./generate-some-output > $@.PHONY: $(phonies)Makefile berbat bir teknoloji. Yalnız, yaptığımız diğer build sistemlerinin çoğundan biraz daha az kötü olduğu için mazoşist bir anlamda kullanışlı hale geliyor
Build sistemleri genelde ya fazlasıyla temel kalıp oyuncak seviyesinin ötesinde bir şey build edince karmaşaya dönüşür; ya da fazla karmaşık olup ön bilgi, bürokrasi, senkronizasyon ve boilerplate yükünü abartır; ya standart kütüphaneleri olmadığı için aynı build kalıpları 10 bin uyumsuz uygulamaya dağılır; ya ihtiyaçlar arttığında taşınmayı gerektirecek kadar kısıtlayıcıdır; ya çok fazla sihir barındırır; ya da sözdizimi anlaşılmaz ve tutarsızdır
Kullandığım yazılımların yarısından fazlası sıradan bir Makefile ve arada bir configure betiğinden ibaret. autotools olmadan isteğe bağlı olarak
./configureçalıştırıpmake,make installdediğinizde çalışıyor. Bu kesinlikle oyuncak bir kurulum değil; tek bir işi iyi yapan küçük programlardan oluşuyorMake ve çalışma biçimi en azından benim için anlaşılması kolay. İnsanların yaşadığı kötü deneyimlerin çoğu zaman Make’in kendisinden çok karmaşık projelerdeki kötü build ayarlarından ya da gerekli bilginin eksikliğinden kaynaklandığını düşünüyorum
Standart kütüphanenin olmaması araçtan bağımsızlığı hedefleyen bir şey ve bu yüzden farklı araçlara, dillere ve kullanım alanlarına evrensel olarak uygulanabiliyor. Bunu kusur değil, özellik olarak görüyorum. Basit katman bağımlılık takibi; hedefler ve bağımlılıklar da açık olduğu için bunu sihirli saymak zor. Sözdizimi ise kesinlikle daha iyi olabilir
Birden fazla dili kapsayan genel amaçlı build sistemleri yalnızca
makeya da CMake gibi hedef olarakmakekullanan sistemler. Bunun kötü olmasının nedenimakein kötü olması.makei doğru kullanmak gerçekten zor; recursive make ve non-recursive make gibi sorunlar var; Unix/POSIX make, BSD make, GNU make, Windowsnmakegibi uyumsuz varyantları çok ve oldukça çirkinYine de doğru yazılmış
makeoldukça iyi. En küçük ortak payda olarakmakein bulunması gerçekten büyük şansMake’in alt dizinleri bile temiz biçimde ele alamaması epey akıl almaz. Make’ten daha iyi bir şey var mı merak ediyorum. Ninja var ama insanların elle yazması için tasarlanmış değil
Ne kadar payı olduğunu düşünüyorsun? %20, %40, %70 kadar doğru değil mi
Make’i gerçekten seviyorum. Son 20 yılda el attığım her iş ya da proje için en az bir tane yazdım
Akıllıca bir şey yok; birkaç değişken içeren bir parçalar toplamından ibaret.
make run,make test,make lintgibimake recent, lint’ten sonra en son değiştirilen betiği çalıştırıyor. Bunu Bash ya da başka shell’lerle de yapabilirsiniz ama o zaman geliştirici dünyasına çekilip yalnızca karmaşıklığı artırırsınız, ek değer yoktur. Make sadece “şu dosya şu dosyalardan yapılır ve o sırada şu bir iki komut çalıştırılır” diyen alana özgü bir dil. Bu yüzden inanılmaz güçlüOn yıllar önce Java ile bir test framework’ü yapmıştım; testleri ve bağımlı sınıfları make benzeri bir sözdizimiyle belirtmeye izin veriyordu. “Temel test paketi”ni tanımlayan test sınıfı kümeleri vardı; onların üzerine bağımlı diğer test sınıfı katmanları ise ancak öndeki başarılı olursa çalıştırılıyordu
Bugünlerde neden her şeyi bu kadar karmaşık hale getirdiğimizi bilmiyorum. Hep standart Unix araçlarına ve onların yöntemine, yani zamanla sınanmış yola bağlı kalmayı öğütledim. Bunu kesinlikle yapamayacağınız bir durum yoksa tabii. Zaman sınırlı; onu problem çözmenin özü olan sistem/program tasarımına, modellemeye, yapıya, kalıplara vb. harcamak istiyorum; geri kalanı ikincil
Stack, dil ve depodan bağımsız olarak
make testçalıştırınca işleyeceğini bilmek büyük rahatlıkÖnceki projemin Makefile’ında dört ana komut vardı:
build,test,frontend,deploy. build ve test maven’ı, frontend npm’i, deploy ise docker ve aws’yi çağırıyorduBu araçların hepsi kendi durum takibine, cache’lemeye ve artımlı işlemeye sahip; ama ne yaptıklarını raporlamıyorlar. Bu yüzden “yalnızca build güncellendiyse deploy et” gibi moleküler düzeyde kurallar yazamıyorsunuz. Çünkü maven/cargo/dotnet/npm/go bu bilgiyi dışarı açmıyor
Yazar mtime tabanlı bağımlılık takibini bile kullanmıyor. Hedeflerin de
.PHONYolması gerekirken işaretlenmemiş.$1i okuyup eşleştirerek yapılacak işi belirleyen bir shell script ile de değiştirilebilirdihttps://just.systems/
.PHONY: $(MAKECMDGOALS)./build.sh dev,make devkadar net değil. Hiç phony olmayan adım olmasa bile Make kullanmanın bir başka nedeni, daha sonra gerekirse böyle adımlar ekleyebilmektir. Elbette yazarın{dev,build,deploy}hedeflerini phony olarak işaretlemesi gerektiğine katılıyorumKendi Makefile’ı teknik olarak iyileştirilebilir, ama daha önemlisi bir projeye girip “başlama” deneyiminin tutarlı olması
İnsanlar onun betik dili, bağımlılık izleyici vb. pek çok başka işi yapmasını istiyor ve şişkin araçları devreye sokmaya çalışıyor. Yeni olan mutlaka daha iyi değildir. Autoconf ve automake, make değildir
Teknik olarak bu make hedeflerinin hepsi hedef adıyla aynı dosyayı arar. Her hedef aslında
.PHONYolarak tanımlanmalıEskiden ben de böyle Makefile’ları sürekli kullanırdım, ama son birkaç yılda just ve justfile’a geçtim. Bu davranış varsayılan ve genel olarak kullanımı daha kolay. Parametreler gibi şeyler de daha basit
https://github.com/casey/just
:ile başlasaydı, adına bakınca ayırt edilebilmeliydiTabii artık bunun için çok geç
just, ripgrep gibi yeni ve kullanışlı araçları paketleyip ihtiyaç duyulan her yere kurulacak yeni bir coreutils dağıtımı gibi bir şeyin zamanı gelmiş gibi görünüyor
make’in böyle hararetli tartışmalar doğurması ilginç. Eskiden yarı din savaşı gibi olan vi-emacs tartışmalarına benziyor
Orijinal yazıya tamamen katılıyorum; özellikle daha karmaşık derleme araçları kullanılsa bile bunları en üst seviye bir Makefile ile sarmalama yaklaşımını akıllıca buluyorum. Avantajı standartlaşma. Hiçbir şeyi hatırlamam gerekmiyor; benim yazdığım bir projeyse sadece
makeyazınca çalışacağını biliyorumÖrneğin C kullanan biri bir Rust projesini derlemek istediğinde cargo kullanımına bakmak zorunda kalmadan yalnızca
makeveyagmakeyazabilir. GNU’ya özgü özellikler kullanmadan POSIX’e uymaya çalışıyorum, ama gerçek make’lerin neredeyse %100’ünün gmake olduğu da doğruZamana meydan okuyan
makei bir tür üst seviye derleme sistemi başlatıcısı olarak kullanma önerisi muhtemelen 250 yıl sonra bile çalışırcargo buildkullanılırMake’in kendisi harika, ama C projelerinde çalıştığım çoğu durumda cmake/autotools ile küresel paket kurulumu kombinasyonu vardı ve bunlara sık sık bakmam gerekiyordu
Buradaki duyguya katılıyorum, ama birçok şeyi Justfile ile yeniden yazıyorum
https://github.com/casey/just
Makefile’a özgü tuhaflıkların çoğundan kaçınmak mümkün
Evet, Make harika. Gerçekten pek çok işte kullanıyorum. İş otomasyonu için iyi bir yöntem
Örneğin kişisel web sitemde Makefile, bash script’lerini çağırıp güncellenen web sayfalarını yeniden oluşturuyor, sunucuya git push yapıyor, ardından sunucudaki git hook dağıtım için Make’i çağırıyor. Ancak ders materyali PDF’leri gibi sık değişen ikili dosya yığınlarını Git deposuna koymak istemiyorum. Sorun değil. Makefile’da yalnızca değişen PDF’leri sunucuya yükleyen bir
uploadshedefi var ve bu hedef git push yapandeployhedefinin bağımlılığı olduğu için bunu düşünmem gerekmiyorAyrıca ders materyalleri için güncellenmiş PDF’leri başka bir Makefile otomatik olarak web sitesi kaynak ağacına koyuyor. O Makefile ile LaTeX kaynaklarından kullandığım PDF’leri ya da aynı kaynaklardan öğrenciler için alternatif sürümleri, örneğin alıştırma çözümleri olmayan sürümleri oluşturabiliyor, bunları yerel web sitesi sürümüne otomatik yayımlayıp istediğimde güncellenmiş web sitesine yükleyebiliyorum
Bir tür sonsuz uzanan Makefile yapısı. Makefile’ı seviyorum
Bu durumda yazarın gerçekten ihtiyacı olan şey make değil, just
https://just.systems/man/en/
Bir görev çalıştırıcı olarak make fena değil, ama günümüzde diğer yorumlarda söylendiği gibi just gibi daha iyi alternatifler var
Bir derleme sistemi olarak make, dezavantajlarına çarpana kadar idare eder. make/Makefile standartlaştırılmış olmadığı için automake var. Bu yüzden doğrudan Makefile yazmak yerine şablon kullanıp gerçek makefile’ın üretilmesini sağlarsınız. Tüm araç zincirinin sahibi sizseniz sorun olmaz, ama çoğu zaman öyle olmadığı için taşınabilir bir Makefile’ı garanti etmek adına bu yapılır
make hiçbir tür bağımlılık çözümlemesi yapamaz ve gereken şeyin tam orada olduğunu varsayar. Bu yüzden configure betikleri ortaya çıktı; configure betikleri de standart olmadığı için bir make hedefini çalıştırmadan önce autoconf/autoreconf ile configure betiği üretilir
make ve automake/autoconf/autoreconf gibi komşu araçlar, girdinin eski olup olmadığına karar verirken mtime kullanır. autoconf/autoreconf/automake/configure çalıştırdığınız halde girdilerin kalıcı olarak eski kaldığı ve hiçbir şey derleyemediğiniz bir duruma düşebilirsiniz. Bu arada birçok derleme sistemi, düzgün bağımlılık takibi varsa mtime kullanmakta sorun yaşamaz
Genel olarak make’in temel tasarım kusuru Unix felsefesine, yani “bir işi yap ve iyi yap” ilkesine göre şekillenmiş olmasıdır. O tek iş de “girdi eskiyse hedefi yeniden derle”mektir; bu çok sınırlı bir araç olduğu için modern derleme sistemleri, onu temel bir görev çalıştırıcının ötesinde kullanışlı kılmak adına üzerine çok iş yığmak zorunda kalır
makebağımlılık çözümlemesi yapamıyor mu?dependency:...target: dependency...make configurekullanmaktır. Bu,make [build]için gerekenleri ayarlarBu yüzden touchfile kullanılıyor; dağınık olsa da hâlâ pek çok şeyden daha iyi çalışıyor. Özellikle docker build önbelleklemesini kastediyorum