Görev Tabanlı Derleme Sistemleri

Bu sayfada göreve dayalı derleme sistemleri, nasıl çalıştıkları ve görev tabanlı sistemlerde oluşabilecek bazı sorunlar ele alınmaktadır. Kabuk komut dosyalarından sonra, derlemenin sonraki mantıksal evrimi, görev tabanlı derleme sistemleridir.

Görev tabanlı derleme sistemlerini anlama

Göreve dayalı bir yapı sisteminde işin temel birimi görevdir. Her görev, her türlü 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 günümüzde 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 olarak yazılır ve görev listesiyle birlikte derlemeyle ilgili bazı basit meta verileri tanımlar (XML'deki <target> etiketleri). (Ant, bir görevi temsil etmek için target kelimesini, komutları belirtmek içinse görev kelimesini kullanır.) Her görev Ant tarafından tanımlanan olası komutların listesini yürütür. Bu işlemler arasında dizin oluşturma ve silme, javac çalıştırma ve 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 üzerinden bağlı olduğu görevleri de tanımlayabilir. Bu bağımlılıklar Şekil 1'de gösterildiği 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ştiriyor. Örneğin, bir kullanıcı ant dist yazdığında Ant aşağıdaki adımları uygular:

  1. Geçerli dizinde build.xml adlı bir dosya 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 tespit eder.
  3. compile adlı görevi arar ve init adlı göreve bağımlı olduğunu keşfeder.
  4. init adlı görevi arar ve bağımlı 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ı koşuluyla, 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ı koşuluyla, dist görevinde tanımlanan komutları yürütür.

Son olarak, dist görevini çalıştırı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 diziminin kaldırılmasıyla birlikte, derleme dosyası ve derleme komut dosyası aslında çok farklı olmaz. Ama bu şekilde ç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 yollarla 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ım parçasıdır. Maven ve Gradle gibi diğer araçlar, aradan geçen yıllarda Ant'i geliştirdiler ve temelde, dış bağımlılıkların otomatik yönetimi ve XML içermeyen daha derli toplu 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ı görev olarak ilkeli ve modüler bir şekilde yazmasına ve bu görevleri yürütüp bunlar arasındaki bağımlılıkları yönetmek için araçlar sağlamasına olanak tanırlar.

Görev tabanlı derleme sistemlerinin karanlık tarafı

Bu araçlar temelde mühendislerin herhangi bir komut dosyasını görev olarak tanımlamasına olanak tanıdığından son derece güçlüdür ve hayal edebileceğiniz hemen hemen her şeyi yapabilmenizi sağlar. 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 sistemlerin sorunu, mühendislere çok fazla güç verilmesi ve sisteme yeterli güç olmamasıdır. Sistem, komut dosyalarının ne yaptığıyla ilgili hiçbir fikri olmadığından, derleme adımlarını planlama ve yürütme şeklinin çok dikkatli olması gerektiğinden performans düşer. Sistemin, her bir komut dosyasının gerektiği gibi çalıştığını onaylaması mümkün değildir. Bu nedenle, komut dosyaları karmaşık bir şekilde büyür ve sonuçta hata ayıklama gerektiren başka bir şey olur.

Derleme adımlarını paralelleştirmenin zorluğu

Birçok derleme adımını paralel olarak yürütebilen çok sayıda çekirdek bulunan modern geliştirme iş istasyonları, oldukça güçlüdür. Ancak görev tabanlı sistemler, bunları yapması gerekirmiş gibi görünse 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 bunları aynı anda çalıştırmak güvenli midir? Belki de kaynakların hiçbirine ulaşmıyorlar. Ancak belki olmayabilir. Belki her ikisi de durumlarını izlemek için aynı dosyayı kullanır ve aynı anda çalıştırmak bir çakışmaya neden olur. Genel olarak sistemin öğrenmesinin bir yolu yoktur. Bu nedenle, sistemin bu çakışmaları riske atması (hata ayıklaması çok zor, nadir görülen ancak çok zor derleme sorunlarına yol açması) veya tüm derlemeyi tek bir işlemdeki tek bir iş parçacığında çalışacak şekilde kısıtlamak zorunda kalır. Bu durum, güçlü bir geliştirici makinesinin israfına yol açabilir 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ı paralel yapamıyorsa önemlidir. Ancak ne yazık ki görev tabanlı derleme sistemleri bu noktada da zorlanıyor. Görevler herhangi bir şey yapabileceği için genel olarak önceden yapılmış olup olmadığını kontrol etmenin bir yolu yoktur. Birçok görev, bir dizi kaynak dosya alıp bir derleyici çalıştırarak ikili program kümesi oluşturur. Böylece, temel kaynak dosyalar değişmemişse görevlerin yeniden çalıştırılması gerekmez. Ancak ek bilgi olmadan sistem 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 sistemin genellikle her derleme sırasında her görevi yeniden çalıştırması gerekir. Bazı derleme sistemleri, mühendislerin bir görevin yeniden çalıştırılması gereken koşulları belirtmesine izin vererek artımlı derlemeler sağlamaya çalışır. Bu bazen uygundur, ancak çoğunlukla göründüğünden çok daha karmaşık bir sorundur. Örneğin, dosyaların doğrudan başka 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ısayollara başvurur ve bu kısayollar, bir görev sonucunun kullanılmasa bile yeniden kullanıldığı nadir ve sinir bozucu sorunlara yol açabilir. Bu durum sık yaşandığında, mühendisler her derlemeden önce temiz bir şekilde çalışarak yeni bir duruma geçme alışkanlığı kazanır. Bu alışkanlık, baştan itibaren artımlı yapı oluşturma amacını tamamen ortadan kaldırır. Bir görevin ne zaman yeniden çalıştırılması gerektiğini belirlemek şaşırtıcı derecede kolay değildir ve makineler tarafından insanlardan daha iyi yönetilen 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ı geliştirilmekte olan sistem gibi bir koddur ve hataların kolayca gizlenebileceği yerlerdir. Aşağıda, göreve dayalı bir derleme sistemiyle çalışırken çok yaygın olan hatalara ilişkin bazı örnekler verilmiştir:

  • A görevi, çıktı olarak belirli bir dosyayı ü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 farklı bir konumda çıktı üretecek şekilde görevi değiştirir. Biri A görevini çalıştırmayı deneyip işlemin başarısız olduğunu bulana kadar bu durum tespit edilemez.
  • A görevi, B görevine bağlıdır. B görevi ise A görevinin gerektirdiği çıktı olarak belirli bir dosyayı üreten C görevine bağlıdır. B görevinin sahibi, artık C görevine bağımlı olmasına gerek olmadığına karar verir ve bu da B göreviyle hiç ilgilenmese 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 yanlışlıkla bir varsayımda bulunur (ör. aracın konumu veya belirli ortam değişkenlerinin değeri). Görev kullanıcının makinesinde çalışır ancak başka bir geliştirici denediğinde başarısız olur.
  • Bir görev, 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 almaktadır. Diğer bir deyişle, mühendisler otomatik derleme sisteminde ortaya çıkan birbirlerinin arızalarını veya hatalarını her zaman yeniden oluşturup düzeltemez.
  • 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 hem B hem de C aynı dosyayı değiştiriyorsa B ve C görevlerinden hangisinin önce bittiğine bağlı olarak A görevi farklı bir sonuç alır.

Bu performans, doğruluk veya sürdürülebilirlik sorunlarını burada açıklanan göreve dayalı çerçeve içinde çözmenin genel bir yolu yoktur. Mühendisler derleme sırasında rastgele çalışan kodlar 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 eline vermemiz ve sistemin rolünü, görevleri yürütmek değil, eserler üretmek olarak yeniden kavramlaştırmamız gerekir.

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