En İyi Örneklerle Makefile Öğrenin
(makefiletutorial.com)- Makefile, C/C++ derleme otomasyonu ve bağımlılık yönetimini basitleştiren bir araçtır
- Zaman damgalarını kullanarak değişen dosyaları tespit etme yöntemiyle yalnızca gerektiğinde derleme çalıştırır
- Kural (rule), komut (command), bağımlılık (prerequisite) gibi temel yapıları örneklerle açıklar
- Otomatik değişkenler, pattern kuralları, değişken genişletme gibi ileri özellikleri de pratik şekilde ele alır
- Orta ölçekli projeler için gerçek dünya Makefile şablonu üzerinden ölçeklenebilirlik ve yönetimin önemini tanıtır
Makefile eğitim rehberine giriş
- Makefile, proje derleme otomasyonu ve bağımlılık yönetiminden sorumlu temel bir araçtır
- Çeşitli gizli kurallar ve semboller nedeniyle ilk bakışta karmaşık gelebilir; ancak bu rehber, ana noktaları kısa ve doğrudan çalıştırılabilir örneklerle düzenler
- Her bölümde uygulamaya dayalı örnekler üzerinden konuyu anlamak mümkündür
Başlarken
Makefile'ın var olma amacı
- Makefile, büyük programlarda yalnızca değişen bölümleri yeniden derlemek için kullanılır
- C/C++ dışında da dillere özel çeşitli build araçları vardır; ancak Make, genel build senaryolarının tamamında kullanılabilir
- Temel mantık, değişen dosyaları algılayıp yalnızca gerekli işleri çalıştırmasıdır
Make'e alternatif build sistemleri
- C/C++ tarafında: SCons, CMake, Bazel, Ninja gibi birçok seçenek vardır
- Java tarafında: Ant, Maven, Gradle
- Go, Rust, TypeScript gibi diller de kendi build araçlarını sunar
- Python, Ruby, JavaScript gibi yorumlanan diller derleme gerektirmediğinden Makefile benzeri ayrı bir yönetim ihtiyacı daha düşüktür
Make sürümleri ve türleri
- Farklı Make uygulamaları olsa da, bu rehber GNU Make için optimize edilmiştir (çoğunlukla Linux ve MacOS'ta kullanılır)
- Örnekler GNU Make 3 ve 4 sürümleriyle uyumludur
Örnekleri çalıştırma yöntemi
- Terminalde make kurulduktan sonra her örneği
Makefiledosyası olarak kaydedipmakekomutunu çalıştırın - Makefile içindeki komut satırları mutlaka sekme karakteri ile girintilenmelidir
Makefile temel sözdizimi
Kuralın (Rule) yapısı
-
hedef: bağımlılık(lar)- komut
- komut
-
Hedef: derleme sonucunda oluşan dosya adı (genellikle bir tane)
-
Komut: fiilen çalışan shell script'i (sekmeyle başlar)
-
Bağımlılık: hedef derlenmeden önce hazır olması gereken dosyaların listesi
Make'in özü
Hello World örneği
hello:
echo "Hello, World"
echo "This line will print if the file hello does not exist."
hellohedefinin bağımlılığı yoktur ve 2 komut çalıştırırmake helloçalıştırıldığındahellodosyası yoksa komutlar yürütülür. Dosya zaten varsa çalışmaz- Genellikle hedef, dosya adıyla aynı olacak şekilde yazılır
C dosyası derleme için temel örnek
blah.cdosyasını oluşturun (int main() { return 0; }içeriğiyle)- Aşağıdaki Makefile'ı yazın
blah:
cc blah.c -o blah
makeçalıştırıldığındablahhedefi yoksa derleme yapılır veblahdosyası oluşturulurblah.cdeğişse bile otomatik yeniden derleme olmaz → bağımlılık eklemek gerekir
Bağımlılık ekleme yöntemi
blah: blah.c
cc blah.c -o blah
- Artık
blah.cyeni değiştirilmişseblahhedefi yeniden derlenir - Değişiklik tespitinde dosya zaman damgaları temel alınır
- Zaman damgaları elle değiştirilirse beklenmedik davranışlar oluşabilir
Daha fazla örnek
Bağlantılı hedef ve bağımlılık örneği
blah: blah.o
cc blah.o -o blah
blah.o: blah.c
cc -c blah.c -o blah.o
blah.c:
echo "int main() { return 0; }" > blah.c
- Ağaç yapısında bağımlılıkları izleyerek her aşamadaki üretim süreci otomatikleştirilir
Her zaman çalışan hedef örneği
some_file: other_file
echo "This will always run, and runs second"
touch some_file
other_file:
echo "This will always run, and runs first"
other_filegerçek bir dosya olarak oluşturulmadığı içinsome_filekomutu her seferinde çalışır
Make clean
cleanhedefi, build çıktılarının silinmesi için sık kullanılır- Make içinde özel bir anahtar sözcük değildir; komut olarak sizin tanımlamanız gerekir
- Dosya adı
cleanise karışıklık yaşanabileceğinden.PHONYkullanılması önerilir
Örnek:
some_file:
touch some_file
clean:
rm -f some_file
Değişken kullanımı
- Değişkenler her zaman string'dir.
- Genellikle
:=önerilir; ayrıca=,?=,+=gibi farklı atama biçimleri de vardır - Kullanım örneği:
files := file1 file2
some_file: $(files)
echo "Look at this variable: " $(files)
touch some_file
file1:
touch file1
file2:
touch file2
clean:
rm -f file1 file2 some_file
- Değişken başvuru biçimi:
$(variable)veya${variable} - Makefile içindeki tırnak işaretlerinin Make açısından özel bir anlamı yoktur (ancak shell komutlarında gerekebilir)
Hedef yönetimi
all hedefi
- Birden çok hedefi tek seferde çalıştırmak için ilk (varsayılan) hedefe bu rol verilir
all: one two three
one:
touch one
two:
touch two
three:
touch three
clean:
rm -f one two three
Çoklu hedefler ve otomatik değişkenler
- Birden fazla hedef için her biri adına ayrı komut çalıştırılabilir.
$@, mevcut hedef adını taşır
all: f1.o f2.o
f1.o f2.o:
echo $@
Otomatik değişkenler ve wildcard'lar
* wildcard'ı
*, dosya sistemindeki adları doğrudan tarar- Kullanırken
wildcardfonksiyonuyla sarmalanması önerilir
print: $(wildcard *.c)
ls -la $?
- Değişken tanımında doğrudan
*kullanmayın
thing_wrong := *.o
thing_right := $(wildcard *.o)
% wildcard'ı
- Çoğunlukla pattern kurallarında kullanılır; belirtilen kalıbı çıkarıp genişletebilir
Fancy Rules
Örtük (Implicit) kurallar
- Make, C/C++ build süreçleriyle ilgili çeşitli gizli varsayılan kuralları gömülü olarak içerir
- Temsilî değişkenler:
CC,CXX,CFLAGS,CPPFLAGS,LDFLAGSvb. - C örneği:
CC = gcc
CFLAGS = -g
blah: blah.o
blah.c:
echo "int main() { return 0; }" > blah.c
clean:
rm -f blah*
Static Pattern Rules
- Aynı kalıbı izleyen çok sayıdaki kural kısa biçimde yazılabilir
objects = foo.o bar.o all.o
all: $(objects)
$(CC) $^ -o all
$(objects): %.o: %.c
$(CC) -c $^ -o $@
all.c:
echo "int main() { return 0; }" > all.c
%.c:
touch $@
clean:
rm -f *.c *.o all
Static Pattern Rules + filter fonksiyonu
filterkullanıldığında yalnızca belirli uzantı kalıbına uyan hedefler seçilebilir
obj_files = foo.result bar.o lose.o
src_files = foo.raw bar.c lose.c
all: $(obj_files)
.PHONY: all
$(filter %.o,$(obj_files)): %.o: %.c
echo "target: $@ prereq: $
1 yorum
Hacker News görüşleri
1985’te Boston University Graphics laboratuvarında birinin Makefile kullanarak animasyon için 3D bir renderer yaptığını bizzat görmüş. O kişi bir Lisp programcısıymış; erken prosedürel üretim ve 3D aktör sistemi üzerinde çalışıyormuş ve yaklaşık 10 satırlık gerçekten zarif bir Makefile yazmış. Basit dosya tarih bağımlılıklarıyla yüzlerce animasyonu otomatik üreten bir yapıymış. Her karenin 3D biçimini Lisp üretiyor, Make de kareleri oluşturuyormuş. 1985’te, bugün 3D ve animasyonu sıradan görmemizin aksine herkesin buna hayran kaldığını; o kişinin daha sonra Iron Giant ve Coraline’ın 3D renderer’larında çalışan Brian Gardner olduğunu hatırlıyor
Acaba bahsedilen kişi 3d-consultant.com/bio.html’deki kişi mi diye merak ediyor
Coraline adlı filmi kastettiğini doğruluyor
Make kullanırken pek bilinmeyen bazı faydalı bayrakları tanıtıyor
--output-sync=recurse -j10: her hedef işi bitene kadar stdout/stderr’ı tamponlayıp sonra yazdırıyor; aksi halde loglar birbirine giriyor ve analiz zorlaşıyor-jyerine--load-averagekullanarak paralel çalışmada sistem yükü kontrol edilebilir (make -j10 --load-average=10)--shuffleseçeneği, CI ortamında Makefile içindeki bağımlılık sorunlarını yakalamakta yararlımake’in çeşitli seçeneklerini resmî olarak derleyip programın içine metin ya da belge olarak eklemenin kullanımı daha erişilebilir kılacağı fikrini dile getiriyor
Kendisinin sık kullandığı seçeneğin, her şeyi zorla yeniden derlemek için kullanılan
-Bbayrağı olduğunu açıklıyormake -jyüzünden DOS makinelerinde oluşan sorunları sık gördüğü için bunu bir bug olarak gördüğünü söylüyorYoğun sistemlerde veya çok kullanıcılı ortamlarda paralelleştirme sorunlarını işletim sistemi zamanlayıcısının çözmesi gerekmiyor mu diye soruyor
Faydalı bayraklar olduklarını ama taşınabilir olmadıkları için, sadece kişisel özel projeler dışında kullanılmamasını öneriyor
.PHONY kullanılmadığı için bunu öğreticide atlamanın zayıf bir bahane olduğunu düşünüyor. Asıl yapılması gerekenin aracı doğru kullanmayı öğretmek olduğu görüşünde
-o pipefailseçeneğini körü körüne uygulamak sorunlu; pipe içinde grep gibi araçlar kullanıldığında kırılabileceği için duruma göre kullanılmasını öneriyorMake’in büyük C kod tabanlarını derlemek için özelleşmiş bir araç olduğu iddiası
Make’in bir iş çalıştırıcısından çok, doğrusal shell script’lerini bildirime dayalı bağımlılıklar biçimine dönüştüren genel amaçlı bir shell aracı olduğu görüşü paylaşılıyor
Make’i yalnızca C kod tabanları için bir derleme aracı olarak görmenin artık doğru olmadığını savunuyor. Son 20 yılda daha sağlam ve daha açık derleme sistemleri geliştirildiğini hatırlatıp bu bakışın güncellenmesi gerektiğini söylüyor
İyi bir iş çalıştırıcısının ne olduğunu soruyor. (Kendisinin “job runner” anlamını karıştırdığını söyleyerek özür de ekliyor)
Makefile’ın karmaşıklaştığı kısımları modern biçimde ikame eden bir araç olarak just öneriliyor
just, shell script listelerinin yerine geçmek için iyi ama Make’in özündeki “yalnızca yeniden çalışması gereken kuralları çalıştırma” yeteneğinin yerini tutmuyor
Diğer alternatifler olarak
Alternatif araçların kendilerini Make’in yerine koyduğunu ama kendisine göre tamamen farklı olduklarını ve doğrudan karşılaştırmanın zor olduğunu söylüyor. Make’in özünün çıktı üretmek ve zaten derlenmiş olanı yeniden derlememek olduğunu; buna karşılık just’ın basit bir komut çalıştırıcısı olduğunu belirtiyor
Make’i komut çalıştırıcısı olarak kullanmanın avantajının, hemen her yerde kurulu bulunan standart bir araç olmasının verdiği güven olduğunu söylüyor. Alternatifler daha iyi tasarlanmış olsa bile ayrıca kurulum gerektirdikleri için buna ihtiyaç duymadığını düşünüyor
Task’ı C ile yaptığı küçük hobi projelerinde memnuniyetle kullandığını, ama büyük projeler için de uygun olup olmadığı konusunda henüz emin olmadığını ekliyor (Task resmî sitesi)
Son dönemde CMake’in, Makefile’ın C++20 modül desteği için uygun olmadığını düşünüp varsayılan olarak ninja’yı seçmesini ilginç buluyor (CMake kılavuzu)
clang-scan-depsgibi araçlarla dinamik analiz yaklaşımının benimsendiğini söylüyor (teknik slaytlar)Aslında bu kısıtın CMake tarafında alınmış bir karar olduğunu ya da Makefile generator’ına destek verecek gönüllü eksikliğiyle ilgili olduğunu düşünüyor. ninja’nın da C++ modüllerini doğrudan desteklemediğini (ilgili issue), hatta ninja’nın Make’ten daha az özellik sunduğunu ve tüm bağımlılıkların statik olarak açıkça belirtilmesini gerektirdiğini vurguluyor
Modül kavramının kendisinin de karmaşık ve kafa karıştırıcı olduğunu düşünüyor
tup kullanan olup olmadığını soruyor. (resmî belge)
Kendisinin, Make’e alternatif olan Task aracının yaratıcısı ve ana geliştiricisi olduğunu söylüyor. 8 yıldan uzun süredir geliştirildiğini ve gelişmeye devam ettiğini belirtiyor
just da başka bir Make alternatifi olarak öneriliyor (just GitHub)
Komik bir tesadüf olarak, kendisinin de Task’ı sık kullandığını ve bu sabah issue açtığını söylüyor
Bu öğreticide tehlikeli ve ince bazı sorunlar var
ifneq (,$(findstring t,$(firstword -$(MAKEFLAGS))))Her GitHub deposunda daima bir Makefile bulundurma alışkanlığı olduğunu söylüyor
makeçalıştırarak proje için beklenen işlemlerin ne olduğunu hatırlamadan hemen yapabildiğini anlatıyor