Modül uzantıları

Sorun bildir Kaynağı görüntüle Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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ında bazel_dep ve use_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ı, belirtilen foo adlı bir depo oluşturuyorsa bu uzantı tarafından oluşturulan tüm depolar için foo, eski depoyu ifade eder.
  • 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 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.