Makrolar

Sorun bildir Kaynağı göster

Bu sayfa, makro kullanımıyla ilgili temel bilgileri ve genel kullanım alanlarını, hata ayıklama kurallarını ve kuralları ele almaktadır.

Makro, BUILD dosyasından çağrılan ve kuralları örneklendirebilen bir işlevdir. Makrolar, genellikle mevcut kuralların ve diğer makroların kapsüllenmesi ve kodların yeniden kullanılması için kullanılır. Yükleme aşamasının sonunda makrolar artık kalmaz ve Bazel yalnızca somut kurallar kümesini görür.

Kullanım

Bir makronun tipik kullanım alanı, bir kuralı yeniden kullanmak istemenizdir.

Örneğin, bir BUILD dosyasındaki genrule, //:generator kullanan ve komutun koduna gömülü bir some_arg bağımsız değişkeniyle birlikte bir dosya oluşturur:

genrule(
    name = "file",
    outs = ["file.txt"],
    cmd = "$(location //:generator) some_arg > $@",
    tools = ["//:generator"],
)

Farklı bağımsız değişkenlere sahip daha fazla dosya oluşturmak istiyorsanız bu kodu bir makro işlevine ayıklamayı düşünebilirsiniz. name ve arg parametrelerine sahip file_generator makrosunu adlandıralım. Genrule ifadesini aşağıdakiyle değiştirin:

load("//path:generator.bzl", "file_generator")

file_generator(
    name = "file",
    arg = "some_arg",
)

file_generator(
    name = "file-two",
    arg = "some_arg_two",
)

file_generator(
    name = "file-three",
    arg = "some_arg_three",
)

Burada, //path paketinde bulunan .bzl dosyasından file_generator simgesini yüklersiniz. Makro işlevi tanımlarını ayrı bir .bzl dosyasına koyarak BUILD dosyalarınızı temiz ve bildirim temelli tutabilirsiniz. .bzl dosyası, çalışma alanındaki herhangi bir paketten yüklenebilir.

Son olarak, path/generator.bzl içinde orijinal tür tanımını kapsamak ve parametreleştirmek için makronun tanımını yazın:

def file_generator(name, arg, visibility=None):
  native.genrule(
    name = name,
    outs = [name + ".txt"],
    cmd = "$(location //:generator) %s > $@" % arg,
    tools = ["//:generator"],
    visibility = visibility,
  )

Kuralları birbirine bağlamak için makroları da kullanabilirsiniz. Bu örnekte, bir genrule türünün giriş olarak önceki bir genrule çıkışının çıkışları kullandığı zincirleme türler gösterilmektedir:

def chained_genrules(name, visibility=None):
  native.genrule(
    name = name + "-one",
    outs = [name + ".one"],
    cmd = "$(location :tool-one) $@",
    tools = [":tool-one"],
    visibility = ["//visibility:private"],
  )

  native.genrule(
    name = name + "-two",
    srcs = [name + ".one"],
    outs = [name + ".two"],
    cmd = "$(location :tool-two) $< $@",
    tools = [":tool-two"],
    visibility = visibility,
  )

Örnek, yalnızca ikinci türe bir görünürlük değeri atar. Böylece, makro yazarları ara kuralların çıkışlarının, çalışma alanındaki diğer hedefler tarafından kullanılmalarını engelleyebilir.

Genişletilen makrolar

Bir makronun ne işe yaradığını incelemek istediğinizde query komutunu --output=build ile birlikte kullanarak genişletilmiş formu görebilirsiniz:

$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
  name = "file",
  tools = ["//:generator"],
  outs = ["//test:file.txt"],
  cmd = "$(location //:generator) some_arg > $@",
)

Yerel kuralları örneklendirme

Yerel kurallar (load() ifadesi gerektirmeyen kurallar) yerel modülünden örneklenebilir:

def my_macro(name, visibility=None):
  native.cc_library(
    name = name,
    srcs = ["main.cc"],
    visibility = visibility,
  )

Paket adını (örneğin, makroyu hangi BUILD dosyasının çağırdığını) bilmeniz gerekiyorsa native.package_name() işlevini kullanın. native öğesinin yalnızca .bzl dosyalarında kullanılabileceğini, BUILD dosyalarında kullanılamayacağını unutmayın.

Makrolarda etiket çözümlemesi

