---
title: 'Derleme Etiketleri'
source: 'https://academia.sh/tr/kurslar/go-uretim/derleme-etiketleri'
course: 'Üretim Go: Test, Araçlar ve Başarım'
language: tr
updated: '2026-10-21T05:03:06+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir dizindeki kaynak dosyaların, hiçbir program çalıştırmadan, yalnızca araçtan sorularak kaç tanesinin derlemeye girdiğini ölçer'
    - 'Derleme etiketlerindeki VE, VEYA ve DEĞİL mantığının dosya sayısını nasıl değiştirdiğini dört senaryoda karşılaştırır'
    - 'Etiketiyle dışarıda kalan bir dosyanın diskte durmaya devam ettiğini ama hiçbir aracın onu sınamadığını gösterir'
---

# Derleme Etiketleri

Aynı dizindeki altı kaynak dosyadan hangisinin derlemeye gireceği, çalıştırılan bir programa değil araca sorularak öğrenilir; dört etiket senaryosunda dosya sayısı bu şekilde okunur. Sınırlayıcı ölçüm, etiketiyle dışarıda kalan bir dosyanın diskten silinmediğini ama hiçbir aracın onu incelemediğini — dosyadaki bir kusurun yalnızca etiket etkinleşince görünür hale geldiğini — koşturarak gösteriyor.

Başarım ve Bellek konusunun son dersi bir izin, topladığı anda programın davranışını değiştirdiğini
ölçtü; sayılar bu yüzden hep bu etkinin altında okundu. Bu ders aynı üç sınıftan (kaynak, araç,
koşum) yalnızca birine bakıyor ve koşumu tamamen devre dışı bırakıyor: bir program hiç
çalıştırılmadan önce, hatta hiç derlenmeden önce bile, kaynak ağacındaki hangi dosyanın o
derlemeye gireceği zaten belli. Bu kararı veren şey ne bir kişinin okuması ne de çalışan bir
programın kendisi — yalnızca araç, kaynağın üstündeki bir yönergeyi okuyup dosya listesini
süzüyor. Sıradaki dört ders de aynı örüntüyü izliyor: hangi platforma çıkılacağı, ikiliye ne
gömüleceği, hangi sürümün uyumlu sayılacağı ve hangi kalıbın kusurlu bulunacağı — hepsi
koşum beklemeden, yalnızca araçtan okunuyor.

## Derleme Etiketi Nedir

**Derleme etiketi (build tag)**, bir kaynak dosyanın en üstüne, paket bildiriminden önce
yazılan özel bir yorum satırıdır: `//go:build`. Yönergenin çalışabilmesi için üç biçim kuralı
var. Yorum dosyanın en üst satırlarından birinde durmalı; paket bildiriminden önce en az bir
boş satır bırakılmalı; ve yönerge ile dosyanın geri kalanı arasında hiçbir kod satırı
girmemeli. Bu üç kural ihlal edilirse derleyici yönergeyi sıradan bir yorum sanır ve dosya her
zaman derlemeye girer — yönergenin kendisi bir hataya değil, sessizce yok sayılmaya yol açar.

Yönergenin gövdesi küçük bir mantık ifadesidir: bir ad tek başına yazılabilir (`genisleme`),
iki ad `&&` ile birleştirilip ikisinin de aynı anda etkin olması istenebilir, `||` ile
ikisinden en az birinin etkin olması istenebilir, ya da başına konan bir değil işaretiyle bir
adın etkin **olmaması** istenebilir. Bu üç işleç herhangi bir derinlikte parantezle
birleştirilebiliyor, ama bu dersin ölçtüğü altı dosya üçünü de yalnızca bir kez kullanıyor —
mantığın kendisini karmaşıklaştırmadan üç köşesini de görmek için bu yeterli.

Bu mantığın pratikteki karşılığı genellikle iki türden bir ayrım. Birincisi, bir paketin
normalde derlenmeyen, yalnızca bir sınama ya da bir tanılama oturumunda etkinleştirilen bir
ek dosya taşıması — kaynak ağacında durur, ama gündelik derlemeyi büyütmez ya da yavaşlatmaz.
İkincisi, aynı paketin birbirini dışlayan iki gerçekleştirimi taşıması — biri varsayılan,
öteki yalnızca belirli bir etiket verildiğinde devreye giren bir değişken. İkisinde de ortak
olan şey, seçimin **derleme anında**, kaynağın kendisine hiç dokunmadan yapılabilmesi;
aynı depo, aynı dal, aynı commit, farklı `-tags` bayrağıyla farklı bir programa dönüşebiliyor.

