Modül uzantıları, kullanıcıların bağımlılık grafiğindeki modüllerden giriş verilerini okuyarak, bağımlılıkları çözmek için gerekli mantığı uygulayarak ve son olarak repo kurallarını çağırarak modül sistemini genişletmesine olanak tanır. Bu uzantılar, dosya G/Ç'si gerçekleştirme, ağ istekleri gönderme gibi işlemleri yapmalarını sağlayan, depo kurallarına benzer özelliklere sahiptir. Bu sistemler, Bazel modüllerinden oluşturulan bağımlılık grafiğine uymakla birlikte Bazel'in diğer paket yönetim sistemleriyle etkileşim kurmasına olanak tanır.
Modül uzantılarını, depo kuralları gibi .bzl
dosyalarında tanımlayabilirsiniz. Doğrudan çağrılmazlar. Bunun yerine, her modül, uzantıların okuması için etiket adı verilen veri parçalarını belirtir. Bazel, uzantıları değerlendirmeden önce modül çözümlemesi gerçekleştirir. Uzantı, bağımlılık grafiğinin tamamında kendisine ait olan tüm etiketleri okur.
Uzantı kullanımı
Uzantılar, Bazel modüllerinde barındırılır. Bir modülde uzantı kullanmak için önce uzantıyı barındıran modüle bir bazel_dep
ekleyin, ardından kapsam içine almak için yerleşik use_extension
işlevini çağırın. Aşağıdaki örneği ele alalım: rules_jvm_external
modülünde tanımlanan "maven" uzantısını kullanmak için MODULE.bazel
dosyasından bir snippet:
bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Bu, use_extension
işlevinin dönüş değerini bir değişkene bağlar. Böylece kullanıcı, uzantı için etiketleri belirtmek üzere nokta söz dizimini kullanabilir. Etiketler, uzantı tanımında belirtilen ilgili etiket sınıfları tarafından tanımlanan şemaya uymalıdır. Bazı maven.install
ve maven.artifact
etiketlerini belirten bir örnek için:
maven.install(artifacts = ["org.junit:junit:4.13.2"])
maven.artifact(group = "com.google.guava",
artifact = "guava",
version = "27.0-jre",
exclusions = ["com.google.j2objc:j2objc-annotations"])
Uzantı tarafından oluşturulan depoları mevcut modülün kapsamına dahil etmek için use_repo
yönergesini kullanın.
use_repo(maven, "maven")
Bir uzantı tarafından oluşturulan depolar, uzantının API'sinin bir parçasıdır. Bu örnekte, "maven" modül uzantısı maven
adlı bir depo oluşturmayı vaat ediyor. Yukarıdaki bildirimle, uzantı @maven//:org_junit_junit
gibi etiketleri "maven" uzantısı tarafından oluşturulan depoya yönlendirecek şekilde doğru şekilde çözümler.
Uzantı tanımı
Modül uzantılarını depo kurallarına benzer şekilde, module_extension
işlevini kullanarak tanımlayabilirsiniz. Ancak depo kuralları çeşitli özelliklere sahipken modül uzantılarında her biri çeşitli özelliklere sahip tag_class
bulunur. Etiket sınıfları, bu uzantı tarafından kullanılan etiketlerin şemalarını tanımlar. Örneğin, yukarıdaki "maven" uzantısı şu şekilde tanımlanabilir:
# @rules_jvm_external//:extensions.bzl
_install = tag_class(attrs = {"artifacts": attr.string_list(), ...})
_artifact = tag_class(attrs = {"group": attr.string(), "artifact": attr.string(), ...})
maven = module_extension(
implementation = _maven_impl,
tag_classes = {"install": _install, "artifact": _artifact},
)
Bu bildirimler, maven.install
ve maven.artifact
etiketlerinin belirtilen özellik şeması kullanılarak belirtilebileceğini gösterir.
Modül uzantılarının uygulama işlevi, uzantıyı kullanan tüm modüllere ve ilgili tüm etiketlere erişim izni veren bir module_ctx
nesnesi alması dışında, depo kurallarına benzer.
Uygulama işlevi, depoları oluşturmak için depo kurallarını çağırır.
# @rules_jvm_external//:extensions.bzl
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") # a repo rule
def _maven_impl(ctx):
# This is a fake implementation for demonstration purposes only
# collect artifacts from across the dependency graph
artifacts = []
for mod in ctx.modules:
for install in mod.tags.install:
artifacts += install.artifacts
artifacts += [_to_artifact(artifact) for artifact in mod.tags.artifact]
# call out to the coursier CLI tool to resolve dependencies
output = ctx.execute(["coursier", "resolve", artifacts])
repo_attrs = _process_coursier_output(output)
# call repo rules to generate repos
for attrs in repo_attrs:
http_file(**attrs)
_generate_hub_repo(name = "maven", repo_attrs)
Uzantı kimliği
Modül uzantıları, .bzl
dosyası ve use_extension
çağrısında görünen adla tanımlanır. Aşağıdaki örnekte, maven
uzantısı .bzl
dosyası @rules_jvm_external//:extension.bzl
ve maven
adı ile tanımlanır:
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Bir uzantıyı farklı bir .bzl
dosyasından yeniden dışa aktarmak, uzantıya yeni bir kimlik verir. Uzantının her iki sürümü de geçişli modül grafiğinde kullanılırsa ayrı ayrı değerlendirilir ve yalnızca söz konusu kimlikle ilişkili etiketleri görür.
Uzantı yazarı olarak, kullanıcıların modül uzantınızı yalnızca tek bir .bzl
dosyasından kullanmasını sağlamanız gerekir.
Depo adları ve görünürlüğü
Uzantılar tarafından oluşturulan depoların module_repo_canonical_name+extension_name+repo_name
biçiminde standart adları vardır. Canonical name biçiminin, bağımlı olmanız gereken bir API olmadığını ve herhangi bir zamanda değişebileceğini unutmayın.
Bu adlandırma politikası, her uzantının kendi "repo ad alanına" sahip olduğu anlamına gelir. İki farklı uzantı, çakışma riski olmadan aynı ada sahip bir depo tanımlayabilir. Bu, repository_ctx.name
komutunun, depo kuralı çağrısında belirtilen adla aynı olmayan deponun standart adını bildirdiği anlamına da gelir.
Modül uzantıları tarafından oluşturulan depoları göz önünde bulundurarak, çeşitli depo görünürlüğü kuralları vardır:
- Bir Bazel modülü deposu,
MODULE.bazel
dosyasındabazel_dep
veuse_repo
aracılığıyla tanıtılan tüm depoları görebilir. - Bir modül uzantısı tarafından oluşturulan depo, uzantıyı barındıran modülün görebildiği tüm depoların yanı sıra aynı modül uzantısı tarafından oluşturulan diğer tüm depoları (depo kuralı çağrılarında belirtilen adları görünen adları olarak kullanarak) görebilir.
- Bu durum çakışmaya neden olabilir. Modül deposu,
foo
adlı bir depo görebiliyorsa ve uzantı, belirtilenfoo
adlı bir depo oluşturuyorsa bu uzantı tarafından oluşturulan tüm depolar içinfoo
, eski depoyu ifade eder.
- Bu durum çakışmaya neden olabilir. Modül deposu,
- Benzer şekilde, bir modül uzantısının uygulama işlevinde, uzantı tarafından oluşturulan depolar, oluşturulma sırasından bağımsız olarak özelliklerdeki görünen adlarıyla birbirlerine başvurabilir.
- Modül tarafından görülebilen bir depoyla çakışma olması durumunda, depo kuralı özelliklerine iletilen etiketler, aynı ada sahip uzantı tarafından oluşturulan depo yerine modül tarafından görülebilen depoyu referans aldıklarından emin olmak için
Label
çağrısına sarmalanabilir.
- Modül tarafından görülebilen bir depoyla çakışma olması durumunda, depo kuralı özelliklerine iletilen etiketler, aynı ada sahip uzantı tarafından oluşturulan depo yerine modül tarafından görülebilen depoyu referans aldıklarından emin olmak için
Modül uzantısı depolarını geçersiz kılma ve ekleme
Kök modül, modül uzantısı depolarını geçersiz kılmak veya eklemek için override_repo
ve inject_repo
kullanabilir.
Örnek: rules_java
'nın java_tools
ürününü tedarikçi tarafından sağlanan bir kopya ile değiştirme
# MODULE.bazel
local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
local_repository(
name = "my_java_tools",
path = "vendor/java_tools",
)
bazel_dep(name = "rules_java", version = "7.11.1")
java_toolchains = use_extension("@rules_java//java:extension.bzl", "toolchains")
override_repo(java_toolchains, remote_java_tools = "my_java_tools")
Örnek: Sistem zlib'i yerine @zlib
'ye bağlı olması için bir Go bağımlılığını düzeltme
# MODULE.bazel
bazel_dep(name = "gazelle", version = "0.38.0")
bazel_dep(name = "zlib", version = "1.3.1.bcr.3")
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
go_deps.module_override(
patches = [
"//patches:my_module_zlib.patch",
],
path = "example.com/my_module",
)
use_repo(go_deps, ...)
inject_repo(go_deps, "zlib")
# patches/my_module_zlib.patch
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,6 +1,6 @@
go_binary(
name = "my_module",
importpath = "example.com/my_module",
srcs = ["my_module.go"],
- copts = ["-lz"],
+ cdeps = ["@zlib"],
)
En iyi uygulamalar
Bu bölümde, uzantıları yazarken en iyi uygulamalar açıklanmaktadır. Bu sayede uzantılar kolayca kullanılabilir, bakımı yapılabilir ve zaman içindeki değişikliklere iyi uyum sağlayabilir.
Her uzantıyı ayrı bir dosyaya yerleştirin.
Uzantılar farklı dosyalarda olduğunda bir uzantının başka bir uzantı tarafından oluşturulan depoları yüklemesine izin verilir. Bu işlevi kullanmasanız bile daha sonra ihtiyacınız olabileceği için bunları ayrı dosyalara yerleştirmeniz önerilir. Bunun nedeni, uzantının kimliğinin dosyasına dayanmasıdır. Bu nedenle, uzantıyı daha sonra başka bir dosyaya taşımak, herkese açık API'nizi değiştirir ve kullanıcılarınız için geriye dönük uyumsuz bir değişiklik olur.
Yeniden üretilebilirliği belirtin
Uzantınız, aynı girişler (uzantı etiketleri, okuduğu dosyalar vb.) verildiğinde her zaman aynı depoları tanımlıyorsa ve özellikle sağlama toplamasıyla korunmayan herhangi bir indirme işlemine dayanmıyorsa reproducible = True
ile extension_metadata
değerini döndürmeyi düşünebilirsiniz. Bu, Bazel'in kilit dosyasına yazarken bu uzantıyı atlamasına olanak tanır.
İşletim sistemini ve mimariyi belirtin
Uzantınız işletim sistemine veya mimari türüne bağlıysa os_dependent
ve arch_dependent
Boole özelliklerini kullanarak bunu uzantı tanımında belirttiğinizden emin olun. Bu, her ikisinde de değişiklik olması durumunda Bazel'in yeniden değerlendirme yapması gerektiğini anlamasını sağlar.
Bu tür bir ana makine bağımlılığı, bu uzantının kilit dosyası girişini korumayı zorlaştırdığından mümkünse uzantıyı yeniden üretilebilir olarak işaretlemeyi düşünebilirsiniz.
Yalnızca kök modül, doğrudan depo adlarını etkilemelidir.
Uzantılar, depoları oluştururken bu depoların uzantının ad alanında oluşturulduğunu unutmayın. Bu, farklı modüller aynı uzantıyı kullanırsa ve aynı ada sahip bir depo oluşturursa çakışmaların meydana gelebileceği anlamına gelir. Bu durum genellikle bir modül uzantısının tag_class
, bir depo kuralının name
değeri olarak iletilen bir name
bağımsız değişkenine sahip olması şeklinde kendini gösterir.
Örneğin, kök modül A
, modül B
'e bağlı olsun. Her iki modül de mylang
modülüne bağlıdır. Hem A
hem de B
, mylang.toolchain(name="foo")
işlevini çağırırsa her ikisi de mylang
modülünde foo
adlı bir depo oluşturmaya çalışır ve hata oluşur.
Bunu önlemek için ya doğrudan depo adını ayarlama özelliğini kaldırın ya da yalnızca kök modülün bunu yapmasına izin verin. Kök modülün bu özelliği kullanmasına izin verebilirsiniz. Çünkü hiçbir şey kök modüle bağlı olmayacağından başka bir modülün çakışan bir ad oluşturması konusunda endişelenmenize gerek kalmaz.