---
title: 'Derleme Aracı Modeli'
source: 'https://academia.sh/tr/kurslar/java-ekosistem/derleme-araci-modeli'
course: 'Derleme, Test ve Ekosistem'
language: tr
updated: '2026-08-23T14:25:06+00:00'
license: 'CC BY-SA 4.0'
---

# Derleme Aracı Modeli

Bir bildirimden bir derleme aracının türettiği evre dizisi sabittir ve bildirimin yazılış sırasından etkilenmez: üç bildirilen ögeden dört evre çıkar, ileri sıralı ve geri sıralı aynı bildirimden aynı dört evre türer. Aynı bildirimden çıkan iki sınıf yolu — biri derlemenin, biri koşumun — kaynak düzeyinde tutulan bir açıklama tipiyle ayrılır: sınıf dosyasının sabit havuzunda hiç görünmez, koşum sınıf yolunda da hiç gerekmez.

JVM, Eşzamanlılık ve Başarım kursunun kapanış dersi tek bir soruyu ölçtü: bir adımı hangi
iş parçacığının koşturduğu, çağrı yazımından okunamıyordu — yalnız koşturarak görülüyordu.
Aynı satır bir iş parçacığında da, başka bir iş parçacığında da aynı yazımdı; koşturan
tarafı belirleyen şey kaynak metnin dışındaydı.

Bu ders aynı sorunun bir katman dışını soruyor. Bir Java programı yalnız bir sınıf yoluna
karşı değil, iki ayrı sınıf yoluna karşı yaşar: derlendiği yol ve koştuğu yol. Kaynak metin
— hatta derlenmiş sınıf dosyasının kendisi — bu iki yolun ne içermesi gerektiğini söylemez;
söyleyen şey kaynağın dışında duran bir bildirimdir. Derleyicinin bir tipi görmesi için
gereken şey ile çalışma zamanının aynı tipi bulması için gereken şey aynı küme değildir, ve
bu ayrımı üreten aracın kendisi bu dersin ve konunun sorusu.

Aracın adı yazılmıyor — hangi ürünün, hangi söz diziminin kullanıldığı bu dersin sorusu
değil. Sorulan şey şu: birkaç satırlık bir bildirimden bir derleme aracı ne kadarını
kendisi türetiyor, ve türettiği şey neden kaynakta hiç görünmeyen iki ayrı listeye
bölünüyor?

Kursun üç konusu da üçüncü taraf araç adlarıyla anılan alanlar; bu yüzden derleme aracının
kendisi burada bir **davranış** olarak kuruluyor, bir ürün olarak değil. Ölçülen şey bir
bildirimden bir evre dizisi türeten kural ve o evrelerin sonunda ortaya çıkan iki sınıf
yolu — hangi ürünün hangi bayrağı kabul ettiği değil. Standart kitaplığın kendisi bu
modelin gerçek çalışan parçasını sağlıyor: derleyici, program içinden `ToolProvider` ile
çağrılıyor ve gerçekten derliyor.

## Derleme Aracı Neyi Türetiyor

Bir bildirimin türettiği şey tek bir liste değil, iki ayrı şeydir: bir **evre dizisi**
(hangi sırayla ne yapılacağı) ve o evrelerin içinde kurulan **sınıf yolları** (her evrenin
hangi dizinlere baktığı). Bu iki şey birbirine karıştırılmamalı. "Kaynakları derle"
evresi tek bir derleme görevi çalıştırıyor: derleyiciyi bir kez çağırıyor. "Derleme sınıf
yolunu kur" evresi ise bildirimdeki her bağımlılığı tek tek gözden geçiren bir görev
çalıştırıyor — kapsamı derleme ya da ikisi olan her bağımlılığı listeye ekliyor,
kapsamı yalnız koşum olanı atlıyor. Aynı evre, "koşum sınıf yolunu kur", aynı bağımlılık
listesini bu kez tersinden süzüyor. Evrenin kendisi bir tane, ama içindeki derleme görevi
bildirimin uzunluğuna göre değişen sayıda iş yapıyor — bu, aşağıdaki ölçümde ayrı ayrı
görülecek iki sayının (bildirilen öge, türetilen evre) neden birbirine eşit çıkmayabileceğini
de açıklıyor.

## Bildirimden Türeyen Evre Dizisi

