---
title: 'Çapraz Derleme'
source: 'https://academia.sh/tr/kurslar/go-uretim/capraz-derleme'
course: 'Üretim Go: Test, Araçlar ve Başarım'
language: tr
updated: '2026-09-13T05:08:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Aynı taşınabilir kaynağın üç ayrı hedef için, hiçbir hedef makine kurulu olmadan derlenip derlenmediğini araçtan okur'
    - 'Platforma bağlı bir çağrının aynı üç hedefte derlenebilirliğinin nasıl değiştiğini ölçerek karşılaştırır'
    - 'Derlenmiş olmanın hedefte çalışacağı anlamına gelmediğini, bir varsayımı modelleyerek sınırlar'
---

# Çapraz Derleme

Aynı taşınabilir kaynağın üç ayrı hedef için, o hedeflerin hiçbiri kurulu olmadan tek bir komutla derlenip derlenmediği ölçülür; derlenebilirlik burada da koşum gerektirmeyen bir araç bilgisidir. İkinci ölçüm aynı üç hedefte platforma bağlı bir çağrının derlenebilirliğinin değiştiğini gösterir. Sınırlayıcı ölçüm, derlenmiş olmanın hedefte çalışacağı anlamına gelmediğini — aynı adı taşıyan bir varsayımın bir hedef ailesinde geçerli, ötekinde geçersiz kalabileceğini — modelleyerek ortaya koyar.

Önceki ders, bir dizindeki hangi dosyanın derlemeye gireceğinin, hiçbir program çalıştırmadan,
yalnızca araçtan okunabildiğini gösterdi. Bu ders aynı soruyu bir adım öteye taşıyor: dosyalar
belli olduktan sonra, o dosyalar hangi hedefe derlenecek? Yanıt yine koşum beklemeden geliyor,
ama bu kez sınırı da beraberinde taşıyor — derlenmiş olmak, o hedefte çalışacağı anlamına
gelmiyor.

## Tek Komut, Üç Hedef

**Çapraz derleme (cross compilation)**, kaynağın derlendiği makineden başka bir hedef için
ikili üretme işlemidir. Dilin kendi derleyicisi bu işi tek bir komutla yapıyor; hedefi seçen
şey iki ortam değişkeni — biri hedef işletim sistemi ailesini, öteki işlemci mimarisini
belirliyor. Bu iki değişken derleme komutunun **dışından** geliyor, tıpkı önceki dersteki
derleme etiketleri gibi; farkları, bir etiketin adı kaynağa özgüyken bu iki değişkenin adının
ve alabileceği değerlerin dilin kendisi tarafından sabitlenmiş olması. Üçüncü bir hedef
makineye ihtiyaç yok: derleyici, çalıştığı makineden bağımsız
olarak, istenen hedef için bir ikili üretiyor ve bunu üretebilip üretemediği yine **araç**
sınıfına giren bir bilgi — hiçbir şey çalıştırılmadan öğreniliyor.

Bu, önceki dersle aynı temel düzenekten besleniyor. Standart kitaplığın kendisi, birçok
işlevi her hedef ailesi için ayrı bir dosyada, o ailenin adını taşıyan bir derleme
etiketinin (ya da eşdeğer bir dosya adı sözleşmesinin) arkasında tutuyor; derleyici, hangi
hedef için çalıştığını iki ortam değişkeninden okuyup doğru dosya kümesini seçiyor. Aradaki
fark ölçek: önceki derste altı dosyalık bir örnekte elle kurulan seçim, standart kitaplığın
kendisinde binlerce dosya üzerinde, her hedef için ayrı ayrı çalışıyor. Bu yüzden bir hedefin
"kurulu" olması gerekmiyor — gereken tek şey, derleyicinin o hedef için hangi kaynak
dosyalarının geçerli olduğunu bilmesi ve bu bilgi de kaynağın kendisinde, dosya adlarında ve
etiketlerinde zaten duruyor.

