Bu sayfada, makro kullanmayla ilgili temel bilgiler, tipik kullanım alanları, hata ayıklama ve kurallar ele alınmaktadır.
Makro, BUILD
dosyasından çağrılan ve kuralları örnekleyebilen bir işlevdir.
Makrolar, temel olarak mevcut kuralların ve diğer makroların sarmalanması ve kodlarının yeniden kullanılması için kullanılır.
Makrolar iki şekilde sunulur: Bu sayfada açıklanan sembolik makrolar ve eski makrolar. Mümkün olduğunda kod netliği için sembolik makrolar kullanmanızı öneririz.
Sembolik makrolar, yazılmış bağımsız değişkenler (makronun çağrıldığı yere göre dize etikete dönüştürme) ve oluşturulan hedeflerin görünürlüğünü kısıtlama ve belirtme olanağı sunar. Bunlar, üşengeç değerlendirmeye (gelecekteki bir Bazel sürümünde eklenecek) uygun olacak şekilde tasarlanmıştır. Sembolik makrolar, Bazel 8'de varsayılan olarak kullanılabilir. Bu belgede macros
ifadesi, sembolik makrolar anlamına gelir.
Sembolik makroların yürütülebilir bir örneğini örnek deposunda bulabilirsiniz.
Kullanım
Makrolar, .bzl
dosyalarında macro()
işlevi attrs
ve implementation
olmak üzere iki gerekli parametreyle çağrılarak tanımlanır.
Özellikler
attrs
, makronun bağımsız değişkenlerini temsil eden özellik adı ile özellik türleri arasındaki bir sözlük kabul eder. İki ortak özellik (name
ve visibility
) tüm makrolara dolaylı olarak eklenir ve attrs
'ye iletilen sözlüğe dahil edilmez.
# macro/macro.bzl
my_macro = macro(
attrs = {
"deps": attr.label_list(mandatory = True, doc = "The dependencies passed to the inner cc_binary and cc_test targets"),
"create_test": attr.bool(default = False, configurable = False, doc = "If true, creates a test target"),
},
implementation = _my_macro_impl,
)
Özellik türü bildirimleri mandatory
, default
ve doc
parametrelerini kabul eder. Çoğu özellik türü, özelliğin select
kabul edip etmediğini belirleyen configurable
parametresini de kabul eder. Bir özellik configurable
ise select
olmayan değerler, yapılandırılabilir olmayan bir select
olarak ayrıştırılır. "foo"
, select({"//conditions:default": "foo"})
olur. Daha fazla bilgiyi seçimler bölümünde bulabilirsiniz.
Özellik devralımı
Makrolar genellikle bir kuralı (veya başka bir makroyu) sarmalamak için tasarlanır ve makronun yazarı genellikle **kwargs
kullanarak sarmalanmış sembolün özelliklerinin büyük kısmını makronun ana hedefine (veya ana iç makroya) değiştirmeden iletmek ister.
Bu kalıbı desteklemek için bir makro, macro()
'nin inherit_attrs
bağımsız değişkenine kural veya makro simgesi ileterek bir kuraldan ya da başka bir makrodan özellikleri devralabilir. (Tüm Starlark derleme kuralları için tanımlanan ortak özellikleri devralmak üzere kural veya makro simgesi yerine "common"
özel dizesini de kullanabilirsiniz.)
Yalnızca herkese açık özellikler devralınır ve makronun kendi attrs
sözlüğündeki özellikler, aynı ada sahip devralınan özellikleri geçersiz kılar. Devralınan özellikleri attrs
sözlüğünde None
değerini kullanarak da kaldırabilirsiniz:
# macro/macro.bzl
my_macro = macro(
inherit_attrs = native.cc_library,
attrs = {
# override native.cc_library's `local_defines` attribute
local_defines = attr.string_list(default = ["FOO"]),
# do not inherit native.cc_library's `defines` attribute
defines = None,
},
...
)
Zorunlu olmayan devralınan özelliklerin varsayılan değeri, orijinal özellik tanımının varsayılan değerinden bağımsız olarak her zaman None
olarak geçersiz kılınır. Devralınan zorunlu olmayan bir özelliği incelemeniz veya değiştirmeniz gerekiyorsa (ör. devralınan bir tags
özelliğine etiket eklemek istiyorsanız) makronuzun uygulama işlevinde None
durumunu ele almanız gerekir:
# macro/macro.bzl
_my_macro_implementation(name, visibility, tags, **kwargs):
# Append a tag; tags attr is an inherited non-mandatory attribute, and
# therefore is None unless explicitly set by the caller of our macro.
my_tags = (tags or []) + ["another_tag"]
native.cc_library(
...
tags = my_tags,
**kwargs,
)
...
Uygulama
implementation
, makronun mantığını içeren bir işlevi kabul eder.
Uygulama işlevleri genellikle bir veya daha fazla kuralı çağırarak hedef oluşturur ve genellikle özeldir (başında alt çizgiyle adlandırılır). Geleneksel olarak, makrolarıyla aynı ada sahiptirler ancak önlerine _
, sonlarına ise _impl
eklenir.
Özelliklere referans içeren tek bir bağımsız değişken (ctx
) alan kural uygulama işlevlerinin aksine, makro uygulama işlevleri her bağımsız değişken için bir parametre kabul eder.
# macro/macro.bzl
def _my_macro_impl(name, visibility, deps, create_test):
cc_library(
name = name + "_cc_lib",
deps = deps,
)
if create_test:
cc_test(
name = name + "_test",
srcs = ["my_test.cc"],
deps = deps,
)
Bir makro özellik devralırsa uygulama işlevinde **kwargs
artık anahtar kelime parametresi bulunmalıdır. Bu parametre, devralınan kuralı veya alt makroyu çağıran çağrıya yönlendirilebilir. (Bu, devraldığınız kural veya makro yeni bir özellik eklerse makronuzun bozulmamasını sağlar.)
Bildirim
Makrolar, tanımları bir BUILD
dosyasına yükleyerek ve çağırarak bildirilir.
# pkg/BUILD
my_macro(
name = "macro_instance",
deps = ["src.cc"] + select(
{
"//config_setting:special": ["special_source.cc"],
"//conditions:default": [],
},
),
create_tests = True,
)
Bu işlem, //pkg:macro_instance_cc_lib
ve//pkg:macro_instance_test
hedeflerini oluşturur.
Kural çağrılarında olduğu gibi, makro çağrısındaki bir özellik değeri None
olarak ayarlanırsa bu özellik, makroyu çağıran tarafından atlanmış gibi değerlendirilir. Örneğin, aşağıdaki iki makro çağrısı eşdeğerdir:
# pkg/BUILD
my_macro(name = "abc", srcs = ["src.cc"], deps = None)
my_macro(name = "abc", srcs = ["src.cc"])
Bu, genellikle BUILD
dosyalarında yararlı değildir ancak bir makroyu programatik olarak başka bir makronun içine sarmalamak için faydalıdır.
Ayrıntılar
Oluşturulan hedefler için adlandırma kuralları
Sembolik bir makro tarafından oluşturulan hedeflerin veya alt makroların adları, makronun name
parametresiyle eşleşmeli ya da name
ile başlamalı ve ardından _
(tercih edilen), .
veya -
gelmelidir. Örneğin, my_macro(name = "foo")
yalnızca foo
adlı veya foo_
, foo-
ya da foo.
ön ekiyle başlayan dosyalar veya hedefler (ör. foo_bar
) oluşturabilir.
Makro adlandırma kuralını ihlal eden hedefler veya dosyalar tanımlanabilir ancak derlenemez ve bağımlılık olarak kullanılamaz.
Makro örneğiyle aynı paketteki makro dışı dosya ve hedeflerin adları, olası makro hedef adlarıyla çakışmamalıdır ancak bu münhasırlık zorunlu kılınmaz. Sembolik makrolar için performans iyileştirmesi olarak tembel değerlendirme özelliğini uygulama sürecindeyiz. Bu özellik, adlandırma şemasını ihlal eden paketlerde bozulacaktır.
Kısıtlamalar
Sembolik makrolar, eski makrolara kıyasla bazı ek kısıtlamalara sahiptir.
Sembolik makrolar
name
bağımsız değişkeni vevisibility
bağımsız değişkeni almalıdırimplementation
işlevi olmalıdır- Değer döndürmez.
- bağımsız değişkenlerini değiştiremez
- özel
finalizer
makroları olmadıkçanative.existing_rules()
çağıramaz native.package()
'yi arayamazglob()
'yi arayamaznative.environment_group()
'yi arayamaz- Adları adlandırma şemasına uygun olan hedefler oluşturmalıdır.
- Tanımlanmamış veya bağımsız değişken olarak iletilmemiş giriş dosyalarına referans veremez.
- arayanlarının gizli hedeflerine atıfta bulunamaz (daha fazla bilgi için görünürlük ve makrolar bölümüne bakın).
Görünürlük ve makrolar
Görünürlük sistemi, hem (sembolik) makroların hem de bu makroları çağıranların uygulama ayrıntılarının korunmasına yardımcı olur.
Varsayılan olarak, sembolik bir makroda oluşturulan hedefler makronun kendisinde görünür ancak makroyu çağıran kullanıcı tarafından görülmeyebilir. Makro, some_rule(..., visibility = visibility)
'te olduğu gibi kendi visibility
özelliğinin değerini yönlendirerek bir hedefi herkese açık API olarak "dışa aktarabilir".
Makro görünürlüğün temel fikirleri şunlardır:
Görünürlük, makroyu hangi paketin çağırdığına değil, hangi makronun hedefi belirttiğine göre kontrol edilir.
- Diğer bir deyişle, aynı pakette olmak tek başına bir hedefin diğerine görünür olmasını sağlamaz. Bu, makronun dahili hedeflerinin paketteki diğer makroların veya üst düzey hedeflerin bağımlılıkları haline gelmesini önler.
Hem kurallarda hem de makrolarda tüm
visibility
özellikleri, kuralın veya makronun çağrıldığı yeri otomatik olarak içerir.- Bu nedenle, bir hedef aynı makroda (veya makroda değilse
BUILD
dosyasında) tanımlanan diğer hedefler tarafından koşulsuz olarak görülebilir.
- Bu nedenle, bir hedef aynı makroda (veya makroda değilse
Pratikte bu, bir makro visibility
değerini ayarlamadan bir hedef beyan ettiğinde hedefin varsayılan olarak makronun içinde olduğu anlamına gelir. (Paketin varsayılan görünürlüğü bir makroda geçerli değildir.) Hedefin dışa aktarılması, hedefin makronun visibility
özelliğinde makronun çağıranın belirttiği her şeye, makronun çağıranın paketine ve makronun kendi koduna göründüğü anlamına gelir.
Bunu başka bir şekilde açıklamak gerekirse, bir makronun görünürlük ayarı, makronun dışa aktarılan hedeflerini kimin (makronun kendisi dışında) görebileceğini belirler.
# tool/BUILD
...
some_rule(
name = "some_tool",
visibility = ["//macro:__pkg__"],
)
# macro/macro.bzl
def _impl(name, visibility):
cc_library(
name = name + "_helper",
...
# No visibility passed in. Same as passing `visibility = None` or
# `visibility = ["//visibility:private"]`. Visible to the //macro
# package only.
)
cc_binary(
name = name + "_exported",
deps = [
# Allowed because we're also in //macro. (Targets in any other
# instance of this macro, or any other macro in //macro, can see it
# too.)
name + "_helper",
# Allowed by some_tool's visibility, regardless of what BUILD file
# we're called from.
"//tool:some_tool",
],
...
visibility = visibility,
)
my_macro = macro(implementation = _impl, ...)
# pkg/BUILD
load("//macro:macro.bzl", "my_macro")
...
my_macro(
name = "foo",
...
)
some_rule(
...
deps = [
# Allowed, its visibility is ["//pkg:__pkg__", "//macro:__pkg__"].
":foo_exported",
# Disallowed, its visibility is ["//macro:__pkg__"] and
# we are not in //macro.
":foo_helper",
]
)
my_macro
, visibility = ["//other_pkg:__pkg__"]
ile çağrılırsa veya //pkg
paketi default_visibility
değerini bu değere ayarlarsa //pkg:foo_exported
, //other_pkg/BUILD
içinde veya //other_pkg:defs.bzl
'de tanımlanan bir makro içinde de kullanılabilir ancak //pkg:foo_helper
korunmaya devam eder.
Bir makro, visibility = ["//some_friend:__pkg__"]
(dahili hedef için) veya visibility = visibility + ["//some_friend:__pkg__"]
(dışa aktarılan hedef için) ileterek bir hedefin arkadaş paketi tarafından görülebileceğini belirtebilir.
Bir makronun, hedefi herkese açık görünürlükle (visibility = ["//visibility:public"]
) tanımlamasının anti-pattern olduğunu unutmayın. Bunun nedeni, arayan daha kısıtlanmış bir görünürlük belirtse bile hedefin her paket için koşulsuz olarak görünür hale gelmesidir.
Tüm görünürlük kontrolleri, şu anda çalışan en iç sembolik makroya göre yapılır. Ancak bir görünürlük yetkilendirme mekanizması vardır: Bir makro, bir etiketi iç makroya özellik değeri olarak aktarırsa iç makroda etiketin tüm kullanımları dış makroya göre kontrol edilir. Daha fazla bilgi için görünürlük sayfasına göz atın.
Eski makroların görünürlük sistemi için tamamen şeffaf olduğunu ve çağrıldıkları BUILD dosyası veya sembolik makronun konumu gibi davrandığını unutmayın.
Seçimler
Bir özellik configurable
(varsayılan) ise ve değeri None
değilse makro uygulama işlevi, özellik değerini önemsiz bir select
içine sarmalanmış olarak görür. Bu, makro yazarının özellik değerinin select
olabileceğini tahmin etmediği durumlardaki hataları yakalamasını kolaylaştırır.
Örneğin, aşağıdaki makroyu ele alalım:
my_macro = macro(
attrs = {"deps": attr.label_list()}, # configurable unless specified otherwise
implementation = _my_macro_impl,
)
my_macro
, deps = ["//a"]
ile çağrılırsa _my_macro_impl
, deps
parametresi select({"//conditions:default":
["//a"]})
olarak ayarlanmış şekilde çağrılır. Bu durum, uygulama işlevinin başarısız olmasına neden olursa (ör. kod, select
için izin verilmeyen deps[0]
'te olduğu gibi değeri dizine eklemeye çalıştıysa) makro yazarı bir seçim yapabilir: Makrosunu yalnızca select
ile uyumlu işlemleri kullanacak şekilde yeniden yazabilir veya özelliği yapılandırılamaz olarak işaretleyebilir (attr.label_list(configurable = False)
). İkinci seçenek, kullanıcıların select
değeri iletmesine izin vermez.
Kural hedefleri bu dönüşümü tersine çevirir ve önemsiz select
'leri koşulsuz değerleri olarak depolar. Yukarıdaki örnekte, _my_macro_impl
bir kural hedefi my_rule(..., deps = deps)
tanımlarsa bu kural hedefinin deps
değeri ["//a"]
olarak depolanır. Bu, select
-sarmalama işleminin, makrolar tarafından oluşturulan tüm hedeflerde önemsiz select
değerlerin depolanmasına neden olmamasını sağlar.
Yapılandırılabilir bir özelliğin değeri None
ise select
içine sarmalanmaz. Bu sayede my_attr == None
gibi testler çalışmaya devam eder ve özellik, hesaplanmış bir varsayılan değere sahip bir kurala yönlendirildiğinde kural düzgün şekilde davranır (yani özellik hiç iletilmemiş gibi). Bir özelliğin None
değerini alması her zaman mümkün değildir ancak attr.label()
türü ve devralınan zorunlu olmayan özellikler için bu durum söz konusu olabilir.
Sonlandırıcılar
Kural sonlandırıcı, BUILD dosyasında söz dizimi konumundan bağımsız olarak, bir paketin yüklenmesinin son aşamasında, sonlandırıcı olmayan tüm hedefler tanımlandıktan sonra değerlendirilen özel bir sembolik makrodur. Normal sembolik makrolardan farklı olarak sonlandırıcı, native.existing_rules()
'ü çağırabilir. Bu durumda, eski makrolardan biraz farklı davranır: Yalnızca sonlandırıcı olmayan kural hedeflerinin kümesini döndürür. Sonlandırıcı, bu kümenin durumu hakkında iddiada bulunabilir veya yeni hedefler tanımlayabilir.
Bir sonlandırıcıyı beyan etmek için finalizer = True
ile macro()
'ü çağırın:
def _my_finalizer_impl(name, visibility, tags_filter):
for r in native.existing_rules().values():
for tag in r.get("tags", []):
if tag in tags_filter:
my_test(
name = name + "_" + r["name"] + "_finalizer_test",
deps = [r["name"]],
data = r["srcs"],
...
)
continue
my_finalizer = macro(
attrs = {"tags_filter": attr.string_list(configurable = False)},
implementation = _impl,
finalizer = True,
)
Tembellik
ÖNEMLİ: Tembel makro genişletmeyi ve değerlendirmeyi uygulama sürecindeyiz. Bu özellik henüz kullanıma sunulmadı.
Şu anda tüm makrolar, BUILD dosyası yüklendikten hemen sonra değerlendirilir. Bu durum, maliyetli ve alakasız makroların da bulunduğu paketlerdeki hedeflerin performansını olumsuz yönde etkileyebilir. Gelecekte, sonlandırıcı olmayan sembolik makrolar yalnızca derleme için gerekliyse değerlendirilecektir. Önek adlandırma şeması, istenen bir hedef verildiğinde Basel'in hangi makronun genişletileceğini belirlemesine yardımcı olur.
Taşımayla ilgili sorunları giderme
Taşımayla ilgili bazı yaygın sorunlar ve bunların nasıl düzeltileceği aşağıda açıklanmıştır.
- Eski makro çağrıları
glob()
glob()
çağrısını BUILD dosyanıza (veya BUILD dosyasından çağrılan eski bir makroya) taşıyın ve glob()
değerini bir etiket listesi özelliği kullanarak sembolik makroya iletin:
# BUILD file
my_macro(
...,
deps = glob(...),
)
- Eski makroda geçerli bir starlark
attr
türü olmayan bir parametre var.
Mümkün olduğunca fazla mantığı iç içe yerleştirilmiş sembolik bir makroya alın ancak üst düzey makroyu eski bir makro olarak tutun.
- Eski makro, adlandırma şemasını ihlal eden bir hedef oluşturan bir kural çağırıyor
Sorun değil, "rahatsız edici" hedefe güvenmeyin. Adlandırma denetimi sessizce yoksayılır.