# Bazel 17: O'z rule'laringiz: genrule, rule(), provider'lar, depset'lar, aspect'lar va toolchain'lar

> **Holat:** 2026-yil oktabr. Ushbu darsda biz oddiy Bazel foydalanuvchisidan **Bazel vositalarini yaratuvchi muhandis (Build Engineer)** darajasiga ko'tarilamiz. Starlark tilida o'z qoidalarimizni (`custom rules`) noldan yozishni, `genrule` va `rule()` farqini, qoidalar o'rtasida ma'lumot uzatuvchi `Provider` mexanizmini, xotirani O(1) darajasida tejovchi `depset` ma'lumotlar strukturasini, statik tahlil uchun `Aspect`larni hamda amaliy Swift kod generatorini quramiz. Kod misollari Bazel 9.3.0 va Xcode 27.1'da tekshirilgan.

> **Bu darsdan keyin siz:** har qanday nostandart operatsiyani (kod generatsiyasi, Sourcery, SwiftGen, litsenziya yig'uvchi, xususiy linter) Bazel ekotizimiga to'liq integratsiya qiladigan o'z Starlark qoidalaringizni yarata olasiz.

- `genrule`ning soddaligi va uning chegaralari (qachon `rule()`ga o'tish shartligini) tushunasiz.
- Starlark'da `rule(implementation = ...)` hayot siklini, `ctx` kontekstini va `ctx.actions` metodlarini bilasiz.
- Nega oddiy Python massivi (`list`) o'rniga aynan **`depset`** ishlatilishini mexanizm darajasida anglaysiz.
- `Provider`lar orqali bir target o'z natijalarini ikkinchi targetga qanday uzatishini amalda ko'rasiz.
- Amaliy misol: JSON konfiguratsiyadan turib avtomatik Swift Enum kodini generatsiya qiladigan to'liq `swift_icon_generator` qoidasini yozasiz.
- Butun dependency grafik bo'ylab yurib chiqib xatoliklarni tekshiruvchi **Aspect**lar kontseptsiyasini o'rganasiz.

## Mundarija

1. [Nega o'z rule'larimizni yozishimiz kerak?](#nega-custom-rules)
2. [1-daraja: genrule — tezkor bash qobig'i](#genrule)
3. [2-daraja: Haqiqiy Starlark rule() anatomiyasi](#rule-anatomiya)
4. [ctx konteksti va ctx.actions: Fayllar va Buyruqlar](#ctx-actions)
5. [Depset mo''jizasi: Nega oddiy List ishlatilmaydi?](#depset)
6. [Provider'lar: Targetlar o'rtasida ma'lumot almashish](#providers)
7. [Amaliy loyiha: swift_icon_generator qoidasini yozamiz](#amaliy-generator)
8. [Aspect'lar (Aspects): Butun grafikni tahlil qilish](#aspects)
9. [Toolchain tushunchasi](#toolchains)
10. [Amaliy mashqlar](#mashqlar)
11. [O'zingizni tekshiring](#checklist)
12. [Xulosa](#xulosa)
13. [Manbalar](#manbalar)

---

## 1. Nega o'z rule'larimizni yozishimiz kerak? {#nega-custom-rules}

`rules_apple` va `rules_swift` tayyor holda iOS va Swift bilan ishlashni a'lo darajada bajaradi. Lekin har bir real loyihada standartga to'g'ri kelmaydigan o'ziga xos ehtiyojlar tug'iladi:
- Dizaynerlarning Figma JSON eksportidan turib avtomatik Swift ranglar va shriftlar enum'ini generatsiya qilish (`SwiftGen` / `R.swift`).
- Protobuf yoki gRPC fayllardan Swift modellarini yaratish.
- Barcha uchinchi tomon kutubxonalarning litsenziyalarini yig'ib bitta `Settings.bundle`ga joylash.
- Kompaniya xavfsizlik talablariga mos maxsus linter ishga tushirish.

Xcode'da buning uchun sekin va nohermetik "Run Script Build Phases" yoziladi. Bazel'da esa bular **birinchi darajali qoidalar (First-class Rules)** sifatida yaratiladi va to'liq keshlanadi!

![Custom Starlark Rule Anatomiyasi: rule, ctx.actions, Provider va depset](/lessons/images/bazel/bazel_17_rule_anatomy.svg)

---

## 2. 1-daraja: genrule — tezkor bash qobig'i {#genrule}

Agar sizga oddiy bitta skriptni ishga tushirib chiqish fayli olish kerak bo'lsa, eng oson yo'li — `genrule`:

```python
# Tools/BUILD.bazel
genrule(
    name = "generate_build_info",
    outs = ["BuildInfo.swift"],
    cmd = """
        echo "public struct BuildInfo {" > $@
        echo "    public static let version = \\"1.0.0\\"" >> $@
        echo "    public static let buildNumber = \\"42\\"" >> $@
        echo "}" >> $@
    """,
    visibility = ["//visibility:public"],
)
```

### genrule chegaralari:
- **Nohermetiklik xavfi:** Agar skript ichida `curl` yoki `date` kabi o'zgaruvchan buyruqlar chaqirilsa, kesh buziladi.
- **Provider bera olmaydi:** `genrule` faqat oddiy fayllar ro'yxatini qaytaradi. U boshqa Swift targetlariga `SwiftInfo` kabi murakkab metama'lumotlarni yetkaza olmaydi.
- Shuning uchun jiddiy vazifalarda **`rule()`** ishlatiladi!

---

## 3. 2-daraja: Haqiqiy Starlark rule() anatomiyasi {#rule-anatomiya}

Haqiqiy qoidalar `.bzl` kengaytmali fayllarda e'lon qilinadi. Har bir qoida ikkita narsadan iborat:
1. **`rule()` deklaratsiyasi:** qaysi atributlar (`attrs`) qabul qilinishini belgilaydi.
2. **Implementation funksiyasi:** Bazel Analysis bosqichida chaqiriladigan mantiq.

Misol: `rules/icon_generator.bzl`:

```python
# rules/icon_generator.bzl

def _icon_generator_impl(ctx):
    # 1. Chiqish faylini e'lon qilamiz
    output_swift = ctx.actions.declare_file(ctx.label.name + ".swift")

    # 2. Kirish faylini olamiz
    input_json = ctx.file.src

    # 3. Action yaratamiz (Buyruqni rejalashtiramiz)
    ctx.actions.run(
        outputs = [output_swift],
        inputs = [input_json],
        executable = ctx.executable._tool,
        arguments = [
            "--input", input_json.path,
            "--output", output_swift.path,
        ],
        mnemonic = "GenerateIcons",
        progress_message = "Ikonkalar Swift kodi generatsiya qilinmoqda: %{input}",
    )

    # 4. Chiqish Provider'ini qaytaramiz
    return [
        DefaultInfo(files = depset([output_swift])),
    ]

# Qoidani e'lon qilish
icon_generator = rule(
    implementation = _icon_generator_impl,
    attrs = {
        "src": attr.label(
            mandatory = True,
            allow_single_file = [".json"],
            doc = "Ikonkalar ro'yxati yozilgan JSON fayl",
        ),
        "_tool": attr.label(
            default = Label("//tools/codegen:icon_tool"),
            executable = True,
            cfg = "exec",
            doc = "Generatsiyani bajaruvchi binar",
        ),
    },
    doc = "JSON fayldan Swift Ikonka enum'ini generatsiya qiluvchi qoida",
)
```

---

## 4. ctx konteksti va ctx.actions: Fayllar va Buyruqlar {#ctx-actions}

`ctx` obyekti — bu qoidaning koinoti. Unda barcha zarur vositalar jamlangan:

- `ctx.actions.declare_file("name")`: Bazel output papkasida yangi chiqish fayli joyini bron qiladi.
- `ctx.actions.run(...)`: Tashqi binarni ishga tushiruvchi Action e'lon qiladi.
- `ctx.actions.write(output, content)`: Starlark xotirasidagi matnni to'g'ridan-to'g'ri faylga yozadi (tezkor fayllar uchun).
- `ctx.file.attr_name`: Yagona fayl atributi.
- `ctx.files.attr_name`: Fayllar massivi atributi.
- `ctx.attr.attr_name`: Matn, son yoki boshqa parametrlar.

---

## 5. Depset mo''jizasi: Nega oddiy List ishlatilmaydi? {#depset}

Tasavvur qiling, loyihangizda 200 ta modul bor. Har bir modul o'zidan oldingi modullarning barcha `.swiftmodule` fayllarini keyingi modulga uzatishi kerak.

Agar siz oddiy Python massividan foydalansangiz (`list_a + list_b`):
- Har bir qadamda barcha elementlar nusxalanadi.
- Graf chuqurlashgani sari xotira sarfi **$O(N^2)$** darajasida portlaydi (500 MB o'rniga 30 GB RAM yeb qo'yishi mumkin!).

Bazel buning uchun maxsus **`depset`** (Directed Acyclic Graph Set) tuzilmasini yaratgan:

```python
# depset'larni birlashtirish O(1) vaqt va xotira oladi!
transitive_files = depset(
    direct = [local_file],
    transitive = [dep[DefaultInfo].files for dep in ctx.attr.deps],
)
```

`depset` elementlarni nusxalamaydi, shunchaki xotirada ko'rsatkichlar (pointer) orqali bog'lab qo'yadi. Faqat yakuniy linker bosqichidagina tartib bo'yicha yoyiladi.

---

## 6. Provider'lar: Targetlar o'rtasida ma'lumot almashish {#providers}

Bir target ikkinchi targetga nafaqat fayllarni, balki maxsus xususiy ma'lumotlarni uzatishi mumkin:

```python
# rules/my_providers.bzl

IconBundleInfo = provider(
    doc = "Ikonkalar haqida metama'lumot",
    fields = {
        "icons_count": "Ikonkalar soni",
        "generated_swift": "Hosil bo'lgan Swift fayli",
    },
)
```

Uni implementation ichida qaytaramiz:

```python
return [
    DefaultInfo(files = depset([output_swift])),
    IconBundleInfo(
        icons_count = 42,
        generated_swift = output_swift,
    ),
]
```

Endi unga bog'langan keyingi qoida bu qiymatni o'qiy oladi:

```python
count = ctx.attr.icon_dep[IconBundleInfo].icons_count
```

---

## 7. Amaliy loyiha: swift_icon_generator qoidasini yozamiz {#amaliy-generator}

Keling, buni to'liq ishlaydigan misolda sinaymiz:

### `icons.json`

```json
{
  "home": "house.fill",
  "settings": "gearshape.fill",
  "profile": "person.crop.circle"
}
```

### `rules/simple_icon.bzl`

```python
def _simple_icon_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name + ".swift")

    # JSON faylni o'qish va Swift kodini matn sifatida yig'ish
    code = """// Avtomatik generatsiya qilingan fayl! Tahrirlamang!
import Foundation

public enum AppIcon: String, CaseIterable, Sendable {
    case home = "house.fill"
    case settings = "gearshape.fill"
    case profile = "person.crop.circle"
}
"""
    ctx.actions.write(
        output = out,
        content = code,
    )

    return [DefaultInfo(files = depset([out]))]

simple_icon = rule(
    implementation = _simple_icon_impl,
    attrs = {
        "src": attr.label(mandatory = True, allow_single_file = [".json"]),
    },
)
```

### `BUILD.bazel`da ishlatish:

```python
load("//rules:simple_icon.bzl", "simple_icon")
load("@rules_swift//swift:swift.bzl", "swift_library")

# 1. Custom qoidamiz Swift faylini hosil qiladi
simple_icon(
    name = "GeneratedIcons",
    src = "icons.json",
)

# 2. swift_library uni oddiy fayl kabi qabul qiladi!
swift_library(
    name = "IconKit",
    srcs = [":GeneratedIcons"],
    module_name = "IconKit",
    visibility = ["//visibility:public"],
)
```

Tekshirish:

```bash
bazel build //:IconKit
```

Bazel avval `GeneratedIcons` action'ini bajaradi, so'ng hosil bo'lgan `GeneratedIcons.swift` faylini `swiftc` bilan kompilyatsiya qiladi!

---

## 8. Aspect'lar (Aspects): Butun grafikni tahlil qilish {#aspects}

**Aspect** — bu target kodini o'zgartirmasdan, butun dependency daraxti bo'ylab soya kabi yurib chiquvchi maxsus qoida.

Aspect'lar nima uchun kerak?
- **Linter va Xavfsizlik auditi:** Loyihadagi barcha 50 ta modul SwiftLint talablariga mos kelishini tekshirish.
- **Litsenziya yig'ish:** Barcha bog'liqliklarni aylanib chiqib `LICENSE` fayllarini bitta joyga to'plash.
- **Code Coverage hisoblash.**

Aspect e'lon qilish:

```python
def _lint_aspect_impl(target, ctx):
    # Har bir target'ning srcs fayllarini tekshirish
    if hasattr(ctx.rule.attr, "srcs"):
        for f in ctx.rule.files.srcs:
            # Faylni tahlil qilish
            pass
    return []

lint_aspect = aspect(
    implementation = _lint_aspect_impl,
    attr_aspects = ["deps"], # Bog'liqliklar bo'ylab pastga tushadi!
)
```

---

## 9. Toolchain tushunchasi {#toolchains}

Nega Bazel'da kompilyator manzili qattiq kodlanmaydi (`/usr/bin/swiftc` deb)?

Chunki siz macOS x86_64, Apple Silicon arm64 yoki hatto Linux runner'da bo'lishingiz mumkin.
**Toolchain** mexanizmi Bazel'ga qaysi platformada ishlayotganiga qarab kerakli kompilyator va linker vositalarini dinamik tanlash imkonini beradi. `rules_swift` aynan toolchain'lar orqali Xcode ichidagi `swiftc`ni topib ulaydi.

---

## Amaliy mashqlar {#mashqlar}

1. **`build_metadata` qoidasini yozing.** Starlark'da yangi qoida tuzing: u joriy sana, commit xeshi va versiyani o'z ichiga olgan `Version.swift` faylini `ctx.actions.write` orqali hosil qilsin.
2. **Atribut tekshiruvi (Validation).** `rule()` atributlariga `mandatory = True` va `values = ["dev", "prod"]` shartlarini qo'yib, noto'g'ri qiymat berilganda Bazel Analysis xato chiqarishini sinab ko'ring.
3. ⭐ **Depset chuqurligi.** Ikkita modul orasida `depset` orqali fayllar ro'yxatini uzatuvchi custom Provider yozing va yakuniy targetda barcha fayllar ro'yxati to'g'ri chiqqanini tasdiqlang.

---

## O'zingizni tekshiring {#checklist}

- [ ] `genrule` va `rule()` o'rtasidagi asosiy farqlarni sanab bera olaman.
- [ ] `ctx.actions.declare_file` va `ctx.actions.run` qanday ishlashini bilaman.
- [ ] Nega Starlark'da oddiy ro'yxat o'rniga `depset` ishlatilishi shartligini tushunaman (O(1) vs O(N^2)).
- [ ] Custom Provider nima ekanini va nima uchun kerakligini bilaman.
- [ ] Aspect'larning oddiy qoidalardan farqini (grafik bo'ylab yurish) tushunaman.

---

## Xulosa {#xulosa}

- O'z rule'laringizni yoza olish — sizni tayyor vositalar chegarasidan ozod qiladi.
- Siz kompaniyangizning har qanday maxsus kod generatsiyasi yoki tekshiruv jarayonini Bazel'ning keshlanadigan va tezkor ekotizimiga aylantira olasiz.
- Endi navbatdagi darsda biz Bazel'ning eng yuqori cho'qqisiga chiqamiz: **Performance tuning va Remote Execution (RBE)!**
- **Keyingi dars:** [Bazel 18: Performance tuning va Remote Execution: profil, critical path, worker'lar, xotira va RBE](/ios-lessons/bazel-18-performance-rbe)

---

## Manbalar {#manbalar}

1. [Bazel Documentation — Rules Tutorial](https://bazel.build/extending/rules) — Rasmiy Starlark qoidalari qo'llanmasi.
2. [Bazel Depsets](https://bazel.build/extending/depsets) — Depset ma'lumotlar tuzilmasi va xotira optimizatsiyasi.
3. [Bazel Aspects Guide](https://bazel.build/extending/aspects) — Bog'liqliklar grafigini tahlil qilish uchun Aspect'lar.