Aşağıdaki ölçüm taşınabilir, hiçbir platforma özgü çağrı içermeyen bir kaynağı üç ayrı hedef
için derliyor ve her denemenin geçip geçmediğini okuyor. Üretilen ikili hiçbir yere
yazılmıyor — çıktı sistemin boş aygıtına yönlendiriliyor, çünkü bu derste ölçülen tek şey
derlemenin başarısı, üretilen dosyanın kendisi değil.

```go
// yardimcilar.go — bu dersin paylastigi yardimcilar: gecici paket kurma ve arac cagirma
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

// goster bir bilgiyi, kimin soyledigini ve kanitini tek satirda yazdirir.
func goster(bilgi, soyleyen, kanit string) {
	fmt.Printf("%-42s %-7s %s\n", bilgi, soyleyen, kanit)
}

// paket, verilen dosyalari yeni bir gecici modulun icine yazar; yol hicbir yerde basilmaz.
func paket(dosyalar map[string]string) string {
	dizin, err := os.MkdirTemp("", "capraz")
	if err != nil {
		return ""
	}
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module capraz\n\ngo 1.24\n"), 0o644)
	for ad, icerik := range dosyalar {
		os.WriteFile(filepath.Join(dizin, ad), []byte(icerik), 0o644)
	}
	return dizin
}

// derlenebilirMi, kaynagi verilen hedef icin derlemeyi dener; cikti hicbir zaman basilmaz,
// yalniz basarili olup olmadigi donuyor. Uretilen ikili sistemin bos aygitina yaziliyor.
func derlenebilirMi(dizin string, ekOrtam []string) bool {
	cmd := exec.Command("go", "build", "-o", os.DevNull, ".")
	cmd.Dir = dizin
	cmd.Env = append(append(os.Environ(), "GOTOOLCHAIN=local", "CGO_ENABLED=0"), ekOrtam...)
	return cmd.Run() == nil
}
```

```go
// ucHedef.go — tasinabilir bir kaynak uc ayri hedef icin, hicbir hedef makine gerekmeden derleniyor
package main

import "fmt"

func main() {
	dizin := paket(map[string]string{
		"tasinabilir.go": "package main\n\nfunc topla(a, b int) int { return a + b }\n\nfunc main() {\n\t_ = topla(1, 2)\n}\n",
	})
	defer func() { _ = dizin }()

	hedefler := []struct{ ad, goos, goarch string }{
		{"birinci hedef", "linux", "amd64"},
		{"ikinci hedef", "windows", "amd64"},
		{"ucuncu hedef", "darwin", "arm64"},
	}

	fmt.Println("-- tek komut, uc hedef: tasinabilir kaynak --")
	for _, h := range hedefler {
		ortam := []string{"GOOS=" + h.goos, "GOARCH=" + h.goarch}
		goster(h.ad+" derlendi mi", "arac", fmt.Sprint(derlenebilirMi(dizin, ortam)))
	}
}
```

```
-- tek komut, uc hedef: tasinabilir kaynak --
birinci hedef derlendi mi                  arac    true
ikinci hedef derlendi mi                   arac    true
ucuncu hedef derlendi mi                   arac    true
```

**DR6.** Üç ayrı hedefin üçü de derlendi ve bu makinede üç hedefin de gerçek bir kopyası hiç
yoktu — çalıştırılan tek şey derleyicinin kendisi, iki ortam değişkeniyle üç kez çağrıldı.
Sonuç yine `koşum` sütununu hiç doldurmuyor: taşınabilir bir kaynağın hangi hedefler için
derlendiği, o hedeflerin hiçbirine dokunmadan, tek bir komutun geçti/kaldı yanıtından
okunuyor.

## Aynı Çağrı, Değişen Sonuç

Yukarıdaki kaynak platforma özgü hiçbir şey içermiyordu, bu yüzden üç hedefin üçü de aynı
yanıtı verdi. Bir çağrı platforma özgü bir sistem işlevine bağlandığında bu tekdüzelik
bozuluyor — bazı işletim sistemi aileleri o işlevi tanımlarken, bazıları hiç tanımlamıyor.
Aşağıdaki ölçüm aynı üç hedefte, standart kitaplığın belirli bir işletim sistemi ailesine özgü
tek bir sistem çağrısını (adlandırılmış boru oluşturma çağrısı) kullanan bir kaynağı deniyor.

