Azimjon Abdurasulov

Bazel 9: Xcode bilan ishlash — rules_xcodeproj, indexing, debugging, SwiftUI Preview va VS Code

📦 Bazel iOS — Mukammal Qo‘llanma & Amaliyot

Bazel 9: Xcode bilan ishlash — rules_xcodeproj, indexing, debugging, SwiftUI Preview va VS Code

Holat: 2026-yil oktabr. Bu darsda Bazel bilan build qiladigan iOS loyihasini Xcode IDE ichida ochib, indexing, debugging va test navigator'dan foydalanamiz — rules_xcodeproj orqali. Kod misollari Bazel 9.3.0, rules_apple 5.3.0, rules_swift 4.2.1, rules_xcodeproj 4.1.0 va Xcode 27.1 bilan Apple M4 Pro'da tekshirilgan.

Bu darsdan keyin siz: Bazel loyihasi uchun bazel run //:xcodeproj bilan haqiqiy .xcodeproj yaratib, uni Xcode'da yoki VS Code'da ochib, kod yozish va debug qilishni biladigan bo'lasiz.

  • rules_xcodeproj nega kerakligini va Bazel'ni iOS'da qabul qilishdagi #1 to'siq aynan IDE/DX ekanini tushunasiz (Asana'ning real hikoyasi bilan).
  • xcodeproj qoidasini (project_name, top_level_targets, top_level_target, focused_targets) yozib, loyihani generatsiya qilasiz.
  • Generatsiya qilingan loyihaning "Build with Bazel" (BwB) modeli qanday ishlashini — BazelDependencies target va "Generate Bazel Dependencies" script phase'ni — ko'rasiz.
  • Indexing, breakpoint/LLDB va test navigator'dan foydalanasiz; SwiftUI Preview'ning 4.1.0 dagi real holatini (va cheklovini) bilasiz.
  • Alternativa sifatida VS Code / Cursor'ni sourcekit-lsp + Build Server Protocol orqali Bazel loyihasiga ulashni ko'rasiz.

Mundarija

  1. Muammo: IDE yo'q bo'lsa, Bazel qabul qilinmaydi
  2. rules_xcodeproj nima qiladi: "Build with Bazel" modeli
  3. O'rnatish va birinchi loyiha generatsiyasi
  4. Generatsiya qilingan loyihani CLI'dan build qilish (BwB)
  5. Indexing, debugging va test navigator
  6. SwiftUI Preview: eng katta kamchilik (halol gap)
  7. .bazelrc config oilasi va command-line API
  8. Focused projects: katta repo uchun
  9. Katta loyihaga xos muammolar va signing
  10. Alternativa: VS Code / Cursor — sourcekit-lsp + BSP
  11. Amaliy mashqlar
  12. O'zingizni tekshiring
  13. Xulosa
  14. Manbalar

