---
title: 'Derleme Sistemleri'
source: 'https://academia.sh/tr/kurslar/c-sistem-programlama/derleme-sistemleri'
course: 'C ile Sistem Programlama'
language: tr
updated: '2026-09-26T03:02:44+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir derleme hedefinin bağımlılık kaydını ve zaman damgası kuralını C yapılarıyla kurar'
    - 'Yazılmamış bir bağımlılığın hedefi neden eski sürümde bıraktığını ölçer'
    - 'Bu görünürlük sorusunu bir önceki modülün aşama sorusundan ayırt eder'
---

# Derleme Sistemleri

Bağımlılığı yazılmamış bir başlığın kural tabanlı bir derleme modelinde neden hâlâ eski sürümde kaldığını, zaman damgası karşılaştırmasını C içinde kurarak ölçer.

Önceki konu bir kaynağın çıkışta kime bırakıldığını ölçtü — sahibi belli bir bloğun, elden
çıkarken kimin elinde kaldığını. Bu ders aynı soruyu kaynak dosyadan derlenmiş programa
taşıyor: bir başlık dosyası değiştiğinde, o değişikliğin bağlı olduğu her hedefe gerçekten
ulaşıp ulaşmadığını. Ulaşmazsa hedef, yazan tarafın elindeki yeni sürümden habersiz, eski
halinin bir kopyasını taşımaya devam eder.

Kural tabanlı bir derleme sistemi bu kararı tek bir karşılaştırmayla verir: bir hedefin
bağımlı olduğu her girdinin zaman damgası, hedefin kendi üretildiği zaman damgasından daha
mı yeni? Öyleyse yeniden üretilir. Bu kural doğru çalışması için hedefin bağımlılık
listesinin **eksiksiz** olmasını ister — listede olmayan bir girdi, değişse bile hiç
sorulmaz.

Bu karar tek bir hedefle sınırlı kalmaz. Bir program genellikle birden çok hedeften
kurulur ve bu hedefler birbirine bağımlı olabilir: bir kaynak dosya bir başlığa, o başlık
başka bir başlığa bağımlı olabilir. Kural tabanlı sistem bu zinciri **geçişli** olarak
izler — zincirin en altındaki bir değişiklik, aradaki her hedefi tek tek yeniden üretim
listesine sokar. Zincirin ortasında bir hedefin bağımlılık listesi eksikse, o hedeften
sonraki her şey de bu eksikliği devralır; eksiklik yalnız bir hedefi değil, ona bağlı
bütün alt ağacı etkiler.

## Zaman Damgası ve Bağımlılık Kararı

Aşağıdaki model bir derleme hedefinin durumunu tutar: en son üretildiği zaman damgası ve o
anda gömülen sürüm numarası. Karar tek bir işlevde toplanır.

```c
// hedef.h — bir derleme hedefinin bagimlilik kaydini tutan yapi
#ifndef HEDEF_H
#define HEDEF_H

struct Hedef {
    int uretim_zamani;    /* en son uretildiginde okunan zaman damgasi */
    int uretilen_surum;   /* o uretimde govdeye gomulen surum numarasi */
};

/* Bagimliligin zaman damgasi hedefin uretim zamanindan yeniyse hedef
   yeniden uretilir ve uretilen_surum baslik_surumu'ne guncellenir. Bu
   cagri hic yapilmazsa -- yani bagimlilik listeye hic yazilmamissa --
   hedef eski durumunu degistirmeden korur. */
void bagimliligi_denetle(struct Hedef *h, int baslik_zamani, int baslik_surumu);

#endif
```

Kural tek yerde yaşar; başlık zamanı, hedefin kendi zamanından yeniyse yeniden üretim
gerçekleşir.

```c
// hedef.c — karar tek kuralla veriliyor: bagimlilik hedeften daha yeni mi
#include "hedef.h"

void bagimliligi_denetle(struct Hedef *h, int baslik_zamani, int baslik_surumu) {
    if (baslik_zamani > h->uretim_zamani) {
        h->uretilen_surum = baslik_surumu;
        h->uretim_zamani = baslik_zamani;
    }
}
```

