Starlark, başlangıçta Bazel'de kullanılmak üzere geliştirilen ve o zamandan beri diğer araçlar tarafından da kullanılan Python benzeri bir yapılandırma dilidir. Bazel'in BUILD
ve .bzl
dosyaları, "Starlark" olarak da bilinen "Build Language" (Derleme Dili) lehçesinde yazılır. Özellikle bir özelliğin Bazel'in yerleşik veya "yerel" bir parçası olmak yerine Build Language'de ifade edildiği vurgulanırken genellikle "Starlark" olarak adlandırılır. Bazel, temel dili glob
, genrule
, java_binary
gibi çok sayıda derlemeyle ilgili işlevle zenginleştirir.
Daha fazla bilgi için Bazel ve Starlark dokümanlarına, yeni kural kümeleri için başlangıç noktası olarak 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ğırdığınızda bir geri çağırma işlevi tanımlamanız gerekir. Mantık buraya gidecek ancak işlevi şimdilik boş bırakabilirsiniz. ctx
bağımsız değişkeni, hedef hakkında bilgi sağlar.
Kuralı BUILD
dosyasından yükleyip 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 herhangi bir işlem yapmasa da diğer kurallar gibi davranır: zorunlu bir adı vardır ve visibility
, testonly
ve tags
gibi yaygın özellikleri destekler.
Değerlendirme modeli
Devam etmeden önce kodun nasıl değerlendirildiğini anlamanız önemlidir.
foo.bzl
uygulamasını bazı yazdırma ifadeleriyle güncelleyin:
def _foo_binary_impl(ctx):
print("analyzing", ctx.label)
foo_binary = rule(
implementation = _foo_binary_impl,
)
print("bzl file evaluation")
ve GELİŞTİRİ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. Bazel,
BUILD
dosyasını değerlendirmeden önce yüklediği tüm dosyaları değerlendirir. Birden fazlaBUILD
dosyası foo.bzl'yi yüklüyorsa Bazel, değerlendirmenin sonucunu önbelleğe aldığından "bzl dosyası değerlendirmesi" yalnızca bir kez gösterilir. _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: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 iki kez (her hedef için bir kez) çağrılıyor.
bazel query
çağrısından sonra foo.bzl
değerlendirmesi önbelleğe alındığı için "bzl file evaluation" ve "BUILD file" ifadelerinin tekrar yazdırılmadığını unutmayın.
Bazel, print
ifadelerini yalnızca gerçekten yürütüldüklerinde yayınlar.
Dosya oluşturma
Kuralınızı daha kullanışlı hale getirmek için dosyalar oluşturacak şekilde güncelleyin. Öncelikle dosyayı bildirip adlandırın. Bu örnekte, hedefle aynı ada sahip bir dosya oluşturun:
ctx.actions.declare_file(ctx.label.name)
bazel build :all
komutunu şimdi çalıştırırsanız hata alırsınız:
The following files have no generating action:
bin2
Bir dosyayı her bildirdiğinizde, bir işlem oluşturarak Bazel'e dosyayı nasıl oluşturacağını söylemeniz gerekir. Belirtilen içeriğe sahip bir dosya oluşturmak için ctx.actions.write
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 istenene kadar oluşturmaz. Bu nedenle, yapılması gereken son şey Bazel'e dosyanın kuralın bir çıktısı olduğunu ve kural uygulamasında kullanılan geçici bir dosya olmadığını 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(),
},
)
Ardından, 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
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 kılabilir veya varsayılan bir değer belirleyebilirsiniz. attr.string
dokümanlarına göz atın.
Boole veya tam sayı listesi gibi diğer özellik türlerini de kullanabilirsiniz.
Bağımlılıklar
attr.label
ve attr.label_list
gibi bağımlılık özellikleri, özelliği içeren hedeften, etiketi özelliğin değerinde görünen hedefe bağımlılık bildirir. 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 hedef, Target
nesnesi olarak erişilebilir. Örneğin, Target.files
kullanarak hedef tarafından döndürülen dosyaları görüntüleyin.
Birden çok dosya
Varsayılan olarak, yalnızca kurallar tarafından oluşturulan hedefler bağımlılık olarak görünebilir (ör. foo_library()
hedefi). Özelliğin, giriş dosyaları (ör. depodaki kaynak dosyalar) olan hedefleri kabul etmesini istiyorsanız bunu allow_files
ile yapabilir ve kabul edilen dosya uzantılarının listesini belirtebilirsiniz (veya herhangi bir dosya uzantısına izin vermek için True
):
"srcs": attr.label_list(allow_files = [".java"]),
Dosya listesine ctx.files.<attribute name>
ile erişilebilir. Örneğin, srcs
özelliğindeki dosya listesine aşağıdaki yöntemlerle erişilebilir:
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>
bölümünden erişebilirsiniz:
ctx.file.src
Şablon kullanarak dosya oluşturma
Şablona dayalı bir .cc dosyası oluşturan bir kural oluşturabilirsiniz. Ayrıca, kural uygulama işlevinde oluşturulan bir dizeyi çıkarmak 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 daha fazla bellek verimliliği sağlar. İkincisi, ayrı bir dosya kullanmak kullanıcı için daha uygundur. Bunun yerine, şablon dosyasında değiştirmeler yapan ctx.actions.expand_template
işlevini kullanın.
Şablon dosyasına bağımlılığı 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 kullanıyorsanız varsayılan bir değer ayarlayabilir ve özelliği özel yapabilirsiniz:
"_template": attr.label(
allow_single_file = True,
default = "file.cc.tpl",
),
Alt çizgiyle başlayan özellikler özeldir ve BUILD
dosyasında ayarlanamaz. Şablon artık örtülü bir bağımlılık: Her hello_world
hedef bu dosyaya bağımlıdır. BUILD
dosyasını güncelleyip exports_files
kullanarak bu dosyayı diğer paketler için görünür hale getirmeyi unutmayın:
exports_files(["file.cc.tpl"])
Daha fazlasını yapma
- Kurallarla ilgili referans belgelerine göz atın.
- Depsetler hakkında bilgi edinin.
- Kurallarla ilgili ek örnekler içeren örnekler deposunu inceleyin.