Görev Tabanlı Derleme Sistemleri

Bu sayfada göreve dayalı derleme sistemleri, bunların işleyiş şekli ve görev tabanlı 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 işin temel birimi görevdir. Her görev, herhangi bir mantığı yürütebilen bir komut dosyasıdır. Görevler ise, diğer görevleri kendilerinden önce çalışması gereken bağımlılıklar olarak belirtir. Ant, Maven, Gradle, Grunt ve Rake gibi bugün kullanılan başlıca derleme sistemlerinin çoğu göreve dayalı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ği ele alalı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'de yazılır ve derlemeyle ilgili bazı basit meta verileri görev listesiyle (XML'deki <target> etiketleri) birlikte tanımlar. (Ant, bir görevi temsil etmek için target kelimesini, komutları için de görev kelimesini kullanır.) Her görev, Ant tarafından tanımlanan olası komutların listesini yürütür. Bu listede dizin oluşturma ve silme, javac çalıştırma ve bir JAR dosyası oluşturma yer alır. Bu komut dizisi, kullanıcı tarafından sağlanan eklentiler tarafından her türlü mantığı kapsayacak şekilde genişletilebilir. Her görev, bağımlı özelliği aracılığıyla bağlı olduğu görevleri de tanımlayabilir. Bu bağımlılıklar, Şekil 1'de görüldüğü gibi çembersel bir grafik oluşturur.

Bağımlılıkları gösteren akrilik grafik

Şekil 1. Bağımlılıkları gösteren çembersel grafik

Kullanıcılar, Ant'in komut satırı aracına görev sağlayarak derlemeler gerçekleştirir. Örneğin, bir kullanıcı ant dist yazdığında Ant şu adımları uygular:

  1. Geçerli dizinde build.xml adlı bir dosyayı yükler ve Şekil 1'de gösterilen grafik yapısını oluşturmak için dosyayı ayrıştırır.
  2. Komut satırında sağlanan dist adlı görevi arar ve compile adlı göreve bağımlı olduğunu keşfeder.
  3. compile adlı görevi arar ve init adlı göreve bir bağımlılığı olduğunu keşfeder.
  4. init adlı görevi arar ve herhangi bir bağımlılığı olmadığını keşfeder.
  5. init görevinde tanımlanan komutları yürütür.
  6. Bu görevin tüm bağımlılıklarının çalıştırılmış olması şartıyla, compile görevinde tanımlanan komutları yürütür.
  7. Bu görevin tüm bağımlılıklarının çalıştırılmış olması şartıyla, dist görevinde tanımlanan komutları yürütür.

Sonunda, 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ı olmaz. Ama bunu yaparak şimdiden çok şey kazandık. Diğer dizinlerde yeni derleme dosyaları oluşturup bunları birbirine bağlayabiliriz. Mevcut görevlere bağlı olan yeni görevleri rastgele ve karmaşık şekillerde kolayca ekleyebiliyoruz. ant komut satırı aracına yalnızca tek bir görevin adını iletmemiz gerekir ve bu araç, çalıştırılması gereken her şeyi belirler.

Karınca, ilk olarak 2000 yılında piyasaya sürülen eski bir yazılımdır. Maven ve Gradle gibi diğer araçlar geçtiğimiz yıllarda Ant'ı iyileştirdi ve dış bağımlılıkların otomatik yönetimi ve XML olmadan daha net bir söz dizimi gibi özellikler ekleyerek onun yerini aldı. Ancak bu yeni sistemlerin doğası aynıdır: Mühendislerin derleme komut dosyalarını ilkeli ve modüler bir şekilde görev şeklinde yazmalarına ve bu görevleri yürütüp aralarında bağımlılıkları yönetmek için araçlar sağlamalarına olanak tanırlar.

Göreve dayalı derleme sistemlerinin karanlık tarafı