1. Muammo: IDE yo'q bo'lsa, Bazel qabul qilinmaydi {#muammo}

Oldingi darslarda (Bazel 5, Bazel 6, Bazel 8) biz hamma narsani terminaldan qildik: bazel build, bazel test, bazel run. Bu CI uchun ajoyib. Lekin kuniga 8 soat kod yozadigan iOS dasturchi uchun terminal yetarli emas. Unga kerak:

  • Autocomplete va jump to definition — BookCatalog.search( deb yozganda Xcode qaysi parametrlarni kutayotganini ko'rsatishi;
  • Breakpoint qo'yib, po book.title deb o'zgaruvchini tekshirish (LLDB);
  • Inline xatolar — kompilyatsiya xatosi aynan qaysi qatorda ekanini qizil chiziq bilan ko'rsatishi;
  • Test navigator — testni bosib ishga tushirish, yashil/qizil belgilar;
  • SwiftUI Preview — #Preview bloki canvas'da jonli ko'rinishi.

Bu ro'yxat — Bazel'ning emas, Xcode'ning super-kuchlari. Bazel faqat build tizimi: u fayllarni kompilyatsiya qiladi, lekin IDE emas. Shuning uchun Bazel'ni iOS jamoasiga olib kirishdagi eng katta to'siq — tezlik emas, balki IDE/developer experience (DX).

🏢 Real-world: Asana muhandislik jamoasi (2023-yil 21-fevral, "Engineering Team" posti) ~10 muhandis va ~300 000 qator Swift kodini modullarga ajratishdan oldin build tizimini tanlagan. Ular Bazel'dan voz kechib, Tuist'ni tanlagan. Sababi — tezlik emas, DX edi: Bazel ular uchun "juda og'ir (very heavyweight) va yangi mental model talab qiladigan" vosita bo'lgan, eng muhimi — "breakpoint'lar bilan ish jarayonini buzadigan muammolar" bo'lgan. Modullarga ajratgandan keyin o'zgarishdan-simulyatorgacha vaqt odatdagi ~45 soniyadan Scalyr'dagi median ~15 soniyagacha tushgan, Inbox moduli esa 30–50 soniyadan ~4 soniyagacha. Manba: asana.com/inside-asana.

Bu hikoyaning darsimizga aloqasi aniq: agar Bazel loyihasini Xcode'da oddiy loyiha kabi ochib, breakpoint qo'yib, kod to'ldirib ishlata olsangiz — Bazel'ning asosiy kamchiligi yo'qoladi. Aynan shu muammoni rules_xcodeproj hal qiladi.

rules_xcodeproj — Bazel target'laridan haqiqiy .xcodeproj fayl generatsiya qiladigan qoidalar to'plami. U Mobile Native Foundation ostida yuritiladi (dastlab BuildBuddy tomonidan yaratilib, keyin MNF'ga topshirilgan), g'oyasi Google'ning Tulsi'si va Target/Lyft'ning ichki generatorlaridan kelib chiqqan. README'da foydalanuvchi sifatida 19 ta loyiha sanab o'tilgan: Cash App, Lyft, Reddit, Robinhood, Slack, Snap, Spotify, Square, Tinder, Gojek, Mercari, Faire, Envoy Mobile, SwiftLint, amo, BazelPods, Ergatta, Ten Ten, Tokopedia (manba: rules_xcodeproj README, 4.1.0).

🚀 Motivatsiya: Bir buyruq — bazel run //:xcodeproj — va sizda Bazel build qiladigan, lekin Xcode'da xuddi oddiy loyihadek ochiladigan .app loyihasi paydo bo'ladi. Dasturchi Bazel borligini deyarli sezmaydi: u Cmd+R bosadi, breakpoint qo'yadi, kodni to'ldiradi. Jamoangiz CI'da Bazel cache'dan foyda ko'radi, dasturchilar esa odatdagi Xcode'da qoladi.


2. rules_xcodeproj nima qiladi: "Build with Bazel" modeli {#model}

Oddiy Xcode loyihasida Xcode'ning o'z build tizimi (XCBBuildService) kodni kompilyatsiya qiladi. rules_xcodeproj generatsiya qilgan loyihada esa boshqacha: Xcode faqat muharrir va ishga tushiruvchi bo'lib qoladi, haqiqiy build'ni esa Bazel bajaradi. Bu model "Build with Bazel" (BwB) deb ataladi.

Mexanizm shunday ishlaydi: generatsiya qilingan loyihada BazelDependencies degan maxsus target va har bir build'da ishga tushadigan "Generate Bazel Dependencies" nomli Run Script build phase bo'ladi. Xcode "build" tugmasini bosganingizda, bu skript bazel buildni chaqiradi; Bazel haqiqiy ishni qiladi va natijani bazel-out/ga qo'yadi; keyin Xcode o'zining build qadamlarini "stub" qilib (bo'sh o'tkazib) natijani oladi. Xcode'ning serialized diagnostics (ogohlantirish/xato) ma'lumotlari qayta o'ynatiladi, shuning uchun inline xatolar va fix-it'lar ishlaydi.

rules_xcodeproj "Build with Bazel" oqimi: Xcode muharrir va BazelDependencies stub, haqiqiy build'ni Bazel bajaradi, natija bazel-out'dan keladi

Diagrammada: siz Xcode'da Cmd+B bosasiz → BazelDependencies script phase bazel buildni chaqiradi → Bazel action graph'ni hisoblab, faqat o'zgargan narsani kompilyatsiya qiladi (cache'dan foydalanib) → natija bazel-out/ga tushadi → Xcode uni DerivedData'ga nusxalaydi va ishga tushiradi. Indexing va debugging Xcode tomonda qoladi.

Nega bu yaxshi? Chunki Xcode ichidagi build ham, CI'dagi bazel build ham bir xil Bazel action'larini ishlatadi. Ya'ni ularning cache'i umumiy: CI "warm" qilgan cache'dan dasturchining Xcode'i ham foyda ko'rishi mumkin (buni Bazel 10 va Bazel 11 darslarida ko'ramiz). Oddiy Xcode build tizimi bunday umumiy cache'ni bermaydi.

🧠 Esda tuting: 4.x versiyada xcodeproj qoidasida build_mode atributi yo'q. Eski versiyalarda "Build with Xcode" (BwX) — Bazel target'larini Xcode'ning o'z build tizimi orqali qurish — rejimi ham bor edi, lekin 3.0.0 (2025-06-13) "legacy generator" ni olib tashladi va 4.1.0 manba kodida build_mode so'zi faqat CHANGELOG.mdda uchraydi. Demak hozir BwB — yagona rejim. (docs/faq.mdda hali ham eski BwX haqida matn qolgan — bu eskirgan, unga ishonmang.)

⚠️ Diqqat: rules_xcodeproj loyihangiz build qilish usulini o'zgartirmaydi. Uning falsafasi (design-goals.md): "Xcode xususiyatini yoqish uchun target build qilinishini o'zgartirmaymiz." Ya'ni u Bazel graph'ingiz ustiga Xcode qatlamini qo'yadi, xolos. Bu — kuch (barcha target'lar bir xil quriladi), lekin ayni paytda cheklov ham (Xcode'ning ba'zi avtomatik xususiyatlari, masalan SwiftUI Preview, oson ishlamaydi — 6-bo'limga qarang).


3. O'rnatish va birinchi loyiha generatsiyasi {#ornatish}

Biz Bazel 5 darsidagi "Kitoblar" ilovasini qayta ishlatamiz: Core moduli (kitob modeli + qidiruv mantig'i) va App moduli (SwiftUI). Loyiha tuzilishi:

kitoblar/
├── .bazelversion
├── .bazelrc
├── MODULE.bazel
├── BUILD.bazel            ← xcodeproj target shu yerda
├── Core/
│   ├── BUILD.bazel
│   ├── Sources/
│   │   ├── Book.swift
│   │   └── BookCatalog.swift
│   └── Tests/
│       └── BookCatalogTests.swift
└── App/
    ├── BUILD.bazel
    ├── Info.plist
    └── Sources/
        ├── KitoblarApp.swift
        └── BookListView.swift

3.1. MODULE.bazel'ga rules_xcodeproj'ni qo'shish

rules_xcodeprojni boshqa dependency'lar qatorida bazel_dep bilan qo'shamiz. U faqat dasturchining mashinasida, loyiha generatsiya qilishda kerak — CI'dagi bazel buildga ta'sir qilmaydi.

# MODULE.bazel
module(name = "kitoblar", version = "1.0.0")

# apple_support rules_cc'dan yuqorida turishi kerak — birinchi ro'yxatdan
# o'tgan toolchain yutadi.
bazel_dep(name = "apple_support", version = "2.10.1")
bazel_dep(name = "rules_swift", version = "4.2.1")
bazel_dep(name = "rules_apple", version = "5.3.0")

# Xcode loyihasini generatsiya qilish uchun.
bazel_dep(name = "rules_xcodeproj", version = "4.1.0")

⚠️ Diqqat — mosligini o'zingiz tekshiring: rules_xcodeproj 4.1.0 — 2026-05-07 da chiqqan oxirgi relizi (o'shandan beri 5 oy yangilanish yo'q). Uning README'dagi "Compatibility" jadvali eskirgan: u hali ham Bazel 7.0–9.x, Xcode 13.3–16.x, macOS 13–15.x deb yozadi va "rasman faqat shular bilan sinaldi" deydi. 4.x CHANGELOG'da Xcode 26/27 umuman tilga olinmagan. Shuning uchun biz "4.1.0 Xcode 27.1 bilan rasman mos" deb da'vo qilmaymiz — buni o'zimiz tekshiramiz (quyida). Siz ham o'z Xcode versiyangizda tekshiring.

3.2. xcodeproj target'ini yozish

Ildiz BUILD.bazel fayliga xcodeproj qoidasini qo'shamiz. Eng kam konfiguratsiya — name, project_name va top_level_targets:

# BUILD.bazel
load("@rules_xcodeproj//xcodeproj:defs.bzl", "top_level_target", "xcodeproj")

xcodeproj(
    name = "xcodeproj",
    project_name = "Kitoblar",
    # Bazel bu targetni "//..." ichida tasodifan build qilmasligi uchun.
    tags = ["manual"],
    top_level_targets = [
        top_level_target("//App:Kitoblar", target_environments = ["simulator"]),
        "//Core:CoreTests",
    ],
)

Bu yerda:

  • top_level_targets — Xcode'da "asosiy" bo'lib ko'rinadigan target'lar ro'yxati. Ularning barcha transitive dependency'lari ham loyihaga tushadi. Bizda ilova (//App:Kitoblar) va test (//Core:CoreTests).
  • top_level_target(label, target_environments = [...]) — target'ni qaysi muhit uchun sozlashni aytadi. "simulator" yoki "device" (yoki ikkalasi). Agar ["device", "simulator"] bersangiz, imkon bo'lsa bitta birlashgan Xcode target yaratiladi. Biz simulyator uchun quramiz.
  • tags = ["manual"] — bazel build //... bu target'ni avtomatik qurmasligi uchun (u dasturchi qo'li bilan ishga tushiriladigan generator).

💡 Pro tip: project_name = "Kitoblar" — hosil bo'ladigan fayl nomi (Kitoblar.xcodeproj). Agar bermasangiz, name (ya'ni xcodeproj) ishlatiladi va fayl xcodeproj.xcodeproj bo'lib ketadi — buni istamaysiz.

3.3. Haqiqiy xato: visibility

Agar xcodeproj target ildizda, //App:Kitoblar esa boshqa paketda bo'lsa, birinchi urinishda shunday xato chiqishi mumkin:

Error: target '//App:Kitoblar' is not visible from target
'@@rules_xcodeproj++...rules_xcodeproj_generated//generator/xcodeproj:xcodeproj'

Sabab — rules_xcodeproj ichki ravishda @rules_xcodeproj_generated repo'sida yangi paket yaratadi va sizning target'laringizga murojaat qiladi. FAQ aytganidek, siz murojaat qiladigan target'lar shu paketni ko'ra olishi kerak. Yechim — visibilityga maxsus guruhni qo'shish:

# Core/BUILD.bazel
ios_unit_test(
    name = "CoreTests",
    minimum_os_version = "17.0",
    deps = [":CoreTestsLib"],
    # xcodeproj generatsiya qilgan paket shu targetni ko'ra olishi uchun.
    visibility = ["@rules_xcodeproj//xcodeproj:generated"],
)
# App/BUILD.bazel
ios_application(
    name = "Kitoblar",
    bundle_id = "uz.azimjondev.kitoblar",
    families = ["iphone"],
    infoplists = ["Info.plist"],
    minimum_os_version = "17.0",
    deps = [":AppLib"],
    visibility = ["@rules_xcodeproj//xcodeproj:generated"],
)

🧠 Esda tuting: "@rules_xcodeproj//xcodeproj:generated" — bu rules_xcodeprojning rasmiy package_group'i. Agar barcha target'lar bitta BUILD.bazelda bo'lsa (README'dagi oddiy misoldagi kabi), bu kerak emas. Lekin haqiqiy loyihalar ko'p paketli, shuning uchun bu xatoga albatta duch kelasiz.

3.4. Generatsiya: bazel run //:xcodeproj

Endi loyihani generatsiya qilamiz:

bazel run //:xcodeproj

Birinchi marta bu buyruq uzoq ishlaydi — chunki Bazel rules_xcodeprojning generator vositasini (Swift'da yozilgan) va u bog'liq bo'lgan kutubxonalarni (jumladan, 4.1.0 bilan birga keladigan rsync'ni) noldan kompilyatsiya qiladi. Bizning o'lchovimiz:

INFO: Elapsed time: 181.209s, Critical Path: 11.49s
INFO: 1481 processes: 4 disk cache hit, 158 internal, 1319 local.
INFO: Build completed successfully, 1481 total action
INFO: Running command line: .../rules_xcodeproj.noindex/build_output_base/.../xcodeproj-installer.sh
Updated project at "Kitoblar.xcodeproj"

Natija: Updated project at "Kitoblar.xcodeproj" — loyiha hosil bo'ldi! (Apple M4 Pro, Xcode 27.1, bo'sh output base. Bu 181 soniya — bir martalik narx: generator keshga tushgach, keyingi generatsiyalar bir necha soniya.)

📊 Real raqamlar: BUILD.bazelga bitta qator qo'shib (yangi fayl/target qo'shishni taqlid qilib) qayta generatsiya qilganda — generator allaqachon kompilyatsiya qilingani uchun — bizda 8.75 soniya (real 8.75) ketdi. Ya'ni kundalik "BUILD o'zgartirdim → qayta generatsiya" aylanmasi arzon.