Şimdi aynı başlangıç durumundaki iki hedef karşılaştırılıyor. `yazilmis` hedefi için
`bagimliligi_denetle` başlığın **güncel** zaman damgası ve sürümüyle çağrılıyor — bağımlılık
listede yazılı, derleme sistemi başlığın değiştiğini biliyor. `yazilmamis` hedefi için bu
çağrı hiç yapılmıyor; sanki o hedefin bağımlılık listesinde başlık hiç geçmiyormuş gibi.
Başlığın kendisi ikisi için de aynı şekilde 2. sürüme çıktı; fark yalnız derleme sisteminin
bunu **bilip bilmediğinde**.

```c
// olcum.c — yazilmis/yazilmamis karari ve uc halkali zincirin karari tek kosumda
#include <stdio.h>
#include "hedef.h"

int main(void) {
    struct Hedef yazilmis = { 10, 1 };     /* uretim_zamani=10, surum=1 */
    struct Hedef yazilmamis = { 10, 1 };   /* ayni baslangic durumu */

    /* baslik gercekte 2. surume, zaman damgasi 20'ye guncellendi */
    bagimliligi_denetle(&yazilmis, 20, 2);
    /* yazilmamis icin bu satir yok -- bagimlilik listede hic gecmiyor */

    printf("yazilmis bagimlilik uretilen_surum: %d\n", yazilmis.uretilen_surum);
    printf("yazilmamis bagimlilik uretilen_surum: %d\n", yazilmamis.uretilen_surum);

    /* Uc halkali zincir: temel -> ara -> son. Tam yazimda ara, temel'e
       bagimli oldugunu bildiriyor ve kendi kaydini guncelliyor; son da
       ara'ya bagli oldugu icin guncellenmis kaydi devraliyor. Eksik
       yazimda ara, temel'e bagimliligini hic bildirmiyor; son ara'ya
       dogru bagli olsa bile devralacak yeni bir kayit bulamiyor. */
    struct Hedef ara_tam = { 10, 1 };
    struct Hedef son_tam = { 10, 1 };
    struct Hedef ara_eksik = { 10, 1 };
    struct Hedef son_eksik = { 10, 1 };

    bagimliligi_denetle(&ara_tam, 20, 2);
    bagimliligi_denetle(&son_tam, ara_tam.uretim_zamani, ara_tam.uretilen_surum);

    /* ara_eksik icin bagimliligi_denetle hic cagrilmiyor */
    bagimliligi_denetle(&son_eksik, ara_eksik.uretim_zamani, ara_eksik.uretilen_surum);

    printf("tam zincir son uretilen_surum: %d\n", son_tam.uretilen_surum);
    printf("eksik zincir son uretilen_surum: %d\n", son_eksik.uretilen_surum);
    return 0;
}
```

```text
yazilmis bagimlilik uretilen_surum: 2
yazilmamis bagimlilik uretilen_surum: 1
tam zincir son uretilen_surum: 2
eksik zincir son uretilen_surum: 1
```

**PA1.** Zaman damgaları burada tamsayı sayaçlarıyla temsil ediliyor; gerçek bir dosya
sisteminin taşıdığı takvim değeri değil. Karşılaştırmanın sonucu değişmiyor, çünkü kural
yalnız **sıralamaya** bakıyor — hangi tamsayının büyük olduğuna.

`yazilmis` hedefi başlığın yeni sürümünü gördü ve `uretilen_surum` alanı 2'ye güncellendi.
`yazilmamis` hedefi ise 1'de kaldı: başlık gerçekte değişmiş olsa da, derleme sisteminin
bağımlılık listesinde bu ilişki hiç yazılı değildi, dolayısıyla karşılaştırma hiç
çalıştırılmadı. `yazilmamis.uretilen_surum` alanı, yazan tarafın bıraktığı yeni değerden
tamamen habersiz, eski sürümün bir **kopyasını** taşımaya devam ediyor. Bu tek karşılaştırma
iki farklı yazımın **kararını** yan yana koyuyor: aynı başlangıç durumundan, aynı gerçek
değişiklikten yola çıkıp, yalnız bağımlılık listesine yazılıp yazılmamasına göre birbirinden
ayrışan iki sonuç.

**PA2.** Üç halkalı zincir burada üç ayrı `struct Hedef` değişkeniyle temsil ediliyor;
gerçek bir derleme sisteminde bu ilişki bir bağımlılık grafiğinde tutulur ve zincir
uzunluğu sabit değildir. Modelin ölçtüğü karar — bir halkanın eksikliğinin zincirin geri
kalanına yayılması — grafiğin boyutundan bağımsızdır.

