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, gelecekteki bir Bazel sürümünde eklenecek olan tembel değerlendirmeye 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.
Kullanım
Makrolar, .bzl
dosyalarında macro()
işlevi iki zorunlu parametreyle (attrs
ve implementation
) ç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 devralma
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 (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
Bazel'deki görünürlük hakkında ayrıntılı bilgi için Görünürlük bölümüne bakın.
Hedef görünürlük
Sembolik makro tarafından oluşturulan hedefler varsayılan olarak yalnızca makroyu tanımlayan .bzl dosyasını içeren pakette görünür. Özellikle, simgesel makroyu çağıran kullanıcı tarafından görülemezler. Çağıran kullanıcı, makronun .bzl dosyasıyla aynı pakette değilse bu durum geçerlidir.
Bir hedefi sembolik makroyu çağıran kullanıcıya görünür hale getirmek için visibility = visibility
değerini kurala veya iç makroya iletin. Hedefe daha geniş (veya herkese açık) bir görünürlük vererek hedefi ek paketlerde de görünür hale getirebilirsiniz.
Bir paketin varsayılan görünürlük değeri (package()
içinde belirtildiği gibi), varsayılan olarak en dış makronun visibility
parametresine iletilir. Ancak bu visibility
değerini, makronun oluşturduğu hedeflere iletmesi (veya iletmemesi) makronun kendisine bağlıdır.
Bağımlılık görünürlüğü
Bir makronun uygulamasında atıfta bulunulan hedefler, söz konusu makronun tanımı tarafından görülebilmelidir. Görünürlük aşağıdaki yöntemlerden biriyle verilebilir:
- Hedefler, makroya etiket, etiket listesi veya etiketli anahtar ya da değer sözlük özellikleri aracılığıyla açıkça iletilirse makro tarafından görülebilir:
# pkg/BUILD
my_macro(... deps = ["//other_package:my_tool"] )
- ... veya özellik varsayılan değerleri olarak:
# my_macro:macro.bzl
my_macro = macro(
attrs = {"deps" : attr.label_list(default = ["//other_package:my_tool"])},
...
)
- Hedefler, makroyu tanımlayan .bzl dosyasını içeren paket için görünür olarak tanımlanmışsa makro tarafından da görülebilir:
# other_package/BUILD
# Any macro defined in a .bzl file in //my_macro package can use this tool.
cc_binary(
name = "my_tool",
visibility = "//my_macro:\\__pkg__",
)
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 etiket listesi özelliğini 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.