3.5. Loyihada nimalar paydo bo'ldi

Generatsiyadan keyin ildizda Kitoblar.xcodeproj paydo bo'ladi. Uning ichida (eslatma: aksariyat fayllar rules_xcodeproj/ ichida — bu normal):

Kitoblar.xcodeproj/
├── project.pbxproj
├── project.xcworkspace/
├── xcshareddata/xcschemes/
│   ├── Core.xcscheme
│   ├── CoreTests.xcscheme
│   └── Kitoblar.xcscheme
├── rules_xcodeproj/
│   └── bazel/
│       ├── xcodeproj.bazelrc          ← config oilasi shu yerda (7-bo'lim)
│       └── ... (BwB uchun skriptlar)
└── xcuserdata/

xcodebuild -list bilan tekshirsak (bizning haqiqiy natijamiz):

    Targets:
        BazelDependencies    ← BwB "ko'prigi" (stub target)
        Core
        CoreTests
        Kitoblar
    Schemes:
        Core
        CoreTests
        Kitoblar

BazelDependencies — biz 2-bo'limda aytgan BwB target. Qolgan uchtasi — sizning target'laringiz. Har bir "buildable" target uchun avtomatik scheme yaratildi (bu scheme_autogeneration_mode = "auto" standart xatti-harakati).

Loyihaning Swift kodi oddiy (bu Bazel 5dan):

// Core/Sources/BookCatalog.swift
public struct BookCatalog: Sendable {
    public private(set) var books: [Book]

    public init(books: [Book] = BookCatalog.sample) {
        self.books = books
    }

    public func search(_ query: String) -> [Book] {
        guard !query.isEmpty else { return books }
        let q = query.lowercased()
        return books.filter {
            $0.title.lowercased().contains(q) || $0.author.lowercased().contains(q)
        }
    }
    // ... (to'liq versiya loyihada)
}

Va swift_library BUILD bo'lagi:

# Core/BUILD.bazel
load("@rules_swift//swift:swift.bzl", "swift_library")
load("@rules_apple//apple:ios.bzl", "ios_unit_test")

swift_library(
    name = "Core",
    srcs = glob(["Sources/**/*.swift"]),
    module_name = "Core",
    visibility = ["//visibility:public"],
)

4. Generatsiya qilingan loyihani CLI'dan build qilish (BwB) {#build}

Odatda loyihani Xcode'da (open Kitoblar.xcodeproj) ochib, Cmd+B bosasiz. Lekin biz avval terminalda tekshiramiz — bu CI uchun ham muhim va "haqiqatan ishladi" degan dalil beradi. xcodebuildni ishlatamiz:

xcodebuild \
  -project Kitoblar.xcodeproj \
  -scheme Kitoblar \
  -destination 'generic/platform=iOS Simulator' \
  -derivedDataPath .dd \
  build

Natijaning oxiri (bizning haqiqiy chiqishimiz, Xcode 27.1):

note: Run script build phase 'Generate Bazel Dependencies' will be run during
every build because the option to run the script phase "Based on dependency
analysis" is unchecked. (in target 'BazelDependencies' from project 'Kitoblar')
note: Run script build phase 'Copy Bazel Outputs / Generate Bazel Dependencies
(Index Build)' will be run during every build ... (in target 'Kitoblar' ...)
** BUILD SUCCEEDED **

**** BUILD SUCCEEDED **** — generatsiya qilingan loyiha Xcode 27.1 da muvaffaqiyatli quriladi! Chiqishda BwB mexanizmini aniq ko'ramiz:

  • 'Generate Bazel Dependencies' script phase — BazelDependencies target ichida, har build'da ishlaydi (chunki "Based on dependency analysis" o'chirilgan — bu ataylab, Bazel o'zi qaror qiladi);
  • 'Copy Bazel Outputs ...' — Bazel natijalarini Xcode'ning DerivedData'siga ko'chiradi.

App bundle bazel-out/ios_sim_arm64-dbg-.../bin/App/Kitoblar.appda paydo bo'ladi — ya'ni haqiqiy ishni Bazel qilgan, Xcode faqat uni o'rab olgan.

📊 Real raqamlar (Apple M4 Pro, 12 core, 24 GB; Xcode 27.1; Bazel 9.3.0; rules_xcodeproj 4.1.0):

| Amal | Vaqt | Izoh | | -------------------------------------------------- | ------------------ | --------------------------------------------------------------- | | bazel run //:xcodeproj (birinchi) | 181.2 s | generator + rsync'ni noldan kompilyatsiya qiladi (bir martalik) | | qayta generatsiya (BUILD o'zgardi) | 8.75 s | generator keshda | | xcodebuild ... build (birinchi, BwB) | ✅ BUILD SUCCEEDED | to'liq Bazel build | | xcodebuild ... build (o'zgarishsiz, incremental) | 1.99 s | BwB stub tez o'tadi | | xcodebuild qayta generatsiyadan keyin | 90.8 s | DerivedData bekor bo'ldi → to'liq qayta build |

Oxirgi qator muhim: loyihani qayta generatsiya qilsangiz, Xcode'ning DerivedData'si bekor bo'ladi va keyingi build to'liq bo'ladi (quyida 9-bo'limga qarang).

