Kurallar Eğiticisi

Sorun bildir Kaynağı göster

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 fazla BUILD 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