```go
// sistemCagrisi.go — ayni cagri, uc hedef: platforma bagli bir sistem cagrisi
package main

import "fmt"

func main() {
	dizin := paket(map[string]string{
		"cagrili.go": "package main\n\nimport \"syscall\"\n\nfunc main() {\n\t_ = syscall.Mkfifo\n}\n",
	})
	defer func() { _ = dizin }()

	hedefler := []struct{ ad, goos, goarch string }{
		{"birinci hedef", "linux", "amd64"},
		{"ikinci hedef", "windows", "amd64"},
		{"ucuncu hedef", "darwin", "arm64"},
	}

	fmt.Println("-- ayni cagri, uc hedef: platforma bagli bir sistem cagrisi --")
	basarili := 0
	for _, h := range hedefler {
		ortam := []string{"GOOS=" + h.goos, "GOARCH=" + h.goarch}
		d := derlenebilirMi(dizin, ortam)
		if d {
			basarili++
		}
		goster(h.ad+" derlendi mi", "arac", fmt.Sprint(d))
	}
	goster("uc hedeften kaci derlendi", "arac", fmt.Sprint(basarili))
}
```

```
-- ayni cagri, uc hedef: platforma bagli bir sistem cagrisi --
birinci hedef derlendi mi                  arac    true
ikinci hedef derlendi mi                   arac    false
ucuncu hedef derlendi mi                   arac    true
uc hedeften kaci derlendi                  arac    2
```

**DR7.** Aynı kaynak, aynı üç hedef, ama bu kez sonuç tekdüze değil: iki hedef derliyor, biri
derlemiyor. Standart kitaplığın sistem çağrısı paketi her işletim sistemi ailesi için ayrı bir
gerçekleştirim taşıyor ve bir ailede hiç bulunmayan bir işlevi çağırmak, o aile hedeflenince
derlemeyi durduruyor — üstelik bu, önceki derste ölçülen "hangi dosya derlemeye giriyor"
sorusuyla aynı katmanda değil, bir düzey daha derinde: dosyanın kendisi her üç hedefte de
derleme listesine giriyor, ama dosyanın içindeki bir ad üç hedefin ikisinde tanımlı, birinde
tanımsız kalıyor. Hangi ailenin hangi çağrıyı tanımladığı da yine **araç** bilgisi: bir kişi
bunu ezbere bilebilir, ama derlemenin kendisi bunu her seferinde aynı şekilde, koşum
gerektirmeden doğruluyor.

Bu farkın pratikteki sonucu, birden çok hedefi aynı anda desteklemesi gereken bir kaynağın
iki yoldan biriyle yazılması gerektiği. Ya platforma özgü çağrı hiç kullanılmıyor — önceki
bölümdeki taşınabilir kaynak gibi, üç hedefin üçünde de aynı davranışı veriyor — ya da
platforma özgü kısım kendi dosyasına, kendi etiketine ayrılıyor ve her hedef için ayrı bir
gerçekleştirim yazılıyor. İkinci yol, önceki dersteki `genisleme`/`deney` örneğiyle aynı
biçimi taşıyor: dosya adı ya da etiket, hangi hedefte hangi gerçekleştirimin devreye
gireceğini belirliyor. Fark, seçimin bu kez elle verilen bir bayrakla değil, derleme
komutunun kendi hedef bilgisiyle otomatik yapılması. Tek bir dosyanın üç hedefin ikisinde
derlenip birinde derlenmemesi bu yüzden bir kusur değil — kaynağın, o çağrıyı desteklemeyen
hedefler için hiç bir alternatif dosya sağlamadığının bir işareti.

## Sınırlayıcı Ölçüm: Derlenmek Çalışmak Değildir