Bu araçlar temelde mühendislerin herhangi bir komut dosyasını görev olarak tanımlamalarını sağladığından, son derece güçlüdür ve onlarla hayal edebildiğiniz her şeyi yapmanıza imkan tanır. Ancak bu gücün bazı dezavantajları vardır ve derleme komut dosyaları daha karmaşık hale geldikçe göreve dayalı derleme sistemleriyle çalışmak zorlaşabilir. Bu tür sistemlerdeki sorun, sonuçta mühendislere çok fazla güç verirken sisteme yeterli güç olmamasıdır. Sistem, komut dosyalarının ne yaptığını bilemez. Bu nedenle, derleme adımlarını planlama ve yürütme konusunda son derece muhafazakar bir yaklaşım benimsemesi gerektiğinden performans düşer. Sistemin, her bir komut dosyasının gerektiği gibi çalıştığını doğrulamasının bir yolu yoktur. Bu nedenle, komut dosyaları karmaşık bir hale gelir ve sonuçta hata ayıklama gerektiren başka bir şey olur.

Derleme adımlarını paralel hale getirmenin zorluğu

Birçok derleme adımını paralel olarak yürütebilen çok sayıda çekirdeğe sahip modern geliştirme iş istasyonları, oldukça güçlüdür. Ancak görev tabanlı sistemler, yapılması gerektiği düşünüldüğünde bile görev yürütmeyi 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? Belki de aynı kaynaklara ulaşmıyorlarsa. Ancak belki olmayabilir. İkisi de durumlarını izlemek için aynı dosyayı kullanır ve aynı anda bunları çalıştırmak çakışmaya neden olur. Genel olarak sistemin bilmesi mümkün değildir. Bu nedenle, bu çakışmaları riske atmalıdır (hata ayıklaması çok zor olan derleme sorunlarına yol açabilir) veya tüm derlemeyi tek bir işlemde tek bir iş parçacığı üzerinde çalışacak şekilde sınırlamak zorundadır. Bu durum, güçlü bir geliştirici makinesinin büyük bir israfa neden olabilir ve derlemenin birden fazla makineye dağıtılması olasılığını tamamen ortadan kaldırır.

Artımlı derlemeleri gerçekleştirmede zorluk

İyi bir derleme sistemi, mühendislerin güvenilir artımlı derlemeler gerçekleştirmesine olanak tanır. Böylece küçük bir değişiklik, kod tabanının tamamının sıfırdan yeniden derlenmesini gerektirmez. Bu, özellikle derleme sistemi yavaşsa ve yukarıda belirtilen nedenlerden dolayı derleme adımlarını paralelleştiremiyorsa önemlidir. Ancak ne yazık ki görev tabanlı derleme sistemleri de burada sıkıntı yaşıyor. Görevler herhangi bir şey yapabileceği için önceden yapılmış olup olmadığını kontrol etmenin bir yolu yoktur. Birçok görev, bir dizi kaynak dosyayı alıp bir derleyici çalıştırması yeterlidir. Böylece, temeldeki kaynak dosyalar değişmediyse bunların yeniden çalıştırılması gerekmez. Ancak sistem, ek bilgi olmadan bunu kesin olarak söyleyemez. Görev, değişmiş olabilecek bir dosya indirir veya her çalıştırmada farklı olabilecek bir zaman damgası yazar. Doğruluğu garantilemek için sistem genellikle her derleme sırasında tüm görevleri yeniden çalıştırmalıdır. Bazı derleme sistemleri, mühendislerin bir görevin yeniden çalıştırılması gereken koşulları belirtmesini sağlayarak artımlı derlemeler sağlamaya çalışır. Bu bazen mümkün olsa da, çoğu zaman göründüğünden çok daha karmaşık bir sorun teşkil eder. Örneğin, dosyaların doğrudan diğer dosyalar tarafından dahil edilmesine izin veren C++ gibi dillerde, giriş kaynaklarını ayrıştırmadan, değişiklikler için izlenmesi gereken dosya grubunun tamamını belirlemek mümkün değildir. Mühendisler genellikle kısayolları kullanırlar ve bu kısayollar, bir görev sonucunun gerekli olmasa bile yeniden kullanıldığı nadir ve sinir bozucu sorunlara yol açabilir. Bu durum sık yaşandığında, mühendisler her yapıdan önce temiz bir şekilde çalışma alışkanlığı kazanır ve yeni bir duruma kavuşur. Bu da daha önce kademeli bir derlemeye sahip olma amacını tamamen ortadan kaldırır. Bir görevin ne zaman yeniden gerçekleştirilmesi gerektiğini belirlemek şaşırtıcı derecede dikkatsizdir ve insanlardan çok makineler tarafından daha iyi ele alınan bir iştir.

