Bzlmod ile harici bağımlılıkları yönetme

Bzlmod, Bazel 5.0'da kullanıma sunulan yeni harici bağımlılık sisteminin kod adıdır. Eski sistemin, aşamalı olarak giderilemeyen birçok sorunu çözmek için sunulmuştur. Daha fazla ayrıntı için orijinal tasarım dokümanının Sorun Beyanı bölümüne bakın.

Bazel 5.0'da Bzlmod varsayılan olarak açık değildir; aşağıdakinin etkili olabilmesi için --experimental_enable_bzlmod işaretinin belirtilmesi gerekir. Bayrak adının de belirttiği gibi, bu özellik şu anda deneme aşamasındadır; özellik resmi olarak kullanıma sunulana kadar API'ler ve davranışlar değişebilir.

Bazel Modülleri

EskiWORKSPACE tabanlı harici bağımlılık sistemidepolar (veyakod depoları ), depo kuralları (veyadepo kuralları ). Depolar yeni sistemde hâlâ önemli bir kavram olsa da modüller, temel bağımlılık birimleridir.

Modül temel olarak birden fazla sürümü olabilecek bir Bazel projesidir. Bu sürümlerin her biri, bağımlı olduğu diğer modüllerle ilgili meta verileri yayınlar. Bu, diğer bağımlılık yönetimi sistemlerindeki aşina olunan kavramlara benzer: Maven yapısı, npm paketi, Kargo sandığı, bir Go modülü vb.

Bir modül yalnızca, WORKSPACE içindeki belirli URL'ler yerine name ve version çiftlerini kullanarak bağımlılıklarını belirtir. Ardından, bağımlılıklar bir Bazel kayıt otoritesinde aranır; Bazel Central Registry'dir. Çalışma alanınızda her modül bir depoya dönüştürülür.

MODÜLÜ.bazel

Her modülün her sürümü, bağımlılıklarını ve diğer meta verileri bildiren bir MODULE.bazel dosyası içerir. Aşağıda basit bir örnek 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 dizininde (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 MODULE.bazel dosyaları, geçişli bağımlılıkları otomatik olarak keşfetmek için işleme alınır.

MODULE.bazel dosyası herhangi bir kontrol akışını desteklemediğinden BUILD dosyalarına benzer; ayrıca load ifadelerini de yasaklar. MODULE.bazel dosyalarının desteklediği yönergeler şunlardır:

Sürüm biçimi

Bazel'in ekosistem çeşitliliği ve projeleri, çeşitli sürüm şemalarını kullanmaktadır. Şu ana kadar en popüler olan SemVer'dir, ancak sürümleri farklı olan Abseil gibi farklı şemalar kullanan önemli projeler de vardır tarih tabanlıdır, örneğin 20210324.2).

