Starlark, başlangıçta Bazel'de kullanılmak üzere geliştirilen ve o zamandan beri diğer araçlar tarafından kullanılmaya başlanan Python benzeri bir yapılandırma dilidir. Bazel'in BUILD
ve .bzl
dosyaları, Starlark'ın "Yapı Dili" olarak bilinen lehçesiyle yazılmıştır. Ancak, özellikle bir özelliğin Bazel'in yerleşik veya "yerel" bir parçası değil, Derleme Dili ile ifade edildiği vurgulandığında genellikle "Starlark" olarak anılır. Bazel, temel dili, derlemeyle ilgili glob
, genrule
, java_binary
gibi birçok işlevle zenginleştiriyor.
Daha ayrıntılı bilgi için Bazel ve Starlark belgelerini, yeni kural kümeleri için başlangıç noktası olarak Kurallar SIG şablonunu inceleyin.
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 işe yarayacaktır, ancak şimdilik işlevi boş bırakabilirsiniz. ctx
bağımsız değişkeni hedef hakkında bilgi sağlar.
Kuralı yükleyip BUILD
dosyasından kullanabilirsiniz.
Aynı dizinde bir BUILD
dosyası oluşturun:
load(":foo.bzl", "foo_binary")
foo_binary(name = "bin")
Artık hedef şu şekilde 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 ve visibility
, testonly
ve tags
gibi yaygın özellikleri destekler.
Değerlendirme modeli
İlerlemeden önce kodun nasıl değerlendirildiğini anlamak ö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 DERLEYİ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
nesnesinin birçok faydalı alanı ve yöntemi vardır. 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özlemde bulunun:
- Önce "bzl dosya değerlendirmesi" yazdırılır.
BUILD
dosyasını değerlendirmeden önce, Bazel yüklediği tüm dosyaları değerlendirir. Fo.bzl birden fazlaBUILD
dosyası yükleniyorsa Bazel değerlendirme sonucunu önbelleğe aldığından, yalnızca bir kez "bzl dosya değerlendirmesi" görürsünüz. _foo_binary_impl
geri çağırma işlevi çağrılmadı. Bazel sorgusu,BUILD
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: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, iki kez çağrılıyor.
foo.bzl
değerlendirmesi, bazel query
çağrısından sonra önbelleğe alındığından "bzl dosyası değerlendirmesi" ve "BUILD dosyası"nın tekrar yazdırılmadığına dikkat edin.
Bazel, yalnızca yürütüldüklerinde print
ifadelerini yayar.
Dosya oluşturma
Daha kullanışlı hale getirmek için kuralı güncelleyerek dosya oluşturun. İlk olarak dosyayı tanımlayın ve bir ad verin. Bu örnekte, hedef ile aynı ada sahip bir dosya oluşturun:
ctx.actions.declare_file(ctx.label.name)
bazel build :all
uygulamasını şimdi çalıştırırsanız bir hata alırsınız:
The following files have no generating action:
bin2
Bir dosya bildirdiğinizde, bir işlem oluşturarak Bazel'a o dosyayı nasıl oluşturacağını bildirmeniz gerekir. Belirtilen içerikle bir dosya oluşturmak için ctx.actions.write
aracını 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 hiçbir şey yapmaz:
$ bazel build bin1
Target //:bin1 up-to-date (nothing to build)
ctx.actions.write
işlevi, Bazel'a dosyayı nasıl oluşturacağınızı öğreten bir işlem kaydetti. Ancak Bazel, gerçekten istenene kadar dosyayı oluşturmaz. Bu yüzden yapılması gereken son şey Bazel'a bu dosyanın kural uygulaması içinde 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]))]
Daha sonra DefaultInfo
ve depset
işlevlerine göz atın. Şimdilik, son satırın bir kuralın çıktılarını seçme yolu olduğunu varsayalım.
Şimdi, Bazel'ı çalıştırın:
$ bazel build bin1
INFO: Found 1 target...
Target //:bin1 up-to-date:
bazel-bin/bin1
$ cat bazel-bin/bin1
Hello!
Başarıyla bir dosya 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
belgelerine göz atın.
boolean 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ğe sahip olan hedeften, etiketi özelliğin değerinde görünen hedefe bağımlılık bildirir. Bu tür bir özellik, 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
kullanarak 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 (foo_library()
hedefi gibi) görünebilir. Özelliğin, giriş dosyası (depodaki kaynak dosyalar gibi) olan hedefleri kabul etmesini istiyorsanız bunu allow_files
ile yapabilir ve kabul edilen dosya uzantılarının listesini (veya herhangi bir dosya uzantısına izin vermek için True
) belirtebilirsiniz:
"srcs": attr.label_list(allow_files = [".java"]),
Dosya listesine ctx.files.<attribute name>
ile erişilebilir. Örneğin, srcs
özelliğindeki dosya listesine
ctx.files.srcs
Tek dosya
Yalnızca bir dosyaya ihtiyacınız varsa allow_single_file
öğesini kullanın:
"src": attr.label(allow_single_file = [".java"])
Ardından bu dosyaya ctx.file.<attribute name>
altından erişilebilir:
ctx.file.src
Şablon kullanarak dosya oluşturma
Belirli bir şablona göre .cc dosyası oluşturan bir kural oluşturabilirsiniz. Ayrıca, kural uygulama işlevinde oluşturulan bir dizenin çıktısını almak için ctx.actions.write
kullanabilirsiniz ancak bunun iki sorunu vardır. İlk olarak, şablon büyüdükçe ayrı bir dosyaya yerleştirmek ve analiz aşamasında büyük dizeler oluşturmaktan kaçınmak bellek açısından daha verimli hale gelir. İkincisi, ayrı bir dosya
kullanmak kullanıcı için daha uygun. Bunun yerine, bir şablon dosyasında değişiklikler gerçekleştiren ctx.actions.expand_template
aracını kullanın.
Şablon dosyasına bir bağımlılık bildirmek için 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 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 istemiyorsanız ve her zaman aynı şablonu kullanmak istiyorsanız varsayılan bir değer belirleyip ö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 dolaylı bir bağımlılık haline gelir: Her hello_world
hedefinin bu dosyaya 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 ileri
- Kurallar için referans belgelerine göz atın.
- Derinlikleri daha iyi tanıyın.
- Ek kural örnekleri içeren örnekler deposuna göz atın.