Starlark, ilk olarak Bazel'de kullanılmak üzere geliştirilen ve diğer araçlar tarafından benimsenen Python benzeri bir yapılandırma dilidir. Bazel'in BUILD
ve .bzl
dosyaları, "Derleme Dili" olarak bilinen Starlark lehçesiyle yazılır. Ancak özellikle bir özelliğin Bazel'in yerleşik veya "yerel" bir parçası yerine Derleme Dili'nde ifade edildiğini vurgulamak için genellikle "Starlark" olarak adlandırılır. Bazel, temel dili glob
, genrule
, java_binary
gibi derlemeyle ilgili birçok işlevle destekler.
Daha fazla bilgi için Bazel ve Starlark dokümanlarına, yeni kural kümeleri için başlangıç noktası olarak da Rules SIG şablonuna bakın.
Boş kural
İlk kuralınızı oluşturmak için foo.bzl
dosyasını oluşturun:
def _foo_binary_impl(ctx):
pass
foo_binary = rule(
implementation = _foo_binary_impl,
)
rule
işlevini çağırırken bir geri çağırma işlevi tanımlamanız gerekir. Mantık burada görünecektir, ancak
fonksiyonu şimdilik boş bırakabilirsiniz. ctx
bağımsız değişkeni, hedef hakkında bilgi sağlar.
Kuralı yükleyip bir BUILD
dosyasından kullanabilirsiniz.
Aynı dizinde bir BUILD
dosyası oluşturun:
load(":foo.bzl", "foo_binary")
foo_binary(name = "bin")
Artık hedef oluşturulabilir:
$ bazel build bin
INFO: Analyzed target //:bin (2 packages loaded, 17 targets configured).
INFO: Found 1 target...
Target //:bin up-to-date (nothing to build)
Kural hiçbir şey yapmasa da zaten diğer kurallar gibi davranır: Zorunlu bir adı vardır, visibility
, testonly
ve tags
gibi ortak özellikleri destekler.
Değerlendirme modeli
Devam etmeden önce kodun nasıl değerlendirildiğini anlamanız önemlidir.
foo.bzl
öğesini bazı basılı ifadelerle güncelleyin:
def _foo_binary_impl(ctx):
print("analyzing", ctx.label)
foo_binary = rule(
implementation = _foo_binary_impl,
)
print("bzl file evaluation")
ve İNŞA EDİN:
load(":foo.bzl", "foo_binary")
print("BUILD file")
foo_binary(name = "bin1")
foo_binary(name = "bin2")
ctx.label
, analiz edilen hedefin etiketine karşılık gelir. ctx
nesnesinde birçok yararlı alan ve yöntem bulunur. Kapsamlı bir listeyi API referansında bulabilirsiniz.
Kodu sorgulayın:
$ bazel query :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:8:1: bzl file evaluation
DEBUG: /usr/home/bazel-codelab/BUILD:2:1: BUILD file
//:bin2
//:bin1
Birkaç gözlem yapın:
- Önce "bzl file evaluation" (bzl dosyası değerlendirmesi) yazdırılır.
BUILD
dosyasını değerlendirmeden önce, Bazel yüklediği tüm dosyaları değerlendirir. foo.bzl birden fazlaBUILD
dosyasıyla yüklüyorsa Bazel, değerlendirmenin sonucunu önbelleğe aldığından "bzl dosyası değerlendirmesi" ifadesini yalnızca bir kez görürsünüz. _foo_binary_impl
geri çağırma işlevi çağrılmıyor. Bazel sorgusuBUILD
dosyalarını yükler ancak hedefleri analiz etmez.
Hedefleri analiz etmek için cquery
("yapılandırılmış sorgu") veya build
komutunu kullanın:
$ bazel build :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:8:1: bzl file evaluation
DEBUG: /usr/home/bazel-codelab/BUILD:2:1: BUILD file
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin1
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin2
INFO: Analyzed 2 targets (0 packages loaded, 0 targets configured).
INFO: Found 2 targets...
Gördüğünüz gibi, _foo_binary_impl
artık her hedef için bir kez olmak üzere iki kez çağrılıyor.
Bazı okuyucular, "bzl dosya değerlendirme"nin tekrar yazdırıldığını fark edecektir. Ancak foo.bzl değerlendirmesi, bazel query
çağrısından sonra önbelleğe alınır. Bazel kodu yeniden değerlendirmez, yalnızca yazdırma etkinliklerini tekrar oynatır. Önbellek durumundan bağımsız olarak aynı çıkışı alırsınız.
Dosya oluşturma
Kuralınızı daha kullanışlı hale getirmek için dosya oluşturacak şekilde güncelleyin. İlk olarak, dosyayı tanımlayın ve bir ad verin. Bu örnekte, hedefle aynı ada sahip bir dosya oluşturun:
ctx.actions.declare_file(ctx.label.name)
bazel build :all
öğesini şimdi çalıştırırsanız bir hata alırsınız:
The following files have no generating action:
bin2
Her dosya bildirdiğinizde, bir işlem oluşturarak Bazel'a dosyayı nasıl oluşturacağını bildirmeniz gerekir. Belirli bir içeriğe sahip bir dosya oluşturmak için ctx.actions.write
simgesini kullanın.
def _foo_binary_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = out,
content = "Hello\n",
)
Kod geçerlidir ancak herhangi bir işlem yapmaz:
$ bazel build bin1
Target //:bin1 up-to-date (nothing to build)
ctx.actions.write
işlevi, Bazel'e dosyayı nasıl oluşturacağını öğreten bir işlem kaydetti. Ancak, Bazel dosya gerçekten talep edilene kadar
dosya oluşturmaz. Dolayısıyla yapılacak son şey, Bazel'a dosyanın kural uygulamasında kullanılan geçici bir dosya değil, kuralın bir çıktısı olduğunu söylemektir.
def _foo_binary_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = out,
content = "Hello!\n",
)
return [DefaultInfo(files = depset([out]))]
DefaultInfo
ve depset
işlevlerine daha sonra göz atın. Şimdilik, son satırın bir kuralın çıkışlarını seçmenin yolu olduğunu varsayın.
Şimdi, Bazel'i çalıştırın:
$ bazel build bin1
INFO: Found 1 target...
Target //:bin1 up-to-date:
bazel-bin/bin1
$ cat bazel-bin/bin1
Hello!
Dosyayı başarıyla oluşturdunuz.
Özellikler
Kuralı daha kullanışlı hale getirmek için attr
modülünü kullanarak yeni özellikler ekleyin ve kural tanımını güncelleyin.
username
adlı bir dize özelliği ekleyin:
foo_binary = rule(
implementation = _foo_binary_impl,
attrs = {
"username": attr.string(),
},
)
Daha sonra, BUILD
dosyasında ayarlayın:
foo_binary(
name = "bin",
username = "Alice",
)
Geri çağırma işlevindeki değere erişmek için ctx.attr.username
işlevini kullanın. Örneğin:
def _foo_binary_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = out,
content = "Hello {}!\n".format(ctx.attr.username),
)
return [DefaultInfo(files = depset([out]))]
Özelliği zorunlu hale getirebileceğinizi veya varsayılan bir değer ayarlayabileceğinizi unutmayın. attr.string
dokümanlarına göz atın.
Boole veya tam sayı listesi gibi başka özellik türlerini de kullanabilirsiniz.
Bağımlılıklar
attr.label
ve attr.label_list
gibi bağımlılık özellikleri, özelliğin sahibi olan hedeften, özelliğin değerinde etiketi görünen hedefe bir bağımlılık belirtir. Bu tür özellikler, hedef grafiğin temelini oluşturur.
BUILD
dosyasında hedef etiket, //pkg:name
gibi bir dize nesnesi olarak görünür. Uygulama işlevinde, hedefe bir Target
nesnesi olarak erişilebilir. Örneğin, Target.files
kullanılarak hedef tarafından döndürülen dosyaları görüntüleyebilirsiniz.
Birden fazla dosya
Varsayılan olarak, yalnızca kurallar tarafından oluşturulan hedefler bağımlılık olarak görünebilir (foo_library()
hedefi gibi). Özelliğin, giriş dosyası olan hedefleri (ör. depoda bulunan kaynak dosyalar) kabul etmesini istiyorsanız allow_files
ile bunu yapabilir ve kabul edilen dosya uzantılarının listesini belirtebilirsiniz (veya tüm dosya uzantılarına izin vermek için True
kullanabilirsiniz):
"srcs": attr.label_list(allow_files = [".java"]),
Dosya listesine ctx.files.<attribute name>
ile erişebilirsiniz. Örneğin, srcs
özelliğindeki dosya listesine
ctx.files.srcs
Tek dosya
Yalnızca bir dosyaya ihtiyacınız varsa allow_single_file
simgesini kullanın:
"src": attr.label(allow_single_file = [".java"])
Bu dosyaya ctx.file.<attribute name>
altından erişilebilir:
ctx.file.src
Şablon kullanarak dosya oluşturma
Şablona dayalı .cc dosyası oluşturan bir kural oluşturabilirsiniz. Ayrıca, kural uygulama işlevinde oluşturulan bir dize çıkışı almak için ctx.actions.write
kullanabilirsiniz ancak bunun iki sorunu vardır. Öncelikle, şablon büyüdükçe onu ayrı bir dosyaya yerleştirmek ve analiz aşamasında büyük dize oluşturmaktan kaçınmak daha fazla bellek tasarrufu sağlar. İkinci olarak, ayrı bir dosya kullanmak kullanıcı için daha uygundur. Bunun yerine, bir şablon dosyasında değişiklikler gerçekleştiren ctx.actions.expand_template
işlevini kullanın.
Şablon dosyasına bağımlılık beyan etmek için bir template
özelliği oluşturun:
def _hello_world_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name + ".cc")
ctx.actions.expand_template(
output = out,
template = ctx.file.template,
substitutions = {"{NAME}": ctx.attr.username},
)
return [DefaultInfo(files = depset([out]))]
hello_world = rule(
implementation = _hello_world_impl,
attrs = {
"username": attr.string(default = "unknown person"),
"template": attr.label(
allow_single_file = [".cc.tpl"],
mandatory = True,
),
},
)
Kullanıcılar bu kuralı şu şekilde kullanabilir:
hello_world(
name = "hello",
username = "Alice",
template = "file.cc.tpl",
)
cc_binary(
name = "hello_bin",
srcs = [":hello"],
)
Şablonu son kullanıcıya göstermek ve her zaman aynı şablonu kullanmak istemiyorsanız varsayılan bir değer ayarlayabilir ve özelliği gizli yapabilirsiniz:
"_template": attr.label(
allow_single_file = True,
default = "file.cc.tpl",
),
Alt çizgi ile başlayan özellikler gizlidir ve BUILD
dosyasında ayarlanamaz. Şablon artık bir dolaylı bağımlılık oldu: Her hello_world
hedefinin bu dosyaya bir bağımlılığı vardır. BUILD
dosyasını güncelleyip exports_files
kullanarak bu dosyayı diğer paketlere görünür hale getirmeyi unutmayın:
exports_files(["file.cc.tpl"])
Daha fazla bilgi
- Kurallarla ilgili referans dokümanlarına göz atın.
- Depsets hakkında bilgi edinin.
- Başka kural örneklerinin de yer aldığı örnekler deposuna göz atın.