Yukarıdaki iki ölçüm de aynı sınırın içinde kaldı: bir kaynağın belirli bir hedef için
derlenip derlenmediği. Bu, o kaynağın o hedefte **çalışıp** çalışmayacağından ayrı bir soru
ve bu ders o ikinci soruyu gerçekten yanıtlayamıyor — üç hedeften ikisinin gerçek bir kopyası
bu makinede yok, üretilen ikili çalıştırılamıyor. Bu sınırı gizlemek yerine açıkça modelliyoruz:
aşağıdaki ölçüm gerçek bir hedefte hiç çalıştırılmıyor, iki hedef ailesinin ortamı kaynak
içinde elle kuruluyor.

```go
// varsayimModeli.go — sinirlayici olcum: derlenmek calismak degildir, model uzerinden gosteriliyor
package main

import "fmt"

// evYoluBul, verilen ortam haritasinda TEK bir degisken adina bakarak ev dizinini arar.
func evYoluBul(ortam map[string]string, degiskenAdi string) (string, bool) {
	deger, varMi := ortam[degiskenAdi]
	return deger, varMi
}

func main() {
	// iki hedef ailesinin ortami burada gercekten calistirilmadan, elle modelleniyor:
	// her aile kullanicinin ev dizinini kendi adiyla tasiyan bir degisken sunuyor.
	birinciAileOrtami := map[string]string{"HOME": "kullanici-dizini"}
	ikinciAileOrtami := map[string]string{"USERPROFILE": "kullanici-dizini"}

	// tasinabilir goruntusune ragmen, kod yalnizca TEK bir degisken adini ariyor.
	arananDegisken := "HOME"

	_, birinciBuldu := evYoluBul(birinciAileOrtami, arananDegisken)
	_, ikinciBuldu := evYoluBul(ikinciAileOrtami, arananDegisken)

	fmt.Println("-- sinirlayici olcum: derlenmek calismak degildir (model) --")
	goster("birinci hedef ailesinde ev yolu bulundu mu", "arac", fmt.Sprint(birinciBuldu))
	goster("ikinci hedef ailesinde ev yolu bulundu mu", "arac", fmt.Sprint(ikinciBuldu))
}
```

```
-- sinirlayici olcum: derlenmek calismak degildir (model) --
birinci hedef ailesinde ev yolu bulundu mu arac    true
ikinci hedef ailesinde ev yolu bulundu mu  arac    false
```

**DR8.** `evYoluBul` işlevi platforma özgü hiçbir çağrı içermiyor — tek bir dizgi anahtarıyla
bir haritaya bakıyor — bu yüzden yukarıdaki gibi üç gerçek hedef için derlenseydi üçünde de
sorunsuz derlenirdi. Kusur derlemede değil, **varsayımda**: kod yalnızca tek bir değişken
adını arıyor ve bu ad birinci hedef ailesinde geçerli, ikincisinde geçersiz. Gerçek bir
programda bu satır, bir kullanıcının ayar dizinini bulmaya çalışan bir çağrı olabilir; ikinci
hedef ailesinde çalıştırıldığında sessizce boş dönebilir, hiçbir derleme hatası, hiçbir
çalışma zamanı çökmesi olmadan. Bu yüzden "derlendi=true" satırı, önceki iki ölçümde olduğu
gibi burada da güvenilir bir bilgi — ama neyin doğru olduğunu söylüyor, neyin **iyi çalıştığını**
değil. Bu ayrım modellenmeden ölçülemiyor, çünkü koşumun kendisi bu derste hiçbir zaman gerçek
bir hedefte gerçekleşmiyor.

Modelin kendisi kasıtlı olarak küçük tutuldu: iki harita, bir dizgi anahtarı, iki arama.
Gerçek bir programda bu tür bir varsayım genellikle çok daha derine gömülü olur — bir yapılandırma
dosyasının aranacağı dizin, bir günlük dosyasının yazılacağı yer, bir geçici dosyanın adı — ve
hepsi aynı örüntüyü paylaşır: kod bir tek adı, bir tek yolu ya da bir tek biçimi doğru kabul
eder, bu kabul üç hedefin de derlemesini geçer ve yalnızca yanlış olduğu hedefte, o hedef
gerçekten çalıştırıldığında ortaya çıkar. Derlenebilirlik ölçümünün gücü tam burada bitiyor:
söylediği şey doğru ve güvenilir, ama sorduğu soru dar — yalnızca "bu kaynak bu hedef için bir
ikiliye dönüşür mü" sorusuna yanıt veriyor, "bu ikili doğru şeyi yapar mı" sorusuna değil.