Bir derleme aracına verilen girdi adım adım bir yordam değil, bir **bildirimdir**
(declaration): hangi kaynak dizininin derleneceği, hangi bağımlılıkların hangi
**bağımlılık kapsamıyla** (dependency scope) kullanılacağı. Aracın kendisi bu bildirimden
bir **derleme yaşam döngüsü** (build lifecycle) türetir — sabit sırayla işleyen bir
**evre** (phase) dizisi. Her evre bir ya da daha çok **derleme görevi** (task) çalıştırır;
burada yalnız evrelerin kendisi ölçülüyor, görevlerin içeriği değil.

Aşağıdaki `Bildirim` sınıfı bir bağımlılığı yalnız adı ve kapsamıyla tutuyor; kapsam üç
değerden birini alıyor: yalnız derlemede gerekli, yalnız koşumda gerekli, ya da ikisinde
birden.

- **DR1.** Derleme aracı burada bir model olarak kuruluyor; hiçbir gerçek ürünün adı, hedef
  adı, komut satırı bayrağı ya da bildirim söz dizimi kullanılmıyor. Evre adları ve bildirim
  biçimi bu dersin kendi kararı.
- **DR2.** Bildirilen öge sayısı, bildirimdeki her satırın — kaynak dizini bir, her
  bağımlılık bir — tek öge sayıldığı bir toplam. Bir bağımlılığın kapsamı öge sayısını
  değiştirmiyor.
- **DR3.** Türetilen evre dizisi sabit dört evreden oluşuyor ve bağımlılık sayısından ya da
  bildirimin yazılış sırasından etkilenmiyor; sıra aracın kendi kuralı, bildirimin tercihi
  değil.

```java
// Bildirim.java — derleme aracina verilen bildirim: kaynak dizini adi ve bagimliliklarin kapsam etiketi
import java.util.*;

public class Bildirim {
    record Bagimlilik(String ad, String kapsam) {}

    final String kaynakAdi;
    final List<Bagimlilik> bagimliliklar;

    Bildirim(String kaynakAdi, List<Bagimlilik> bagimliliklar) {
        this.kaynakAdi = kaynakAdi;
        this.bagimliliklar = bagimliliklar;
    }

    int bildirilenOgeSayisi() {
        return 1 + bagimliliklar.size();
    }
}
```

```java
// Evreler.java — bildirimden turetilen evre dizisi: sira bildirimin yazilis sirasina bakmaz
import java.util.*;

public class Evreler {
    static List<String> turet(Bildirim b) {
        List<String> dizi = new ArrayList<>();
        dizi.add("temizle");
        dizi.add("kaynaklari derle");
        dizi.add("derleme sinif yolunu kur");
        dizi.add("kosum sinif yolunu kur");
        return dizi;
    }
}
```

`Evreler.turet` bildirimin içeriğine hiç bakmadan dört evre üretiyor: temizleme, kaynak
derleme, derleme sınıf yolunu kurma, koşum sınıf yolunu kurma. Bildirimdeki bağımlılık
sayısı bir, iki ya da on olsun, dönen dizinin uzunluğu değişmiyor — çünkü ölçülen şey
bağımlılıkların kendisi değil, aracın onlardan ne türettiği.

## Aynı Bildirimden İki Sayı, İki Sınıf Yolu

Ölçüm iki ayrı bildirimle başlıyor: aynı iki bağımlılık, biri ileri sırada yazılmış, öbürü
ters sırada. `Etiket` yalnız derlemede gerekli olarak, `Servis` yalnız koşumda gerekli
olarak işaretleniyor. `Etiket`, saklama politikası (retention policy) kaynak düzeyinde
tutulan bir açıklama (annotation) tipi: derleyici onu tip denetiminde kullanıyor, ama sınıf
dosyasına hiçbir iz bırakmıyor. `Servis`'e erişimse tam ters yoldan kuruluyor: `Program` onu
hiç doğrudan adlandırmıyor, yalnız bir ad dizgisiyle arıyor — bu yüzden derleme sınıf yoluna
hiç girmesi gerekmiyor.

- **DR4.** Etiket, saklama politikası kaynak düzeyinde tutulan bir açıklama tipi; derleyici
  tip denetiminde kullanır, sınıf dosyasına hiçbir referans bırakmaz.
- **DR5.** Servis'e erişim doğrudan tip referansıyla değil, ada göre aranan bir çağrıyla
  kuruluyor; bu yüzden derleme sınıf yoluna hiç girmiyor.
