Kurallar Eğiticisi

7.3 · 7.2 · 7.1 · 7.0 · 6.5

Starlark, başlangıçta Bazel'de kullanılmak üzere geliştirilmiş ve o zamandan beri diğer araçlar tarafından benimsenmiş, 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 çok sayıda 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 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 buraya eklenecek ancak işlevi ş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 diğer kurallar gibi davranır: Zorunlu bir adı vardır, 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 öğ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 nesnesi birçok yararlı alana ve yönteme sahiptir. Bunların kapsamlı bir listesini 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 fazla BUILD dosyası yükleniyorsa foo.bzl dosyasını yalnızca bir kez "bzl dosya değerlendirmesi" olarak görürsünüz. Bunun nedeni, Bazel'in değerlendirme sonucunu önbelleğe almasıdır.
  • _foo_binary_impl geri çağırma işlevi çağrılmıyor. 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: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 baskı etkinliklerini yeniden 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. Önce 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'ü şimdi çalıştırırsanız bir hata alırsınız:

The following files have no generating action:
bin2

Bir dosyayı her tanımladığınızda, 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 işlevini 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, gerçekten istenene kadar dosyayı oluşturmaz. Bu nedenle, son olarak Bazel'e dosyanın kuralın bir çıktısı olduğunu ve kural uygulamasında kullanılan geçici bir dosya olmadığını söylemeniz gerekir.

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!

Dosya başarıyla oluşturuldu.

Ö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 değerini 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 getirebilir veya varsayılan bir değer ayarlayabilirsiniz. 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ğ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 etiketi, //pkg:name gibi bir dize nesnesi olarak görünür. Hedefe, uygulama işlevinde Target nesnesi olarak erişilebilir. Örneğin, Target.files kullanarak hedef tarafından döndürülen dosyaları görüntüleyin.

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ş dosyaları (depodaki kaynak dosyalar gibi) olan hedefleri kabul etmesini istiyorsanız bunu allow_files ile yapıp 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şebilirsiniz. Örneğin, srcs özelliğindeki dosya listesine

ctx.files.srcs

Tek dosya

Yalnızca bir dosyaya ihtiyacınız varsa allow_single_file uzantısını 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. İlk olarak, şablon büyüdükçe, bunu ayrı bir dosyaya yerleştirmek ve analiz aşamasında büyük dizeler oluşturmaktan kaçınmak bellek daha verimli hale gelir. İkinci olarak, ayrı bir dosya kullanmak kullanıcı için daha uygundur. Bunun yerine, şablon dosyasında değiştirme yapan 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 istemiyorsanız ve her zaman aynı şablonu kullanmak istiyorsanız varsayılan bir değer ayarlayabilir ve özelliği gizli hale getirebilirsiniz:

    "_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 örtülü bir bağımlılıktır: Her hello_world hedefi bu dosyaya bağımlıdı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