Bu sınırın pratikte kapatılma yolu, yine aynı üç sınıfın içinde kalıyor: bir hedefte
gerçekten çalıştığını göstermenin tek güvenilir yolu, o hedefte gerçekten **koşturmak** —
gerçek bir makinede ya da o hedefi taklit eden bir ortamda. Derlenebilirlik sınaması bu
adımın yerini almıyor, önüne geçiyor: bir kaynak üç hedefin birinde bile derlenmiyorsa, o
hedefte hiç koşum denemeye gerek kalmıyor. Üç hedefin üçünde de derlenen bir kaynak için ise
soru hâlâ açık — derlenebilirlik testi bu açık soruyu **erken**, ucuz bir adımda eliyor, ama
kapatmıyor. Bu derste ikinci adım (gerçek koşum) hiç yapılmadı, çünkü üç hedeften ikisinin bu
makinede gerçek bir kopyası yok; bu, ölçümün kapsamının bir sınırı, çapraz derlemenin
kendisinin bir sınırı değil.

Aynı sınır, çapraz derlemenin kendi sınırlarından biri için de geçerli: kaynak `cgo` üzerinden
bir yerel kütüphaneye bağlanıyorsa, derleyici genellikle hedef için gerekli dış derleyiciyi
bulamıyor ve derleme bu kez gerçekten reddediliyor — bu, kursun son dersinin konusu. Bu derste
ölçülen üç hedefin üçü de saf kaynaktı; bir dış derleyici hiç gerekmedi ve bu yüzden hiçbirinde
reddedilme yaşanmadı.

## Özet

- Çapraz derleme, hedef bir sistem çevresi hiç kurulu olmadan, tek bir komutla o hedef için
  ikili üretme işlemidir; derlenebilirlik burada da koşum gerektirmeyen bir **araç** bilgisi.
- Taşınabilir bir kaynak üç hedefin üçünde de aynı yanıtı verdi; platforma bağlı tek bir
  sistem çağrısı eklendiğinde aynı üç hedeften yalnızca ikisi derlendi.
- Bir çağrının hangi hedef ailesinde tanımlı olduğu da araçtan okunan bir bilgi — derleme,
  bu bilgiyi her denemede aynı şekilde, hiçbir kişisel bilgiye ihtiyaç duymadan doğruluyor.
- Sınırlayıcı ölçüm: derlenmiş olmak hedefte doğru çalışacağı anlamına gelmiyor; aynı ada
  bağlı bir varsayım bir hedef ailesinde geçerli, ötekinde geçersiz kalabiliyor.
- Bu sınır gerçek bir hedefte koşturularak değil, iki hedefin ortamı kaynak içinde açıkça
  modellenerek gösterildi; kursun hiçbir yerinde gerçek bir hedef makineye bağlanılmadı.

## Sonraki Adım

Bu derste hedef, dosyanın kendisinin hiç değişmediği bir seçimdi — kaynak sabitti, yalnızca
hangi ortama derlendiği değişti. Sıradaki ders kaynağın içine hiç yazılmayan, ama yine de
ikiliye giren bir değeri ele alıyor: bir sürüm bilgisi, kaynağın hiçbir satırında durmadan,
yalnızca derleme anında bir başka araç tarafından ikiliye yerleştiriliyor. Bu kez sorulan soru
"hangi hedef" değil, "kaynakta hiç yazılmayan bir değer nereden geliyor" — ve bu derste
seçilen hedefin ikiliye hiçbir iz bırakmadığı gibi, o değer de kaynağın kendisinde hiçbir iz
bırakmıyor; ikisi de yalnızca derleme sürecinin kendi araçlarıyla, kaynağın dışından ikiliye
ekleniyor.