**⚠️ Diqqat — har doim -derivedDataPath bering:** biz -derivedDataPath .dd berdik, ya'ni DerivedData loyiha ichidagi .dd papkaga tushadi. Bu — izolyatsiya uchun yaxshi odat (ayniqsa CI'da): umumiy ~/Library/Developer/Xcode/DerivedDatani ifloslantirmaysiz.


5. Indexing, debugging va test navigator {#indexing}

Endi open Kitoblar.xcodeproj qilib Xcode'da ochamiz. rules_xcodeproj README'si va'da qiladigan xususiyatlar: indexing, debugging, runtime sanitizer'lar, inline ogohlantirish/xatolar, test tanlash va ishga tushirish, embedded target'lar (App Clip, extension, Watch), dynamic framework'lar va Xcode Previews. Qaysilari haqiqatan ishlaydi? Keling, mexanizmini ko'raylik.

5.1. Indexing (autocomplete, jump to definition)

Indexing — Xcode kodingizni "tushunishi": catalog. deb yozganingizda metodlar ro'yxati, BookCatalogni Cmd+bosib ta'rifiga o'tish, syntax rang berish. Buning uchun Xcode fayl bo'yicha kompilyatsiya qila olishi kerak.

rules_xcodeproj buni "Index Build" (Prepare for Indexing) orqali hal qiladi: Xcode fonda loyihani alohida output papkaga quradi, indexing uchun kerakli artefaktlarni tayyorlaydi. Generatsiya qilingan xcodeproj.bazelrcda buni ko'rsatuvchi feature'lar bor (bizning fayldan):

common:rules_xcodeproj --features=swift.index_while_building
common:rules_xcodeproj --features=swift.use_global_index_store
common:rules_xcodeproj_indexbuild --config=rules_xcodeproj
common:rules_xcodeproj_indexbuild --bes_backend= --bes_results_url=
  • swift.index_while_building — Swift kompilyatori build paytida index store yozib boradi;
  • swift.use_global_index_store — index'ni umumiy joyda saqlaydi (qayta ishlatish uchun);
  • rules_xcodeproj_indexbuild config — Index Build fonda tez-tez ishlagani uchun telemetriyani (BES upload'ni) o'chiradi.

xcodeproj qoidasida import_index_build_indexstores atributi standart holatda True — bu Index Build hosil qilgan index store'larni import qilib, Xcode'ning indekslashini tezlashtiradi. Agar disk IO ko'p bo'lsa va arzimasa, uni False qilishingiz mumkin.

💡 Pro tip: birinchi marta loyihani ochganingizda Xcode biroz "o'ylaydi" — bu Index Build. Bir marta tugagach, autocomplete va jump-to-definition oddiy Xcode kabi ishlaydi. Agar indexing "qotib qolsa", Xcode'ni yopib-ochish yoki loyihani qayta generatsiya qilish yordam beradi.

5.2. Debugging (breakpoint, LLDB)

rules_xcodeprojning asosiy maqsadi (design-goals.md so'zlari bilan) — "target'larni ishga tushirish va debug qilishni qo'llab-quvvatlash". Esingizda bo'lsa, Asana aynan breakpoint muammosi tufayli Bazel'dan voz kechgan edi — rules_xcodeproj esa shu muammoni hal qilish uchun yaratilgan.

Amalda: BookCatalog.searchga breakpoint qo'yasiz, Cmd+R bilan ilovani simulyatorda ishga tushirasiz, qidiruv maydoniga "kunlar" yozasiz — Xcode breakpoint'da to'xtaydi, siz po q yoki po books.count deb LLDB'da tekshirasiz. Bu oddiy Xcode debug tajribasi, farqi — binar'ni Bazel qurgan.

🧠 Esda tuting: BwB rejimida debug ma'lumotlari (.dSYM, serialized diagnostics) Bazel tomonidan hosil qilinadi va Xcode'ga uzatiladi. xcodeproj.bazelrcdagi --features=oso_prefix_is_pwd va --features=relative_ast_path — aynan debugging/indexing yo'llarini to'g'ri xaritalash uchun.

5.3. Test navigator

//Core:CoreTests target uchun avtomatik CoreTests scheme yaratilgani uchun, Xcode'ning Test navigator'ida (Cmd+6) testlar ko'rinadi. Bizning testlar:

// Core/Tests/BookCatalogTests.swift
final class BookCatalogTests: XCTestCase {
    func testSearchByTitle() {
        let catalog = BookCatalog()
        let results = catalog.search("kunlar")
        XCTAssertEqual(results.count, 1)
        XCTAssertEqual(results.first?.author, "Abdulla Qodiriy")
    }

    func testSortByYear() {
        let catalog = BookCatalog()
        XCTAssertEqual(catalog.sorted(by: \.year).first?.year, 1925)
    }
}

Test yonidagi olmosni bosib bitta testni, yoki butun klassni ishga tushirasiz. Natija yashil/qizil belgi bilan ko'rinadi. Ichkarida bu bazel test bilan bir xil ishni qiladi (test cache haqida Bazel 6da).

⚠️ Diqqat — coverage va cache: agar Xcode'da "Gather coverage data" ni yoqsangiz, rules_xcodeproj_coverage config ishga tushadi. Hujjat ogohlantiradi: coverage build'lari manba fayllarga mutlaq (absolute) yo'llar yozadi, bu esa hermetik emas va cache hit'larni kamaytiradi. Tavsiya: coverage'ni Xcode'da faqat faol ishlatganda yoqing; aks holda coverage'ni oddiy bazel coverage (CLI) orqali oling.


6. SwiftUI Preview: eng katta kamchilik (halol gap) {#preview}

rules_xcodeproj README'si xususiyatlar ro'yxatida "Xcode Previews"ni sanaydi. Lekin 4.1.0 da SwiftUI Preview — eng katta DX kamchiligi. Buni ochiq aytamiz, chunki bu ko'p dasturchini hafsalasini pir qiladi.

Bizning BookListViewda #Preview bor:

// App/Sources/BookListView.swift
#Preview {
    BookListView(catalog: BookCatalog())
}

Oddiy Xcode loyihasida bu canvas'da jonli ko'rinardi. rules_xcodeproj loyihasida esa — ishlamaydi. Sabab mexanizmda: Xcode 16+ SwiftUI Preview'lari ishlashi uchun ENABLE_DEBUG_DYLIB = YES build setting kerak. Lekin rules_xcodeproj buni NO qilib qo'yadi. Biz buni generatsiya qilingan project.pbxprojda o'z ko'zimiz bilan tekshirdik:

grep -o "ENABLE_DEBUG_DYLIB = [A-Z]*" Kitoblar.xcodeproj/project.pbxproj | sort | uniq -c
   1 ENABLE_DEBUG_DYLIB = NO

Bu rules_xcodeprojda ataylab qo'yilgan (#3042 — "Xcode 16'ni to'g'ri qo'llab-quvvatlagunimizcha"). Natija: #Preview bloklari canvas'da render bo'lmaydi.

Holat (2026-oktabr, tekshirilgan):

  • Issue #3201 "Feature Request: Xcode SwiftUI Preview Support" — 2025-06-20 da ochilgan, hali ham ochiq (oxirgi faollik 2026-09-17).
  • Native preview'ni qo'shish uchun urinilgan PR'lar (#3348 "opt-in native Xcode Preview configurations" (draft), #3325) 2026-10-13 holatiga yopilgan, lekin merge qilinmagan. Ya'ni 4.1.0 da native Preview chiqmagan.
  • xcodeproj.bazelrcda rules_xcodeproj_swiftuipreviews config bor (u -enable-implicit-dynamic, -enable-private-imports, -enable-dynamic-replacement-chaining copt'larini qo'shadi), lekin ENABLE_DEBUG_DYLIB = NO tufayli Xcode 16+/27 da bu to'liq preview tajribasini bermaydi.

⚠️ Diqqat: Agar sizning ishingiz SwiftUI Preview'ga juda bog'liq bo'lsa (masalan, dizayn-og'ir ekranlar ustida ishlayotgan bo'lsangiz), Bazel + rules_xcodeproj 4.1.0 bu borada sizni cheklaydi. Amaldagi yechimlar: (1) ilovani simulyatorda tez-tez ishga tushirish (hot reload o'rniga); (2) preview-og'ir modullar uchun alohida kichik SwiftPM/Xcode loyihasi saqlash; (3) rules_xcodeprojning yangi relizlarini kuzatib borish — Preview ustida ish ketyapti, lekin hali chiqmagan. Buni loyiha tanlashda hisobga oling (20-dars, Bazel vs Tuist).

🧠 Esda tuting — nega shunday: rules_xcodeproj falsafasi (design-goals.md) aniq aytadi: "Xcode xususiyatini yoqish uchun target build qilinishini o'zgartirmaymiz." SwiftUI Preview static kutubxonalar bilan ishlamaydi va rules_xcodeproj sizning target'ingiz mahsulot turini o'zgartirib qo'ymaydi. Bu — printsipial tanlov: natija bazel build bilan bir xil bo'lishi preview qulayligidan muhimroq deb hisoblanadi.


7. .bazelrc config oilasi va command-line API {#configlar}

7.1. Config oilasi

Generatsiya qilinganda Kitoblar.xcodeproj/rules_xcodeproj/bazel/xcodeproj.bazelrc fayli yaratiladi. Unda bir oila config bor — har biri --config= orqali chaqiriladi:

ConfigQachon ishlaydiNima qiladi
rules_xcodeprojXcode ichida har buildAsosiy config; qolganlari undan meros oladi
rules_xcodeproj_generatorbazel run //:xcodeproj paytidaGeneratsiya (build'ga ta'sir qilmaydigan sozlamalar)
rules_xcodeproj_indexbuildXcode Index Build (fon)Telemetriya/log'ni o'chiradi (tez-tez ishlaydi)
rules_xcodeproj_swiftuipreviewsXcode Preview buildPreview uchun maxsus copt'lar
rules_xcodeproj_coverageCoverage yoqilgan test buildCoverage ma'lumotini mutlaq yo'llar bilan yozadi

Asosiy config'ning bir qismi (bizning generatsiya qilingan fayldan):

common:rules_xcodeproj --verbose_failures
common:rules_xcodeproj --cache_computed_file_digests=500000
common:rules_xcodeproj --compilation_mode=dbg
common:rules_xcodeproj --features=swift.index_while_building
common:rules_xcodeproj --features=swift.use_global_index_store
common:rules_xcodeproj --spawn_strategy=remote,worker,local
build:rules_xcodeproj --remote_download_outputs=toplevel

💡 Pro tip: biror build flag'ni Xcode ichidagi build uchun ham, generatsiya uchun ham qo'shmoqchi bo'lsangiz — uni common:rules_xcodeproj (asosiy config) ga qo'ying, chunki qolganlari undan meros oladi. Hujjat ogohlantiradi: "build'ga ta'sir qiluvchi flag'lar barcha rules_xcodeproj{_*} config'larda bir xil bo'lishi kerak", shuning uchun odatda asosiy config'ni o'zgartirasiz. O'zingizning sozlamalaringizni ildiz .bazelrcingizdagi common:rules_xcodeproj --... qatorlariga yozishingiz mumkin.

7.2. rules_xcodeproj o'zining alohida output base'ida ishlaydi

Muhim nozik jihat: rules_xcodeproj Xcode ichida qurar ekan, u alohida output base ishlatadi. Buning sababi — Xcode build'lari sizning oddiy bazel buildlaringizning analysis cache'ini buzmasligi uchun. Buni command-line API bilan tekshirdik:

bazel run //:xcodeproj -- 'info output_path'
INFO: Elapsed time: 1.739s, Critical Path: 0.04s
/Users/.../Caches/bazel/_bazel_codingtech/<hash>/rules_xcodeproj.noindex/build_output_base/execroot/_main/bazel-out

E'tibor bering: yo'lda rules_xcodeproj.noindex/build_output_base bor — bu sizning oddiy bazel-outingiz emas, alohida muhit.

7.3. Command-line API

Shu alohida muhitda buyruq berish uchun maxsus API bor:

bazel run //:xcodeproj -- [variantlar] 'buyruq matni'

Foydali misollar:

# rules_xcodeproj muhitida output_path'ni ko'rsatish (yuqorida)
bazel run //:xcodeproj -- 'info output_path'

# rules_xcodeproj output base'ini tozalash (oddiy "bazel clean" buni tozalamaydi!)
bazel run //:xcodeproj -- clean

# CI'da cache'ni "warm" qilish uchun barcha target'larni qurish
bazel run //:xcodeproj -- --generator_output_groups=all_targets 'build --remote_download_minimal'

⚠️ Diqqat: oddiy bazel clean rules_xcodeprojning alohida output base'ini tozalamaydi. Agar Xcode ichidagi build'da g'alati muammo bo'lsa, bazel run //:xcodeproj -- clean ni ishlating (yoki butunlay hammasini tozalaydigan bazel clean --expunge). Bu — yangi boshlovchilar ko'p adashadigan joy.

💡 Pro tip (CI uchun): --generator_output_groups=all_targets bilan cache warming — Bazel 11 darsining muhim qismi. BuildBuddy'ning migratsiya qo'llanmasi (Brentley Jones, 2024) aytganidek: "rules_xcodeproj CLI'dan biroz boshqacha konfiguratsiya bilan quradi, shuning uchun CI cache'ini aynan rules_xcodeproj buyrug'i orqali warm qilish kerak." Aks holda dasturchining Xcode'i cache'dan to'liq foyda ko'rmaydi.


8. Focused projects: katta repo uchun {#focused}

Katta monorepo'da (yuzlab target) hamma target'ni Xcode'ga tiqish — loyihani og'irlashtiradi va indexing'ni sekinlashtiradi. Yechim — Focused Projects: Xcode'ga faqat siz hozir ustida ishlayotgan target'larni qo'yish, qolganlarini Bazel binar sifatida qurish.

xcodeproj qoidasida ikkita atribut shu uchun:

  • focused_targets — faqat shu target'lar (va ularga kerakli narsalar) Xcode'ga tushadi; qolganlari "unfocused" bo'ladi.
  • unfocused_targets — aksincha, faqat shu target'larni Xcode'dan chiqarib tashlaydi.

Masalan, agar siz faqat Appustida ishlayotgan bo'lsangiz, lekin Coreni Bazel qursin desangiz:

# BUILD.bazel
xcodeproj(
    name = "xcodeproj",
    project_name = "Kitoblar",
    tags = ["manual"],
    top_level_targets = [
        top_level_target("//App:Kitoblar", target_environments = ["simulator"]),
        "//Core:CoreTests",
    ],
    # Core'ni Xcode'da ko'rsatmaymiz — u Bazel tomonidan binar sifatida quriladi.
    unfocused_targets = ["//Core:Core"],
)

🧠 Esda tuting: "Unfocused" target'lar yo'qolmaydi — ular hamon Bazel tomonidan quriladi va ilovaga ulanadi. Shunchaki Xcode navigator'ida ularning manba fayllarini ko'rmaysiz. Bu Xcode loyihasini yengillashtiradi: kamroq fayl, tezroq indexing, kamroq scheme.

💡 Pro tip: katta jamoalarda focused_targetsni dinamik qilib qo'yishadi — har dasturchi o'zi ishlayotgan modullarni environment variable yoki alohida .bzl fayl orqali ko'rsatadi. rules_xcodeproj design-goals.md aynan shuni tavsiya qiladi: "dinamik holatlar uchun .bzl fayllarni generatsiya qilib, ulardagi macro'lar orqali kerakli target'larni yarating." Shunda har kim o'ziga kerakli "kichik" loyiha oladi, lekin cache umumiy qoladi.


9. Katta loyihaga xos muammolar va signing {#muammolar}

rules_xcodeproj kuchli, lekin kamchiliksiz emas. Mana halol kamchiliklar ro'yxati (hammasi hujjat yoki bizning tajribamizdan):

9.1. Archive ishlamaydi — .ipa'ni Bazel bilan quring

Xcode'ning Product > Archive > Organizer oqimi rules_xcodeproj loyihalarida qo'llab-quvvatlanmaydi. FAQ ochiq aytadi: "Hozircha Archive action'ni to'g'ri ishlashiga kuch sarflanmagan" va optimizatsiyalar hosil bo'ladigan .xcarchiveni buzishi mumkin.

Yechim — App Store uchun .ipani to'g'ridan-to'g'ri bazel build bilan quring:

# Release build (device uchun, arm64)
bazel build //App:Kitoblar -c opt --ios_multi_cpus=arm64

Biz simulyator uchun .ipani allaqachon qurgandik (FACTS tasdiqlaydi):

Target //App:Kitoblar up-to-date:
  bazel-bin/App/Kitoblar.ipa        (149 KB)

⚠️ Diqqat — device signing: haqiqiy qurilma/App Store uchun kod imzolash kerak. Bu Apple Developer hisobini talab qiladi, shuning uchun biz uni bu yerda hujjat asosida tushuntiramiz (bizning muhitda device signing tekshirilmagan). Bazel 8da signing'ni ko'rgandik; releasi va Fastlane esa Bazel 11da.

9.2. Signing: xcode_provisioning_profile

Bazel'da signing Xcode'ning "Automatically manage signing"idan boshqacha ishlaydi. ios_applicationda provisioning_profile atributi bo'ladi (odatda local_provisioning_profile bilan to'ldiriladi). Agar Xcode boshqaradigan profilni ishlatmoqchi bo'lsangiz, rules_xcodeprojga buni aytish uchun uni xcode_provisioning_profile bilan o'raysiz:

ios_application(
    name = "Kitoblar",
    # ...
    provisioning_profile = ":xcode_profile",
)

xcode_provisioning_profile(
    name = "xcode_profile",
    managed_by_xcode = True,
    provisioning_profile = ":provisioning_profile",
)

local_provisioning_profile(
    name = "provisioning_profile",
    profile_name = "iOS Team Provisioning Profile: uz.azimjondev.kitoblar",
    team_id = "A12B3CDEFG",
)

managed_by_xcode = True — Xcode'da "Automatic Code Signing" yoqiladi; team_id esa DEVELOPER_TEAM build setting'ini belgilaydi. FAQ eslatadi: profil ~/Library/MobileDevice/Provisioning Profilesga o'rnatilgan bo'lishi kerak, aks holda Xcode "couldn't find any provisioning profiles matching 'TEAM_ID/PROFILE_NAME'" deydi.

9.3. BUILD o'zgarsa — qayta generatsiya kerak

Bu — eng muhim kundalik cheklov. BUILD.bazelni o'zgartirsangiz (yangi fayl, yangi dependency, yangi target), Xcode loyihasi avtomatik yangilanmaydi — bazel run //:xcodeprojni qayta ishga tushirishingiz kerak. Bizning o'lchovimizda bu arzon (8.75 s), lekin qayta generatsiyadan keyin Xcode'ning DerivedData'si bekor bo'ladi va keyingi build to'liq bo'ladi (biz buni ko'rdik: o'zgarishsiz incremental 1.99 s edi, lekin qayta generatsiyadan keyin 90.8 s to'liq build).

⚠️ Diqqat: yangi Swift faylini modulning mavjud Sources/ papkasiga qo'shsangiz va BUILD.bazelda glob(["Sources/**/*.swift"]) ishlatgan bo'lsangiz — target o'zgaradi, lekin Xcode uni ko'rishi uchun baribir qayta generatsiya kerak. (Tuist'ning "buildable folders" xususiyati bu muammoni yumshatadi — Tuist darslariga qarang. Bazel tomonda yangi "synchronized folders" ustida ish ketyapti, lekin 4.1.0 da chiqmagan.)

9.4. Stale fayllar va build mode

FAQ yana ikki nozik holatni aytadi:

  • Stale fayllar: agar dependency o'chirsangiz yoki fayl yo'li o'zgarsa va loyihani yopib-ochmasdan qayta generatsiya qilsangiz, Xcode eski yo'llar haqida ogohlantirishi mumkin (bu Xcode'ning caching bug'i). Yechim — loyihani yopib-ochish yoki clean build.
  • Har modulning manbasi o'z papkasida bo'lishi kerak (BuildBuddy qo'llanmasi): glob() paket chegarasidan o'tmaydi, shuning uchun aralash tuzilishlar muammo beradi.

9.5. Kamchiliklar jadvali (halol xulosa)

KamchilikTa'siriNega
SwiftUI Preview ishlamaydiDizayn-og'ir ish qiyinlashadiENABLE_DEBUG_DYLIB = NO (PR #3042); #3201 ochiq
Archive yo'qApp Store'ga .ipani Bazel quradiFAQ: Archive qo'llab-quvvatlanmaydi
BUILD o'zgarsa qayta generatsiyaKichik ortiqcha qadamloyiha BUILD fayllardan generatsiya qilinadi
Qayta generatsiya → DerivedData bekorKeyingi build to'liqXcode caching
Coverage mutlaq yo'llar yozadiCache hit kamayadihermetik emas
Majorlar tez-tez buziladiYangilashda ehtiyot2.0→3.0→4.0 ~2 yilda
README jadvali eskirganXcode 27 "rasman" sinalmagano'zingiz tekshiring

🏢 Real-world: rules_ios (muqobil iOS ruleset) ni Square tark etyapti: muhokama #952da (2025-11-07) aytilishicha, ular "faol hissa qo'shishni to'xtatadi", chunki "rules_ios kamroq maqsadga xizmat qilyapti va build'ga ortiqcha murakkablik qo'shyapti" — zero rules_apple va rules_swift kerakli funksiyalarni olgan. rules_xcodeproj 4.0.0 ham o'zining rules_ios misolini olib tashlagan. Xulosa: bugun rules_apple + rules_swift + rules_xcodeproj — standart yo'l.


10. Alternativa: VS Code / Cursor — sourcekit-lsp + BSP {#vscode}

Hamma ham Xcode'ni yoqtirmaydi. Ba'zilar VS Code yoki Cursor'da (yoki AI agent, masalan Claude Code'da) ishlashni afzal ko'radi. Bu ham mumkin — sourcekit-lsp (Swift'ning rasmiy Language Server'i) orqali.

Muammo: sourcekit-lsp kodingiz qanday qurilishini bilishi kerak (qaysi flag'lar, qaysi modullar). Oddiy SwiftPM loyihada buni o'zi tushunadi, lekin Bazel loyihasida yo'q. Bu bo'shliqni Build Server Protocol (BSP) to'ldiradi: BSP server sourcekit-lspga "bu fayl shunday quriladi" deb aytadi.

Bazel uchun bunday BSP server bor: **spotify/sourcekit-bazel-bsp** (Apache-2.0, Spotify ochiq kodi, misol versiya 0.8.3). Zanjir shunday:

Cursor / VSCode / Claude Code
        │
        ▼
   sourcekit-lsp  ──►  sourcekit-bazel-bsp (BSP server)  ──►  Bazel

Uch xil IDE varianti: Xcode + rules_xcodeproj, VS Code/Cursor + sourcekit-bazel-bsp, oddiy Xcode loyihasi + xcode-build-server — har birining ishlaydigan va ishlamaydigan tomoni

10.1. Nima ishlaydi

sourcekit-bazel-bsp README'siga ko'ra:

  • Barcha IDE + Claude: odatdagi indexing xususiyatlari — code completion, jump to definition (Swift, Obj-C, C++ uchun).
  • Faqat Cursor / VSCode: build qilish, ishga tushirish, debug (lldb-dap orqali), test, hamda simulyator boshqaruvi — IDE ichidan (ixtiyoriy "companion extension" orqali).

10.2. Sozlash (qisqa)

MODULE.bazelga dependency qo'shiladi va BUILD'da setup_sourcekit_bsp qoidasi e'lon qilinadi:

# MODULE.bazel
bazel_dep(name = "sourcekit_bazel_bsp", version = "0.8.3")
# BUILD.bazel
load("@sourcekit_bazel_bsp//rules:setup_sourcekit_bsp.bzl", "setup_sourcekit_bsp")

setup_sourcekit_bsp(
    name = "setup_sourcekit_bsp",
    files_to_watch = ["App/**/*.swift", "Core/**/*.swift"],
    targets = ["//App:Kitoblar"],
)
# BSP sozlamasini yaratish (qoida argumentlari o'zgarsa qayta ishga tushiring)
bazel run //:setup_sourcekit_bsp

Keyin VS Code / Cursor'da Swift extension o'rnatiladi va .vscode/settings.jsonda sourcekit-lsp binary yo'li ko'rsatiladi; "Swift: Restart LSP Server" bosiladi. Talablar: Swift 6.1+ toolchain (Xcode 16.4+ ekvivalenti), Xcode baribir o'rnatilgan bo'lishi kerak.

⚠️ Diqqat — bu tekshirilmagan: biz bu bo'limni sourcekit-bazel-bsp README'si va Swift forumlari asosida tushuntirdik; o'z muhitimizda sinab ko'rmadik (u alohida o'rnatish va toolchain talab qiladi). Shuning uchun aniq buyruqlar versiyaga qarab o'zgarishi mumkin — repo'ni tekshiring.

10.3. Cheklovlar (README + forum)

  • Faqat Bazel loyihalari uchun — oddiy Xcode loyihalarida ishlamaydi (ular uchun xcode-build-server).
  • WMO (Whole Module Optimization) ishlatmang — u indexing'ni buzadi.
  • Birinchi indexing sekin — katta loyihada "to'liq indexing uzoq vaqt olishi mumkin". //... kabi keng wildcard'lardan qoching.
  • SwiftPM va Swiftly integratsiyalarini o'chiring — ular BSP bilan to'qnashadi.
  • Xcode'ning o'z sourcekit-lspidan foydalaniladi — "Xcode bilan hali kelmagan LSP xususiyatlari" bo'lishi mumkin, ya'ni performans va qulaylikda murosalar.
  • Forumda xabar qilingan real muammo: Xcode 26.0.1 ga yangilangandan keyin har Swift faylda "Loading the standard library failed" xatosi (sourcekit-lsp issue #2328); katta iOS loyihada Bazel chaqiruvlari o'rtacha 30 soniya–1 daqiqa, completion shu vaqtda "qotadi".

10.4. Oddiy Xcode loyihasi + xcode-build-server (uchinchi variant)

Agar sizda Bazel generatsiya qilgan .xcodeproj bor bo'lsa-yu, lekin VS Code'da ishlamoqchi bo'lsangiz, uchinchi yo'l bor: xcode-build-server — har qanday Xcode loyihasidan (jumladan Bazel generatsiya qilgani) BSP orqali sourcekit-lspni ishlata oladi. sourcekit-bazel-bsp README'si oddiy Xcode loyihalari uchun aynan shuni tavsiya qiladi.

10.5. Taqqoslash jadvali

Xcode + rules_xcodeprojVS Code/Cursor + sourcekit-bazel-bsp
MuharrirXcode (tanish)VS Code / Cursor / Claude
Indexing✅ (Index Build)✅ (Swift/ObjC/C++)
Build/Run✅ Cmd+R✅ (Cursor/VSCode)
Debug✅ LLDB✅ lldb-dap (Cursor/VSCode)
Test navigator✅✅ (Cursor/VSCode)
SwiftUI Preview❌ (4.1.0)❌
YetuklikYuqori (19 kompaniya)Yangiroq (Spotify, 0.8.x)
Birinchi indexingIndex Buildsekin (katta loyihada)
AI agent (Claude)—✅ indexing

🚀 Motivatsiya: AI agent'lar davrida sourcekit-bazel-bspning muhim yutug'i — u Claude kabi agent'larga LSP integratsiyasi orqali "kod aqlini" beradi. Ya'ni agent sizning Bazel loyihangizda jump-to-definition va completion'dan foydalanib, kodni to'g'riroq o'zgartiradi. Bu rules_xcodeproj (Xcode-markazli) bera olmaydigan narsa.

💡 Pro tip — qaysi birini tanlash: jamoangiz Xcode'ga o'rgangan bo'lsa va breakpoint/test navigator muhim bo'lsa — rules_xcodeproj (sinovdan o'tgan, keng tarqalgan). Dasturchilaringiz VS Code/Cursor/AI-agent'ni afzal ko'rsa va WMO'siz yashay olsangiz — sourcekit-bazel-bsp. Ko'pincha jamoalar ikkala yo'lni ham ochiq qoldiradi: bitta Bazel graph, ikki xil muharrir.


Amaliy mashqlar {#mashqlar}

  1. Loyihani generatsiya qiling. Bazel 5dagi "Kitoblar" ilovangizga rules_xcodeproj 4.1.0 ni qo'shing, ildiz BUILD.bazelga xcodeproj target yozing va bazel run //:xcodeproj bilan loyihani yarating. open Kitoblar.xcodeproj qilib, ilovani simulyatorda (Cmd+R) ishga tushiring.
  2. Visibility xatosini ataylab keltirib chiqaring. //App:Kitoblar target'idan visibility = ["@rules_xcodeproj//xcodeproj:generated"] ni olib tashlang va qayta generatsiya qiling. Xatoni o'qing, keyin visibility'ni qaytaring. Maqsad: xatoni tanish.
  3. Breakpoint va LLDB. BookCatalog.search metodiga breakpoint qo'ying, ilovani ishga tushirib qidiruvda biror so'z yozing. To'xtaganda LLDB konsolida po q va po books.count ni sinang. Natijani yozib qo'ying.
  4. Command-line API. bazel run //:xcodeproj -- 'info output_path' va bazel run //:xcodeproj -- clean ni ishlating. Birinchisi ko'rsatgan yo'lda rules_xcodeproj.noindex borligini tasdiqlang. Nega oddiy bazel-outdan farq qilishini bir jumlada tushuntiring.
  5. ⭐ Focused project. unfocused_targets = ["//Core:Core"] qo'shib qayta generatsiya qiling. Xcode navigator'ida Core modulining manba fayllari yo'qolganini, lekin ilova baribir qurilib ishlashini tekshiring. Qachon bu foydali ekanini yozing.
  6. ⭐ SwiftUI Preview holatini tekshiring. grep -o "ENABLE_DEBUG_DYLIB = [A-Z]*" Kitoblar.xcodeproj/project.pbxproj | sort | uniq -c ni ishlating. Qiymatni toping. Xcode'da #Previewni ochib, canvas'da nima bo'lishini (yoki bo'lmasligni) yozing.
  7. ⭐⭐ O'lchang. bazel run //:xcodeprojni birinchi marta (bo'sh output base) va BUILD.bazelga bir qator qo'shgandan keyin (qayta) o'lchang (/usr/bin/time -p bilan). Keyin xcodebuild ... buildni ikki marta ketma-ket qiling va ikkinchisini o'lchang. O'z raqamlaringizni bizning jadvalimiz bilan solishtiring — mashinangiz sekinroq/tezroq bo'lishi mumkin.

O'zingizni tekshiring {#checklist}

  • [ ] rules_xcodeproj nega kerakligini va Bazel'ni iOS'da qabul qilishdagi #1 to'siq IDE/DX ekanini tushuntira olaman (Asana misoli).
  • [ ] xcodeproj qoidasidagi project_name, top_level_targets, top_level_target(... target_environments=...) nima qilishini bilaman.
  • [ ] Ko'p paketli loyihada @rules_xcodeproj//xcodeproj:generated visibility nega kerakligini ayta olaman.
  • [ ] "Build with Bazel" (BwB) modelini — BazelDependencies target va "Generate Bazel Dependencies" script phase'ni — tushuntira olaman.
  • [ ] 4.1.0 da build_mode atributi yo'q va BwB — yagona rejim ekanini bilaman.
  • [ ] SwiftUI Preview 4.1.0 da nega ishlamasligini (ENABLE_DEBUG_DYLIB = NO, #3201) va yechimlarni bilaman.
  • [ ] Archive qo'llab-quvvatlanmasligini va .ipani bazel build bilan qurishni bilaman.
  • [ ] bazel run //:xcodeproj -- clean oddiy bazel cleandan nega farq qilishini tushunaman.
  • [ ] VS Code/Cursor'ni sourcekit-bazel-bsp orqali Bazel'ga ulash mumkinligini va uning cheklovlarini bilaman.

Xulosa {#xulosa}

  • IDE integratsiyasi — Bazel'ni iOS'da qabul qilishdagi asosiy to'siq. rules_xcodeproj aynan shu muammoni hal qiladi: Bazel target'laridan haqiqiy .xcodeproj generatsiya qiladi (bazel run //:xcodeproj).
  • Model — "Build with Bazel" (BwB): Xcode muharrir, Bazel esa haqiqiy build'ni bajaradi (BazelDependencies target orqali). 4.1.0 da bu — yagona rejim (build_mode atributi yo'q).
  • Biz tekshirdik (Xcode 27.1 + rules_xcodeproj 4.1.0): loyiha generatsiya qilindi (181 s birinchi marta, keyin ~9 s), xcodebuild bilan BUILD SUCCEEDED, incremental build 1.99 s. README jadvali eskirgan bo'lsa ham, 4.1.0 bu yerda ishladi.
  • Ishlaydi: indexing, debugging (LLDB), test navigator, inline xatolar. Ishlamaydi/cheklangan: SwiftUI Preview (ENABLE_DEBUG_DYLIB = NO, #3201 ochiq), Archive (.ipani Bazel quradi), coverage cache'ni buzadi.
  • Katta repo uchun: focused_targets/unfocused_targets bilan Xcode'ni yengillashtiring; CI cache'ni --generator_output_groups=all_targets bilan warm qiling.
  • Alternativa: VS Code/Cursor/Claude — sourcekit-lsp + sourcekit-bazel-bsp (Spotify) orqali; indexing hamma joyda, build/debug Cursor/VSCode'da; lekin WMO'siz va sekinroq birinchi indexing bilan.
  • Keyingi dars: Bazel 10: Cache — action key, disk cache, remote cache, Build without the Bytes va cache-miss detektivligi — Xcode ichidagi build ham, CI ham bir xil cache'ni bo'lishishi sirini ochamiz.

Manbalar {#manbalar}

  1. rules_xcodeproj — GitHub (Mobile Native Foundation) — README, xususiyatlar ro'yxati, foydalanuvchi loyihalar.
  2. rules_xcodeproj — Usage docs — config oilasi, command-line API, output base.
  3. rules_xcodeproj — Bazel rules reference — xcodeproj, top_level_target, xcode_provisioning_profile atributlari.
  4. rules_xcodeproj — FAQ — Archive, visibility, stale fayllar, signing.
  5. rules_xcodeproj — Design goals — BwB/BwX falsafasi, SwiftUI Preview non-goal.
  6. rules_xcodeproj — CHANGELOG — 4.0.0/4.1.0 o'zgarishlar, legacy generator olib tashlanishi.
  7. Issue #3201 — Xcode SwiftUI Preview Support — Preview bo'shlig'i (ochiq).
  8. Asana — Scaling a mature iOS codebase with Tuist — nega Bazel'dan voz kechildi (breakpoint/DX), 2023-02-21.
  9. spotify/sourcekit-bazel-bsp — VS Code/Cursor/Claude + sourcekit-lsp + Bazel (Apache-2.0).
  10. sourcekit-lsp — issue #2328 — "Loading the standard library failed" (BSP bilan).
  11. rules_ios — discussion #952 — Square'ning rules_ios'dan chiqishi (2025-11-07).
  12. BuildBuddy — How to migrate an iOS app to Bazel — Brentley Jones, 2024; Archive/cache warming caveat'lari.

Markdown manba

iOS Lessons