Bazel 17: O'z rule'laringiz: genrule, rule(), provider'lar, depset'lar, aspect'lar va toolchain'lar
📦 Bazel iOS — Mukammal Qo‘llanma & Amaliyot
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,genrulevarule()farqini, qoidalar o'rtasida ma'lumot uzatuvchiProvidermexanizmini, xotirani O(1) darajasida tejovchidepsetma'lumotlar strukturasini, statik tahlil uchunAspectlarni 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.
genrulening soddaligi va uning chegaralari (qachonrule()ga o'tish shartligini) tushunasiz.- Starlark'da
rule(implementation = ...)hayot siklini,ctxkontekstini vactx.actionsmetodlarini bilasiz. - Nega oddiy Python massivi (
list) o'rniga aynan **depset** ishlatilishini mexanizm darajasida anglaysiz. Providerlar 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_generatorqoidasini yozasiz. - Butun dependency grafik bo'ylab yurib chiqib xatoliklarni tekshiruvchi Aspectlar kontseptsiyasini o'rganasiz.
Mundarija
- Nega o'z rule'larimizni yozishimiz kerak?
- 1-daraja: genrule — tezkor bash qobig'i
- 2-daraja: Haqiqiy Starlark rule() anatomiyasi
- ctx konteksti va ctx.actions: Fayllar va Buyruqlar
- Depset mo''jizasi: Nega oddiy List ishlatilmaydi?
- Provider'lar: Targetlar o'rtasida ma'lumot almashish
- Amaliy loyiha: swift_icon_generator qoidasini yozamiz
- Aspect'lar (Aspects): Butun grafikni tahlil qilish
- Toolchain tushunchasi
- Amaliy mashqlar
- O'zingizni tekshiring
- Xulosa
- 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.bundlega 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
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:
# 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
curlyokidatekabi o'zgaruvchan buyruqlar chaqirilsa, kesh buziladi. - Provider bera olmaydi:
genrulefaqat oddiy fayllar ro'yxatini qaytaradi. U boshqa Swift targetlarigaSwiftInfokabi 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:
- **
rule()deklaratsiyasi:** qaysi atributlar (attrs) qabul qilinishini belgilaydi. - Implementation funksiyasi: Bazel Analysis bosqichida chaqiriladigan mantiq.
Misol: rules/icon_generator.bzl:
# 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:
# 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:
# 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:
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:
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
{
"home": "house.fill",
"settings": "gearshape.fill",
"profile": "person.crop.circle"
}
rules/simple_icon.bzl
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.bazelda ishlatish:
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:
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
LICENSEfayllarini bitta joyga to'plash. - Code Coverage hisoblash.
Aspect e'lon qilish:
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 swiftcni topib ulaydi.
Amaliy mashqlar {#mashqlar}
- **
build_metadataqoidasini yozing.** Starlark'da yangi qoida tuzing: u joriy sana, commit xeshi va versiyani o'z ichiga olganVersion.swiftfaylinictx.actions.writeorqali hosil qilsin. - Atribut tekshiruvi (Validation).
rule()atributlarigamandatory = Truevavalues = ["dev", "prod"]shartlarini qo'yib, noto'g'ri qiymat berilganda Bazel Analysis xato chiqarishini sinab ko'ring. - ⭐ Depset chuqurligi. Ikkita modul orasida
depsetorqali fayllar ro'yxatini uzatuvchi custom Provider yozing va yakuniy targetda barcha fayllar ro'yxati to'g'ri chiqqanini tasdiqlang.
O'zingizni tekshiring {#checklist}
- [ ]
genrulevarule()o'rtasidagi asosiy farqlarni sanab bera olaman. - [ ]
ctx.actions.declare_filevactx.actions.runqanday ishlashini bilaman. - [ ] Nega Starlark'da oddiy ro'yxat o'rniga
depsetishlatilishi 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
Manbalar {#manbalar}
- Bazel Documentation — Rules Tutorial — Rasmiy Starlark qoidalari qo'llanmasi.
- Bazel Depsets — Depset ma'lumotlar tuzilmasi va xotira optimizatsiyasi.
- Bazel Aspects Guide — Bog'liqliklar grafigini tahlil qilish uchun Aspect'lar.