- **DR6.** Derleme sınıf yolu ile koşum sınıf yolu iki ayrı dizin listesi olarak kuruluyor;
  ikisi arasında ortak hiçbir dizin yok — Etiket'in dizini yalnız derlemede, Servis'in
  dizini yalnız koşumda veriliyor.

```java
// AracModeli.java — bildirimden turetilen evre dizisi bildirim sirasindan bagimsizdir, ve tek bildirimden iki sinif yolu cikar
import java.io.*;
import java.lang.classfile.*;
import java.lang.classfile.constantpool.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;

public class AracModeli {

    static Path yaz(Path dizin, String ad, String kaynak) throws IOException {
        Files.createDirectories(dizin);
        Path p = dizin.resolve(ad + ".java");
        Files.writeString(p, kaynak);
        return p;
    }

    static boolean derle(Path cikti, List<Path> sinifYolu, Path kaynak) throws IOException {
        Files.createDirectories(cikti);
        List<String> arg = new ArrayList<>(List.of("-d", cikti.toString()));
        if (!sinifYolu.isEmpty()) {
            StringJoiner sj = new StringJoiner(File.pathSeparator);
            for (Path p : sinifYolu) sj.add(p.toString());
            arg.add("-classpath");
            arg.add(sj.toString());
        }
        arg.add(kaynak.toString());
        ToolProvider derleyici = ToolProvider.findFirst("javac").orElseThrow();
        StringWriter sessiz = new StringWriter();
        int sonuc = derleyici.run(new PrintWriter(sessiz), new PrintWriter(sessiz), arg.toArray(new String[0]));
        return sonuc == 0;
    }

    static boolean sabitHavuzundaAdiGeciyorMu(Path sinifDosyasi, String ad) throws IOException {
        ClassModel model = ClassFile.of().parse(sinifDosyasi);
        for (PoolEntry e : model.constantPool()) {
            if (e instanceof ClassEntry ce && ce.asInternalName().equals(ad)) return true;
        }
        return false;
    }

    public static void main(String[] args) throws Exception {
        var etiket = new Bildirim.Bagimlilik("Etiket", "derleme");
        var servis = new Bildirim.Bagimlilik("Servis", "kosum");

        Bildirim ileriSira = new Bildirim("kaynak", List.of(etiket, servis));
        Bildirim geriSira = new Bildirim("kaynak", List.of(servis, etiket));

        List<String> evreIleri = Evreler.turet(ileriSira);
        List<String> evreGeri = Evreler.turet(geriSira);

        System.out.println("-- evre dizisi, bildirim sirasindan bagimsiz --");
        System.out.println("ileri sirali bildirimden turetilen evreler: " + evreIleri);
        System.out.println("geri sirali bildirimden turetilen evreler : " + evreGeri);
        System.out.println("iki evre dizisi ayni mi                   : " + evreIleri.equals(evreGeri));
        System.out.println("bildirilen oge sayisi                     : " + ileriSira.bildirilenOgeSayisi());
        System.out.println("turetilen evre sayisi                     : " + evreIleri.size());

        Path kok = Files.createTempDirectory("arac");
        Path etiketD = kok.resolve("etiket"), servisD = kok.resolve("servis"), programD = kok.resolve("program");

        Path sEtiket = yaz(kok.resolve("kaynak"), "Etiket",
                "import java.lang.annotation.*;\n"
              + "@Retention(RetentionPolicy.SOURCE)\n"
              + "public @interface Etiket { String deger(); }");
        Path sServis = yaz(kok.resolve("kaynak"), "Servis",
                "public class Servis { public static String calistir() { return \"servis calisti\"; } }");
        Path sProgram = yaz(kok.resolve("kaynak"), "Program",
                "@Etiket(deger = \"ornek\")\n"
              + "public class Program {\n"
              + "  public static void main(String[] a) throws Exception {\n"
              + "    Class<?> s = Class.forName(\"Servis\");\n"
              + "    String r = (String) s.getMethod(\"calistir\").invoke(null);\n"
              + "    System.out.println(\"servis (yansima): \" + r);\n"
              + "  }\n"
              + "}");

        derle(etiketD, List.of(), sEtiket);
        derle(servisD, List.of(), sServis);

        List<Path> derlemeSinifYolu = List.of(etiketD);
        boolean programDerlendi = derle(programD, derlemeSinifYolu, sProgram);
        System.out.println();
        System.out.println("-- tek bildirimden iki sinif yolu --");
        System.out.println("Program, derleme sinif yoluna (yalniz Etiket) karsi derlendi mi   : " + programDerlendi);
        System.out.println("Program.class sabit havuzunda Etiket'e referans var mi            : "
                + sabitHavuzundaAdiGeciyorMu(programD.resolve("Program.class"), "Etiket"));

        List<Path> kosumSinifYolu = List.of(programD, servisD);
        URL[] url = new URL[kosumSinifYolu.size()];
        for (int i = 0; i < url.length; i++) url[i] = kosumSinifYolu.get(i).toUri().toURL();
        try (URLClassLoader yukleyici = new URLClassLoader(url, AracModeli.class.getClassLoader().getParent())) {
            Class<?> sinif = Class.forName("Program", true, yukleyici);
            System.out.println("Program, kosum sinif yoluna (Etiket yok, Servis var) karsi kosuyor mu:");
            sinif.getMethod("main", String[].class).invoke(null, (Object) new String[0]);
        }
    }
}
```