`ara_tam` ile `son_tam`, temel'in güncellendiğini iki adımda devralarak son sürümü 2'ye
taşıyor: `ara_tam` temel'e bağlı olduğunu bildiriyor, `son_tam` da `ara_tam`'a bağlı olduğunu
bildiriyor. `ara_eksik` ise temel'e bağlı olduğunu hiç bildirmediği için kendi kaydı 1'de
donuk kalıyor; `son_eksik`'in `ara_eksik`'e olan bağlılığı **tam** yazılmış olsa bile,
devralacağı yeni bir kayıt zaten yok — `son_eksik.uretim_zamani` ile `ara_eksik.uretim_zamani`
eşit çıktığı için karşılaştırma `baslik_zamani > h->uretim_zamani` koşulunu hiç sağlamıyor.
Bu, zincirin ortasındaki tek bir eksik kaydın neden yalnız kendi hedefini değil, ona bağlı
bütün alt ağacı da etkilediğini somutlaştırıyor: son halka kendi bağlılığını doğru yazmış
olsa bile, önceki halkanın eksikliğini telafi edemiyor.

Gerçek bir kaynak ağacında zincir üç halkayla sınırlı kalmaz ve dahası doğrusal da değildir:
bir ara hedefe onlarca son hedef bağlı olabilir, ve bunların hepsi aynı eksikliği aynı anda
devralır. `ara_eksik` örneğinde tek bir `son_eksik` vardı; gerçek bir ağaçta bu, `ara_eksik`'e
bağlı her hedefin eski sürümde kalması demektir — eksikliğin etkisi tek bir hedefte değil,
o hedefe bağlı bütün hedeflerde aynı anda görünür. Bu yayılma, zincirin ortasındaki bir
eksikliği bulmayı hem daha önemli hem de bir bakıma daha kolay kılar: birden fazla hedefin
aynı anda ve aynı şekilde eski kaldığını görmek, tek bir hedefin izole bir şekilde eski
kalmasından daha belirgin bir örüntüdür ve genelde araştırmayı doğrudan ortak ataya, yani
`ara_eksik`'e yönlendirir.

Bazı derleme sistemleri bu listeyi elle yazdırmak yerine bir üreteçten türetir: kaynak
dosyaları tarayıp hangi başlığın hangi hedefe dahil edildiğini kendisi çıkarır ve
bağımlılık listesini otomatik üretir (model). Üreteç tabanlı yaklaşımın kazandırdığı şey
listenin unutulma olasılığını azaltmaktır — kural kendisi aynı kalır, yalnız listeyi kim
yazdığı değişir.

Bu iki yaklaşım arasındaki fark, elle yazılan listenin **statik**, üretilen listenin ise
**her derleme öncesi yeniden çıkarılan** olmasıdır. Elle yazılan liste bir kaynak dosya
yeni bir başlık eklediğinde güncellenmezse eksik kalır; üreteç ise her koşumda kaynağın
kendisine bakarak listeyi yeniden kurduğu için bu unutmayı yapısal olarak dışlar. Bedeli,
üreteç adımının kendisinin de doğru çalışması gerekliliğidir — üreteç bir başlığı
atlıyorsa, sonuç yine elle yazılmış eksik bir listeyle aynı sınıfa düşer, yalnız eksikliğin
kaynağı insan değil üretecin kendisi olur.

Genel hatlarıyla üreteç tabanlı yaklaşım şöyle çalışır: derleyici (ya da ona eşlik eden ayrı
bir araç) bir kaynak dosyayı işlerken hangi başlıkların gerçekten dahil edildiğini zaten
görür — çünkü önişlemci aşaması her `#include` satırını açar. Bu bilgi atılmak yerine ayrı
bir kayıt dosyasına yazılır: kaynak dosyanın adı ve o kaynağın geçişli olarak bağlı olduğu
her başlığın listesi. Bu kayıt dosyası bir sonraki derlemeden önce okunur ve derleme
sisteminin bağımlılık listesine dahil edilir. Sonuç olarak liste elle değil, kaynağın kendi
gerçek içeriğinden türetilmiş olur; bir kaynak dosya yeni bir başlık eklediğinde, bir dahaki
derlemede üreteç bu değişikliği kendiliğinden yakalar ve kayıt dosyasını günceller. Küçük bir
kaynak ağacında elle yazılan liste sorun çıkarmayabilir — birkaç dosya, birkaç başlık, hepsi
tek bakışta görülebilir. Ağaç büyüdükçe, her yeni `#include` satırının bir yerde de listeye
elle eklenmesi gerekliliği ölçeklenmiyor: eklemeyi unutmak, kod yazmaktan daha kolay hâle
geliyor, çünkü liste güncellemesi kaynak dosyayı değiştirmekle aynı anda, aynı dikkatle
yapılmıyor.