Makrolar yükleme aşamasında değerlendirildiğinden, bir makroda meydana gelen "//foo:bar" gibi etiket dizeleri, tanımlandığı .bzl dosyasına göre değil, makronun kullanıldığı BUILD dosyasına göre yorumlanır. Bu davranış, yayınlanan bir Starlark kural kümesinin parçası olmaları gibi diğer depolarda kullanılması amaçlanan makrolar için genellikle istenmeyen bir davranıştır.

Starlark kurallarıyla aynı davranışı elde etmek için etiket dizelerini Label oluşturucuyla sarmalayın:

# @my_ruleset//rules:defs.bzl
def my_cc_wrapper(name, deps = [], **kwargs):
  native.cc_library(
    name = name,
    deps = deps + select({
      # Due to the use of Label, this label is resolved within @my_ruleset,
      # regardless of its site of use.
      Label("//config:needs_foo"): [
        # Due to the use of Label, this label will resolve to the correct target
        # even if the canonical name of @dep_of_my_ruleset should be different
        # in the main repo, such as due to repo mappings.
        Label("@dep_of_my_ruleset//tools:foo"),
      ],
      "//conditions:default": [],
    }),
    **kwargs,
  )

Hata ayıklama

  • bazel query --output=build //my/path:all, değerlendirmeden sonra BUILD dosyasının nasıl göründüğünü gösterir. Tüm makrolar, glob'lar, döngüler genişletilir. Bilinen sınırlama: select ifadeleri şu anda çıkışta gösterilmemektedir.

  • Çıkışı, generator_function (kuralları oluşturan işlev) veya generator_name (makronun ad özelliği) ölçütlerine göre filtreleyebilirsiniz: bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'

  • Bir BUILD dosyasında foo kuralının tam olarak nerede oluşturulduğunu öğrenmek için aşağıdaki yolu deneyebilirsiniz. Şu satırı BUILD dosyasının üst kısmına yakın bir yere ekleyin: cc_library(name = "foo"). Bazel'ı çalıştır. foo kuralı oluşturulduğunda (ad çakışması nedeniyle) bir istisna alırsınız. Bu kural, tam yığın izlemeyi (stack trace) gösterir.

  • Hata ayıklama için yazdır aracını da kullanabilirsiniz. Yükleme aşamasında mesajı DEBUG günlük satırı olarak gösterir. Nadir durumlar haricinde, print çağrılarını kaldırın veya kodu depoya göndermeden önce varsayılan olarak False olan bir debugging parametresiyle koşullu hâle getirin.

Hatalar

Hata bildirmek istiyorsanız fail işlevini kullanın. Kullanıcıya sorunun ne olduğunu ve BUILD dosyasının nasıl düzeltileceğini açıkça açıklayın. Hata yakalamak mümkün değildir.

def my_macro(name, deps, visibility=None):
  if len(deps) < 2:
    fail("Expected at least two values in deps")
  # ...

Kongreler

  • Kuralları örnekleyen tüm ortak işlevlerin (alt çizgi ile başlamayan işlevler) bir name bağımsız değişkenine sahip olması gerekir. Bu bağımsız değişken isteğe bağlı olmamalıdır (varsayılan değer vermeyin).

  • Genel işlevler, Python kurallarına uygun bir docstring kullanmalıdır.

  • BUILD dosyalarında, makroların name bağımsız değişkeni bir anahtar kelime bağımsız değişkeni olmalıdır (konumsal bağımsız değişken değil).

  • Bir makro tarafından oluşturulan kuralların name özelliği, ad bağımsız değişkenini ön ek olarak içermelidir. Örneğin macro(name = "foo"), cc_library foo ve genrule foo_gen oluşturabilir.

  • Çoğu durumda, isteğe bağlı parametrelerin varsayılan değeri None olmalıdır. None, doğrudan yerel kurallara geçirilebilir. Bu kurallar, herhangi bir bağımsız değişkende iletilmemiş gibi davranır. Dolayısıyla, bu amaçla bunu 0, False veya [] ile değiştirmenize gerek yoktur. Bunun yerine, varsayılan değerleri karmaşık olabileceği veya zaman içinde değişebileceği için makro, oluşturduğu kurallara bağlı kalmalıdır. Ayrıca, açıkça varsayılan değerine ayarlanan bir parametre, sorgu dili veya derleme sisteminin dahili bileşenleri üzerinden erişildiğinde hiçbir zaman ayarlanmayan (veya None olarak ayarlanmayan) bir parametreden farklı görünür.

  • Makrolar, isteğe bağlı visibility bağımsız değişkenlerine sahip olmalıdır.