Bazel 3: Asosiy tushunchalar — workspace, package, target, label, rule, visibility va build graph
📦 Bazel iOS — Mukammal Qo‘llanma & Amaliyot
Bazel 3: Asosiy tushunchalar — workspace, package, target, label, rule, visibility va build graph
Holat: 2026-yil oktabr. Bu darsda Bazel'ning asosiy lug'atini — repository/workspace, package chegaralari, target (rule/file/generated), label va target pattern'lar, rule va attribute'lar,
glob(), visibility, build'ning uch bosqichi (loading, analysis, execution), Skyframe, action graph, sandboxing hamdabazel query/cquery/aquery— bitta 3-paketli macOS workspace misolida o'rganamiz. Barcha kod va o'lchovlar Bazel 9.3.0 (Bazelisk 1.29.0), rules_swift 4.2.1, apple_support 2.10.1 va Xcode 27.1 bilan Apple M4 Pro (12 core, 24 GB) kompyuterida tekshirilgan.
Bu darsdan keyin siz: har qanday BUILD faylni o'qib, "bu yerda nechta target bor, ularning manzili nima, kim kimga bog'liq, nima qachon qayta quriladi?" degan savollarga aniq javob bera olasiz — va
bazel querybilan buni o'zingiz tekshira olasiz.
- Repository, workspace va package o'rtasidagi farqni, package chegarasini (BUILD fayli) va
glob()nega chegaradan o'tmasligini tushunasiz. - Label sintaksisini (
@repo//package:target) va barcha qisqartmalarni (//Core,:name, file target) hamda target pattern'larni (//...,//Core/...,:all, istisno) erkin yozasiz. - Rule va uning attribute'larini (
srcs,deps,data,visibility,testonly) va visibility'ni arxitektura quroli sifatida ishlatasiz — haqiqiy visibility xatosini o'zingiz chiqarib ko'rasiz. - Build'ning uch bosqichini, Skyframe nega ikkinchi build'ni tezlashtirishini, action graph va sandbox nega e'lon qilinmagan input'ni ushlab qolishini bilasiz.
bazel query,cqueryvaaquerybilan target grafini, konfiguratsiyani va haqiqiyswiftcbuyruq satrini ko'rasiz.
Mundarija
- Nega bu tushunchalar kerak
- Repository, workspace va package
- Target: rule, file va generated
- Label: har bir target'ning manzili
- Rule va attribute'lar
- glob() va package chegaralari
- Visibility: arxitekturani majburlash
- Build'ning uch bosqichi va Skyframe
- Action graph va sandboxing
- bazel query, cquery va aquery
- Amaliy mashqlar
- O'zingizni tekshiring
- Xulosa
- Manbalar
1. Nega bu tushunchalar kerak {#nega-kerak}
Bazel 2 darsida siz bitta paketli "Salom, Bazel!" workspace'ini qurdingiz va bazel build ishlaganini ko'rdingiz. U yerda hammasi "shunchaki ishladi". Lekin haqiqiy loyihada yuzlab target, o'nlab modul va murakkab bog'liqliklar bo'ladi. O'shanda "ishladi" yetarli emas — sizga model kerak: kod qanday tashkil qilinadi, target'lar qanday manzillanadi, nima qachon qayta quriladi.
Xcode'da bu modelning yarmi yashirin. project.pbxproj ichida target'lar, build phase'lar va implicit bog'liqliklar bor, lekin siz ularni to'g'ridan-to'g'ri o'qiy olmaysiz va "agar men bu faylni o'zgartirsam, nima qayta quriladi?" degan savolga ishonchli javob bera olmaysiz. Bazel'da aksincha — hamma narsa aniq e'lon qilinadi va so'rab olinadi. Bu darsning oxirida siz shu savolga bir buyruq bilan javob berasiz:
# "Agar Models modulini o'zgartirsam, nima qayta quriladi?"
bazel query 'rdeps(//..., //Models)'
//App:App
//Models:Models
//Services:Services
🚀 Motivatsiya: Aynan shu savol — "o'zgarishim nimaga ta'sir qiladi?" — katta jamoalarda CI'ni tezlashtiradigan "faqat ta'sirlanganini build/test qil" strategiyasining poydevori. G'oya oddiy:
git diff→ o'zgargan fayllar → ularga tegishli target'lar →rdepsbilan ta'sirlangan target'lar → faqat o'shalarni qur va testla. Gojek jamoasi o'z iOS build'larini Bazel bilan "3 barobar" tezlashtirgani haqida muhandislik maqolasi e'lon qilgan (Gojek Engineering, Medium — Manbalar). Biz bu strategiyani Bazel 11 va Bazel 16 darslaridabazel-diffbilan to'liq quramiz — lekin uning "dvigateli" shu darsdagiqueryva build graph.
Bu darsni o'qib chiqqach, BUILD fayl siz uchun sirli emas, balki o'qiladigan arxitektura xaritasi bo'ladi.
Dars davomidagi workspace: shaharlar
Butun darsni bitta kichik, lekin haqiqiy ko'p-paketli workspace'da olib boramiz. U uchta package'dan iborat: //Models (ma'lumot turlari), //Services (biznes mantiq) va //App (macOS buyruq qatori dasturi). Mana uning tuzilishi:
shaharlar/
├── .bazelversion # 9.3.0
├── MODULE.bazel # modul + bazel_dep (rules_swift, apple_support)
├── .bazelrc # umumiy flaglar (disk_cache, verbose_failures)
├── BUILD.bazel # ildiz package (bo'sh)
├── Models/
│ ├── BUILD.bazel # swift_library "Models"
│ └── City.swift
├── Services/
│ ├── BUILD.bazel # swift_library "Services" → //Models
│ └── CityService.swift
└── App/
├── BUILD.bazel # swift_binary "App" → //Services
└── main.swift
MODULE.bazel Bazel 2 dagi kabi — faqat modul nomi va ikkita tashqi rule to'plami:
# MODULE.bazel
module(name = "shaharlar", version = "1.0.0")
bazel_dep(name = "apple_support", version = "2.10.1")
bazel_dep(name = "rules_swift", version = "4.2.1")
Swift kodi juda sodda — chunki darsning mavzusi Swift emas, balki Bazel tushunchalari. //Models bitta ma'lumot turini e'lon qiladi:
// Models/City.swift
// Bitta shaharni ifodalovchi oddiy ma'lumot turi.
public struct City {
public let name: String
public let population: Int
public let region: String
public init(name: String, population: Int, region: String) {
self.name = name
self.population = population
self.region = region
}
}
//Services //Models'ga tayanib, saralash va yig'indi mantig'ini beradi:
// Services/CityService.swift
import Models
public struct CityService {
public let cities: [City]
public init(cities: [City]) {
self.cities = cities
}
// aholisi bo'yicha kamayish tartibida saralaydi
public func sortedByPopulation() -> [City] {
cities.sorted { $0.population > $1.population }
}
// barcha shaharlar aholisining yig'indisi
public func totalPopulation() -> Int {
cities.reduce(0) { $0 + $1.population }
}
}
//App esa macOS buyruq qatori dasturi — u //Services'ga tayanadi:
// App/main.swift
import Models
import Services
// Namuna ma'lumotlar (aniq statistik raqamlar emas)
let cities = [
City(name: "Toshkent", population: 2_900_000, region: "Toshkent"),
City(name: "Namangan", population: 650_000, region: "Namangan"),
City(name: "Samarqand", population: 560_000, region: "Samarqand"),
City(name: "Andijon", population: 450_000, region: "Andijon"),
City(name: "Buxoro", population: 280_000, region: "Buxoro")
]
let service = CityService(cities: cities)
print("Shaharlar (aholisi bo'yicha):")
for city in service.sortedByPopulation() {
print(" \(city.name) — \(city.population) kishi")
}
print("Jami: \(service.totalPopulation()) kishi")
Har bir package'ning BUILD faylini quyida, tegishli bo'limlarda ochamiz. Hozircha bitta narsani ko'rib qo'yaylik — bu workspace quriladi va ishlaydi:
bazel run //App
Shaharlar (aholisi bo'yicha):
Toshkent — 2900000 kishi
Namangan — 650000 kishi
Samarqand — 560000 kishi
Andijon — 450000 kishi
Buxoro — 280000 kishi
Jami: 4840000 kishi
Endi shu //App, //Services, //Models degan nomlar aslida nima ekanligini qism-qismga ajratamiz.
2. Repository, workspace va package {#workspace-package}
Bazel kodni uch darajali ierarxiyada ko'radi: repository → package → target. Bu uchta so'zni ko'pincha chalkashtirishadi, shuning uchun har birini aniq ta'riflaymiz (manba: Bazel rasmiy hujjatlari, concepts/build-ref).
Repository (repo)
Repository — ildizida chegara belgisi fayli bo'lgan papkalar daraxti. Bazel 9'da bu belgi MODULE.bazel (yoki REPO.bazel; eski kontekstda WORKSPACE). Bizning shaharlar/ papkamiz ildizida MODULE.bazel bor — demak u repository.
Hozirgi buyruq ishlayotgan repo main repo deyiladi. Qolgan (tashqi) repo'lar — masalan rules_swift, apple_support — repo rule'lar orqali aniqlanadi va ular ham build'ga qo'shiladi.
🧠 Esda tuting: Bazel 9.0'da eski
WORKSPACEmexanizmi butunlay olib tashlangan — endi bog'liqliklar faqatMODULE.bazel(Bzlmod) orqali e'lon qilinadi,--enable_workspaceesa hech narsa qilmaydigan ("no-op") flagga aylangan (manba: Bazel 9.0 release notes, Manbalar). Shuning uchun eski darsliklardagiWORKSPACEfayli bizda umuman yo'q.
Workspace
Workspace — bitta main repo'dan ishga tushirilgan barcha Bazel buyruqlari bo'lishadigan muhit. U main repo'ni va barcha aniqlangan tashqi repo'larni o'z ichiga oladi. Tarixan "repository" va "workspace" so'zlari bir-birining o'rnida ishlatilib kelingan, shuning uchun amalda ko'pchilik "workspace" deganda main repo'ni nazarda tutadi — biz ham shunday qilamiz.
Package
Package — repository ichidagi kodni tashkil qilishning asosiy birligi. Ta'rif juda aniq:
Package — ichida
BUILD(yokiBUILD.bazel) fayli bo'lgan papka. Package o'z papkasidagi barcha fayllarni va quyidagi barcha papkalarni o'z ichiga oladi — faqat ichida o'ziningBUILDfayli bo'lgan papkalardan tashqari.
Shu ta'rifdan juda muhim xulosa chiqadi: hech bir fayl ikkita package'ga tegishli bo'la olmaydi. BUILD fayli — bu chegara. Qaerda yangi BUILD fayli paydo bo'lsa, o'sha yerda yangi package boshlanadi.
Bizning workspace'da to'rtta joyda BUILD fayli bor: ildizda, Models/, Services/ va App/ da. Demak to'rtta package. Buni o'zimiz tekshiramiz:
bazel query //... --output package
App
Models
Services
Bo'sh ildiz package ro'yxatda yo'q — chunki unda birorta ham target yo'q, --output package esa faqat target'i bor package'larni ko'rsatadi. BUILD.bazel nomi BUILD'dan ustun turadi (ikkalasi ham bo'lsa). Ildiz package'ni biz ataylab bo'sh qoldirdik — bu Bazel hujjatlari tavsiya qiladigan amaliyot: shunda barcha ma'noli package'lar tavsiflovchi nomga ega bo'ladi (Models, Services, App), ildiz esa faqat workspace chegarasini belgilaydi.
💡 Pro tip: Package ≠ papka. Papka — bu fayl tizimidagi tushuncha. Package — bu BUILD fayli bilan belgilangan Bazel tushunchasi.
Models/papkasiModels/Geo/papkasini o'z ichiga oladi, lekin agarModels/Geo/da o'z BUILD fayli bo'lsa, u alohida package (//Models/Geo) bo'ladi va//Modelspackage'iga kirmaydi. Buni 6-bo'limdaglob()bilan amalda ko'ramiz.
3. Target: rule, file va generated {#target}
Package — bu targetlar uchun idish. Target'lar package'ning BUILD faylida aniqlanadi. Ularning ikki asosiy turi bor: filelar va rulelar.
File target'lar
File target ikki xil: source file (odam yozadigan va repo'ga commit qilinadigan fayl, masalan City.swift) va generated file (boshqa rule chiqaradigan, commit qilinmaydigan fayl, masalan kompilyator chiqargan Models.swiftmodule).
Rule target'lar
Rule target rule chaqiruvi bilan e'lon qilinadi va bir to'plam input'ni bir to'plam output'ga bog'laydi. swift_library, swift_binary, genrule — bularning hammasi rule. Rule target'ning input'i source file bo'lishi mumkin, lekin boshqa rule'ning output'i ham bo'lishi mumkin — aynan shundan bog'liqlik grafi tug'iladi.
Bizning workspace'dagi barcha target'larni, ularning turi bilan birga, bitta buyruq bilan ko'rsak bo'ladi:
bazel query //... --output label_kind
swift_binary rule //App:App
swift_library rule //Models:Models
swift_library rule //Services:Services
Bu uchta rule target. Ularning har biri ortida yashiringan file targetlar ham bor — masalan source fayllar. Ularni deps ichida ko'rish uchun deps so'rovidan foydalanamiz (buni 10-bo'limda chuqur o'rganamiz):
bazel query 'deps(//App)' --noimplicit_deps
//App:App
//App:main.swift
//Models:City.swift
//Models:Models
//Services:CityService.swift
//Services:Services
E'tibor bering: //App:main.swift, //Models:City.swift, //Services:CityService.swift — bular source file targetlar. Ular BUILD faylda alohida e'lon qilinmagan, lekin rule'larning srcs ichida ishlatilgani uchun avtomatik target bo'lib qoladi.
🧠 Esda tuting: Rule target va file target o'rtasidagi farq muhim, chunki Bazel ularni bir xil "manzil tizimi" — label bilan ataydi.
//Models:Models— rule target;//Models:City.swift— file target. Ikkalasi ham label, ikkalasi ham bog'liqlik grafining tuguni.
Rule chaqiruvi target yaratganda, o'sha rule chiqaradigan generated filelar har doim rule bilan bir xil packageda bo'ladi — boshqa package'ga fayl "chiqarib bo'lmaydi". Bu qoida build grafini bashorat qilinadigan qiladi.
4. Label: har bir target'ning manzili {#label}
Har bir target noyob label bilan ataladi. Label — bu target'ning to'liq manzili. Uning to'liq (canonical) ko'rinishi uch qismdan iborat:
Label anatomiyasi: repository, package va target qismlari hamda qisqartmalar jadvali
Yuqoridagi rasmda @rules_swift//swift:swift_library labeli uch bo'lakka ajratilgan: @rules_swift — repository, //swift — package, :swift_library — target. Pastdagi jadval esa qisqartmalarni ko'rsatadi.
Uch qism
@rules_swift//swift:swift_library
│ │ │
│ │ └─ target nomi (:target)
│ └─ package (repo ildizidan yo'l)
└─ repository nomi (@repo)
- Repository nomi (
@rules_swift): qaysi repo. Bitta@— bu apparent (ko'rinadigan) repo nomi, kontekstga bog'liq. Ikkita@@— bu canonical (yagona) repo nomi, hamma joyda bir xil. Agar repo nomi yozilmasa, label o'z (main) repo'siga ishora qiladi. - Package nomi (
//swift): repo ildizidan package papkasigacha bo'lgan yo'l.//— har doim repo ildizidan boshlanishini bildiradi. - Target nomi (
:swift_library): BUILD ichidaginame = "..."qiymati (rule target), yoki package ichidagi fayl yo'li (file target).
Qisqartmalar
Amalda label'lar deyarli har doim qisqargan ko'rinishda yoziladi. Uchta asosiy qoida bor va biz ularni o'zimizning workspace'da tekshiramiz:
1-qoida: package nomi target nomiga teng bo'lsa, target nomi tushib qoladi. //Models — bu aslida //Models:Models. Tekshiramiz — ikkala so'rov bir xil javob beradi:
bazel query //Models
bazel query //Models:Models
//Models:Models
//Models:Models
**2-qoida: shu package ichida repo va package qismini tashlab, :target deb yozsa bo'ladi.** Masalan App/BUILD.bazel ichida :App — bu //App:App.
**3-qoida: file target odatda : bilan yoziladi.** //Models:City.swift — Models package'idagi City.swift fayli. (Konvensiya bo'yicha fayl uchun : saqlanadi, rule uchun esa ko'pincha tushiriladi — lekin bu faqat o'qish uchun, ma'nosi bir xil.)
⚠️ Diqqat:
//Models— bu package emas, target. Ko'p yangi boshlovchilar//Modelsdeb package'ni yoki "Models ichidagi hamma target"ni nazarda tutadi — bu xato.//Modelshar doim//Models:Modelstarget'iga teng. "Models package'idagi hamma target" uchun//Models:allyoki//Models:*yoziladi. Faqatpackage_groupva.bzlfayllar ichida//Modelspackage ma'nosida ishlatiladi.
Load label'i
BUILD faylining birinchi qatoridagi load(...) ham label ishlatadi — lekin u rule target'ga emas, .bzl fayliga ishora qiladi:
# Models/BUILD.bazel (birinchi qator)
load("@rules_swift//swift:swift.bzl", "swift_library")
Bu yerda @rules_swift//swift:swift.bzl — rules_swift repo'sidagi swift/ package'idagi swift.bzl fayli. Undan swift_library belgisini (symbol) olib kelamiz. Bazel 2 da ko'rganimizdek, swift_library/swift_binary Bazel'ning o'zida emas — ular rules_swift'dan load() bilan keltiriladi.
Target pattern'lar: bir vaqtda ko'p target
Bitta label bitta target'ni ko'rsatadi. Target pattern esa bir to'plam target'ni ko'rsatadi — build va query buyruqlarida juda ko'p ishlatiladi:
| Pattern | Ma'nosi | shaharlar'da natija |
|---|---|---|
//... | butun repo'dagi hamma target | //App:App, //Models:Models, //Services:Services |
//Services/... | Services va uning ost-package'lari | //Services:Services |
//App:all (yoki //App:*) | bitta package ichidagi hamma target | //App:App |
//... except //Models:all | istisno (- yoki except) | //App:App, //Services:Services |
Haqiqiy natijalarni o'zimiz ko'ramiz:
bazel query '//...'
//App:App
//Models:Models
//Services:Services
bazel query '//... except //Models:all'
//App:App
//Services:Services
💡 Pro tip:
bazel build //...— "hammasini qur".bazel test //...— "hamma testni ishga tushir".bazel build //Services/...— "faqat Services subtree'sini qur". Bu pattern'lar CI'da va kundalik ishda eng ko'p ishlatiladigan narsa.//...ildizdan boshlab,//Services/...esa faqat shu papkadan pastdagi package'larni qamrab oladi.
5. Rule va attribute'lar {#rule-attribute}
Endi BUILD fayllarning ichiga kiramiz. Har bir rule chaqiruvi — bu name va bir nechta attribute bilan target yaratish. Mana //Models package'ining to'liq BUILD fayli:
# Models/BUILD.bazel
load("@rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "Models",
srcs = glob(["*.swift"]),
module_name = "Models",
visibility = ["//visibility:public"],
)
Bu yerda to'rtta attribute bor. Ularning ba'zilari hamma ruleda uchraydi (umumiy attribute'lar), ba'zilari esa rule'ga xos (swift_library'ning module_name'i). Keling, eng muhim umumiy attribute'larni ko'rib chiqamiz.
srcs — manba fayllar
srcs — rule bevosita kompilyatsiya qiladigan source (yoki generated) fayllar ro'yxati. swift_library uchun bu .swift fayllar. Biz glob(["*.swift"]) ishlatdik — u package ichidagi barcha .swift fayllarni oladi (glob() haqida keyingi bo'limda batafsil).
Tur jihatidan srcs — bu "label ro'yxati": har bir element — rule'ga input bo'lgan target label'i. swift_library'da srcs majburiy (manba: rules_swift 4.2.1 rules.md).
deps — modul bog'liqliklari
deps — alohida kompilyatsiya qilingan modullarga bog'liqlik. //Services //Models'ga deps orqali bog'lanadi:
# Services/BUILD.bazel
load("@rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "Services",
srcs = glob(["*.swift"]),
module_name = "Services",
visibility = ["//visibility:public"],
deps = ["//Models"],
)
deps = ["//Models"] — bu "Services'ni qurish uchun avval Models'ni qur, uning .swiftmodule'ini va simvollarini menga ber" degani. rules_swift hujjatlariga ko'ra, swift_library'ning deps'iga swift_library (yoki SwiftInfo tarqatadigan boshqa target), cc_library va objc_library kirishi mumkin.
Va //App — bu swift_binary, u //Services'ga bog'liq:
# App/BUILD.bazel
load("@rules_swift//swift:swift.bzl", "swift_binary")
swift_binary(
name = "App",
srcs = glob(["*.swift"]),
deps = ["//Services"],
)
🧠 Esda tuting:
swift_binaryApple platformasida bitta arxitekturali ijro etiluvchi fayl chiqaradi va asosan lokal mashinada ishlaydigan Swift vositalari (CLI) uchun mo'ljallangan (manba: rules_swiftrules.md). iOS ilovasi yoki universal binary kerak bo'lsa, Bazel 5 dagirules_apple'ningios_application'i ishlatiladi. Shuning uchun bizning//App— macOS CLI.
srcs vs deps vs data — farqi nimada?
Ko'p rule'da uchta umumiy bog'liqlik attribute'i bor va ularni ajratish muhim (manba: concepts/dependencies):
| Attribute | Nima | Qachon kerak |
|---|---|---|
srcs | rule bevosita kompilyatsiya/qayta ishlaydigan fayllar | build vaqtida |
deps | alohida kompilyatsiya qilingan modullar (header, simvol, kutubxona beradi) | build vaqtida |
data | ishga tushirish paytida kerak bo'ladigan fayllar (build'ga ta'sir qilmaydi) | run/test vaqtida |
data nozik nuqta: unga qo'yilgan fayllar target'ning runfiles papkasida paydo bo'ladi va dastur/test ishlaganda ochiladi. Test faqat data'da e'lon qilingan fayllarni ko'ra oladi (bu Bazel 6 da muhim bo'ladi).
module_name, visibility, testonly
- **
module_name** (swift_library/swift_binary'ga xos): Swift modulining nomi. Biz"Models"deb aniq berdik, shundaimport Modelsishlaydi. Agar berilmasa, Bazel uni label'dan hisoblab chiqaradi —//ni olib tashlab,/,:va boshqa belgilarni_ga almashtirib (manba: rules_swiftrules.md). - **
visibility** (hamma rule'da): bu target'ni kimdeps'ida ishlatishi mumkinligini belgilaydi. 7-bo'limda chuqur ko'ramiz. - **
testonly** (hamma rule'da):Truebo'lsa, bu target'ni faqat boshqa test yokitestonlytarget ishlatishi mumkin — test yordamchi kodini xatolik bilan production'ga qo'shib yuborishdan saqlaydi.
💡 Pro tip: Attribute'lar — lug'at (dictionary): kalit (nom) → tipik qiymat. Har bir attribute'ning aniq turi bor:
string,label,label ro'yxati,string ro'yxativa h.k. Qaysi rule'da qaysi attribute borligini bilish uchun rules_swift/rules_apple hujjatlariga yokibazel query --output=build //Models:Modelsga qarang — oxirgisi target'ning barcha haqiqiy attribute qiymatlarini ko'rsatadi.
6. glob() va package chegaralari {#glob}
glob() — BUILD faylda fayllarni qo'lda sanab o'tirmaslik uchun ishlatiladigan funksiya. glob(["*.swift"]) package ichidagi barcha .swift fayllarni oladi. U loading bosqichida ishlaydi (buni 8-bo'limda ko'ramiz).
Lekin glob()ning bitta muhim va ko'pincha e'tibordan chetda qoladigan xususiyati bor: u package chegarasidan hech qachon o'tmaydi. Buni o'z ko'zimiz bilan ko'ramiz. //Models package'iga yangi ost-papka qo'shamiz — Geo/ — va unga o'z BUILD faylini beramiz:
Models/
├── BUILD.bazel
├── City.swift
└── Geo/
├── BUILD.bazel # ← yangi: Geo'ni ALOHIDA package qiladi
└── Coordinate.swift
// Models/Geo/Coordinate.swift
public struct Coordinate {
public let lat: Double
public let lon: Double
public init(lat: Double, lon: Double) { self.lat = lat; self.lon = lon }
}
# Models/Geo/BUILD.bazel
load("@rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "Geo",
srcs = glob(["*.swift"]),
module_name = "Geo",
visibility = ["//visibility:public"],
)
Models/Geo/ da BUILD fayli paydo bo'lishi bilanoq, u alohida package — //Models/Geo — ga aylanadi. Coordinate.swift endi //Models emas, //Models/Geo package'iga tegishli. Tekshiramiz:
bazel query 'Models/Geo/Coordinate.swift'
//Models/Geo:Coordinate.swift
bazel query 'Models/...' --output package
Models
Models/Geo
Endi eng muhim test. //Models'ning srcs'i glob(["*.swift"]). Nazariy jihatdan *.swift faqat yuqori darajani oladi. Lekin uni rekursiv glob(["**/*.swift"]) ga o'zgartirsak-chi — u Geo/Coordinate.swift'ni oladimi? Ko'pchilik "ha, ** hamma narsani oladi" deb o'ylaydi. Haqiqat esa boshqacha:
# Models/BUILD.bazel da: srcs = glob(["**/*.swift"])
bazel query 'kind("source file", deps(//Models))'
//Models:City.swift
Coordinate.swift yo'q. ** bo'lishiga qaramay, glob() Models/Geo/ package'iga kirmadi — chunki u alohida package. Eng xavflisi: build xatosiz o'tadi, shunchaki Coordinate.swift jim turib qoladi:
bazel build //Models # srcs = glob(["**/*.swift"]) bilan
INFO: Build completed successfully, 1 total action
⚠️ Diqqat: Bu — Bazel'ga yangi o'tganlar tez-tez tushadigan tuzoq. Siz kodni ost-papkaga ko'chirasiz, lekin uning o'z BUILD faylini unutmaysiz (yoki yaratmaysiz) — natijada
glob()o'sha fayllarni jimgina chetlab o'tadi va "nega mening yangi klasim topilmayapti?" degan chalkashlik boshlanadi. Qoida oddiy: **har bir package faqat o'z fayllariniglobqiladi.** Ost-package kerak bo'lsa, unidepsorqali bog'lang, fayllarni emas.
🧠 Esda tuting:
glob()ning bu xatti-harakati bejiz emas — u "hech bir fayl ikkita package'ga tegishli bo'la olmaydi" qoidasini majburlaydi. Agar//Models'ning glob'iGeo/fayllarini ham olsa,Coordinate.swiftikkita package'da (//Modelsva//Models/Geo) bo'lib qolardi — bu esa build grafini chalkashtirar edi.
Biz //Models ni asl holiga — glob(["*.swift"]) ga — qaytaramiz va //Models/Geo ni mustaqil package sifatida qoldiramiz.
7. Visibility: arxitekturani majburlash {#visibility}
Sukut bo'yicha har bir target private (shaxsiy) — ya'ni uni faqat o'sha package ichidagi target'lar ishlatishi mumkin (agar package'da default_visibility berilmagan bo'lsa). Boshqa package'dan foydalanish uchun target o'zini ochib berishi kerak — bu visibility attribute'i orqali qilinadi.
Visibility formati
visibility — label ro'yxati. Har bir element quyidagi shakllardan biri (manba: concepts/visibility):
| Shakl | Ma'nosi |
|---|---|
"//visibility:public" | hamma package ko'ra oladi |
"//visibility:private" | faqat shu package (sukut bo'yicha) |
"//foo/bar:__pkg__" | faqat //foo/bar package (ost-package'larsiz) |
"//foo/bar:__subpackages__" | //foo/bar va uning barcha ost-package'lari |
"//some_pkg:my_group" | package_group ichidagi barcha package'lar |
Haqiqiy visibility xatosi
Buni gapirib emas, chiqarib ko'ramiz. //Models ni private qilamiz va //Services ni qurishga urinamiz (Services //Models'ga bog'liq):
# Models/BUILD.bazel — vaqtincha private qilamiz
swift_library(
name = "Models",
srcs = glob(["*.swift"]),
module_name = "Models",
visibility = ["//visibility:private"], # ← faqat //Models o'zi
)
bazel build //Services
ERROR: .../Services/BUILD.bazel:3:14: in swift_library rule //Services:Services:
Visibility error:
target '//Models:Models' is not visible from
target '//Services:Services'
Recommendation: modify the visibility declaration if you think the dependency
is legitimate. For more info see https://bazel.build/concepts/visibility
ERROR: .../Services/BUILD.bazel:3:14: Analysis of target '//Services:Services'
(config: 2d89340) failed
ERROR: Analysis of target '//Services:Services' failed; build aborted
Ikki narsaga e'tibor bering. Birinchidan, xato aniq: //Models:Models //Services:Services'dan ko'rinmaydi. Ikkinchidan — va bu muhim — xato Analysis bosqichida yuz berdi (Analysis of target ... failed). Visibility hech qachon kompilyatsiyagacha yetib bormaydi; u analysis'da, ya'ni haqiqiy ish boshlanishidan oldin tekshiriladi. Bu bizni keyingi bo'limdagi uch bosqich mavzusiga olib keladi.
Visibility — arxitektura quroli
Visibility'ni shunchaki "public/private" deb tushunmang. U bilan siz arxitekturani majburlashingiz mumkin: "App faqat Services orqali ishlasin, Models'ga to'g'ridan-to'g'ri tegmasin". Buni amalda ko'ramiz. //Models ni faqat //Services package'iga ochamiz:
# Models/BUILD.bazel
swift_library(
name = "Models",
srcs = glob(["*.swift"]),
module_name = "Models",
visibility = ["//Services:__pkg__"], # ← faqat Services package
)
Endi //Services ham, //App ham quriladi:
bazel build //Services # Services — ruxsat etilgan package
bazel build //App # App — Services orqali, Models'ga to'g'ridan-to'g'ri tegmaydi
INFO: Build completed successfully, 1 total action
INFO: Build completed successfully, 1 total action
//App ishladi, chunki u //Models'ga to'g'ridan-to'g'ri bog'lanmagan — u //Services'ga bog'langan, Services esa Models'ga. Visibility faqat bevosita deps'ni tekshiradi. Endi //App'ga Models'ga to'g'ridan-to'g'ri bog'lanishni qo'shsak:
# App/BUILD.bazel — ataylab Models'ga to'g'ridan-to'g'ri bog'lanamiz
swift_binary(
name = "App",
srcs = glob(["*.swift"]),
deps = ["//Services", "//Models"], # ← //Models endi bevosita dep
)
bazel build //App
ERROR: .../App/BUILD.bazel:3:13: in swift_binary rule //App:App:
Visibility error:
target '//Models:Models' is not visible from
target '//App:App'
Mana — arxitektura qoidasi kod bilan majburlandi. //App qancha urinmasin, //Models'ning ichki detallariga to'g'ridan-to'g'ri yeta olmaydi; u Services'ning ommaviy API'si orqali o'tishi shart.
🏢 Real-world: Aynan shu usul — visibility bilan qatlamlarni majburlash — katta monorepo'larda arxitekturani saqlab turadi. Bazel hujjatlari bir nechta target'ni bir xil to'plamga ochish uchun har joyda ro'yxatni takrorlamay,
package_groupishlatishni tavsiya qiladi; "boshqa jamoaning loyihasiga ochganda__pkg__o'rniga__subpackages__ni afzal ko'ring" deydi (manba:concepts/visibility). Biz buni Bazel 16 — Monorepo dapackage_groupbilan to'liq quramiz, u yerda "app internal package'ga qo'l cho'zsa" haqiqiy xato chiqaramiz.
💡 Pro tip: Prototip paytida visibility'ni vaqtincha o'chirib turish uchun
--check_visibility=falsebor — lekin uni commit qilinadigan kodda hech qachon ishlatmang. Yaxshi amaliyot:default_visibility'ni public qilib qo'ymang; har bir package o'zining ommaviy interfeysini aniq ko'rsatsin.
Biz //Models ni va //App ni asl holiga (//visibility:public va deps = ["//Services"]) qaytaramiz.
8. Build'ning uch bosqichi va Skyframe {#uch-bosqich}
bazel build ishlaganda, u uchta ketma-ket bosqichdan o'tadi: loading → analysis → execution. Bu uchlikni tushunish — Bazel'ni chinakam tushunishning kaliti, chunki xatolar qaysi bosqichda yuz berishi, nima qachon keshlanishini va nega ikkinchi build tez bo'lishini aynan shu izohlaydi.
Build'ning uch bosqichi: loading, analysis, execution va ularning kirish/natijalari; Skyframe keshi
1. Loading bosqichi
Bazel BUILD va .bzl fayllarni bajarib, package'larni yaratadi. Aynan shu yerda:
- BUILD fayllar Starlark kodi sifatida bajariladi;
glob()shu yerda ishlaydi (fayl tizimiga qaraydi);- macro'lar ochiladi (ular haqida Bazel 4 da).
Natija: target graph — target'lar va ular orasidagi e'lon qilingan bog'liqliklar.
2. Analysis bosqichi
Bazel target graph'ni olib, rule'larning implementatsiyasini baholaydi va action graph quradi. Shu yerda:
- har bir rule o'z action'larini (masalan "bu
.swiftfayllarni kompilyatsiya qil") aniqlaydi; - configuration qo'llanadi (masalan
darwin_arm64-fastbuild— platforma + build turi); - visibility tekshiriladi (7-bo'limdagi xato aynan shu yerda chiqqan edi).
Natija: action graph — bajariladigan aniq action'lar va ular chiqaradigan artefaktlar (lekin hali hech narsa ishlamagan).
🧠 Esda tuting: Loading va analysis bir-biriga aralashib ishlaydi — Bazel target'larni yuklab borar ekan, darhol ularni tahlil qila boshlaydi. Shuning uchun amalda siz ularni "grafni tushunish" bosqichi deb bir butun qilib tasavvur qilsangiz bo'ladi. Execution esa alohida, oxirgi bosqich.
3. Execution bosqichi
Bazel action graph'dagi action'larni bajaradi: swiftc ishga tushadi, obyekt fayllar yoziladi, binary link qilinadi. Har bir action sandbox ichida, izolyatsiyada ishlaydi (keyingi bo'lim). Faqat eskirgan yoki hali qurilmagan action'lar bajariladi — qolganlari keshdan olinadi.
Natija: artefaktlar — masalan bazel-bin/App/App.
Bosqichlarni o'z ko'zimiz bilan ajratish
--nobuild flagi Bazel'ni loading + analysis'dan keyin to'xtatadi — execution'siz. Bu bosqichlarni ajratib ko'rishning eng oson usuli:
bazel build //App --nobuild
INFO: Analyzed target //App:App (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
INFO: Elapsed time: 0.106s, Critical Path: 0.00s
INFO: 0 processes.
INFO: Build completed successfully, 0 total actions
Diqqat: 0 total actions. Bazel target'ni to'liq tahlil qildi (label'ni topdi, grafni qurdi, visibility'ni tekshirdi), lekin birorta ham action bajarmadi. Oddiy bazel build'ning chiqishida esa siz avval Loading:, keyin Analyzing:, so'ng [N / M] action hisoblagichini ko'rasiz — bu uch bosqichning jonli ko'rinishi.
Skyframe: nega ikkinchi build tez?
Bazel'ning yuragida Skyframe turadi — parallel, funksional va inkremental baholash dvigateli. Skyframe har bir bosqich natijasini (yuklangan package'lar, tahlil qilingan target'lar, bajarilgan action'lar) JVM server'ining xotirasida saqlaydi. Ikkinchi marta build qilganingizda, o'zgarmagan tugunlar qayta hisoblanmaydi:
bazel build //App # hech narsa o'zgarmagan
Target //App:App up-to-date:
INFO: Elapsed time: 0.114s, Critical Path: 0.00s
Butun build 0.114 soniyada tugadi — chunki Skyframe "hech narsa o'zgarmadi" deb bildi. Bu — Bazel 2 da ko'rgan client/server arxitekturasining mevasi: server xotirada grafni ushlab turadi. bazel shutdown server'ni to'xtatadi va bu xotiradagi grafni (analiz keshini) tozalaydi.
Inkremental build: faqat kerakli qism
Eng muhim amaliy xulosa: faqat o'zgargan fayldan ta'sirlangan target'lar qayta quriladi. Buni o'lchab ko'ramiz. Avval "bitta faylni o'zgartirsam, nima qayta quriladi?" degan savolga rdeps javob beradi:
bazel query 'rdeps(//..., //Models)'
//App:App
//Models:Models
//Services:Services
Models o'zgarsa, uchalasi ham ta'sirlanadi (chunki Services → Models, App → Services). Endi haqiqiy o'lchov. Ikki xil o'zgarish qilamiz va har birida bazel build //App'ning nechta swiftc (worker) ishga tushishini sanaymiz:
O'lchov sharti: Apple M4 Pro (12 core, 24 GB), Bazel 9.3.0, rules_swift 4.2.1, Xcode 27.1, umumiy disk cache yoqilgan, o'lchovlar eksklyuziv rejimda (bitta vaqtda bitta build).
| Operatsiya | Vaqt | Nima ishladi |
|---|---|---|
| Butunlay sovuq build (bo'sh cache, yangi output base) | ~83.7 s\* | 350 action; vaqtning katta qismi — rules_swift worker'ini abseil'dan kompilyatsiya (mashinaga bir martalik) |
bazel clean + disk cache bilan qayta build | 2.7 s | 298 process, 179 disk cache hit |
| No-op (hech narsa o'zgarmagan) | 0.1 s | 0 action (Skyframe xotiradan) |
Leaf o'zgarish (App/main.swift) | 0.8 s | 1 swiftc — faqat App qayta kompilyatsiya + link |
Core API o'zgarish (Models/City.swift) | 1.2 s | 3 swiftc — Models, Services, App uchalasi (fan-out) |
\* "Sovuq" build raqami (~83.7 s) FACTS.md'dan — shu mashinada, shunga o'xshash 2-modulli ilova uchun o'lchangan. Mening bu yerdagi birinchi build'im umumiy disk cache allaqachon to'la bo'lgani uchun 20.3 s bo'ldi (173 disk cache hit); demak butunlay "sovuq" raqam faqat bo'sh keshda ko'rinadi.
Ikki pastki qatorni solishtiring. App/main.swift (grafning tepasi, "leaf") o'zgarsa — faqat 1 ta modul qayta kompilyatsiya qilinadi. Models/City.swift'ga yangi public property qo'shsak (grafning tubi, "core") — 3 ta modul qayta kompilyatsiya qilinadi, chunki Models'ning interfeysi o'zgardi va yuqoridagilar unga bog'liq. Bu aynan rdeps ko'rsatgan to'plam.
📊 Real raqamlar: Leaf o'zgarish
1.230 semas,0.823 s; core o'zgarish3taswiftc. Farq kichik ko'rinadi, chunki loyiha kichik. Lekin 200 modulli ilovada bu farq ulkanlashadi: leaf o'zgarish hamon 1 modulni qayta quradi, core o'zgarish esa unga bog'liq o'nlab modulni. Aynan shu sabab katta jamoalar arxitekturani shunday quradiki, tez-tez o'zgaradigan kod "leaf"da, barqaror kod "core"da bo'lsin — Bazel 13 da buni rebuild fan-out bilan o'lchaymiz.
💡 Pro tip: Bazel fayl o'zgarganini vaqt belgisi (mtime) bilan emas, mazmun hashi bilan aniqlaydi. Shuning uchun
touch file.swifthech narsani qayta qurdirmaydi — fayl mazmuni o'zgarmagan. Bundan ham qizig'i:Models/City.swift'ga shunchaki izoh (comment) qo'shsak, Models qayta kompilyatsiya bo'ladi, lekin uning.swiftmodulechiqishi o'zgarmagani uchun Services va App qayta qurilmaydi — Bazel natija bo'yicha ham keshni tekshiradi. Fan-out faqat haqiqatan interfeys o'zgarganda (yangi public a'zo) yuz beradi.
9. Action graph va sandboxing {#action-graph}
Siz BUILD faylda target yozasiz. Lekin Bazel haqiqatda actionlarni bajaradi. Bitta target analysis'da bir nechta action'ga yoyiladi. Bu farqni ko'rish — Bazel'ni "sehr" bo'lishdan to'xtatib, tushunarli mexanizmga aylantiradi.
Target graph va action graph yonma-yon: bitta target bir nechta SwiftCompile action'iga yoyiladi
Chap tomonda — siz yozgan target graph (bazel query ko'rsatadi): 3 ta target. O'ng tomonda — Bazel bajaradigan action graph (bazel aquery ko'rsatadi): har bir swift_library kamida bitta SwiftCompile action'iga aylanadi, swift_binary esa kompilyatsiya + link'ga.
Action nima?
Action — build paytida ishga tushadigan buyruq: masalan kompilyatorga chaqiruv. Har bir action'da metama'lumot bor: buyruq satri, input artefaktlar, output artefaktlar, muhit o'zgaruvchilari va action key. bazel aquery bilan //Models'ni quradigan action'ni ko'ramiz:
bazel aquery '//Models'
action 'Compiling Swift module //Models:Models'
Mnemonic: SwiftCompile
Target: //Models:Models
Configuration: darwin_arm64-fastbuild
Execution platform: @@platforms//host:host
ActionKey: ba9da2be0ed0427411fcaf33a3adc46a6f24077c21775da4fb7a704dd7de569f
Inputs: [Models/City.swift, .../Models.output_file_map.json, ... (toolchain)]
Outputs: [.../Models.swiftmodule, .../Models_objs/City.swift.o, ...]
Mana — target emas, action. Uning:
- Mnemonic'i (
SwiftCompile) — action turining qisqa nomi; - Configuration'i (
darwin_arm64-fastbuild) — qaysi platforma/rejim uchun; - Inputs/Outputs — aniq fayllar;
- ActionKey (bu uzun hash) — keshning kaliti. Agar action'ning biror jihati (buyruq, input mazmuni, muhit) o'zgarsa, kalit o'zgaradi va action qayta ishlaydi. Bu — Bazel 10 — Cache ning asosi.
Haqiqiy swiftc buyruq satri
aquery buyruq satrini ham ko'rsatadi — bu "expert" darajadagi ko'rinish. Mana SwiftCompile //Models aslida qanday swiftc chaqiruvi (qisqartirilgan):
Command Line: (exec bazel-out/.../rules_swift+/tools/worker/worker \
swiftc \
-target arm64-apple-macos27.0 \
-sdk __BAZEL_XCODE_SDKROOT__ \
-emit-object \
-output-file-map .../Models.output_file_map.json \
-emit-module-path .../Models.swiftmodule \
-module-name Models \
... )
E'tibor bering: swiftc to'g'ridan-to'g'ri emas, rules_swift'ning persistent worker'i orqali chaqiriladi (tools/worker/worker) — bu kompilyatorni jarayon sifatida tirik saqlab, keyingi kompilyatsiyalarni tezlashtiradi. SDK yo'li esa __BAZEL_XCODE_SDKROOT__ kabi joy egasi (placeholder) bilan yoziladi — bu hermetiklik uchun: aniq yo'l mashinaga bog'liq bo'lib action key'ni "iflos" qilmasligi kerak.
cquery: konfiguratsiyalangan target
bazel query konfiguratsiyagacha bo'lgan grafni ko'rsatadi. bazel cquery esa konfiguratsiyalangan target'ni — ya'ni qaysi platforma/rejim uchun ekanligini — ko'rsatadi:
bazel cquery 'deps(//App)' --noimplicit_deps
//App:App (2d89340)
//Services:Services (2d89340)
//App:main.swift (null)
//Models:Models (2d89340)
//Services:CityService.swift (null)
//Models:City.swift (null)
Qavs ichidagi (2d89340) — konfiguratsiya hashi. Rule target'lar konfiguratsiyaga ega; source fayllar esa (null) — ular konfiguratsiyaga bog'liq emas (fayl har qanday platformada bir xil fayl). Agar //App'ni ikki xil arxitektura uchun qursangiz, bitta target ikkita konfiguratsiyalangan target bo'lib ko'rinadi — Bazel 5 dagi --ios_multi_cpus aynan shuni qiladi.
Sandboxing: nega e'lon qilinmagan input ishdan chiqadi
Sandboxing — har bir action'ni cheklangan, vaqtinchalik papkada izolyatsiya qilish, shunda u e'lon qilinmagan input'ni o'qiy olmasin va e'lon qilinmagan output yoza olmasin. macOS'da bu darwin-sandbox strategiyasi (biz build chiqishida darwin-sandbox process'larini ko'rdik). Bu — hermetiklikning asosiy quroli: build faqat o'zi e'lon qilgan narsalarga tayanadi.
Nega bu muhim? Chunki sandbox xatoni darhol chiqaradi — u CI'da, boshqa mashinada yoki remote cache'da "portlab" ketishidan oldin. Buni amalda ko'ramiz. Ataylab buzuq genrule yozamiz — u version.txt faylini o'qiydi, lekin uni srcs'da e'lon qilmaydi:
# Report/BUILD.bazel
# DIQQAT: bu genrule ataylab buzuq — version.txt srcs'da e'lon qilinmagan.
genrule(
name = "report",
srcs = [], # version.txt YO'Q — e'lon qilinmagan input
outs = ["report.txt"],
cmd = "cat Report/version.txt > $@",
)
Sukut bo'yicha (darwin-sandbox) qurganda — ishdan chiqadi:
bazel build //Report:report
ERROR: .../Report/BUILD.bazel:4:8: Executing genrule //Report:report failed:
(Exit 1): bash failed: error executing Genrule command
cat: Report/version.txt: No such file or directory
Target //Report:report failed to build
version.txt fayl tizimida bor, lekin sandbox ichida yo'q — chunki u e'lon qilinmagan. Endi sandbox'ni o'chiramiz (--spawn_strategy=local) va o'sha genrule muvaffaqiyatli o'tadi:
bazel build //Report:report --spawn_strategy=local
INFO: Build completed successfully, 2 total actions
cat bazel-bin/Report/report.txt
v1.0.0
Mana xavf: sandbox'siz, bu buzuq genrule sizning mashinangizda ishlaydi, lekin CI'da yoki hamkasbingizning mashinasida version.txt boshqacha bo'lsa yoki umuman bo'lmasa — build sinadi. Sandbox bu bombani oldindan portlatib, sizni ogohlantiradi.
To'g'ri yechim — input'ni e'lon qilish va $(location ...) bilan to'g'ri yo'lni olish:
# Report/BUILD.bazel — TO'G'RILANGAN
genrule(
name = "report",
srcs = ["version.txt"], # endi e'lon qilingan
outs = ["report.txt"],
cmd = "cat $(location version.txt) > $@",
)
Endi sandbox ichida ham muammosiz quriladi:
bazel build //Report:report
INFO: 2 processes: 1 internal, 1 darwin-sandbox.
INFO: Build completed successfully, 2 total actions
🧠 Esda tuting: Bazel'ning asosiy va'dasi — "build = e'lon qilingan input'lardan kelib chiqadigan sof funksiya". Sandbox shu va'dani majburlaydi: e'lon qilmagan narsangizga tayana olmaysiz. Shuning uchun Bazel'da "mening mashinamda ishlayapti, CI'da esa yo'q" muammosi kamayadi — agar sandbox'da ishlasa, u deyarli hamma joyda ishlaydi.
10. bazel query, cquery va aquery {#query}
Butun dars davomida biz bazel query bilan grafni "so'rab" keldik. Endi uni alohida vosita sifatida jamlaymiz, chunki bu — Bazel'ning eng kuchli kundalik quroli. query — build grafiga savol berish tili.
Eng ko'p ishlatiladigan funksiyalar
| Funksiya | Savol | Misol |
|---|---|---|
deps(x) | x nimaga bog'liq? (barcha transitiv) | deps(//App) |
deps(x, N) | x ning N-darajagacha bog'liqliklari | deps(//App, 1) — faqat bevosita |
rdeps(u, x) | u ichida kim x'ga bog'liq? | rdeps(//..., //Models) |
somepath(x, y) | x'dan y'gacha bitta yo'l | somepath(//App, //Models) |
allpaths(x, y) | x'dan y'gacha barcha yo'llar | allpaths(//App, //Models) |
kind(k, x) | x ichidagi k turidagi target'lar | kind(swift_library, //...) |
attr(a, v, x) | a attribute'i v'ga teng target'lar | attr(module_name, Services, //...) |
To'plam amallari ham bor: except (yoki -), intersect, union (yoki +). Bir nechtasini birlashtirib ko'ramiz:
# App'ga faqat bevosita bog'langan target'lar:
bazel query 'deps(//App, 1)' --noimplicit_deps
//App:App
//App:main.swift
//Services:Services
# App'dan Models'gacha bitta yo'l — "nega App Models'ga bog'liq?"
bazel query 'somepath(//App, //Models)' --noimplicit_deps
//App:App
//Services:Services
//Models:Models
kind bilan turlarni filtrlang. Diqqat: 6-bo'limda //Models/Geo package'ini qo'shganimiz uchun endi uchta swift_library bor:
bazel query 'kind(swift_library, //...)'
//Models:Models
//Models/Geo:Geo
//Services:Services
--output formatlari
Bir xil so'rovni har xil ko'rinishda olish mumkin:
--output | Nima beradi |
|---|---|
label (sukut) | faqat label'lar |
label_kind | label + rule turi (swift_library rule //Models:Models) |
package | package nomlari |
build | target'ning to'liq BUILD ko'rinishi (hamma attribute) |
graph | Graphviz DOT formati |
Grafni rasm qilib chizish
--output graph DOT matnini beradi; uni dot (Graphviz) bilan rasmga aylantirasiz:
brew install graphviz # bir martalik
bazel query 'deps(//App)' --noimplicit_deps --output graph > graph.dot
dot -Tsvg graph.dot -o graph.svg
DOT matnining o'zi ham o'qiladigan:
digraph mygraph {
node [shape=box];
"//App:App" -> "//App:main.swift"
"//App:App" -> "//Services:Services"
"//Services:Services" -> "//Services:CityService.swift"
"//Services:Services" -> "//Models:Models"
"//Models:Models" -> "//Models:City.swift"
}
Chizilgan rasmda //App:App eng tepada turadi; undan ikkita strelka pastga tushadi — biri main.swift'ga (uning manba fayli), ikkinchisi //Services:Services'ga; Services'dan CityService.swift va //Models:Models'ga; Models'dan esa City.swift'ga. Ya'ni aynan biz qurgan uch qavatli graf.
💡 Pro tip: Katta loyihada
deps(//App)grafi minglab tugunli bo'lib, rasm o'qib bo'lmaydigan "makaron"ga aylanadi. Shuning uchun amaldasomepath/allpathsbilan grafni ikki nuqta orasiga toraytiring, yoki--output grapho'rniga--output packagebilan "qaysi package'lar kerak?" degan savolga javob oling.
query vs cquery vs aquery
Uchta "query" bor va ular grafning uch bosqichiga mos keladi (8-bo'limga qarang):
| Vosita | Qaysi bosqichdan keyin | Nimani ko'rsatadi |
|---|---|---|
bazel query | loading | target graph — konfiguratsiyasiz target'lar va e'lon qilingan deps |
bazel cquery | analysis | konfiguratsiyalangan target'lar (platforma/rejim bilan, (2d89340) hash) |
bazel aquery | analysis | action graph — aniq buyruqlar, input/output, ActionKey |
Oddiy qoida: "kim kimga bog'liq?" — query. "qaysi konfiguratsiyada?" — cquery. "aslida qanday buyruq ishlaydi?" — aquery. Biz 9-bo'limda aquery bilan haqiqiy swiftc buyruq satrini va cquery bilan konfiguratsiya hashini ko'rdik.
🧠 Esda tuting:
bazel querybuild qilmaydi — u faqat grafni tahlil qiladi, shuning uchun juda tez.cqueryvaaqueryesa analysis'ni talab qiladi (lekin execution'ni emas). Hech biri haqiqiy kompilyatsiyani ishga tushirmaydi — shuning uchun ularni CI'da "o'zgarish nimaga ta'sir qiladi?" degan savolga tez javob olish uchun bemalol ishlatish mumkin.
Amaliy mashqlar {#mashqlar}
Barcha mashqlar shu darsdagi shaharlar workspace'ida bajariladi. Har birida avval javobni o'ylab, keyin buyruq bilan tekshiring.
- Label tarjimoni. Quyidagilarni qisqartiring yoki yoying: (a)
//Services:Services→ ?; (b)App/BUILD.bazelichida//App:Appni qanday qisqa yozasiz?; (c)Modelspackage'idagiCity.swiftfaylining to'liq label'i qanday? Javobnibazel querybilan tekshiring. - Source fayllarni sanash.
//Appni qurish uchun nechta.swiftsource fayl kerak? Avval taxmin qiling, keyinbazel query 'kind("source file", deps(//App))'bilan tekshiring. Natija nega aynan shuncha? - Yangi package qo'shish.
//Utilsdegan yangiswift_librarypackage yarating (bitta oddiyStringkengaytmasi bilan),//Servicesunideps'iga qo'shsin. Keyinbazel query 'rdeps(//..., //Utils)'ni ishga tushiring — natija nimani ko'rsatadi va nega//Appham ro'yxatda? - ⭐ package_group bilan visibility.
//Modelsnipackage_grouporqali faqat//Servicesko'radigan qiling (__pkg__o'rniga guruh ishlatib).//Apphamon qurilishini tekshiring. Keyin//App'ga//Modelsni bevositadepsqilib qo'shing va analysis bosqichidagi visibility xatosini o'z ko'zingiz bilan ko'ring. (Eslatma:package_grouphar doim ommaviy,visibilityattribute'i yo'q.) - ⭐ Sandbox detektivi. Ikkita e'lon qilingan faylni birlashtiradigan
genruleyozing (cat $(location a.txt) $(location b.txt) > $@). U ishlasin. Keyin bittasinisrcs'dan olib tashlang (lekincmd'da qoldiring) vabazel buildni ishga tushiring — qanday xato chiqadi, qaysi bosqichda?--spawn_strategy=localbilan nima o'zgaradi va nega bu xavfli? - ⭐ Grafni chizish.
brew install graphvizqiling, so'ngallpaths(//App, //Models)grafini SVG qilib chizing va brauzerda oching. Keyindeps(//App)(to'liq,--noimplicit_depssiz) grafini chizing — u nega ancha kattaroq? (Maslahat: implicit toolchain bog'liqliklari.)
O'zingizni tekshiring {#checklist}
- [ ] Label'ning uch qismini (
@repo//package:target) va@vs@@farqini ayta olaman. - [ ]
//Foo=//Foo:Fooekanini, va//Foopackage emas, target ekanini bilaman. - [ ] Package BUILD fayli bilan belgilanishini va
glob()package chegarasidan o'tmasligini tushunaman. - [ ] Visibility xatosi qaysi bosqichda (analysis) chiqishini va
__pkg__/__subpackages__/package_groupfarqini bilaman. - [ ] Loading, analysis va execution bosqichlarini va har birida nima bo'lishini ayta olaman.
- [ ] Skyframe nega ikkinchi build'ni tez qilishini va
touchnega qayta build qildirmasligini bilaman. - [ ] Leaf va core o'zgarish rebuild fan-out'ini
rdepsbilan oldindan bashorat qila olaman. - [ ]
query,cqueryvaaqueryfarqini vaaquerybilan haqiqiy buyruq satrini ko'rishni bilaman.
Xulosa {#xulosa}
- Bazel kodni repository → package → target ierarxiyasida ko'radi. Package — BUILD fayli bo'lgan papka; BUILD fayli — package chegarasi. Bitta fayl ikkita package'ga tegishli bo'la olmaydi.
- Label — target'ning manzili:
@repo//package:target. Amalda qisqartmalar ishlatiladi (//Services,:App,//Models:City.swift). Target pattern (//...,//Services/...,:all,except) bir vaqtda ko'p target'ni qamraydi. - Rule target'lar
srcs/deps/data/visibility/testonlykabi attribute'lar bilan aniqlanadi.srcs— kompilyatsiya qilinadigan fayllar,deps— modullar,data— run vaqtidagi fayllar. - **
glob()** package chegarasidan o'tmaydi — ost-package fayllarini jimgina chetlab o'tadi. Ularnidepsbilan bog'lang. - Visibility sukut bo'yicha private; u arxitekturani majburlaydi va buzilsa analysis bosqichida xato beradi.
- Build uch bosqichdan iborat: loading (target graph) → analysis (action graph, visibility, configuration) → execution (action'lar sandbox'da). Skyframe natijalarni xotirada saqlab, ikkinchi build'ni sekundning ulushiga tushiradi.
- O'zimiz o'lchadik: leaf o'zgarish 1 ta modulni, core API o'zgarish esa butun fan-out'ni (3 ta modul) qayta quradi — bu aynan
rdepsko'rsatgan to'plam. - Sandboxing hermetiklikni majburlaydi: e'lon qilinmagan input'ni o'qib bo'lmaydi — biz buni haqiqiy genrule xatosi bilan ko'rdik.
- **
bazel query/cquery/aquery** — grafni, konfiguratsiyani va haqiqiy buyruqlarni ko'rish quroli; "o'zgarishim nimaga ta'sir qiladi?" degan savolning javobi. - Keyingi dars: Bazel 4: Starlark tili — BUILD va .bzl fayllar, macro'lar, select() va konfiguratsiya
Manbalar {#manbalar}
- Bazel — Repositories, workspaces, packages, and targets — package ta'rifi va chegaralari.
- Bazel — Labels — label sintaksisi, canonical/apparent repo, qisqartmalar.
- Bazel — BUILD files — Starlark,
load(), rule oilalari. - Bazel — Dependencies —
srcs/deps/datafarqi, e'lon qilingan vs haqiqiy bog'liqlik. - Bazel — Visibility —
public/private/__pkg__/__subpackages__/package_group. - Bazel — Query guide —
deps,rdeps,somepath,allpaths,kind,attr. - Bazel — Action Graph Query (aquery) — action'lar, ActionKey, buyruq satri.
- Bazel — Configurable Query (cquery) — konfiguratsiyalangan target'lar.
- Bazel — Glossary — loading/analysis/execution phase, Skyframe, action, sandboxing ta'riflari.
- Bazel — Build programs (target patterns) —
//...,:all, istisno sintaksisi. - rules_swift — Rules reference —
swift_library/swift_binaryattribute'lari (4.2.1). - Bazel 9.0 release notes — WORKSPACE olib tashlangani, Bzlmod har doim yoqiq.
- Gojek Engineering — Gojek's Journey to 3x Faster iOS Builds with Bazel (Medium) — "faqat ta'sirlanganini test qil" strategiyasi va
bazel-diffhaqida muhandislik hikoyasi.