Üreteç tabanlı yaklaşımın kendi maliyeti de var: kayıt dosyası bir önceki derlemenin
çıktısıdır, dolayısıyla ilk derlemede — henüz hiçbir kayıt dosyası üretilmemişken — derleme
sistemi bağımlılık listesi olmadan başlamak zorunda kalır. Bu, birçok kural tabanlı sistemin
neden iki geçişli çalıştığını açıklar: önce var olan kayıt dosyalarını (varsa) okuyup
bağımlılık grafiğini kurar, sonra gerçek derlemeyi bu grafiğe göre sıralar. Kayıt dosyası
eksikse ya da bozuksa, sistem o hedefi güvenli tarafta kalıp yeniden üretir — eksik bilgiyi
"değişmedi" değil "bilinmiyor, o yüzden üret" olarak okur. Bu, elle yazılan listenin sessizce
eksik kalma riskine karşı üreteç tabanlı yaklaşımın yapısal avantajıdır: bir hata olduğunda
sonuç genelde gereksiz bir yeniden derleme olur, sessizce atlanan bir derleme değil.

## Sınırlayıcı Ölçüm

Bu model yalnız **derleme sisteminin bildiği** bağımlılık ilişkisini görüyor; başlığın
içeriğini gerçekten okuyup okumadığını değil. `bagimliligi_denetle` çağrılmadığı sürece
model, başlığın hiç değişmediğini varsaydığı bir durumla aynı sonucu üretir — oysa başlık
gerçekten değişmiştir. Ölçülen şey bağımlılık **kaydının** doğruluğu; kaynağın kendisinin
doğruluğu değil. Bu ayrım önemli çünkü iki farklı hata sınıfını karıştırmamak gerekiyor:
kaydın kendisi bozuk olabilir (bu dersin konusu) ya da kayıt doğru olduğu hâlde derleyicinin
o kaydı kullanma biçimi hatalı olabilir — örneğin bir önceki modülün aşama sorununa giren bir
durum. İkisi de sonuçta "hedef beklenenden eski davranıyor" belirtisini verir, ama kökleri
tamamen ayrıdır ve düzeltmesi de ayrıdır: birincisi bağımlılık listesine eksik satırı
eklemekle, ikincisi ilgili aşamayı (önişlemci, derleyici, bağlayıcı) yeniden incelemekle
çözülür.

Bir önceki modülde bir değişikliğin hangi **aşamada** göründüğü sorulmuştu: önişlemci mi,
derleyici mi, bağlayıcı mı, yoksa çalışma zamanı mı. Buradaki soru farklı bir eksende
durur — başlığı değişen ama yeniden derlenmeyen bir birim, aşama sorusu açısından sıradan
bir önişlemci girdisidir; görünürlük sorusu açısından ise kopyadır, çünkü hedef değişikliği
hiç görmemiştir.

Bu iki eksenin birbirinden bağımsız olması, bir hatayı ayıklarken hangi soruyu sorduğunuzu
netleştirmeyi gerektirir. Bir hedefin beklenenden eski davranması iki farklı nedenden
kaynaklanabilir: ya kaynak dosya doğru aşamadan geçmedi (önişlemci bir makroyu genişletmedi,
derleyici eski bir nesne dosyasını kullandı), ya da kaynak dosya doğru aşamalardan geçti
ama derleme sistemi onu hiç yeniden derlemedi. Bu modelin ölçtüğü yalnız ikincisi — hedefin
`uretilen_surum` alanına hangi değerin ulaştığı, o değerin nasıl işlendiği değil. Bir
derleme sistemi günlüğü bu iki durumu ayırt etmenin en doğrudan yoludur: hangi hedeflerin
yeniden üretildiği, hangilerinin **hiç dokunulmadan** bırakıldığı günlükte görünür, ve
beklenmedik şekilde dokunulmamış bir hedef bu dersin ölçtüğü sınıfın somut bir belirtisidir.