Bu nedenle, Bzlmod SemVer spesifikasyonunun daha rahat bir sürümünü kullanır; özellikle de sürümün "yayınlama" bölümünde herhangi bir sayıda rakam dizisine izin verir (SemVer'in belirttiği şekilde tam olarak 3 yerine: MAJOR.MINOR.PATCH). Ayrıca, büyük, küçük ve yama sürümü artış anlamları zorunlu kılınmaz. (Bununla birlikte, geriye dönük uyumluluğu nasıl gösterdiğimize ilişkin ayrıntılar için uyumluluk düzeyi konusuna bakın.) SemVer spesifikasyonunun bir ön sürüm sürümünü gösteren bir kısa çizgi gibi diğer bölümleri değiştirilmez.

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

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

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

Hangi D sürümü kullanılmalıdır? Bzlmod, bu soruyu çözmek için Go modül sisteminde sunulan Minimal Sürüm Seçimi (MVS) algoritmasını kullanmaktadır. MVS bir modülün tüm yeni sürümlerinin geriye dönük olarak uyumlu olduğunu varsayar ve bu nedenle herhangi bir bağımlı tarafından belirtilen en yüksek sürümü seçer (örneğimizde D 1.1). Burada D 1.1, gereksinimlerimizi karşılayan minimum sürüm olduğundan, "minimal" olarak adlandırılır; D 1.2 veya daha yeni bir sürüm mevcut olsa bile biz bu seçimi yapmıyoruz. Bunun ek avantajı, sürüm seçiminin yüksek doğruluk ve yeniden oluşturulabilir olmasıdır.

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

Uyumluluk düzeyi

Bir modülün geriye dönük uyumsuz sürümlerini ayrı bir modül olarak ele aldığı için MVS'nin geriye dönük uyumluluk hakkındaki varsayımının uygulanabilir olduğunu unutmayın. SemVer açısından bu, A 1.x ve A 2.x'in ayrı modüller olarak kabul edildiği ve çözülmüş bağımlılık grafiğinde bir arada olabileceği anlamına gelir. Bu da ana sürümün, Go'da paket yolunda kodlanmasıyla mümkün kılındığı için derleme süresi veya bağlantı zamanı çakışmaları yoktur.

Bazel'de bu konuda bir garantimiz yok. Bu nedenle, geriye dönük uyumsuz sürümleri tespit etmek için "ana sürüm" numarasını belirtmek için bir yönteme ihtiyacımız vardır. Bu sayıya uyumluluk düzeyi denir ve her bir modül sürümü tarafından module() yönergesinde belirtilir. Bu bilgiler ışığında, aynı modülün farklı bağımlılık düzeylerine sahip sürümlerinin çözümlenmiş bağımlılık grafiğinde bulunduğunu tespit ettiğimizde hata verebiliriz.

Depo adları

Bazel'de her harici bağımlılığın bir depo adı vardır. Bazen aynı bağımlılık farklı veri havuzu adları (örneğin, @io_bazel_skylib ve @bazel_skylib Bazel skylib) veya aynı ad aracılığıyla kullanılabilir depo adı, farklı projelerde farklı bağımlılıklar için kullanılabilir.

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

  • Standart kod deposu adı: Her bir kod deposu için küresel olarak benzersiz kod deposu adı. Bu, kod deposunun bulunduğu dizin adı olacaktır.
    Şu şekilde oluşturulur (Uyarı: Standart ad biçimi, bağımlı olmanız gereken bir API değildir, 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)
  • Yerel depo adı: Depodaki BUILD ve .bzl dosyalarında kullanılacak depo adı. Aynı bağımlılık, farklı depolar için farklı yerel 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 sağlanan depo adı.

Her depo, doğrudan bağımlılıklarının yer aldığı bir depo eşleme sözlüğüne sahiptir. Bu sözlük, yerel kod deposu adından standart depo adıyla bir haritadır. Kod deposu eşlemesini, etiket oluştururken depo adını çözmek için kullanırız. Standart depo adlarında çakışma olmadığını ve yerel kod deposu adlarının kullanımlarının MODULE.bazel dosyasını ayrıştırarak keşfedilebileceğini unutmayın. Bu nedenle çakışmalar, diğer bağımlılıklar.

Katı değerler

Yeni bağımlılık spesifikasyonu biçimi, daha katı kontroller gerçekleştirmemizi sağlar. Özellikle, bir modülün yalnızca doğrudan bağımlılarından oluşturulan depoları kullanmasını zorunlu kılıyoruz. Bu, geçişli bağımlılık grafiğindeki bir şey değiştiğinde yanlışlıkla yapılan ve hata ayıklaması zor kesintileri önlemeye yardımcı olur.

Katı sınırlar, havuz eşlemeye göre uygulanır. Temel olarak, her bir depo için depo eşlemesi tüm doğrudan bağımlılıklarını içerir, diğer herhangi bir depo görünmez. Her deponun görünür bağımlılıkları şu şekilde belirlenir:

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

Kayıtlar

Bzlmod, Bazel kayıt otoritelerinden bilgi isteyerek bağımlılıkları keşfediyor. Bazel kayıt defteri, sadece Bazel modüllerinin veritabanıdır. Desteklenen tek kayıt defteri biçimi, yerel bir dizin veya belirli bir biçimi izleyen statik bir HTTP sunucusu olan dizin kayıt defteridir. Gelecekte bu sürüme destek eklemeyi planlıyoruz.tek modüllü kayıt otoriteleri . Bu proje, bir projenin kaynağını ve geçmişini içeren basit depodur.

Dizin kaydı

Dizin kayıt defteri; ana sayfalar, korumacılar, her sürümün MODULE.bazel dosyası ve her bir kaynağın nasıl getirileceği dahil olmak üzere bir modül listesi hakkında bilgi içeren yerel bir dizin veya statik HTTP sunucusudur sürümü. Özellikle, kaynak arşivlerin kendisinin sunulması gerekmez.

Dizin kayıt defteri aşağıdaki biçime uymalıdır:

  • /bazel_registry.json: Kayıt defteri için meta verileri içeren bir JSON dosyası. Şu anda kaynak arşivler için kullanılacak aynaların listesini belirten tek bir anahtar (mirrors) vardır.
  • /modules: Bu kayıt otoritesindeki her modül için bir alt dizin içeren dizindir.
  • /modules/$MODULE: Bu modülün her bir sürümü için alt dizin içeren bir dizin ve aşağıdaki dosya:
    • metadata.json: Aşağıdaki alanlarla birlikte modülle ilgili bilgileri içeren bir JSON dosyasıdır:
      • homepage: Proje ana sayfasının URL'si.
      • maintainers: Her biri kayıt otoritesinde modülün koruyucusunun bilgilerine karşılık gelen JSON nesnelerinin listesi. Bunun, projenin yazarlarıyla aynı olmadığını unutmayın.
      • versions: Bu kayıt defterinde bulunan bu modülün tüm sürümlerinin listesidir.
      • yanked_versions: Bu modülün sağlanmış sürümlerinin listesi. Bu, şu anda işlemsizdir ancak gelecekte, yansıtı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ıdır.
    • source.json: Bu modülün kaynağının nasıl getirileceğine dair bilgileri içeren bir JSON dosyası ve aşağıdaki alanlar:
      • url: Kaynak arşivin URL'si.
      • integrity: Arşivin Alt Kaynak Bütünlüğü sağlaması.
      • strip_prefix: Kaynak arşivi çıkarılırken çıkarılacak bir dizin ön eki.
      • patches: Çıkarılan arşive uygulanacak her biri bir yama dosyasına verilen dizelerin listesidir. Yama dosyaları /modules/$MODULE/$VERSION/patches dizininin altında bulunur.
      • patch_strip: Unix yamasının --strip bağımsız değişkeniyle aynıdır.
    • patches/: Yama dosyaları içeren isteğe bağlı bir dizin.

Bazel Merkezi Kayıt Otoritesi

Bazel Merkezi Kayıt Defteri (BCR), registry.bazel.build dizininde yer alan bir dizin kayıt defteridir. İçeriği GitHub deposu bazelbuild/bazel-central-registry tarafından desteklenir.

BCR, Bazel topluluğu tarafından yönetilmektedir; katkıda bulunanların pull istekleri gönderebilirler. Bazel Merkezi Kayıt Defteri Politikaları ve Prosedürleri'ne göz atın.

Normal dizin kayıt defterinin biçiminin izlenmesine ek olarak, BCR her modül sürümü (/modules/$MODULE/$VERSION/presubmit.yml) için bir presubmit.yml dosyası gerektirir. Bu dosya, bu modül sürümünün geçerliliğini kontrol etmek için kullanılabilecek birkaç temel derleme ve test hedefi belirtir ve BCR'deki modüller arasında birlikte çalışabilirlik sağlamak için BCR'nin CI ardışık düzenleri tarafından kullanılır ,

Kayıt otoriteleri seçme

Tekrarlanabilir Bazel işareti --registry, modül istemek için kullanılan kayıt otoritelerinin listesini belirtmek amacıyla kullanılabilir. Böylece projenizi, üçüncü taraf veya dahili kayıt otoritesinden bağımlılığı getirecek şekilde ayarlayabilirsiniz. Önceki kayıt otoriteleri öncelik alır. Kolaylık olması 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 gelen giriş verilerini okuyarak, bağımlılıkları çözmek için gerekli mantığı gerçekleştirerek ve son olarak depo kuralları çağırarak depo oluşturarak modül sistemini genişletmenizi sağlar. Bunlar, günümüzün WORKSPACE makrolarına benzer işlevlere sahiptir, ancak modüller ve geçişli bağımlılıklar dünyasında daha uygundur.

Modül uzantıları, depo kuralları veya WORKSPACE makroları gibi .bzl dosyalarında tanımlanır. Doğrudan çağrılamaz; çünkü her modül, uzantıların okunması için etiket adı verilen veri parçalarını belirtebilir. Ardından, modül sürüm çözünürlüğü tamamlandıktan sonra modül uzantıları çalıştırılır. Her bir uzantı, modül çözünürlüğünden sonra (bir derleme gerçekleşmeden önce hala çalışır) çalışır ve ona ait tüm etiketleri tüm bağımlılık grafiğinde okur.

          [ 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. Her birini bir MODULE.bazel dosyası olarak düşünebilirsiniz. Her modül, modül uzantıları için bazı etiketler belirtebilir. bazıları "maven" uzantısı için, bazıları da "cargo" için belirtilmiştir. Bu bağımlılık grafiği kesinleştiğinde (örneğin, B 1.2 belki D 1.3 tarihinde bir bazel_dep içeriyor ancak C nedeniyle D 1.4 sürümüne geçirildi) "maven" uzantıları çalışır ve hangi depoların oluşturulacağına karar vermek için oradaki bilgileri kullanarak tüm maven.* etiketlerini okur. Benzer şekilde, "kargo" uzantısı için.

Uzantı kullanımı

Uzantılar, Bazel modüllerinin kendisinde barındırılır. Bu nedenle, modülünüzde bir uzantı kullanmak için önce bu modüle bir bazel_dep eklemeniz ve ardından, use_extension yerleşik işlevi vardır. Aşağıdaki örneği, rules_jvm_external modülünde tanımlanan varsayımsal bir "maven" uzantısını kullanmak için bir MODULE.bazel dosyasından bir snippet'i düşünün:

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

Uzantıyı kapsamın içine ekledikten sonra, uzantı için etiketler belirtmek üzere nokta söz dizimini kullanabilirsiniz. Etiketlerin, ilgili etiket sınıflarının tanımladığı şemaya uygun olması gerektiğini unutmayın (aşağıdaki uzantı tanımı bölümüne bakın). Aşağıda, bazı maven.dep ve maven.pom etiketlerinin belirtildiği 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ı bildirmek için use_repo yönergesini kullanın. Bu, katı deps 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 kod depoları, kendi API'sinin bir parçasıdır. Bu nedenle, belirttiğiniz etiketlerden, "maven" uzantısının "org_junit_junit" ve "com_google_guava_guava" adlı bir kod deposu oluşturacağını bilmeniz gerekir. ". use_repo kullanarak isteğe bağlı olarak bu modülleri modülünüzün kapsamında yeniden adlandırabilirsiniz (örneğin, burada "guava").

Uzantı tanımı

Modül uzantıları, depo kurallarına benzer şekilde module_extension işlevi kullanılarak tanımlanır. Her ikisinin de bir uygulama işlevi vardır; ancak depo kuralları bir dizi özelliğe sahip olsa da modül uzantılarının her biri bir dizi özelliğe sahip bir dizi tag_classes özelliği vardır. Etiket sınıfları, bu uzantı tarafından kullanılan etiketler için şemaları tanımlar. Yukarıdaki varsayımsal "maven" uzantı örneğimizi 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 bildirimler, maven.dep ve maven.pom etiketlerinin, yukarıda tanımlanan özellik şeması kullanılarak belirtilebileceğini açıkça belirtiyor.

Uygulama işlevi, bir WORKSPACE makrosuna benzer. Ancak bu makro, bağımlılık grafiğine ve ilgili tüm etiketlere erişim sağlayan bir module_ctx nesnesi alır. 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 tüm modülleri gözden geçiriyoruz (ctx.modules ), her biribazel_module nesnenin değeritags alanı, tümmaven.* etiketlerini görebilirsiniz. Ardından Malin ile iletişime geçip çözümü gerçekleştirmesi için CLI yardımcı programı Coursier'ı çağırırız. Son olarak, varsayımsal maven_single_jar kod deposu kuralını kullanarak bir dizi kod deposu oluşturmak için çözüm sonucunu kullanırız.