Bzlmod, Bazel 5.0'da kullanıma sunulan yeni harici bağımlılık sisteminin kod adıdır. Bu sistem, eski sistemin kademeli olarak düzeltilmesi mümkün olmayan çeşitli sorunlarını gidermek için kullanıma sunulmuştur. Daha fazla bilgi için orijinal tasarım belgesinin Sorun Açıklaması bölümüne 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şareti belirtilmelidir. İşaret adından da anlaşılacağı gibi bu özellik şu anda deneyseldir. Özellik resmi olarak kullanıma sunulana kadar API'ler ve davranışlar değişebilir.
Projenizi Bzlmod'a taşımak için Bzlmod Taşıma Rehberi'ni inceleyin. Ö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 repolar) etrafında şekillenir.
Yeni sistemde depolar hâlâ önemli bir kavram olsa da modüller, bağımlılığın temel birimleridir.
Modül, temelde birden fazla sürümü olabilen bir Bazel projesidir. Bu sürümlerin her biri, bağlı olduğu diğer modüllerle ilgili meta veriler yayınlar. Bu, diğer bağımlılık yönetimi sistemlerindeki benzer kavramlara (ör. Maven artifact, npm package, Cargo crate, Go module) benzer.
Bir modül, WORKSPACE
içindeki belirli URL'ler yerine name
ve version
çiftlerini kullanarak bağımlılıklarını belirtir. Bağımlılıklar daha sonra Bazel kayıt defterinde (varsayılan olarak Bazel Merkezi Kayıt Defteri) aranır. Ç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ını ve diğer meta verilerini bildiren bir MODULE.bazel
dosyası bulunur. Temel bir örnek:
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ının aksine, 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 bulmak için işlenir.
MODULE.bazel
dosyası, herhangi bir kontrol akışını desteklemediği için BUILD
dosyalarına benzer. Ayrıca load
ifadelerini de yasaklar. Desteklenen yönergeler şunlardır:
MODULE.bazel
module
: Geçerli modülle ilgili meta verileri (ör. adı, sürümü) belirtmek için kullanılır.- Diğer Bazel modüllerine doğrudan bağımlılıkları belirtmek için
bazel_dep
; - Yalnızca kök modül tarafından (yani bağımlılık olarak kullanılan bir modül tarafından değil) belirli bir doğrudan veya geçişli bağımlılığın davranışını özelleştirmek için kullanılabilen geçersiz kılmalar:
- Modül uzantılarıyla ilgili yönergeler:
Sürüm biçimi
Bazel'in çeşitli bir ekosistemi vardır ve projelerde çeşitli sürüm oluşturma şemaları kullanılır. En popüler olanı SemVer olsa da Abseil gibi farklı şemalar kullanan önemli projeler de vardır. Abseil'in sürümleri tarih tabanlıdı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 "yayın" bölümünün 3 segmentten oluşması gerektiğini belirtir:
MAJOR.MINOR.PATCH
. Bazel'de bu şart gevşetilir ve herhangi bir sayıda segmente izin verilir. - SemVer'de "yayın" bölümündeki segmentlerin her biri yalnızca rakamlardan oluşmalıdır. Bazel'de bu kısıtlama, harflere de izin verecek şekilde gevşetilir ve karşılaştırma semantiği, "ön sürüm" bölümündeki "tanımlayıcılarla" eşleşir.
- Ayrıca, ana, alt ve yama sürümü artışlarının semantiği zorunlu tutulmaz. (Ancak geriye dönük uyumluluğu nasıl gösterdiğimizle ilgili ayrıntılar için uyumluluk düzeyine bakın.)
Geçerli herhangi bir SemVer sürümü, geçerli bir 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ıklarında aynı durum geçerliyse a < b
karşılaştırılır.
Sürüm çözünürlüğü
Elmas bağımlılığı sorunu, sürümlendirilmiş bağımlılık yönetimi alanında temel bir 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
D'nin hangi sürümü kullanılmalıdır? Bzlmod, bu sorunu çözmek için Go modül sisteminde kullanılan Minimal Version Selection (MVS) algoritmasını kullanır. MVS, bir modülün tüm yeni sürümlerinin geriye dönük olarak uyumlu olduğunu varsayar ve bu nedenle, bağımlı olan herhangi bir öğe (örneğimizde D 1.1) tarafından belirtilen en yüksek sürümü seçer. Buradaki D 1.1, şartlarımızı karşılayabilecek en küçük sürüm olduğundan bu sürüme "minimal" adı verilir. D 1.2 veya daha yeni sürümler olsa bile bunları seçmeyiz. Bu, sürüm seçiminin yüksek doğrulukta ve tekrar edilebilir olması gibi ek bir avantaj sağlar.
Sürüm çözümü, kayıt defteri tarafından değil, makinenizde yerel olarak gerçekleştirilir.
Uyumluluk düzeyi
MVS'nin geriye dönük uyumlulukla ilgili varsayımının, geriye dönük uyumlu olmayan modül sürümlerini ayrı bir modül olarak ele alması nedeniyle 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ümlenen bağımlılık grafiğinde birlikte bulunabileceği anlamına gelir. Bu da Go'daki paket yolunda ana sürümün kodlanması sayesinde mümkün olur. Böylece derleme zamanı veya bağlantı zamanı çakışmaları olmaz.
Bazel'de bu tür garantiler yoktur. Bu nedenle, geriye dönük olarak uyumsuz sürümleri tespit etmek için "ana sürüm" numarasını belirtmemiz gerekir. Bu sayıya uyumluluk düzeyi denir ve her modül sürümü tarafından module()
yönergesinde belirtilir. Bu bilgiler sayesinde, çözümlenen bağımlılık grafiğinde farklı uyumluluk düzeylerine sahip aynı modülün sürümlerinin 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ı depo adları aracılığıyla (ö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 depolar, Bazel modülleri ve modül uzantıları tarafından 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 depo için genel olarak benzersiz depo adı. Bu, deponun bulunduğu dizinin adı olacaktır.
Aşağıdaki şekilde oluşturulur (Uyarı: Standart ad biçimi, bağımlı olmanız gereken bir API değildir ve herhangi bir zamanda değişebilir):- 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
)
- Bazel modülü depoları için:
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.
Şu şekilde belirlenir:
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 giden bir haritadır.
Bir etiket oluştururken depo adını çözmek için depo eşlemesini kullanırız. Kanonik depo adları arasında çakışma olmadığını ve görünen depo adlarının kullanımının MODULE.bazel
dosyası ayrıştırılarak bulunabileceğini unutmayın. Bu nedenle, çakışmalar diğer bağımlılıkları etkilemeden kolayca tespit edilebilir ve çözülebilir.
Katı bağımlılıklar
Yeni bağımlılık belirtme biçimi, daha katı kontroller yapmamıza olanak tanır. Özellikle, artık bir modülün yalnızca doğrudan bağımlılıklarından oluşturulan depoları kullanmasını zorunlu kılıyoruz. Bu, geçişli bağımlılık grafiğinde bir şey değiştiğinde yanlışlıkla oluşan ve hata ayıklaması zor olan bozulmaları önlemeye yardımcı olur.
Strict deps, depo eşlemeye göre uygulanır. Temel olarak, her depo için depo eşlemesi, doğrudan bağımlılıklarının tümünü içerir. Diğer depolar görünmez. Her depo için görünür bağımlılıklar aşağıdaki gibi belirlenir:
- Bir Bazel modül deposu,
MODULE.bazel
dosyasında tanıtılan tüm depolarıbazel_dep
veuse_repo
aracılığıyla görebilir. - Bir modül uzantısı deposu, uzantıyı sağlayan modülün tüm görünür bağımlılıklarını ve aynı modül uzantısı tarafından oluşturulan diğer tüm depoları görebilir.
Kayıtlar
Bzlmod, Bazel kayıtlarından bilgilerini isteyerek bağımlılıkları keşfeder. Bazel kayıt defteri, Bazel modüllerinin bulunduğu bir veritabanıdır. Kayıtların desteklenen tek biçimi, belirli bir biçime uyan yerel bir dizin veya statik bir HTTP sunucusu olan dizin kaydıdır. Gelecekte, bir projenin kaynağını ve geçmişini içeren git depoları olan tek modüllü kayıtlar için destek eklemeyi planlıyoruz.
Dizin kaydı
Dizin kayıt otoritesi, bir modül listesiyle ilgili bilgileri (ana sayfası, bakımını yapanlar, her sürümün MODULE.bazel
dosyası ve her sürümün kaynağının nasıl alınacağı dahil) içeren yerel bir dizin veya statik bir HTTP sunucusudur. Özellikle, kaynak arşivlerin kendisini sunması gerekmez.
Dizin kaydı aşağıdaki biçimde olmalıdır:
/bazel_registry.json
: Kayıt defterinin meta verilerini içeren bir JSON dosyası (örneğin):mirrors
, kaynak arşivler için kullanılacak yansıtma listesini belirtir.module_base_path
,source.json
dosyasındalocal_repository
türündeki modüller için temel yolu 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ülle ilgili bilgileri içeren bir JSON dosyası. Aşağıdaki alanları içerir:homepage
: Projenin ana sayfasının URL'si.maintainers
: Her biri kayıt defterindeki modülün bakım sorumlusunun bilgilerine karşılık gelen bir JSON nesneleri listesi. Bunun, projenin yazarlarıyla aynı olması gerekmediğini unutmayın.versions
: Bu kayıt defterinde bulunacak bu modülün tüm sürümlerinin listesi.yanked_versions
: Bu modülün kaldırılmış sürümlerinin listesi. Bu özellik şu anda etkisizdir ancak gelecekte geri çekilen 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ünMODULE.bazel
dosyası.source.json
: Bu modül sürümünün kaynağının nasıl getirileceğiyle ilgili bilgileri içeren bir JSON dosyası.- Varsayılan tür, aşağıdaki alanları içeren "archive"dır:
url
: Kaynak arşivin URL'si.integrity
: Arşivin Alt Kaynak Bütünlüğü sağlama toplamı.strip_prefix
: Kaynak arşivi çıkarılırken kaldırılacak bir dizin öneki.patches
: Her biri çıkarılan arşive uygulanacak bir yama dosyasını adlandıran bir dizeler listesi. Yama dosyaları/modules/$MODULE/$VERSION/patches
dizininde bulunur.patch_strip
: Unix patch'in--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 gibi hesaplanır:- Yol mutlak bir yol ise 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ümlenir. - Hem yol hem de
module_base_path
göreli yolsa yol,<registry_path>/<module_base_path>/<path>
olarak çözümlenir. Kayıt defteri yerel olarak barındırılmalı ve--registry=file://<registry_path>
tarafından kullanılmalıdır. Aksi takdirde Bazel hata verir.
- Varsayılan tür, aşağıdaki alanları içeren "archive"dır:
patches/
: Yalnızcasource.json
"arşiv" türündeyken kullanılan, yama dosyalarını içeren isteğe bağlı bir dizin.
Bazel Central Registry
Bazel Central Registry (BCR), bcr.bazel.build adresinde bulunan bir dizin kayıt defteridir. İçeriği, GitHub deposu bazelbuild/bazel-central-registry
tarafından desteklenir.
BCR, Bazel topluluğu tarafından yönetilir. Katkıda bulunanlar, çekme istekleri gönderebilir. Bazel Merkezi Kayıt Politikaları ve Prosedürleri'ne bakın.
BCR, normal bir dizin kaydının biçimine uymanın 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 kontrol etmek için kullanılabilecek birkaç temel derleme ve test hedefi belirtir ve BCR'deki modüller arasında birlikte çalışabilirliği sağlamak için BCR'nin CI işlem hatları tarafından kullanılır.
Kayıtları seçme
Modül isteğinde bulunulacak kayıt listesini belirtmek için tekrarlanabilir Bazel işareti --registry
kullanılabilir. Böylece projenizi, bağımlılıkları üçüncü taraf veya dahili bir kayıt defterinden getirecek şekilde ayarlayabilirsiniz. Daha önceki kayıtlar önceliklidir. Kolaylık sağlaması için --registry
işaretlerinin listesini projenizin .bazelrc
dosyasına 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 depolar oluşturarak modül sistemini genişletmenize olanak tanır. Günümüzdeki WORKSPACE
makrolarıyla 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ılmazlar. Bunun yerine, her modül, uzantıların okuması için 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ümlendirmesinden sonra (ancak herhangi bir derleme gerçekleşmeden önce) bir kez çalıştırılır 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
gibi öğeler Bazel modülleridir. Bunların her birini MODULE.bazel
dosyası olarak düşünebilirsiniz. Her modül, modül uzantıları için bazı etiketler belirtebilir. Burada, "maven" uzantısı için bazı etiketler, "cargo" için ise bazı etiketler belirtilmiştir. Bu bağımlılık grafiği tamamlandığında (örneğin, B 1.2
aslında D 1.3
üzerinde bazel_dep
'ye sahip olabilir ancak C
nedeniyle D 1.4
'ye yükseltilmiştir) "maven" uzantıları çalıştırılır ve oluşturulacak depoları belirlemek için bu bilgilerden yararlanarak tüm maven.*
etiketlerini okur.
Aynı durum "cargo" uzantısı için de geçerlidir.
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 kapsam içine almak için yerleşik use_extension
işlevini çağırmanız gerekir. rules_jvm_external
modülünde tanımlanan varsayımsal bir "maven" uzantısını kullanmak için MODULE.bazel
dosyasından alınan aşağıdaki snippet'i inceleyin:
bazel_dep(name = "rules_jvm_external", version = "1.0")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Uzantıyı kapsam içine aldıktan sonra, nokta söz dizimini kullanarak bunun için etiket belirtebilirsiniz. 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
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ı 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, uzantının 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ı birer depo oluşturacağını anlamanız gerekir. use_repo
ile bunları isteğe bağlı olarak modülünüz kapsamında yeniden adlandırabilirsiniz. Örneğin, burada "guava" olarak adlandırılmıştır.
Uzantı tanımı
Modül uzantıları, module_extension
işlevi kullanılarak depo kurallarına benzer şekilde tanımlanır.
Her ikisinde de bir uygulama işlevi vardır. Ancak depo kurallarında bir dizi özellik bulunurken modül uzantılarında bir dizi tag_class
bulunur ve bunların her birinde bir dizi özellik vardır. Etiket sınıfları, bu uzantı tarafından kullanılan etiketlerin şemalarını 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 bildirimler, yukarıda tanımlanan özellik şeması kullanılarak maven.dep
ve maven.pom
etiketlerinin belirtilebileceğini açıkça gösterir.
Uygulama işlevi, WORKSPACE
makrosuna benzer ancak bağımlılık grafiğine ve ilgili tüm etiketlere erişim sağlayan bir module_ctx
nesnesi alır. Uygulama işlevi, depoları 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 inceliyoruz. Bunların her biri, tags
alanı modüldeki tüm maven.*
etiketlerini gösteren bir bazel_module
nesnesidir. Ardından, Maven ile iletişime geçmek ve çözümleme gerçekleştirmek için CLI yardımcı programı Coursier'i çağırırız. Son olarak, varsayımsal maven_single_jar
depo kuralını kullanarak bir dizi depo oluşturmak için çözümleme sonucunu kullanırız.