Zincirleme örnek bu iki ekseni birlikte gösteriyor. `son_eksik`'in eski sürümde kalması
tek bir eksik kayıttan kaynaklanıyor — `ara_eksik`'in temel'e bağımlılığını bildirmemesi —
ama bu eksikliğin görünen yeri zincirin **sonunda**. Bir hatayı ayıklarken `son_eksik`'i
inceleyen biri önce kendi bağımlılık listesine bakar, orayı doğru bulur (`son_eksik`,
`ara_eksik`'e bağlı olduğunu doğru bildirmiş) ve hatayı orada aramaz; gerçek eksiklik bir
önceki halkada durur. Bu, geçişli bir zincirde hatayı ayıklamanın neden tek bir hedefe
bakmakla bitmediğini gösteriyor — sorun, zincirin görünen ucundan değil, zincirin kendisi
boyunca geriye doğru izlenerek bulunur. Büyük bir kaynak ağacında bu iz sürme elle
yapılamayacak kadar çok halka içerebilir; bu yüzden üreteç tabanlı yaklaşımın kayıt
dosyaları aynı zamanda bir tanı aracı gibi de kullanılır — hangi hedefin hangi başlıklara
gerçekten bağlı olduğunu, insan hafızasına değil dosyaya bakarak doğrulamak için.

## Özet

- Kural tabanlı bir derleme sistemi, hedefin bağımlı olduğu her girdinin zaman damgasını
  hedefin kendi üretim zamanıyla karşılaştırır.
- Bağımlılık listesi eksiksiz olmadığında, listede yer almayan bir değişiklik hiç
  sorulmaz ve hedef eskisini korur.
- `yazilmamis` hedefinin `uretilen_surum` alanı, başlığın gerçek sürümünden bağımsız,
  yazan tarafın eski bıraktığı değerin bir kopyasıdır — bu dersin ölçtüğü sınıf **kopya**.
- Üreteç tabanlı yaklaşım bağımlılık listesini otomatik türeterek unutulma olasılığını
  azaltır; kuralın kendisini değiştirmez.
- Görünürlük sorusu, bir önceki modülün aşama sorusundan bağımsız bir eksendir.
- Bağımlılık zinciri geçişlidir: zincirin ortasındaki eksik bir kayıt, ona bağlı bütün
  alt ağacı da eski durumda bırakır — hata zincirin görünen ucunda değil, eksik kaydın
  bulunduğu halkada aranmalıdır; üreteç tabanlı sistemler eksik bilgiyi "değişmedi" değil
  "bilinmiyor" olarak okuyup güvenli tarafta kalıp yeniden ürettiği için hata payı gereksiz
  derlemeye kayar, sessiz atlamaya değil.

Pratikte bir bağımlılığın listeye yazılmadan kalmasının birkaç tipik nedeni var. Bir kaynak
dosya yeniden düzenlenip yeni bir başlığa bölündüğünde, eski listenin güncellenmesi ayrı bir
adımdır ve atlanabilir. Bir dosya bir dizinden başka bir dizine taşındığında, listedeki yol
eski konumu göstermeye devam edebilir; kural bu durumda ya hiç eşleşmez ya da yanlış dosyayı
izler. Kopyala-yapıştırla oluşturulan yeni bir hedef, kaynağındaki bağımlılık listesini de
kopyalar ama kendi gerçek bağımlılıklarını yansıtmayabilir — yeni hedefin kendine özgü
başlıkları listeye hiç girmemiş olur. Bu üç durum da aynı sonuca varıyor: liste, kaynağın
şu anki gerçek durumunu değil, bir önceki düzenlemenin anlık görüntüsünü taşıyor. Üreteç
tabanlı yaklaşımın kapattığı boşluk tam olarak bu — listenin kaynaktan **ayrı bir yapı**
olarak elle bakımının hiç gerekmemesi.

## Sonraki Adım

Bir hedef doğru derlense bile üretilen kod tek başına bir programa dönüşmez — çoğu zaman
başka birimlerle **bağlanır**. Bu bağlanma da kendi zaman damgası sorusunu taşır: bir
kütüphane güncellendiğinde, ona bağlı program bu güncellemeyi ne zaman görür — o an mı,
yoksa bir sonraki yeniden derlemede mi, hatta hiç mi? Bir sonraki ders, bir kütüphaneye
bağlanmanın iki farklı biçiminin, o kütüphane güncellendiğinde programın hangi sürümü
göreceğini nasıl belirlediğini ölçüyor; bu derste kurulan "listede yazılı mı, değil mi"
sorusunun bağlanma aşamasındaki karşılığı budur.
