Harici bağımlılıkları Bzlmod ile yönetin

Sorun bildirme Kaynağı görüntüleme Nightly · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Bzlmod, Bazel 5.0'da kullanıma sunulan yeni harici bağımlılık sisteminin kod adıdır. Eski sistemde aşamalı olarak düzeltilmesi mümkün olmayan çeşitli sorunları gidermek için kullanıma sunulmuştur. Daha fazla bilgi için orijinal tasarım dokümanındaki Sorun Beyanıyla ilgili bölüme bakın.

Bazel 5.0'da Bzlmod varsayılan olarak etkin değildir. Aşağıdakilerin geçerli olması için --experimental_enable_bzlmod işaretinin belirtilmesi gerekir. İşaretin adından da anlaşılacağı gibi bu özellik şu anda deneyseldir; özellik resmi olarak kullanıma sunuluncaya kadar API'ler ve davranışlar değişebilir.

Projenizi Bzlmod'a taşımak için Bzlmod Taşıma Kılavuzu'nu uygulayın. Örnek Bzlmod kullanımlarını examples deposunda da bulabilirsiniz.

Bazel Modülleri

Eski WORKSPACE tabanlı harici bağımlılık sistemi, depo kuralları (veya repo kuralları) aracılığıyla oluşturulan depolar (veya repo'lar) etrafında odaklanır. Yeni sistemde de önemli bir kavram olan depoların aksine modüller, bağımlılıkların temel birimleridir.

Modül, temelde birden fazla sürümü olabilecek bir Bazel projesidir. Her sürüm, bağlı olduğu diğer modüller hakkında meta veriler yayınlar. Bu, diğer bağımlılık yönetimi sistemlerindeki bilinen kavramlara benzer: Maven öğesi, npm paketi, Cargo kutusu, Go modülü vb.

Bir modül, WORKSPACE içindeki belirli URL'ler yerine bağımlılıkları name ve version çiftlerini kullanarak belirtir. Ardından, bağımlılıklar bir Bazel sicil dairesinde (varsayılan olarak Bazel Merkez Sicil Dairesi) aranır. Ardından, çalışma alanınızda her modül bir depoya dönüştürülür.

MODULE.bazel

Her modülün her sürümünde, bağımlılıkları ve diğer meta verileri açıklayan bir MODULE.bazel dosyası bulunur. Temel bir örnek aşağıda verilmiştir:

module(
    name = "my-module",
    version = "1.0",
)

bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")

MODULE.bazel dosyası, çalışma alanı dizininin kökünde (WORKSPACE dosyasının yanında) bulunmalıdır. WORKSPACE dosyasından farklı olarak, geçişli bağımlılıklarınızı belirtmeniz gerekmez. Bunun yerine yalnızca doğrudan bağımlılıkları belirtmeniz gerekir. Bağımlılıklarınızın MODULE.bazel dosyaları, geçişli bağımlılıkları otomatik olarak keşfetmek için işlenir.

MODULE.bazel dosyası, herhangi bir kontrol akışı biçimini desteklemediği için BUILD dosyalarına benzer. Ayrıca load ifadelerini de yasaklar. MODULE.bazel dosyaları aşağıdaki yönergeleri destekler:

Sürüm biçimi

Bazel'in çeşitli bir ekosistemi vardır ve projelerde çeşitli sürümlendirme şemaları kullanılır. En popüler olan SemVer olsa da Abseil gibi farklı şemalar kullanan önemli projeler de vardır. Bu projelerde sürümler tarihe dayanır (ör. 20210324.2).

Bu nedenle Bzlmod, SemVer spesifikasyonunun daha esnek bir sürümünü kullanır. Farklılıklar şunlardır:

  • SemVer, sürümün "sürüm" kısmının 3 segmentten oluşması gerektiğini belirtir: MAJOR.MINOR.PATCH. Bazel'de bu şart, herhangi bir sayıda segmente izin verecek şekilde gevşetilmiştir.
  • SemVer'de "sürüm" bölümündeki her segment yalnızca rakam içermelidir. Bazel'de bu, harflere de izin verecek şekilde gevşetilir ve karşılaştırma semantikleri, "ön sürüm" bölümündeki "tanımlayıcılar" ile eşleşir.
  • Ayrıca, ana, alt ve yama sürümü artışlarının semantikleri zorunlu kılınmaz. (Ancak geriye dönük uyumluluğu nasıl belirttiğimizle ilgili ayrıntılar için uyumluluk düzeyine bakın.)

Geçerli tüm SemVer sürümleri geçerli Bazel modülü sürümüdür. Ayrıca, iki SemVer sürümü a ve b, Bazel modülü sürümleri olarak karşılaştırıldığında aynı sonucu verirse a < b ile karşılaştırılır.

Sürüm çözünürlüğü

Elmas bağımlılığı sorunu, sürümlü bağımlılık yönetimi alanında temel bir sorundur. Aşağıdaki bağımlılığı grafiğiniz olduğunu varsayalım:

       A 1.0
      /     \
   B 1.0    C 1.1
     |        |
   D 1.0    D 1.1

D'nin hangi sürümü kullanılmalıdır? Bzlmod, bu sorunu çözmek için Go modül sisteminde tanıtılan Minimum Sürüm Seçimi (MVS) algoritmasını kullanır. MVS, bir modülün tüm yeni sürümlerinin geriye dönük uyumlu olduğunu varsayar ve bu nedenle, herhangi bir bağımlı tarafından belirtilen en yüksek sürümü (örneğimizde D 1.1) seçer. Buradaki D 1.1, şartlarımızı karşılayabilecek minimum sürüm olduğu için "minimum" olarak adlandırılır. D 1.2 veya daha yeni sürümler olsa bile bunları seçmeyiz. Bu, sürüm seçiminin yüksek kaliteli ve tekrarlanabilir olması avantajını da beraberinde getirir.

Sürüm çözümü, kayıt defteri tarafından değil, makinenizde yerel olarak gerçekleştirilir.

Uyumluluk seviyesi

MVS'nin geriye dönük uyumlulukla ilgili varsayımını, bir modülün geriye dönük uyumlu olmayan sürümlerini ayrı bir modül olarak ele aldığı için uygulanabilir olduğunu unutmayın. SemVer açısından bu, A 1.x ve A 2.x'in farklı modüller olarak kabul edildiği ve çözülmüş bağımlılık grafiğinde birlikte bulunabileceği anlamına gelir. Bu da büyük sürümün Go'da paket yolunda kodlanmış olması sayesinde mümkün olur. Böylece derleme veya bağlantı sırasında herhangi bir çakışma yaşanmaz.

Bazel\'de böyle garantilerimiz yok. Bu nedenle, geriye dönük uyumlu olmayan sürümleri tespit etmek için "büyük sürüm" numarasını belirtmemiz gerekir. Bu sayıya uyumluluk düzeyi denir ve her modül sürümü, module() yönergesinde bu sayıyı belirtir. Bu bilgilerle, çözülmüş bağımlılık grafiğinde aynı modülün farklı uyumluluk düzeylerine sahip sürümlerinin bulunduğunu tespit ettiğimizde hata mesajı gösterebiliriz.

Depo adları

Bazel'de her harici bağımlılık için bir depo adı bulunur. Bazen aynı bağımlılık farklı depo adları aracılığıyla kullanılabilir (örneğin, hem @io_bazel_skylib hem de @bazel_skylib Bazel skylib anlamına gelir) veya aynı depo adı farklı projelerdeki farklı bağımlılıklar için kullanılabilir.

Bzlmod'da, Bazel modülleri ve modül uzantıları ile depo oluşturulabilir. Depo adı çakışmalarını çözmek için yeni sistemde depo eşleme mekanizmasını kullanıyoruz. Burada iki önemli kavram söz konusudur:

  • Kurallı depo adı: Her deponun küresel olarak benzersiz depo adıdır. Bu, deposunun bulunduğu dizin adıdır.
    Şu şekilde oluşturulur (Uyarı: Standart ad biçimi, güvenebileceğiniz bir API değildir ve herhangi bir zamanda değiştirilebilir):

    • Bazel modülü depoları için: module_name~version
      (Örnek. @bazel_skylib~1.0.3)
    • Modül uzantısı depoları için: module_name~version~extension_name~repo_name
      (Örnek. @rules_cc~0.0.1~cc_configure~local_config_cc)
  • Görünen depo adı: Bir depodaki BUILD ve .bzl dosyalarında kullanılacak depo adı. Aynı bağımlılık, farklı depolarda farklı görünen adlara sahip olabilir.
    Aşağıdaki şekilde belirlenir:

    • Bazel modül depoları için: Varsayılan olarak module_name veya bazel_dep içindeki repo_name özelliği tarafından belirtilen ad.
    • Modül uzantısı depoları için: use_repo aracılığıyla tanıtılan depo adı.

Her deponun, doğrudan bağımlılıklarının depo eşleme sözlüğü vardır. Bu sözlük, görünen depo adından kanonik depo adına bir eşlemedir. Etiket oluştururken depo adını çözmek için depo eşlemesini kullanırız. Resmi depo adlarında çakışma olmadığını ve görünen depo adlarının kullanımlarının MODULE.bazeldosyası ayrıştırılarak bulunabileceğini unutmayın. Bu nedenle, diğer bağımlılıkları etkilemeden çakışmalar kolayca yakalanıp çözülebilir.

Katı bağımlılık

Yeni bağımlılık spesifikasyonu biçimi, daha sıkı kontroller gerçekleştirmemize olanak tanır. Özellikle, bir modülün yalnızca doğrudan bağımlılıklarından oluşturulan depoları kullanabileceğini zorunlu kılıyoruz. Bu, geçişli bağımlılık grafiğinde bir şey değiştiğinde yanlışlıkla yapılan ve hata ayıklamanın zor olduğu kesintileri önlemeye yardımcı olur.

Katı bağımlılık, depo eşlemeye göre uygulanır. Temel olarak, her deponun depo eşlemesi, doğrudan bağımlılıklarının tümünü içerir. Diğer depolar görünmez. Her deponun görünür bağımlılıkları aşağıdaki şekilde belirlenir:

  • Bazel modül deposu, bazel_dep ve use_repo aracılığıyla MODULE.bazel dosyasında tanıtılan tüm depoları görebilir.
  • Modül uzantısı deposu, uzantıyı sağlayan modülün tüm görünür bağımlılıkları ve aynı modül uzantısı tarafından oluşturulan diğer tüm depoları görebilir.

Kayıtlar

Bzlmod, Bazel kayıtlarından bilgi isteyerek bağımlılıklarını keşfeder. Bazel kayıt defteri, Bazel modüllerinin veritabanıdır. Yalnızca dizin kayıt defteri desteklenir. Bu, yerel bir dizin veya belirli bir biçimi izleyen statik bir HTTP sunucusudur. Gelecekte, bir projenin kaynağını ve geçmişini içeren git depoları olan tek modüllü kayıt defteri desteğini eklemeyi planlıyoruz.

Dizin kayıt otoritesi

Dizin kayıt otoritesi, ana sayfaları, bakım verenleri, her bir sürümün MODULE.bazel dosyası ve her bir sürümün kaynağının nasıl alınacağı da dahil olmak üzere bir modül listesi hakkında bilgi içeren yerel bir dizin veya statik bir HTTP sunucusudur. Kaynak arşivlerin kendisini sunması gerekmez.

Dizin sicil dairesi aşağıdaki biçime uygun olmalıdır:

  • /bazel_registry.json: Aşağıdaki gibi kayıt otoritesinin meta verilerini içeren bir JSON dosyası:
    • mirrors, kaynak arşivler için kullanılacak aynaların listesini belirtir.
    • module_base_path, source.json dosyasında local_repository türüne sahip modüllerin temel yolunu belirtir.
  • /modules: Bu kayıt defterindeki her modül için bir alt dizin içeren bir dizin.
  • /modules/$MODULE: Bu modülün her sürümü için bir alt dizin ve aşağıdaki dosyayı içeren bir dizin:
    • metadata.json: Modül hakkında bilgi içeren bir JSON dosyası. Aşağıdaki alanlara sahiptir:
      • homepage: Projenin ana sayfasının URL'si.
      • maintainers: Her biri kayıt defterindeki modülün bir koruyucusunun bilgilerine karşılık gelen JSON nesnelerinin listesi. Bu kullanıcıların, projenin yazarlarıyla aynı olmayabileceğini unutmayın.
      • versions: Bu modülün bu kayıt defterinde bulunan tüm sürümlerinin listesi.
      • yanked_versions: Bu modülün geri çekilen sürümlerinin listesi. Bu işlem şu anda hiçbir işlem yapmaz ancak gelecekte, yayından kaldırılan sürümler atlanır veya hata verir.
  • /modules/$MODULE/$VERSION: Aşağıdaki dosyaları içeren bir dizin:
    • MODULE.bazel: Bu modül sürümünün MODULE.bazel dosyası.
    • source.json: Bu modül sürümünün kaynağının nasıl alınacağıyla ilgili bilgileri içeren bir JSON dosyası.
      • Varsayılan tür, aşağıdaki alanlara sahip "arşiv" türüdür:
        • url: Kaynak arşivin URL'si.
        • integrity: Arşivin alt öğe bütünlüğü sağlama toplamı.
        • strip_prefix: Kaynak arşiv ayıklanırken kaldırılacak bir dizin ön eki.
        • patches: Her biri, ayıklanan arşive uygulanacak bir yamalı dosya adını içeren dize listesi. Yama dosyaları /modules/$MODULE/$VERSION/patches dizininde bulunur.
        • patch_strip: Unix yamasının --strip bağımsız değişkeniyle aynıdır.
      • Tür, aşağıdaki alanlarla yerel bir yol kullanacak şekilde değiştirilebilir:
        • type: local_path
        • path: Deponun yerel yolu. Aşağıdaki şekilde hesaplanır:
          • Yol mutlak bir yolsa olduğu gibi kullanılır.
          • yol göreli bir yolsa ve module_base_path mutlak bir yolsa yol, <module_base_path>/<path> olarak çözülür
          • Hem yol hem de module_base_path göreli yolsa yol, <registry_path>/<module_base_path>/<path> olarak çözülür. Kayıt otoritesi yerel olarak barındırılmalı ve --registry=file://<registry_path> tarafından kullanılmalıdır. Aksi takdirde Bazel bir hata oluşturur.
    • patches/: Yalnızca source.json "arşiv" türüne sahip olduğunda kullanılan, yama dosyalarını içeren isteğe bağlı bir dizin.

Bazel Merkezi Kaydı

Bazel Merkezi Kaydı (BCR), bcr.bazel.build adresinde bulunan bir dizin kaydıdır. İçeriği, GitHub deposu bazelbuild/bazel-central-registry tarafından desteklenir.

BCR, Bazel topluluğu tarafından yönetilir. Katkıda bulunanların çekme isteğinde bulunmasına izin verilir. Bazel Merkezi Sicil Dairesi Politikaları ve Prosedürleri'ne bakın.

BCR, normal bir dizin kayıt defteri biçimini izlemenin yanı sıra her modül sürümü için bir presubmit.yml dosyası (/modules/$MODULE/$VERSION/presubmit.yml) gerektirir. Bu dosya, bu modül sürümünün geçerliliğini doğrulamak için kullanılabilecek birkaç temel derleme ve test hedefini belirtir ve BCR'deki modüller arasında birlikte çalışabilirliği sağlamak için BCR'nin CI ardışık düzenleri tarafından kullanılır.

Kayıt otoritelerini seçme

Tekrarlanabilir Bazel işareti --registry, modül isteğinde bulunulacak kayıt defteri listesini belirtmek için kullanılabilir. Böylece projenizi, bağımlılıklarını üçüncü taraf veya dahili bir kayıt defterinden alacak şekilde ayarlayabilirsiniz. Önceki kayıtlar önceliklidir. Kolaylık sağlamak için projenizin .bazelrc dosyasına --registry işaretlerinin listesini ekleyebilirsiniz.

Modül Uzantıları

Modül uzantıları, bağımlılık grafiğindeki modüllerden giriş verilerini okuyarak, bağımlılıkları çözmek için gerekli mantığı uygulayarak ve son olarak depo kurallarını çağırarak depo oluşturarak modül sistemini genişletmenize olanak tanır. İşlev olarak günümüzün WORKSPACE makrolarına benzerler ancak modüller ve geçişli bağımlılık dünyasında daha uygundurlar.

Modül uzantıları, repo kuralları veya WORKSPACE makroları gibi .bzl dosyalarında tanımlanır. Doğrudan çağrılmazlar. Bunun yerine, her modül uzantıların okuyabileceği etiket adı verilen veri parçalarını belirtebilir. Ardından, modül sürümü çözümü tamamlandıktan sonra modül uzantıları çalıştırılır. Her uzantı, modül çözümlemesinden sonra bir kez çalıştırılır (ancak herhangi bir derleme gerçekleşmeden önce) ve bağımlılık grafiğinin tamamında kendisine ait tüm etiketleri okuyabilir.

          [ A 1.1                ]
          [   * maven.dep(X 2.1) ]
          [   * maven.pom(...)   ]
              /              \
   bazel_dep /                \ bazel_dep
            /                  \
[ B 1.2                ]     [ C 1.0                ]
[   * maven.dep(X 1.2) ]     [   * maven.dep(X 2.1) ]
[   * maven.dep(Y 1.3) ]     [   * cargo.dep(P 1.1) ]
            \                  /
   bazel_dep \                / bazel_dep
              \              /
          [ D 1.4                ]
          [   * maven.dep(Z 1.4) ]
          [   * cargo.dep(Q 1.1) ]

Yukarıdaki örnek bağımlılık grafiğinde A 1.1 ve B 1.2 vb. Bazel modülleridir. Bunları her birini bir MODULE.bazel dosyası olarak düşünebilirsiniz. Her modül, modül uzantıları için bazı etiketler belirtebilir. Burada bazı etiketler "maven" uzantısı için, bazıları ise "cargo" uzantısı için belirtilmiştir. Bu bağımlılık grafiği tamamlandığında ("maven" uzantıları çalıştırıldığında) B 1.2'ün D 1.3'de bazel_dep olduğu ancak C nedeniyle D 1.4'a yükseltildiği varsayılır. Bu durumda, tüm maven.* etiketleri okunur ve hangi depoların oluşturulacağına karar vermek için bu etiketlerdeki bilgiler kullanılır. "kargo" uzantısı için de benzer bir durum söz konusudur.

Uzantı kullanımı

Uzantıların barındırıldığı yer Bazel modülleridir. Bu nedenle, modülünüzde bir uzantı kullanmak için önce söz konusu modüle bir bazel_dep eklemeniz, ardından kapsama almak için use_extension yerleşik işlevini çağırmanız gerekir. Aşağıdaki örneği inceleyin. Bu örnek, rules_jvm_external modülünde tanımlanan varsayımsal bir "maven" uzantısını kullanmak için bir MODULE.bazel dosyasından alınmış bir snippet'tir:

bazel_dep(name = "rules_jvm_external", version = "1.0")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")

Uzantıyı kapsama aldıktan sonra, etiketlerini belirtmek için nokta söz dizimini kullanabilirsiniz. Etiketlerin, ilgili etiket sınıfları tarafından tanımlanan şemaya uyması gerektiğini unutmayın (aşağıdaki uzantı tanımına bakın). Aşağıda, bazı maven.dep ve maven.pom etiketlerini belirten bir örnek verilmiştir.

maven.dep(coord="org.junit:junit:3.0")
maven.dep(coord="com.google.guava:guava:1.2")
maven.pom(pom_xml="//:pom.xml")

Uzantı, modülünüzde kullanmak istediğiniz depoları oluşturuyorsa bunları beyan etmek için use_repo yönergesini kullanın. Bu, katı bağımlılık koşulunu karşılamak ve yerel depo adı çakışmasını önlemek içindir.

use_repo(
    maven,
    "org_junit_junit",
    guava="com_google_guava_guava",
)

Bir uzantı tarafından oluşturulan depolar, API'sinin bir parçasıdır. Bu nedenle, belirttiğiniz etiketlere göre "maven" uzantısının "org_junit_junit" ve "com_google_guava_guava" adlı iki depo oluşturacağını bilmeniz gerekir. use_repo ile bunları isteğe bağlı olarak modülünüzün kapsamında yeniden adlandırabilirsiniz (burada "guava" olarak).

Uzantı tanımı

Modül uzantıları, module_extension işlevi kullanılarak depo kurallarına benzer şekilde tanımlanır. Her ikisinin de bir uygulama işlevi vardır. Ancak depo kurallarının bir dizi özelliği varken modül uzantılarının her biri bir dizi tag_class içerir. Etiket sınıfları, bu uzantı tarafından kullanılan etiketler için şemaları tanımlar. Yukarıdaki varsayımsal "maven" uzantısı örneğimize devam edelim:

# @rules_jvm_external//:extensions.bzl
maven_dep = tag_class(attrs = {"coord": attr.string()})
maven_pom = tag_class(attrs = {"pom_xml": attr.label()})
maven = module_extension(
    implementation=_maven_impl,
    tag_classes={"dep": maven_dep, "pom": maven_pom},
)

Bu beyanlar, yukarıda tanımlanan özellik şeması kullanılarak maven.dep ve maven.pom etiketlerinin belirtilebileceğini açıkça belirtir.

Uygulama işlevi, bir WORKSPACE makrosuna benzer. Tek farkı, bağımlılığı grafiğine ve ilgili tüm etiketlere erişim izni veren bir module_ctx nesnesi almasıdır. Ardından uygulama işlevi, depo oluşturmak için depo kurallarını çağırmalıdır:

# @rules_jvm_external//:extensions.bzl
load("//:repo_rules.bzl", "maven_single_jar")
def _maven_impl(ctx):
  coords = []
  for mod in ctx.modules:
    coords += [dep.coord for dep in mod.tags.dep]
  output = ctx.execute(["coursier", "resolve", coords])  # hypothetical call
  repo_attrs = process_coursier(output)
  [maven_single_jar(**attrs) for attrs in repo_attrs]

Yukarıdaki örnekte, bağımlılık grafiğindeki (ctx.modules) tüm modülleri inceleriz. Bu modüllerin her biri, tags alanı modüldeki tüm maven.* etiketlerini gösteren bir bazel_module nesnesi olur. Ardından, Maven ile iletişime geçip çözümü gerçekleştirmek için CLI yardımcı programı Coursier'ı çağırırız. Son olarak, çözüm sonucunu kullanarak varsayımsal maven_single_jar depo kuralını kullanarak bir dizi depo oluştururuz.