```
-- evre dizisi, bildirim sirasindan bagimsiz --
ileri sirali bildirimden turetilen evreler: [temizle, kaynaklari derle, derleme sinif yolunu kur, kosum sinif yolunu kur]
geri sirali bildirimden turetilen evreler : [temizle, kaynaklari derle, derleme sinif yolunu kur, kosum sinif yolunu kur]
iki evre dizisi ayni mi                   : true
bildirilen oge sayisi                     : 3
turetilen evre sayisi                     : 4

-- tek bildirimden iki sinif yolu --
Program, derleme sinif yoluna (yalniz Etiket) karsi derlendi mi   : true
Program.class sabit havuzunda Etiket'e referans var mi            : false
Program, kosum sinif yoluna (Etiket yok, Servis var) karsi kosuyor mu:
servis (yansima): servis calisti
```

Çıktının ilk yarısı bildirim sırasının evre dizisini etkilemediğini gösteriyor: ileri
sıralı ve geri sıralı bildirimden türeyen evre dizileri birebir aynı, `equals`
karşılaştırması doğru veriyor. Bildirilen öge sayısı **3** (bir kaynak dizini, iki
bağımlılık), türetilen evre sayısı **4** — iki sayı birbirinden bağımsız, biri öbürünü
belirlemiyor. Aracın türettiği şey bildirimin uzunluğuna değil, kendi kuralına bağlı.

İkinci yarı asıl iddiayı ölçüyor. `Program`, yalnız `Etiket`in bulunduğu bir derleme sınıf
yoluna karşı derleniyor ve derleme başarılı — derleyici `Etiket`i tip denetiminde kullandı
ama sınıf dosyasına hiçbir iz bırakmadı; sabit havuzunda `Etiket` adı hiç geçmiyor, bu satır
sınıf dosyası okunarak doğrulanıyor, tahmin edilmiyor. Sonra aynı derlenmiş `Program`,
tamamen farklı bir koşum sınıf yoluna karşı çalıştırılıyor: bu yolda `Etiket` hiç yok, ama
`Servis` var. Program hatasız koşuyor ve `Servis`i yansımayla buluyor. Derleyicinin gördüğü
ad kümesi (`Etiket`, `Program`) ile çalışma zamanının bulduğu ad kümesi (`Servis`,
`Program`) örtüşmüyor — aynı tek bildirimden, aynı tek derlenmiş sınıftan, iki ayrı sınıf
yolu çıktı.

Bu iki sonuç aynı satırda birlikte okunmalı. Sabit havuzu denetimi olumsuz bir kanıt
veriyor: `Etiket` orada değil, dolayısıyla koşum sınıf yolunun onu içermesi gerekmiyor —
bu, tahmine değil doğrudan sınıf dosyasının kendisine dayanıyor. Koşum denemesi ise olumlu
bir kanıt veriyor: `Servis` gerçekten koşum sınıf yolunda bulunuyor ve gerçekten
çağrılıyor. İki kanıt birlikte, derleme sınıf yolu ile koşum sınıf yolunun aynı bildirimden
çıkan iki ayrı küme olduğunu, birinin öbürünün alt kümesi ya da üst kümesi olmak zorunda
olmadığını gösteriyor.

