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:
module
: Mevcut modülle ilgili meta verileri (ör. adı, sürümü vb.) belirtmek için;- Diğer Bazel modüllerine doğrudan bağımlılıkları belirtmek için
bazel_dep
; - Belirli bir doğrudan veya geçişli bağımlılık öğesinin davranışını özelleştirmek için yalnızca kök modül tarafından kullanılabilen (yani bağımlılık olarak kullanılan bir modül tarafından kullanılamayan) geçersiz kılma işlemleri:
- 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ü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
)
- 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.
Aşağıdaki ş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 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.bazel
dosyası 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
veuse_repo
aracılığıylaMODULE.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ındalocal_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ünMODULE.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.
- Varsayılan tür, aşağıdaki alanlara sahip "arşiv" türüdür:
patches/
: Yalnızcasource.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.