Görev Tabanlı Derleme Sistemleri

Sorun bildirme Kaynağı görüntüleme Nightly · 7.4 .

Bu sayfada, görev tabanlı derleme sistemleri, bunların işleyiş şekli ve görev tabanlı sistemlerde yaşanabilecek bazı komplikasyonlar ele alınmaktadır. Kabuk komut dosyalarından sonra, görev tabanlı derleme sistemleri derlemenin mantıksal olarak bir sonraki evrimidir.

Görev tabanlı derleme sistemlerini anlama

Görev tabanlı bir derleme sisteminde temel çalışma birimi görevdir. Her görev, her türlü mantığı yürütebilen bir komut dosyasıdır ve görevler, diğer görevleri kendilerinden önce çalıştırılması gereken bağımlılık olarak belirtir. Ant, Maven, Gradle, Grunt ve Rake gibi günümüzde kullanılan büyük yapı sistemlerinin çoğu görev tabanlı. Modern derleme sistemlerinin çoğu, kabuk komut dosyaları yerine derlemenin nasıl yapılacağını açıklayan derleme dosyaları oluşturmaları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 biçiminde yazılmıştır ve görevlerin listesinin (XML'deki <target> etiketleri) yanı sıra derlemeyle ilgili bazı basit meta verileri tanımlar. (Ant, görev'i temsil etmek için hedef kelimesini, komutları belirtmek için ise görev 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 her tür mantığı kapsayacak şekilde genişletilebilir. Her görev, bağımlı özelliği aracılığıyla bağımlı olduğu görevleri de tanımlayabilir. Bu bağımlılıklar, Şekil 1'de görüldüğü gibi döngüsel olmayan bir grafik oluşturur.

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

Şekil 1. Bağımlılıkları gösteren döngüsel olmayan bir grafik

Kullanıcılar, Ant'ın komut satırı aracına görevler sağlayarak derleme yapar. Örneğin, bir kullanıcı ant dist yazdığında Ant aşağıdaki adımları uygular:

  1. Mevcut 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 keşfeder.
  3. compile adlı görevi arar ve init adlı göreve bağımlılığı olduğunu keşfeder.
  4. init adlı görevi arar ve bu görevin bağımlılığı olmadığını tespit eder.
  5. init görevinde tanımlanan komutları yürütür.
  6. compile görevinin tüm bağımlılıkları çalıştırılmışsa compile görevinde tanımlanan komutları yürütür.
  7. dist görevinin tüm bağımlılıkları çalıştırılmışsa dist görevinde tanımlanan komutları yürütür.

Sonuç olarak, dist görevi çalıştırıldığında 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şturabilir ve bunları birbirine bağlayabiliriz. Mevcut görevlere bağlı yeni görevleri kolayca keyfi ve karmaşık şekillerde 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.

Karınca, ilk olarak 2000'de piyasaya sürülen eski bir yazılımdır. Maven ve Gradle gibi diğer araçlar, geçen yıllar içinde Ant'ı geliştirdi ve harici bağımlıların otomatik yönetimi ve XML içermeyen daha temiz bir söz dizimi gibi özellikler ekleyerek temelde Ant'ın yerini aldı. Ancak bu yeni sistemlerin yapısı aynıdır: Mühendislerin, derleme komut dosyalarını görevler olarak temelli ve modüler bir şekilde yazmalarına olanak tanır ve bu görevleri yürütmek ve aralarındaki bağımlılıkları yönetmek için araçlar sağlar.

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

Bu araçlar, mühendislerin herhangi bir komut dosyasını görev olarak tanımlamasına olanak tanır. Bu nedenle son derece güçlüdür ve hayal edebileceğiniz hemen hemen her şeyi yapmanıza olanak tanır. Ancak bu gücün dezavantajları da vardır. Görev tabanlı derleme sistemlerinin derleme komut dosyaları daha karmaşık hale geldikçe bu sistemlerle çalışmak zor olabilir. Bu tür sistemlerin sorunu, mühendislere çok fazla, sisteme ise yeterince güç vermesidir. Sistem, komut dosyalarının ne yaptığı hakkında hiçbir fikre sahip olmadığından derleme adımlarını planlama ve yürütme konusunda çok ihtiyatlı olması gerekir. Bu da performansı olumsuz etkiler. Ayrıca sistemin her komut dosyasının yapması gerekeni yaptığını onaylaması mümkün değildir. Bu nedenle komut dosyalarının karmaşıklığı artar ve sonunda hata ayıklama gerektiren başka bir şey haline gelir.

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

Modern geliştirme iş istasyonları, paralel olarak birkaç derleme adımı yürütebilen birden fazla çekirdeğe sahip olduğundan oldukça güçlüdür. Ancak görev tabanlı sistemler, yapabilecekleri halde çoğu zaman görev yürütmeyi paralelleştiremez. 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 mi? Aynı kaynaklara dokunmuyorlarsa evet. Ancak belki de değil; her ikisi de durumlarını izlemek için aynı dosyayı kullanır ve bunları aynı anda çalıştırmak bir çakışmaya neden olur. Genel olarak sistemin bunu bilmesi mümkün değildir. Bu nedenle, ya bu çakışmaları göze alması (nadir ancak hata ayıklaması çok zor olan derleme sorunlarına neden olur) ya da derlemenin tamamını tek bir işlemde tek bir iş parçacığında çalıştıracak şekilde kısıtlaması gerekir. Bu durum, güçlü bir geliştirici makinesinin büyük bir israfına yol açabilir ve derlemenin birden fazla makineye dağıtılmasını tamamen imkansız kılar.

Artımlı derleme yapmanın zorluğu

İyi bir derleme sistemi, mühendislerin güvenilir artımlı derlemeler yapmasına olanak tanır. Böylece küçük bir değişiklik, tüm kod tabanının sıfırdan yeniden oluşturulmasını gerektirmez. Bu, özellikle derleme sistemi yavaşsa ve yukarıda belirtilen nedenlerle derleme adımlarını paralelleştiremiyorsa önemlidir. Ancak maalesef görev tabanlı derleme sistemleri de bu konuda zorlanıyor. Görevler her şeyi yapabildiğinden, genel olarak bu görevlerin daha önce yapılıp yapılmadığını kontrol etmek mümkün değildir. Birçok görev, bir dizi kaynak dosya alır ve bir dizi ikili dosya oluşturmak için derleyici çalıştırır. 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. Görev, değişmiş olabilecek bir dosyayı indirebilir veya her çalıştırmada farklı olabilecek bir zaman damgası yazabilir. 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 yeniden çalıştırılması gereken koşulları belirtmesine izin vererek artımlı derlemeleri etkinleştirmeye çalışır. Bazen bu mümkün olsa da çoğu zaman göründüğünden çok daha karmaşık bir sorundur. Ö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ır ve bu kısayollar, bir görev sonucunun gerekmediği halde yeniden kullanıldığı nadir ve can sıkıcı sorunlara yol açabilir. Bu durum sık sık yaşandığında mühendisler yeni bir durum elde etmek için her derlemeden önce temiz derleme yapma alışkanlığı edinir. Bu da ek derlemenin amacını tamamen ortadan kaldırır. Bir görevin ne zaman yeniden çalıştırılması gerektiğini belirlemek şaşırtıcı derecede hassas bir konudur ve makineler tarafından insanlardan daha iyi yönetilen bir iştir.

Komut dosyalarını yönetme ve hata ayıklama konusunda zorluk

Son olarak, görev tabanlı derleme sistemlerinin uyguladığı derleme komut dosyalarıyla çalışmak genellikle oldukça zordur. Derleme komut dosyaları, genellikle daha az incelik gösterse de aynı sistem üzerinde çalışır ve hataların kolayca gizlenebildiği yerlerdir. Görev tabanlı bir derleme sistemiyle çalışırken çok sık karşılaşılan hatalara örnek olarak şunlar verilebilir:

  • A görevi, çıkış olarak belirli bir dosya üretmesi için B görevine bağlıdır. B görevinin sahibi, diğer görevlerin bu göreve bağlı olduğunu fark etmez ve farklı bir konumda çıktı oluşturacak şekilde değiştirir. Bu durum, bir kullanıcı A görevini çalıştırmayı deneyip başarısız olduğunu görene kadar algılanmaz.
  • Görev A, görev B'ye bağlıdır. Görev B, görev C'ye bağlıdır. Görev C, A görevinin ihtiyaç duyduğu belirli bir dosyayı çıkış olarak üretir. Görev B'nin sahibi, artık görev C'ye bağlı kalmasına gerek olmadığına karar verir. Bu durum, görev B'nin görev C ile hiçbir ilgisi olmaması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ış 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 kesin olmayan bir bileşen içerir. Artık kullanıcılar derlemeyi her çalıştırdığında farklı sonuçlar elde ediyor. Bu da mühendislerin her zaman birbirlerinin hatalarını veya otomatik derleme sisteminde oluşan hataları yeniden oluşturup düzeltemeyebileceği anlamına geliyor.
  • Birden fazla bağımlılığı olan görevler yarış koşulları oluşturabilir. A görevi hem B görevi hem de C görevine bağlıysa ve B görevi ile C görevi 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.

Burada açıklanan görev tabanlı çerçevede bu performans, doğruluk veya sürdürülebilirlik sorunlarını çözmenin genel bir yolu yoktur. Mühendisler derleme sırasında çalıştırılacak rastgele kod yazabildikleri sürece sistem, derlemeleri her zaman hızlı ve doğru şekilde çalıştırabilecek yeterli bilgiye sahip olamaz. Sorunu çözmek için mühendislerin elindeki bazı yetkileri sisteme geri vermemiz ve sistemin rolünü görevleri yürütmek yerine yapı üretmek olarak yeniden tanımlamamız gerekiyor.

Bu yaklaşım, Blaze ve Bazel gibi yapıya dayalı derleme sistemlerinin oluşturulmasına yol açtı.