Komut dosyalarını yönetme ve hata ayıklamada zorluk

Son olarak, göreve dayalı derleme sistemlerinin uyguladığı derleme komut dosyalarıyla çalışmak genellikle zordur. Çoğunlukla daha az çalışılsa da derleme komut dosyaları, tıpkı oluşturulan sistem gibi bir koddur ve hataların kolayca gizlenebileceği yerlerdir. Görev tabanlı bir derleme sistemiyle çalışırken çok sık karşılaşılan hatalara ilişkin bazı örnekleri burada bulabilirsiniz:

  • A görevi, belirli bir dosyayı çıktı olarak üretmek için B görevine bağlıdır. B görevinin sahibi, diğer görevlerin buna bağlı olduğunu fark etmez ve bu nedenle görevi farklı bir konumda çıktı üretecek şekilde değiştirir. Biri A görevini çalıştırmaya çalışıp bunun başarısız olduğunu anlayana kadar bu durum tespit edilemez.
  • A görevi, B görevine bağlıdır. B görevi de B görevine bağlıdır. Bu görev, A görevinin gerektirdiği çıktı olarak belirli bir dosya üreten C görevine bağlıdır. B görevinin sahibi, artık C görevine bağımlı hale gelmesi gerekmediğine karar verir ve bu da B görevi C görevini hiç önemsemese de A görevinin başarısız olmasına neden olur.
  • Yeni bir görevin geliştiricisi, görevi çalıştıran makine hakkında kazayla bir aracın konumu veya belirli ortam değişkenlerinin değeri gibi bir varsayımda bulunur. Görev kullanıcının makinesinde çalışır, ancak başka bir geliştirici onu denediğinde başarısız olur.
  • Görevler, internetten dosya indirme veya bir derlemeye zaman damgası ekleme gibi belirli olmayan bir bileşen içerir. Artık insanlar derlemeyi her çalıştırdıklarında potansiyel olarak farklı sonuçlar alıyorlar. Diğer bir deyişle, mühendisler her zaman otomatik derleme sisteminde ortaya çıkan birbirlerinin hatalarını veya arızalarını yeniden oluşturup düzeltemiyor.
  • 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 ve C görevleri aynı dosyayı değiştiriyorsa A görevi, B ve C görevlerinden hangisinin önce bittiğine bağlı olarak farklı bir sonuç alır.

Performans, doğruluk veya sürdürülebilirlik ile ilgili bu problemleri, burada açıklanan göreve dayalı çerçeve içinde çözmenin genel amaçlı bir yolu yoktur. Mühendisler derleme sırasında rastgele kod yazabildikleri için sistem, derlemeleri her zaman hızlı ve doğru bir şekilde çalıştırabilmek için yeterli bilgiye sahip olamaz. Sorunu çözmek için gücü mühendislerin elinden alıp tekrar sistemin ellerine bırakmamız ve sistemin rolünü, çalışan görevler olarak değil, eserler üretmek olarak yeniden kavramlaştırmamız gerekiyor.

Bu yaklaşım, Blaze ve Bazel gibi yapı tabanlı derleme sistemlerinin geliştirilmesini sağladı.