Bu sayfada, göreve dayalı derleme sistemleri, bunların işleyiş şekli ve göreve dayalı sistemlerde ortaya çıkabilecek bazı sorunlar ele alınmaktadır. Kabuk komut dosyalarından sonra, görev tabanlı derleme sistemleri derlemenin bir sonraki mantıksal evrimidir.
Göreve dayalı derleme sistemlerini anlama
Göreve dayalı bir derleme sisteminde, temel iş birimi görevdir. Her görev, her türlü mantığı yürütebilen bir komut dosyasıdır ve görevler, kendilerinden önce çalıştırılması gereken diğer görevleri bağımlılık olarak belirtir. Günümüzde kullanılan Ant, Maven, Gradle, Grunt ve Rake gibi çoğu büyük derleme sistemi görev tabanlıdır. Çoğu modern derleme sistemi, kabuk komut dosyaları yerine mühendislerin derlemenin nasıl yapılacağını açıklayan derleme dosyaları oluşturmasını gerektirir.
Ant kılavuzundaki şu örneğe bakalım:
<project name="MyProject" default="dist" basedir=".">
<description>
simple example build file
</description>
<!-- set global properties for this build -->
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source">
<!-- Compile the Java code from ${src} into ${build} -->
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile"
description="generate the distribution">
<!-- Create the distribution directory -->
<mkdir dir="${dist}/lib"/>
<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
</target>
<target name="clean"
description="clean up">
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
Derleme dosyası XML ile yazılır ve derlemeyle ilgili bazı basit meta verileri, görev listesiyle (XML'deki <target>
etiketleri) birlikte tanımlar. (Ant, görev için target kelimesini, komutlar için ise task kelimesini kullanır.) Her görev, Ant tarafından tanımlanan olası komutların bir listesini yürütür. Bu komutlar arasında dizin oluşturma ve silme, javac
çalıştırma ve JAR dosyası oluşturma yer alır. Bu komut grubu, kullanıcı tarafından sağlanan eklentilerle genişletilerek her türlü mantığı kapsayabilir. Her görev, depends özelliği aracılığıyla bağlı olduğu görevleri de tanımlayabilir. Bu bağımlılıklar, Şekil 1'de gösterildiği gibi döngüsüz bir grafik oluşturur.
1. şekil Bağımlılıkları gösteren döngüsüz bir grafik
Kullanıcılar, Ant'in komut satırı aracına görevler sağlayarak derleme işlemleri gerçekleştirir. Örneğin, bir kullanıcı ant dist
yazdığında Ant aşağıdaki adımları uygular:
- Geçerli dizinde
build.xml
adlı bir dosyayı yükler ve Şekil 1'de gösterilen grafik yapısını oluşturmak için ayrıştırır. - Komut satırında sağlanan
dist
adlı görevi arar vecompile
adlı göreve bağımlı olduğunu keşfeder. compile
adlı görevi arar veinit
adlı göreve bağımlı olduğunu keşfeder.init
adlı görevi arar ve bu görevin bağımlılıkları olmadığını keşfeder.init
görevinde tanımlanan komutları yürütür.compile
görevinde tanımlanan komutları, bu görevin tüm bağımlılıkları çalıştırıldıktan sonra yürütür.dist
görevinde tanımlanan komutları, bu görevin tüm bağımlılıkları çalıştırıldıktan sonra yürütür.
Sonuç olarak, dist
görevi çalıştırılırken Ant tarafından yürütülen kod, aşağıdaki kabuk komut dosyasına eşdeğerdir:
./createTimestamp.sh
mkdir build/
javac src/* -d build/
mkdir -p dist/lib/
jar cf dist/lib/MyProject-$(date --iso-8601).jar build/*
Söz dizimi kaldırıldığında, derleme dosyası ve derleme komut dosyası aslında çok farklı değildir. Ancak bunu yaparak çok şey kazandık. Diğer dizinlerde yeni derleme dosyaları oluşturup bunları birbirine bağlayabiliriz. Mevcut görevlere bağlı yeni görevleri rastgele ve karmaşık şekillerde kolayca ekleyebiliriz. ant
komut satırı aracına yalnızca tek bir görevin adını iletmemiz yeterlidir. Araç, çalıştırılması gereken her şeyi belirler.
Ant, ilk olarak 2000 yılında yayınlanan eski bir yazılımdır. Maven ve Gradle gibi diğer araçlar, geçen yıllar içinde Ant'ı geliştirerek harici bağımlılıkların otomatik yönetimi ve XML içermeyen daha temiz bir söz dizimi gibi özellikler ekleyerek Ant'ın yerini almıştır. Ancak bu yeni sistemlerin doğası aynı kalır: Mühendislerin, görevler olarak derleme komut dosyaları yazmasına ve bu görevleri yürütmek için araçlar sağlamasına ve aralarındaki bağımlılıkları yönetmesine olanak tanır.
Göreve dayalı derleme sistemlerinin karanlık tarafı
Bu araçlar, mühendislerin herhangi bir komut dosyasını görev olarak tanımlamasına olanak tanıdığından oldukça güçlüdür ve hayal edebileceğiniz hemen her şeyi yapmanıza olanak tanır. Ancak bu gücün dezavantajları vardır ve görev tabanlı derleme sistemlerinin derleme komut dosyaları daha karmaşık hale geldikçe bunlarla çalışmak zorlaşabilir. Bu tür sistemlerle ilgili sorun, mühendislere çok fazla güç verip sisteme yeterli güç vermemeleridir. Sistem, komut dosyalarının ne yaptığını bilmediğinden derleme adımlarını planlama ve yürütme konusunda çok tutucu davranması gerektiği için performans düşer. Ayrıca sistemin her komut dosyasının yapması gerekeni yaptığını onaylaması mümkün olmadığından komut dosyaları giderek karmaşıklaşır ve hata ayıklanması gereken başka bir öğe haline gelir.
Derleme adımlarını paralelleştirmenin zorluğu
Modern geliştirme iş istasyonları, birden fazla çekirdeğe sahip olduğundan oldukça güçlüdür ve birden fazla derleme adımını paralel olarak yürütebilir. Ancak görev tabanlı sistemler, yapabilecekleri görülse bile görev yürütmeyi genellikle paralel hale getiremez. A görevinin B ve C görevlerine bağlı olduğunu varsayalım. B ve C görevleri birbirine bağımlı olmadığından, sistemin A görevine daha hızlı ulaşabilmesi için bu görevleri aynı anda çalıştırmak güvenli midir? Aynı kaynaklara dokunmadıkları sürece bu mümkün olabilir. Ancak bu durum her zaman geçerli olmayabilir. Belki her ikisi de durumlarını izlemek için aynı dosyayı kullanıyordur ve bunları aynı anda çalıştırmak çakışmaya neden oluyordur. Genel olarak sistemin bunu bilmesinin bir yolu yoktur. Bu nedenle, ya bu çakışmaları riske atması (nadir ancak hata ayıklaması çok zor olan derleme sorunlarına yol açar) ya da tüm derlemeyi tek bir işlemde tek bir iş parçacığında çalışacak şekilde kısıtlaması gerekir. Bu durum, güçlü bir geliştirici makinesinin büyük bir israfı olabilir ve derlemenin birden fazla makineye dağıtılması olasılığını tamamen ortadan kaldırır.
Artımlı derlemeler gerçekleştirilirken zorluk yaşanıyor
İyi bir derleme sistemi, mühendislerin güvenilir artımlı derlemeler yapmasına olanak tanır. Bu sayede, küçük bir değişiklik için tüm kod tabanının sıfırdan yeniden derlenmesi gerekmez. Bu durum, özellikle derleme sistemi yavaşsa ve yukarıda belirtilen nedenlerden dolayı derleme adımlarını paralelleştiremiyorsa önemlidir. Ancak görev tabanlı derleme sistemleri de bu konuda zorlanmaktadır. Görevler her şeyi yapabildiğinden, genel olarak bunların daha önce yapılıp yapılmadığını kontrol etmenin bir yolu yoktur. Birçok görevde yalnızca bir dizi kaynak dosya alınır ve derleyici çalıştırılarak bir dizi ikili dosya oluşturulur. Bu nedenle, temel kaynak dosyalar değişmediyse bu görevlerin yeniden çalıştırılması gerekmez. Ancak ek bilgi olmadan sistem bunu kesin olarak söyleyemez. Belki görev, değişmiş olabilecek bir dosyayı indiriyordur veya her çalıştırmada farklı olabilecek bir zaman damgası yazıyordur. Doğruluğu garanti etmek için sistemin genellikle her derleme sırasında her görevi yeniden çalıştırması gerekir. Bazı derleme sistemleri, mühendislerin bir görevin hangi koşullarda yeniden çalıştırılması gerektiğini belirtmesine izin vererek artımlı derlemeleri etkinleştirmeye çalışır. Bazen bu mümkün olsa da genellikle göründüğünden çok daha zor bir sorundur. Örneğin, dosyaların doğrudan diğer dosyalar tarafından dahil edilmesine izin veren C++ gibi dillerde, giriş kaynakları ayrıştırılmadan değişiklikler için izlenmesi gereken tüm dosyaları belirlemek imkansızdır. Mühendisler genellikle kısa yollar kullanır ve bu kısa yollar, görev sonucu kullanılmaması gerektiği halde yeniden kullanıldığında nadir ve sinir bozucu sorunlara yol açabilir. Bu durum sık sık yaşandığında mühendisler, temiz bir durum elde etmek için her derlemeden önce temizleme işlemi yapma alışkanlığı kazanır. Bu da artımlı derlemenin amacını tamamen ortadan kaldırır. Bir görevin ne zaman yeniden çalıştırılması gerektiğini anlamak şaşırtıcı derecede zor bir iştir ve bu işi insanlar yerine makinelerin yapması daha iyidir.
Komut dosyalarını koruma ve hatalarını ayıklama zorluğu
Son olarak, göreve dayalı derleme sistemlerinin uyguladığı derleme komut dosyalarıyla çalışmak genellikle zordur. Genellikle daha az incelemeye tabi tutulsalar da derleme komut dosyaları, oluşturulan sistem gibi kodlardır ve hataların kolayca gizlenebileceği yerlerdir. Göreve dayalı bir derleme sistemiyle çalışırken çok sık karşılaşılan bazı hata örneklerini aşağıda bulabilirsiniz:
- Görev A, çıktı olarak belirli bir dosya oluşturmak için görev B'ye bağlıdır. B görevini oluşturan kullanıcı, diğer görevlerin bu göreve bağlı olduğunu fark etmediği için görevi farklı bir konumda çıktı üretecek şekilde değiştirir. Bu durum, A görevini çalıştırmayı deneyen ve başarısız olduğunu gören bir kullanıcı olana kadar algılanamaz.
- A görevi, B görevine; B görevi, C görevine bağlıdır. C görevi, A görevinin ihtiyaç duyduğu belirli bir dosyayı çıktı olarak üretir. Görev B'nin sahibi, görev B'nin artık görev C'ye bağlı olmasına gerek olmadığına karar verir. Bu durum, görev B'nin görev C'yi hiç umursamamasına rağmen görev A'nın başarısız olmasına neden olur.
- Yeni bir görevin geliştiricisi, görevi çalıştıran makineyle ilgili yanlışlıkla bir varsayımda bulunur (ör. bir aracın konumu veya belirli ortam değişkenlerinin değeri). Görev, geliştiricinin makinesinde çalışıyor ancak başka bir geliştirici denediğinde başarısız oluyor.
- Görev, internetten dosya indirme veya derlemeye zaman damgası ekleme gibi nondeterministik bir bileşen içeriyor. Artık kullanıcılar, derlemeyi her çalıştırdıklarında farklı sonuçlar alabiliyor. Bu da mühendislerin, birbirlerinin hatalarını veya otomatik derleme sisteminde oluşan hataları her zaman yeniden oluşturup düzeltemeyeceği anlamına geliyor.
- Birden fazla bağımlılığı olan görevler yarış koşulları oluşturabilir. A görevi hem B görevine hem de C görevine bağlıysa ve B ile C görevleri aynı dosyayı değiştiriyorsa A görevi, B ve C görevlerinden hangisinin önce tamamlandığına bağlı olarak farklı bir sonuç alır.
Burada belirtilen görev tabanlı çerçevede bu performans, doğruluk veya sürdürülebilirlik sorunlarını çözmenin genel amaçlı bir yolu yoktur. Mühendisler, derleme sırasında çalışan rastgele kod yazabildiği sürece sistem, derlemeleri her zaman hızlı ve doğru bir şekilde çalıştırabilmek için yeterli bilgiye sahip olamaz. Sorunu çözmek için mühendislerin elindeki gücün bir kısmını alıp sisteme geri vermemiz ve sistemin rolünü görevleri çalıştırmak yerine yapılar üretmek olarak yeniden kavramamız gerekiyor.
Bu yaklaşım, Blaze ve Bazel gibi yapay öğe tabanlı derleme sistemlerinin oluşturulmasına yol açtı.