## Sınırlayıcı Ölçüm: Sıra Bir Kural, Bir Tercih Değil

Yukarıdaki ölçümün ilk satırı bir sınır koyuyor: evre sırasının bildirim sırasından
bağımsız çıkması bir gözlem değil, aracın modelinin bir sonucu. `Evreler.turet` bildirimin
`bagimliliklar` listesine hiç bakmıyor; yalnız bildirimin var olduğunu biliyor. Bu yüzden
aynı bağımlılık kümesi yazılış sırası tersine çevrilerek verildiğinde bile evre dizisi
değişmiyor — sıra bir tercih olsaydı, iki bildirimden iki ayrı dizi çıkardı.

Bu ayrım önemli, çünkü bildirim ile evre dizisi arasındaki ilişki bir eşleme değil, bir
kural uygulamasıdır. Bildirimdeki bir satırı silmek ya da eklemek türetilen evrelerin
**içeriğini** değiştirmiyor, yalnız o evrelerin **içindeki** derleme görevlerinin kapsamını
değiştiriyor — örneğin derleme sınıf yolunu kurma evresi artık bir bağımlılık daha
içeriyor. Evre sırasının kendisi sabit kalıyor.

Aynı kural bildirimin uzunluğundan da bağımsız. Ölçümdeki bildirim iki bağımlılık
taşıyordu; bildirim beş, on ya da hiç bağımlılık taşımasaydı da `Evreler.turet` yine dört
evre dönerdi, çünkü yöntemin gövdesi bağımlılık listesini hiç okumuyor. Bu, aracın bir
**yorumlayıcı** değil bir **kural uygulayıcı** olduğunu gösteriyor: girdi ne kadar
değişirse değişsin, evre kümesi aynı kalıyor, değişen yalnız her evrenin içinde koşan
derleme görevinin ne kadar veriyle çalıştığı. Sonraki derste ölçülecek altı tip de bu
sabit evre kümesinin **içinden** geçecek — "derleme sınıf yolunu kur" ve "koşum sınıf
yolunu kur" evrelerinin her ikisi de aynı altı bağımlılığı görecek, ama her biri onlardan
farklı bir alt kümeyi seçecek.

Sıradaki ders bu ayrımı derinleştiriyor: bağımlılığın kapsamı yalnız o evrenin içindeki
listeyi değil, iki sınıf yolunun içeriğini de belirliyor — ve o belirleme, bu derste
görülen iki ayrı kümenin (biri derlemede, biri koşumda gerekli olan) tam ölçüsünü verecek.

## Özet

- Bir derleme aracı, bir bildirimden sabit bir evre dizisi türetir; sıra bildirimin yazılış
  sırasından bağımsızdır ve aracın kendi kuralıdır.
- Bildirilen öge sayısı (bildirimdeki satır sayısı) ile türetilen evre sayısı ayrı
  kavramlardır; biri öbürünü belirlemez — burada 3 bildirilen ögeden 4 evre türer.
- Aynı bildirimden iki ayrı sınıf yolu çıkar: derleme sınıf yolu ve koşum sınıf yolu; bir
  tipin hangisinde gerekli olduğu, tipin kaynakta nasıl kullanıldığına bağlıdır (doğrudan
  referans mı, ada göre arama mı).
- Saklama politikası kaynak düzeyinde tutulan bir açıklama tipi (`Etiket`), derleyicinin
  gördüğü ama sınıf dosyasının hiç taşımadığı bir bağımlılık örneğidir; bu, sabit havuzu
  okunarak doğrudan doğrulanır.

## Sonraki Adım

Bu derste bir bağımlılığın hangi sınıf yolunda gerekli olduğu iki örnekle — biri derlemede,
biri koşumda — gösterildi. Ama altı ayrı bağımlılık tipi, aralarında doğrudan bağımlılık ve
geçişli bağımlılık, tek bir arayüz ve onun iki gerçekleştirimi bir arada bulunduğunda tablo
çok daha karmaşık bir hâl alıyor. Sıradaki ders bağımlılık kapsamının kendisini ölçüyor:
altı tipin kaçı yalnız derlemede, kaçı yalnız koşumda, kaçı ikisinde birden gerekiyor — ve
iki sınıf yolu ayrıldığında ortaya çıkan dört kusurdan kaçının hiç ses çıkarmadığı.