Etiketin adları program dışından geliyor: kaynağın kendisinde hiçbir yerde tanımlanmıyorlar,
yalnızca derleme komutuna `-tags` bayrağıyla veriliyor. Bu, önceki konudaki derleme
sabitlerinden temel bir farkı işaret ediyor — kaynak içinde `const` ile yazılan bir değer
her derlemede aynı kalırken, bir derleme etiketi kaynağın **dışından**, her çağrıda ayrı ayrı
belirleniyor. Aynı kaynak ağacı, hiçbir satırı değişmeden, verilen etiket kümesine göre
tamamen farklı bir dosya kümesinden derlenebiliyor.

## Dört Senaryo, Bir Mantık

Aşağıdaki ölçüm altı dosyalık küçük bir paket kuruyor — biri etiketsiz, beşi farklı bir
etiket ifadesiyle işaretli — ve dört ayrı etiket kümesiyle derlemeye giren dosya sayısını
okuyor. Paket gerçek bir dizine değil, `os.MkdirTemp` ile açılan geçici bir modüle yazılıyor;
bu dersin geri kalanında da yol hiçbir yerde basılmıyor.

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

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

// goster bir bilgiyi, kimin soyledigini ve kanitini tek satirda yazdirir.
func goster(bilgi, soyleyen, kanit string) {
	fmt.Printf("%-38s %-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("", "etiket")
	if err != nil {
		return ""
	}
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module etiketdeneme\n\ngo 1.24\n"), 0o644)
	for ad, icerik := range dosyalar {
		os.WriteFile(filepath.Join(dizin, ad), []byte(icerik), 0o644)
	}
	return dizin
}

// aracCalistir dilin kendi komutunu kosturur, cikisin basarili olup olmadigini doner; cikti basilmaz.
func aracCalistir(dizin string, arg ...string) (string, bool) {
	cmd := exec.Command("go", arg...)
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	cikti, err := cmd.CombinedOutput()
	return string(cikti), err == nil
}

// dosyaSayisi, dizindeki .go uzantili dosyalarin toplam sayisini hicbir arac cagirmadan sayar.
func dosyaSayisi(dizin string) int {
	girdiler, _ := os.ReadDir(dizin)
	n := 0
	for _, g := range girdiler {
		if strings.HasSuffix(g.Name(), ".go") {
			n++
		}
	}
	return n
}

// goFilesSayisi, verilen etiket kumesiyle derlemeye giren dosya sayisini araçtan okur.
func goFilesSayisi(dizin, etiketler string) int {
	arg := []string{"list", "-f", "{{len .GoFiles}}", "."}
	if etiketler != "" {
		arg = []string{"list", "-tags", etiketler, "-f", "{{len .GoFiles}}", "."}
	}
	cikti, _ := aracCalistir(dizin, arg...)
	n, _ := strconv.Atoi(strings.TrimSpace(cikti))
	return n
}

// alanSayisi, "go list" ciktisindaki herhangi bir listeleme alaninin (GoFiles, TestGoFiles)
// uzunlugunu okur; hangi alanin sorulacagi sablon parametresiyle verilir.
func alanSayisi(dizin, sablon string) int {
	cikti, _ := aracCalistir(dizin, "list", "-f", sablon, ".")
	n, _ := strconv.Atoi(strings.TrimSpace(cikti))
	return n
}
```

```go
// olcum.go — hangi dosyalar derlemeye giriyor, dort etiket senaryosunda arac uzerinden okunuyor
package main

import "fmt"

func main() {
	dosyalar := map[string]string{
		"cekirdek.go": "package main\n\nfunc temelIslev() int { return 1 }\n",
		"genisleme.go": "//go:build genisleme\n\npackage main\n\nfunc genislemeIslevi() int { return 2 }\n",
		"deney.go": "//go:build deney\n\npackage main\n\nimport \"fmt\"\n\nfunc deneyIslevi() int {\n\tfmt.Printf(\"%d\\n\", \"hata\")\n\treturn 3\n}\n",
		"ikisi_birden.go": "//go:build genisleme && deney\n\npackage main\n\nfunc ikisiBirdenIslevi() int { return 4 }\n",
		"herhangi.go": "//go:build genisleme || deney\n\npackage main\n\nfunc herhangiIslevi() int { return 5 }\n",
		"hic_degil.go": "//go:build !deney\n\npackage main\n\nfunc hicDegilIslevi() int { return 6 }\n",
	}
	dizin := paket(dosyalar)
	defer func() { _ = dizin }()

	fmt.Println("-- hangi dosyalar derlemeye giriyor: dort etiket senaryosu --")
	goster("etiketsiz dosya sayisi", "arac", fmt.Sprint(goFilesSayisi(dizin, "")))
	goster("genisleme etiketiyle dosya sayisi", "arac", fmt.Sprint(goFilesSayisi(dizin, "genisleme")))
	goster("deney etiketiyle dosya sayisi", "arac", fmt.Sprint(goFilesSayisi(dizin, "deney")))
	goster("iki etiketle birden dosya sayisi", "arac", fmt.Sprint(goFilesSayisi(dizin, "genisleme,deney")))

	fmt.Println()
	fmt.Println("-- sinirlayici olcum: disarida kalan dosya silinmiyor, sinanmiyor --")
	goster("diskteki .go dosyasi sayisi", "arac", fmt.Sprint(dosyaSayisi(dizin)))
	goster("etiketsiz derlemeye giren sayi", "arac", fmt.Sprint(goFilesSayisi(dizin, "")))

	_, etiketsizTemiz := aracCalistir(dizin, "vet", "./...")
	_, deneyliTemiz := aracCalistir(dizin, "vet", "-tags", "deney", "./...")
	goster("etiketsiz cozumleme sonucu", "arac", fmt.Sprint("temiz=", etiketsizTemiz))
	goster("deney etiketiyle cozumleme sonucu", "arac", fmt.Sprint("temiz=", deneyliTemiz))
}
```

```
-- hangi dosyalar derlemeye giriyor: dort etiket senaryosu --
etiketsiz dosya sayisi                 arac    2
genisleme etiketiyle dosya sayisi      arac    4
deney etiketiyle dosya sayisi          arac    3
iki etiketle birden dosya sayisi       arac    5

-- sinirlayici olcum: disarida kalan dosya silinmiyor, sinanmiyor --
diskteki .go dosyasi sayisi            arac    6
etiketsiz derlemeye giren sayi         arac    2
etiketsiz cozumleme sonucu             arac    temiz=true
deney etiketiyle cozumleme sonucu      arac    temiz=false
```

**DR1.** Bu on satırın hiçbiri bir program çalıştırmıyor; `deneyIslevi` ve `ikisiBirdenIslevi`
gibi işlevlerin gövdesi hiç yürütülmedi. Ölçülen tek şey, dosya listesinin kendisi — bu yüzden
tablonun tamamı **araç** sınıfına yazılıyor, kursun `koşum` sütunu bu derste hiç dolmuyor.

Altı dosyanın etiketleri üç işlecin üçünü de örnekliyor: `cekirdek.go` hiç etiketsiz,
`genisleme.go` ve `deney.go` birer tek ad, `ikisi_birden.go` bir `&&`, `herhangi.go` bir
`||`, `hic_degil.go` da başına değil işareti konmuş bir `deney`. Etiketsiz çağrıda yalnızca
etiketsiz dosyayla değil-işaretli koşulu sağlayan iki dosya kalıyor — `deney` etkin olmadığı
için `hic_degil.go` da içeride. `genisleme` etiketiyle çağrıldığında hem kendi etiketli dosyası
hem `||` koşulunu sağlayan `herhangi.go` tabloya ekleniyor, ama `&&` koşulunu sağlamak için
ikinci etiket eksik olduğundan `ikisi_birden.go` hâlâ dışarıda. **DR2.** Yalnız her iki etiket
birden verildiğinde `ikisi_birden.go` tabloya giriyor ve aynı anda `hic_degil.go` çıkıyor —
çünkü artık `deney` etkin ve değil-işaretli koşul bozuluyor. Dört sayı (2, 4, 3, 5) bu yüzden
dört ayrı mantık değerlendirmesinin sonucu, dört ayrı çalıştırmanın değil.

## Adlandırma da Bir Örtük Etiket: `_test.go`

Derleme etiketi tek mekanizma değil. Bir dosya adının sonuna eklenen `_test.go` soneki, hiçbir
`//go:build` yönergesi yazılmadan aynı soruya yanıt veriyor: bu dosya normal derlemeye girer mi?
Yanıt hayır — ama bu, dosyanın hiç derlenmediği anlamına gelmiyor; yalnızca ayrı bir listede,
`TestGoFiles` altında derleniyor. Aşağıdaki ölçüm iki dosyalık bir paket kuruyor — biri
etiketsiz, biri `_test.go` soneki taşıyan bir sınama dosyası — ve iki ayrı alanı okuyor.

```go
// adlandirma.go — _test.go soneki, hicbir yonerge yazilmadan dosyayi normal derlemeden cikariyor
package main

import "fmt"

func main() {
	dosyalar := map[string]string{
		"cekirdek.go": "package main\n\nfunc temelIslev() int { return 1 }\n",
		"cekirdek_test.go": "package main\n\nimport \"testing\"\n\nfunc TestTemelIslev(t *testing.T) {\n\tif temelIslev() != 1 {\n\t\tt.Fatal(\"beklenmeyen\")\n\t}\n}\n",
	}
	dizin := paket(dosyalar)
	defer func() { _ = dizin }()

	fmt.Println("-- adlandirma da bir ortuk etiket: _test.go --")
	goster("diskteki .go dosyasi sayisi", "arac", fmt.Sprint(dosyaSayisi(dizin)))
	goster("normal derlemeye giren dosya sayisi", "arac", fmt.Sprint(alanSayisi(dizin, "{{len .GoFiles}}")))
	goster("sinama icin derlenen dosya sayisi", "arac", fmt.Sprint(alanSayisi(dizin, "{{len .TestGoFiles}}")))
}
```

```
-- adlandirma da bir ortuk etiket: _test.go --
diskteki .go dosyasi sayisi            arac    2
normal derlemeye giren dosya sayisi    arac    1
sinama icin derlenen dosya sayisi      arac    1
```

**DR3.** İki dosya diskte duruyor ama hiçbiri diğerinin listesine karışmıyor: normal derleme
yalnızca `cekirdek.go`yu görüyor, sınama derlemesi yalnızca `cekirdek_test.go`yu. Bu ayrım bir
yönergeye değil, saf bir ada dayanıyor — dosya adının sonunda `_test.go` görmek, aracın
kendisine gömülü, kaynakta hiç yazılmayan bir kuraldır. `//go:build` yönergesi bu kuralın
üstüne, isteğe bağlı bir ikinci katman ekliyor; adlandırma sözleşmesi hep orada duruyor ve
her paket için otomatik işliyor. İki mekanizma da aynı soruyu yanıtlıyor — hangi dosya hangi
listeye giriyor — ama biri kaynakta açıkça yazılı bir yönergeye, öteki yalnızca bir ada
dayanıyor.

## Sınırlayıcı Ölçüm: Dosya Silinmiyor, Sınanmıyor

Yukarıdaki tablonun ikinci yarısı bambaşka bir soru soruyor: etiketiyle dışarıda kalan bir
dosyaya ne oluyor? Yanıt iki parçalı ve ikisi de ölçülüyor. Birincisi: dizindeki `.go` dosyası
sayısı her zaman altı — hiçbir etiket kombinasyonu bir dosyayı diskten silmiyor, yalnızca o
çalıştırmanın derleme listesinden çıkarıyor. `dosyaSayisi` işlevi bunu doğrudan `os.ReadDir`
ile sayıyor, hiçbir `go` komutu çağırmadan; **DR4.** diskteki altı dosyaya karşılık etiketsiz
derlemeye giren yalnızca iki — aradaki dört dosya ne kaybolmuş ne bozulmuş, yalnızca o
çalıştırmanın görüş alanının dışında duruyor.

İkincisi ve daha keskin olanı: `deney.go` içine bilinçli olarak bir kusur yazıldı — `%d`
biçimlendiricisine bir dizgi veriliyor, bu tür bir uyuşmazlığı derleyici değil, Go Komut Ailesi
dersinde tanıtılan inceleyici yakalar. Etiketsiz çalıştırıldığında bu araç `temiz=true` diyor,
çünkü kusurlu dosya derleme listesinde hiç yok — araç onu okumuyor bile. **DR5.** Aynı araç
`deney` etiketiyle çağrıldığında `temiz=false`a dönüyor; kusur değişmedi, yalnızca dosyanın
görünürlüğü değişti. Bu, "etiketle dışarıda kalan dosya sınanmaz" iddiasının tam kanıtı: kusur,
etiketi etkinleştiren biri onu çalıştırana kadar hiçbir aracın erişemeyeceği bir köşede bekliyor.

Bu sınır pratikte iki yönlü çalışıyor. Bir yandan, deneysel ya da platforma özgü kod bir
etiketin arkasına yazıldığında, o kodun kalitesi ana derlemenin ve ana çözümlemenin hiçbir
zaman denetlemediği bir alanda kalıyor — etiket yalnızca hangi ortamda derleneceğini
belirliyor, o kodun doğruluğunu garanti etmiyor. Öte yandan, bu tam olarak etiketlerin var
oluş nedeni: bir kaynak ağacı, aynı anda çalışması beklenmeyen birden çok varyantı taşıyabiliyor
— ana akışın dışında kalan bir varyant, ana akışı hiç etkilemeden diskte durabiliyor. Etiketin
kararı derleme öncesinde, tek bir metin karşılaştırmasıyla veriliyor; bu yüzden hem çok ucuz
hem de çok kesin, ama sonuçları yalnızca etiketi veren tarafından görülebiliyor.

Bu iki yön arasındaki denge bir sorumluluk paylaşımına dönüşüyor. Bir dosyayı bir etiketin
arkasına yazan kişi, o dosyanın normal akışın gözünden kaybolduğunu biliyor; dolayısıyla onu
düzenli aralıklarla etiketiyle birlikte derlemek ve çözümlemek, o dosyanın kalitesini
sürdürmenin **tek** yolu haline geliyor. Etiket kendi başına hiçbir hatırlatma yapmıyor —
`deney.go` içindeki kusur, biri açıkça `-tags deney` vermediği sürece haftalarca, hatta
kaynak ağacı büyüdükçe fark edilmeden kalabilir. Bu ders bu riski somut bir sayı üzerinden
gösteriyor: altı dosyalık küçük bir örnekte fark tek bir satırdı, ama gerçek bir depoda
etiketli dosya sayısı arttıkça, etiketsiz derlemenin ve etiketsiz çözümlemenin **görmediği**
kaynak miktarı da büyüyor — üstelik bu miktarın kendisi, yine yalnızca aracın kendisine
sorularak, dosya listesi karşılaştırılarak ölçülebiliyor.

## Özet

- Derleme etiketi, bir dosyanın derlemeye girip girmeyeceğini kaynağın dışından, `-tags`
  bayrağıyla belirleyen bir yönergedir; kararı hiçbir program çalıştırmadan **araç** verir.
- Etiket ifadeleri `&&`, `||` ve değil işareti ile kurulur; aynı altı dosyalık kaynak, verilen
  etiket kümesine göre iki ile beş arasında değişen bir dosya listesi üretebiliyor.
- Etiketiyle dışarıda kalan bir dosya diskten silinmiyor — yalnızca o çalıştırmanın derleme
  listesinin dışında kalıyor; dizindeki dosya sayısı her zaman sabit kalıyor.
- Dışarıda kalan bir dosyadaki kusur, o dosyayı hiçbir araç okumadığı için görünmüyor;
  aynı araç, etiket etkinleştirildiğinde aynı kusuru hemen buluyor.
- Bu ders `koşum` sınıfını hiç kullanmadı — bütün sonuçlar, hiçbir işlev çağrılmadan, yalnızca
  dosya listesi ve çözümleme sonucu okunarak elde edildi.

## Sonraki Adım

Bu derste ölçülen soru "hangi dosya" ile sınırlıydı — dosyalar belli olduktan sonra hepsi aynı
tek bir hedefe, çalıştırılan makinenin kendisine derleniyordu. Sıradaki ders aynı sorunun bir
adım ötesini soruyor: aynı kaynak, hiç değişmeden, makineden tamamen farklı bir hedefe
derlenebilir mi? Yanıt yine koşum beklemeden, tek bir komutun verdiği geçti/kaldı bilgisinden
okunuyor — ama bu kez derlenmiş olmanın, hedefte çalışacağı anlamına gelmediği bir sınırla
birlikte.
