Kurallar

Sorun bildirin Kaynağı göster

Kural, kuralın uygulama işlevi tarafından döndürülen sağlayıcılarda referans verilen bir dizi çıkış oluşturmak için Bazel'in girişlerde gerçekleştirdiği bir dizi işlemi tanımlar. Örneğin, bir C++ ikili kuralı şu şekilde olabilir:

  1. .cpp kaynak dosyası (giriş) alın.
  2. Kaynak dosyalarda g++ komutunu çalıştırın (işlem).
  3. Çalışma zamanında kullanılabilir hale getirmek için, yürütülebilir çıktıyı ve diğer dosyaları içeren DefaultInfo sağlayıcısını iade edin.
  4. CcInfo sağlayıcısını, hedeften ve bağımlılıklarından toplanan C++'a özel bilgilerle iade edin.

Bazel açısından, g++ ve standart C++ kitaplıkları da bu kurala girilir. Kural yazarı olarak, yalnızca kural için kullanıcı tarafından sağlanan girişleri değil, işlemleri yürütmek için gereken tüm araçları ve kitaplıkları da göz önünde bulundurmalısınız.

Herhangi bir kuralı oluşturmadan veya değiştirmeden önce Bazel'ın derleme aşamaları hakkında bilgi sahibi olduğunuzdan emin olun. Bir derlemenin üç aşamasını (yükleme, analiz ve yürütme) anlamak önemlidir. Kurallar ve makrolar arasındaki farkı anlamak için makrolar hakkında bilgi edinmek de faydalı olur. Başlamak için önce Kurallar Eğitimi'ni inceleyin. Ardından, bu sayfayı referans olarak kullanın.

Bazel'in kendisinde birkaç kural oluşturulmuştur. cc_library ve java_binary gibi bu yerel kurallar, belirli diller için bazı temel destek sağlar. Kendi kurallarınızı tanımlayarak Bazel'in yerel olarak desteklemediği diller ve araçlar için benzer bir destek ekleyebilirsiniz.

Bazel, Starlark dilini kullanarak kural yazmak için bir genişletilebilirlik modeli sağlar. Bu kurallar, doğrudan BUILD dosyadan yüklenebilen .bzl dosyalarında yazılır.

Kendi kuralınızı tanımlarken hangi özellikleri desteklediğine ve sonuçlarını nasıl ürettiğine karar vermeniz gerekir.

Kuralın implementation işlevi, analiz aşamasında tam davranışını tanımlar. Bu işlev harici komut çalıştırmaz. Bunun yerine, gerektiğinde kuralın çıktılarını oluşturmak için yürütme aşamasının sonlarında kullanılacak işlemleri kaydeder.

Kural oluşturma

Bir .bzl dosyasında, yeni bir kural tanımlamak için rule işlevini kullanın ve sonucu, genel değişkende depolayın. rule çağrısı, özellikleri ve bir uygulama işlevini belirtir:

example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        "deps": attr.label_list(),
        ...
    },
)

Bu, example_library adlı bir kural türünü tanımlar.

rule çağrısı, kuralın executable=True ile bir yürütülebilir çıktı mı (executable=True ile) yoksa özellikle test=True ile yürütülebilir bir test mi oluşturacağını da belirtmelidir. İkincisiyse kural bir test kuralı olur ve kuralın adı _test ile bitmelidir.

Hedef örneklendirme

Kurallar yüklenebilir ve BUILD dosyalarında çağrılabilir:

load('//some/pkg:rules.bzl', 'example_library')

example_library(
    name = "example_target",
    deps = [":another_target"],
    ...
)

Bir derleme kuralına yapılan her çağrı, hiçbir değer döndürmez ancak bunun bir hedef tanımlama gibi yan etkisi vardır. Buna, kuralın tetiklenmesi denir. Bu, yeni hedef için bir ad ve hedefin özelliklerinin değerlerini belirtir.

Kurallar, Starlark işlevlerinden de çağrılabilir ve .bzl dosyalarına yüklenebilir. Kuralları çağıran Starlark işlevleri Starlark makroları olarak adlandırılır. Starlark makroları sonuçta BUILD dosyalarından çağrılmalıdır ve yalnızca hedefleri örneklendirmek için BUILD dosyalarının değerlendirildiği yükleme aşamasında çağrılabilir.

Özellikler

Özellik, bir kural bağımsız değişkenidir. Özellikler, bir hedefin uygulamasına belirli değerler sağlayabilir veya başka hedeflere referans vererek bir bağımlılık grafiği oluşturabilir.

srcs veya deps gibi kurala özgü özellikler, bir eşlemenin özellik adlarından şemalara (attr modülü kullanılarak oluşturulan) rule attrs parametresine geçirilmesiyle tanımlanır. name ve visibility gibi ortak özellikler tüm kurallara dolaylı olarak eklenir. Ek özellikler, özel olarak yürütülebilir ve test kurallarına dolaylı olarak eklenir. Kurala dolaylı olarak eklenen özellikler, attrs ürününe iletilen sözlüğe dahil edilemez.

Bağımlılık özellikleri

Kaynak kodu işleyen kurallar, genellikle çeşitli bağımlılık türlerini yönetmek için aşağıdaki özellikleri tanımlar:

  • srcs, hedefin işlemleri tarafından işlenen kaynak dosyaları belirtir. Özellik şeması genellikle kuralın işlediği kaynak dosya sıralaması için hangi dosya uzantılarının beklendiğini belirtir. Başlık dosyalarına sahip dillerin kuralları, bir hedef ve tüketicileri tarafından işlenen başlıklar için genellikle ayrı bir hdrs özelliği belirtir.
  • deps, bir hedef için kod bağımlılıklarını belirtir. Özellik şeması, bu bağımlılıkların hangi sağlayıcıları sağlaması gerektiğini belirtmelidir. (Örneğin, cc_library, CcInfo değerini sağlar.)
  • data, hedefe bağlı olan yürütülebilir dosyalar için çalışma zamanında kullanıma sunulacak dosyaları belirtir. Bu, rastgele dosyaların belirtilmesine izin verir.
example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = [".example"]),
        "hdrs": attr.label_list(allow_files = [".header"]),
        "deps": attr.label_list(providers = [ExampleInfo]),
        "data": attr.label_list(allow_files = True),
        ...
    },
)

Bunlar, bağımlılık özelliklerine örnektir. Giriş etiketi belirten tüm özellikler (attr.label_list, attr.label veya attr.label_keyed_string_dict ile tanımlananlar), hedef tanımlandığında bir hedef ile etiketleri (veya karşılık gelen Label nesneleri) listelenen hedefler arasındaki belirli bir türdeki bağımlılıkları belirtir. Bu etiketlerin deposu ve muhtemelen yolu, tanımlanan hedefe göre çözümlenir.

example_library(
    name = "my_target",
    deps = [":other_target"],
)

example_library(
    name = "other_target",
    ...
)

Bu örnekte other_target, my_target bağımlılığıdır ve bu nedenle önce other_target analiz edilir. Hedeflerin bağımlılık grafiğinde bir döngü varsa bu bir hatadır.

Özel özellikler ve örtülü bağımlılıklar

Varsayılan değere sahip bir bağımlılık özelliği dolaylı bağımlılık oluşturur. Kullanıcının BUILD dosyasında belirtmediği hedef grafiğin bir parçası olduğu için örtülüdür. Örtülü bağımlılıklar, bir kural ile araç (derleyici gibi bir derleme zamanı bağımlılığı) arasındaki ilişkinin sabit bir şekilde kodlanması için yararlıdır. Çünkü kullanıcının çoğu, kuralın hangi aracı kullandığını belirtmekle ilgilenmez. Kuralın uygulama işlevinde bu durum, diğer bağımlılıklarla aynı şekilde ele alınır.

Kullanıcının bu değeri geçersiz kılmasına izin vermeden örtülü bir bağımlılık sağlamak istiyorsanız alt çizgi (_) ile başlayan bir ad vererek özelliği gizli hale getirebilirsiniz. Özel özelliklerin varsayılan değerleri olmalıdır. Özel özelliklerin yalnızca örtülü bağımlılıklar için kullanılması genellikle anlamlıdır.

example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        ...
        "_compiler": attr.label(
            default = Label("//tools:example_compiler"),
            allow_single_file = True,
            executable = True,
            cfg = "exec",
        ),
    },
)

Bu örnekte, example_library türündeki her hedef //tools:example_compiler derleyicisine dolaylı bir bağımlılığa sahiptir. Bu sayede, kullanıcı etiketini giriş olarak iletmemiş olsa bile example_library uygulama işlevi, derleyiciyi çağıran işlemler oluşturabilir. _compiler gizli bir özellik olduğundan, ctx.attr._compiler bu kural türünün tüm hedeflerinde her zaman //tools:example_compiler öğesini gösterir. Alternatif olarak, compiler özelliğini alt çizgi olmadan adlandırıp varsayılan değeri koruyabilirsiniz. Bu, kullanıcıların gerekirse farklı bir derleyici yerine koymasına olanak tanır, ancak derleyici etiketi hakkında bilgi sahibi olmayı gerektirmez.

Dolaylı bağımlılıklar, genellikle kural uygulamasıyla aynı depoda bulunan araçlar için kullanılır. Araç, bunun yerine yürütme platformundan veya farklı bir depodan geliyorsa kural, aracı bir araç zincirinden edinmelidir.

Çıkış özellikleri

attr.output ve attr.output_list gibi çıkış özellikleri, hedefin oluşturduğu bir çıkış dosyasını bildirir. Bunlar bağımlılık özelliklerinden iki şekilde ayrılır:

  • Bu uzantılar, başka bir yerde tanımlanan hedeflere başvurmak yerine çıkış dosyası hedeflerini tanımlar.
  • Çıkış dosyası hedefleri, örneklenen kural hedefine bağlıdır. Bunun tersi olmaz.

Genel olarak çıkış özellikleri, yalnızca bir kuralın hedef ada dayalı olamayan kullanıcı tanımlı adlara sahip çıkışlar oluşturması gerektiğinde kullanılır. Bir kuralın tek bir çıkış özelliği varsa genellikle out veya outs olarak adlandırılır.

Önceden tanımlanmış çıkışlar oluşturmanın tercih edilen yöntemi çıkış özellikleridir. Bu çıkışlara özellikle bağlı olabilir veya komut satırında istenebilir.

Uygulama işlevi

Her kural için bir implementation işlevi gerekir. Bu işlevler, sıkı bir şekilde analiz aşamasında yürütülür ve yükleme aşamasında oluşturulan hedeflerin grafiğini, yürütme aşamasında gerçekleştirilecek işlemlerin grafiğine dönüştürür. Dolayısıyla, uygulama işlevleri aslında dosyaları okuyamaz veya yazamaz.

Kural uygulama işlevleri genellikle gizlidir (başta alt çizgiyle belirtilir). Geleneksel olarak kurallarıyla aynı şekilde adlandırılır ancak son ekleri _impl ile belirtilir.

Uygulama işlevleri için tam olarak tek bir parametre kullanılır: Geleneksel olarak ctx olarak adlandırılan kural bağlamı. Bir sağlayıcı listesi döndürürler.

Hedefler

Bağımlılıklar analiz zamanında Target nesneleri olarak temsil edilir. Bu nesneler, hedefin uygulama işlevi yürütüldüğünde oluşturulan providers içerir.

ctx.attr, her bağımlılık özelliğinin adlarına karşılık gelen alanlara sahiptir. Bu alanlarda, bu özellik aracılığıyla her bir doğrudan bağımlılığı temsil eden Target nesne bulunur. label_list özellikleri için bu, Targets öğesinin bir listesidir. label özellikleri için bu, tek bir Target veya None değeridir.

Bir hedefin uygulama işlevi, sağlayıcı nesnelerinin bir listesini döndürür:

return [ExampleInfo(headers = depset(...))]

Bunlara, anahtar olarak sağlayıcı türü ile dizin gösterimi ([]) kullanılarak erişilebilir. Bunlar, Starlark'ta tanımlanan özel sağlayıcılar veya Starlark global değişkenleri olarak kullanılabilen yerel kural sağlayıcıları olabilir.

Örneğin, bir kural başlık dosyalarını hdrs özelliği aracılığıyla alıyor ve bunları hedef ile tüketicilerinin derleme işlemlerine sağlıyorsa bunları şu şekilde toplayabilir:

def _example_library_impl(ctx):
    ...
    transitive_headers = [hdr[ExampleInfo].headers for hdr in ctx.attr.hdrs]

struct öğesinin, sağlayıcı nesneleri listesi yerine bir hedefin uygulama işlevinden döndürüldüğü eski stil için:

return struct(example_info = struct(headers = depset(...)))

Sağlayıcılar, Target nesnesinin ilgili alanından alınabilir:

transitive_headers = [hdr.example_info.headers for hdr in ctx.attr.hdrs]

Bu stil kesinlikle önerilmez ve kuralların kendisinden taşınması gerekir.

Files

Dosyalar File nesneleriyle temsil edilir. Bazel analiz aşamasında dosya G/Ç işlemi gerçekleştirmediğinden bu nesneler, dosya içeriğini doğrudan okumak veya yazmak için kullanılamaz. Bunun yerine, eylem grafiğinin parçalarını oluşturmak için işlem yayan işlevlere (bkz. ctx.actions) yönlendirilirler.

File, bir kaynak dosya veya oluşturulmuş bir dosya olabilir. Oluşturulan her dosya, tam olarak bir işlemin sonucu olmalıdır. Kaynak dosyalar, herhangi bir işlemin sonucu olamaz.

Her bağımlılık özelliği için karşılık gelen ctx.files alanı, bu özellik aracılığıyla tüm bağımlılıkların varsayılan çıkışlarının bir listesini içerir:

def _example_library_impl(ctx):
    ...
    headers = depset(ctx.files.hdrs, transitive=transitive_headers)
    srcs = ctx.files.srcs
    ...

ctx.file, spesifikasyonları allow_single_file=True olarak ayarlanmış bağımlılık özellikleri için tek bir File veya None içerir. ctx.executable, ctx.file ile aynı şekilde davranır ancak yalnızca özellikleri executable=True olarak ayarlanmış bağımlılık özellikleri için alanlar içerir.

Çıkışları bildirme

Analiz aşamasında, bir kuralın uygulama işlevi çıktılar oluşturabilir. Yükleme aşamasında tüm etiketlerin bilinmesi gerektiğinden bu ek çıkışların etiketi yoktur. ctx.actions.declare_file ve ctx.actions.declare_directory kullanılarak çıkışlar için File nesne oluşturulabilir. Çıkışların adları genellikle hedefin adına (ctx.label.name) dayanır:

def _example_library_impl(ctx):
  ...
  output_file = ctx.actions.declare_file(ctx.label.name + ".output")
  ...

Çıkış özellikleri için oluşturulanlar gibi önceden bildirilmiş çıkışlarda, File nesneleri ctx.outputs öğesinin karşılık gelen alanlarından alınabilir.

İşlemler

İşlem, bir dizi girişten nasıl çıkış grubu oluşturulacağını açıklar. Örneğin, "hello.c'de gcc'yi çalıştır ve hello.o'yu al". Bir işlem oluşturulduğunda Bazel, komutu hemen çalıştırmaz. Eylemi bağımlılıklar grafiğine kaydeder, çünkü bir eylem başka bir eylemin sonucuna bağlı olabilir. Örneğin, C'de bağlayıcı, derleyiciden sonra çağrılmalıdır.

İşlem oluşturan genel amaçlı işlevler ctx.actions bölümünde tanımlanmıştır:

ctx.actions.args, eylemler için bağımsız değişkenleri verimli bir şekilde toplamak amacıyla kullanılabilir. Yürütme zamanına kadar noktaların düzleştirilmesini önler:

def _example_library_impl(ctx):
    ...

    transitive_headers = [dep[ExampleInfo].headers for dep in ctx.attr.deps]
    headers = depset(ctx.files.hdrs, transitive=transitive_headers)
    srcs = ctx.files.srcs
    inputs = depset(srcs, transitive=[headers])
    output_file = ctx.actions.declare_file(ctx.label.name + ".output")

    args = ctx.actions.args()
    args.add_joined("-h", headers, join_with=",")
    args.add_joined("-s", srcs, join_with=",")
    args.add("-o", output_file)

    ctx.actions.run(
        mnemonic = "ExampleCompile",
        executable = ctx.executable._compiler,
        arguments = [args],
        inputs = inputs,
        outputs = [output_file],
    )
    ...

İşlemler, giriş dosyalarının bir listesini veya alt kümesini alır ve çıkış dosyalarının (boş olmayan) bir listesini oluşturur. Giriş ve çıkış dosyaları grubu analiz aşamasında bilinmelidir. Bağımlılıklardan gelen sağlayıcılar da dahil olmak üzere özelliklerin değerine bağlı olabilir ancak yürütmenin sonucuna bağlı olamaz. Örneğin, işleminiz sıkıştırılmış dosya açma komutunu çalıştırıyorsa, hangi dosyaların şişirilmesini beklediğinizi (sıkıştırma açmayı çalıştırmadan önce) belirtmeniz gerekir. Dahili olarak değişken sayıda dosya oluşturan işlemler, bunları tek bir dosyaya (zip, tar veya başka bir arşiv biçimi gibi) sarmalayabilir.

İşlemlerde tüm girişler listelenmelidir. Kullanılmayan girişleri listelemeye izin verilir, ancak bu yöntem verimsizdir.

İşlemler tüm çıkışlarını oluşturmalıdır. Başka dosyalar yazabilirler ama çıktılarda olmayan hiçbir şey tüketicilerin kullanımına sunulmaz. Beyan edilen tüm çıkışlar bir işlemle yazılmalıdır.

Eylemler salt işlevlerle karşılaştırılabilir: Yalnızca sağlanan girişlere bağlı olmalı ve bilgisayar bilgilerine, kullanıcı adına, saate, ağa veya G/Ç cihazlarına (okuma girişleri ve yazma çıkışları hariç) erişilmemelidir. Çıkış önbelleğe alınacağı ve yeniden kullanılacağı için bu önemlidir.

Bağımlılıklar Bazel tarafından çözülür. Bazel, hangi işlemlerin yürütüleceğine karar verir. Bağımlılık grafiğinde bir döngü varsa bu bir hatadır. Bir eylemin oluşturulması, yürütüleceğini garanti etmez. Bu, çıktılarının derleme için gerekli olup olmadığına bağlıdır.

Sağlayıcılar

Sağlayıcılar, bir kuralın diğer kurallara bağlı olduğu bilgilerdir. Bu veriler arasında çıkış dosyaları, kitaplıklar, bir aracın komut satırında iletilecek parametreler veya hedef tüketicilerinin bilmesi gereken diğer her şey yer alabilir.

Bir kuralın uygulama işlevi, sağlayıcıları yalnızca belirlenen hedefin doğrudan bağımlılıklarından okuyabildiğinden, kuralların bir hedefin tüketicileri tarafından bilinmesi gereken bir hedef bağımlılıklarından gelen tüm bilgileri, genellikle bir depset olarak toplayarak iletmesi gerekir.

Bir hedefin sağlayıcıları, uygulama işlevi tarafından döndürülen Provider nesnelerinin listesiyle belirtilir.

Eski uygulama işlevleri, uygulama işlevinin sağlayıcı nesneleri listesi yerine bir struct döndürdüğü eski bir stilde de yazılabilir. Bu stil kesinlikle önerilmez ve kuralların kendisinden taşınması gerekir.

Varsayılan çıkışlar

Bir hedefin varsayılan çıkışları, komut satırında derleme için hedef istendiğinde varsayılan olarak istenen çıkışlardır. Örneğin, java_library hedefi //pkg:foo için varsayılan çıkış foo.jar olduğundan bu, bazel build //pkg:foo komutuyla derlenir.

Varsayılan çıkışlar, DefaultInfo işlevinin files parametresiyle belirtilir:

def _example_library_impl(ctx):
    ...
    return [
        DefaultInfo(files = depset([output_file]), ...),
        ...
    ]

Bir kural uygulaması tarafından DefaultInfo döndürülmezse veya files parametresi belirtilmezse DefaultInfo.files varsayılan olarak tüm önceden beyan edilmiş çıkışları (genellikle çıkış özellikleri tarafından oluşturulanlar) alır.

İşlem gerçekleştiren kurallar, doğrudan kullanılması beklenmiyor olsa bile varsayılan çıkışlar sağlamalıdır. İstenen çıkışların grafiğinde olmayan işlemler kısaltılır. Bir çıkış yalnızca hedefin tüketicileri tarafından kullanılıyorsa hedef bağımsız olarak oluşturulduğunda bu işlemler gerçekleştirilmez. Bu da başarısız olan hedefi yeniden oluşturmak hatayı yeniden oluşturmayacağı için hata ayıklamayı da zorlaştırır.

Çalıştırma dosyaları

Çalıştırma dosyaları, bir hedef tarafından çalışma zamanında (derleme zamanı yerine) kullanılan dosya kümesidir. Yürütme aşamasında, Bazel çalıştırma dosyalarını işaret eden sembolik bağlantıları içeren bir dizin ağacı oluşturur. Bu işlem, ikili program ortamını aşamalayarak çalışma zamanında çalıştırma dosyalarına erişebilmesini sağlar.

Çalıştırma dosyaları, kural oluşturulurken manuel olarak eklenebilir. runfiles nesneleri, kural bağlamında ctx.runfiles üzerinde runfiles yöntemi ile oluşturulabilir ve DefaultInfo tarihinde runfiles parametresine iletilebilir. Yürütülebilir kuralların yürütülebilir çıktısı, dolaylı olarak çalıştırma dosyalarına eklenir.

Bazı kurallar, genellikle data adlı özellikleri belirtir. Bu özellikler, çıkışları hedeflerin çalıştırma dosyalarına eklenir. Ayrıca çalıştırma dosyaları, data ve nihai yürütme için kod sağlayabilecek tüm özelliklerden, genellikle srcs (ilişkili data ile filegroup hedefleri içerebilir) ve deps özelliklerinden de birleştirilmelidir.

def _example_library_impl(ctx):
    ...
    runfiles = ctx.runfiles(files = ctx.files.data)
    transitive_runfiles = []
    for runfiles_attr in (
        ctx.attr.srcs,
        ctx.attr.hdrs,
        ctx.attr.deps,
        ctx.attr.data,
    ):
        for target in runfiles_attr:
            transitive_runfiles.append(target[DefaultInfo].default_runfiles)
    runfiles = runfiles.merge_all(transitive_runfiles)
    return [
        DefaultInfo(..., runfiles = runfiles),
        ...
    ]

Özel sağlayıcılar

Sağlayıcılar, kurala özel bilgileri aktarmak için provider işlevi kullanılarak tanımlanabilir:

ExampleInfo = provider(
    "Info needed to compile/link Example code.",
    fields={
        "headers": "depset of header Files from transitive dependencies.",
        "files_to_link": "depset of Files from compilation.",
    })

Daha sonra kural uygulama işlevleri, sağlayıcı örneklerini oluşturup döndürebilir:

def _example_library_impl(ctx):
  ...
  return [
      ...
      ExampleInfo(
          headers = headers,
          files_to_link = depset(
              [output_file],
              transitive = [
                  dep[ExampleInfo].files_to_link for dep in ctx.attr.deps
              ],
          ),
      )
  ]
Sağlayıcıların özel başlatılması

Özel ön işleme ve doğrulama mantığıyla sağlayıcının örneklendirmesini korumak mümkündür. Bu, tüm sağlayıcı örneklerinin belirli değişken değerlere uymasını sağlamak veya kullanıcılara örnek almaları için daha net bir API sunmak amacıyla kullanılabilir.

Bu işlem, provider işlevine bir init geri çağırması geçirilerek yapılır. Bu geri çağırma verilirse provider() işleminin dönüş türü, iki değerin toplamı olacak şekilde değişir: init kullanılmadığında normal döndürülen değer olan sağlayıcı simgesi ve "ham oluşturucu".

Bu durumda, provider simgesi çağrıldığında, doğrudan yeni bir örnek döndürmek yerine bağımsız değişkenleri init geri çağırmasına yönlendirir. Geri çağırmanın döndürülen değeri, değerler ile alan adları (dizeler) eşleştirilen bir dize olmalıdır. Bu, yeni örneğin alanlarını başlatmak için kullanılır. Geri çağırma işleminin herhangi bir imzası olabileceğini ve bağımsız değişkenlerin imzayla eşleşmemesi durumunda, geri çağırma doğrudan çağrılmış gibi bir hata bildirileceğini unutmayın.

Buna karşılık, ham oluşturucu init geri çağırmasını atlar.

Aşağıdaki örnekte, bağımsız değişkenlerini önceden işlemek ve doğrulamak için init kullanılmaktadır:

# //pkg:exampleinfo.bzl

_core_headers = [...]  # private constant representing standard library files

# It's possible to define an init accepting positional arguments, but
# keyword-only arguments are preferred.
def _exampleinfo_init(*, files_to_link, headers = None, allow_empty_files_to_link = False):
    if not files_to_link and not allow_empty_files_to_link:
        fail("files_to_link may not be empty")
    all_headers = depset(_core_headers, transitive = headers)
    return {'files_to_link': files_to_link, 'headers': all_headers}

ExampleInfo, _new_exampleinfo = provider(
    ...
    init = _exampleinfo_init)

export ExampleInfo

Daha sonra bir kural uygulaması, sağlayıcıyı şu şekilde örneklendirebilir:

    ExampleInfo(
        files_to_link=my_files_to_link,  # may not be empty
        headers = my_headers,  # will automatically include the core headers
    )

Ham oluşturucu, init mantığından geçmeyen alternatif kamu fabrikası işlevlerini tanımlamak için kullanılabilir. Örneğin, exampleinfo.bzl'de:

def make_barebones_exampleinfo(headers):
    """Returns an ExampleInfo with no files_to_link and only the specified headers."""
    return _new_exampleinfo(files_to_link = depset(), headers = all_headers)

Genellikle ham oluşturucu, adı alt çizgiyle (yukarıda _new_exampleinfo) başlayan bir değişkene bağlıdır. Bu nedenle, kullanıcı kodu bu değişkeni yükleyemez ve rastgele sağlayıcı örnekleri oluşturamaz.

init için başka bir kullanım da kullanıcının sağlayıcı simgesini tamamen çağırmasını engelleyip kullanıcıyı bunun yerine fabrika işlevini kullanmaya zorlamaktır:

def _exampleinfo_init_banned(*args, **kwargs):
    fail("Do not call ExampleInfo(). Use make_exampleinfo() instead.")

ExampleInfo, _new_exampleinfo = provider(
    ...
    init = _exampleinfo_init_banned)

def make_exampleinfo(...):
    ...
    return _new_exampleinfo(...)

Yürütülebilir kurallar ve test kuralları

Yürütülebilir kurallar, bir bazel run komutu tarafından çağrılabilecek hedefleri tanımlar. Test kuralları, hedefleri bir bazel test komutuyla da çağrılabilen, yürütülebilir özel bir kural türüdür. Yürütülebilir ve test kuralları, rule çağrısında ilgili executable veya test bağımsız değişkeni True değerine ayarlanarak oluşturulur:

example_binary = rule(
   implementation = _example_binary_impl,
   executable = True,
   ...
)

example_test = rule(
   implementation = _example_binary_impl,
   test = True,
   ...
)

Test kurallarının adları _test ile biten adlara sahip olmalıdır. (Test hedef adları da genellikle kurallara göre _test ile biter, ancak bu zorunlu değildir.) Test dışı kurallarda bu son ek olmamalıdır.

Her iki kural türü de run veya test komutları tarafından çağrılacak yürütülebilir bir çıkış dosyası (önceden bildirilmiş veya bildirilmemiş olabilir) oluşturmalıdır. Bazel'a bir kural çıkışlarından hangilerinin yürütülebilir olarak kullanılacağını bildirmek için bunu, döndürülen DefaultInfo sağlayıcısının executable bağımsız değişkeni olarak iletin. Bu executable, kuralın varsayılan çıkışlarına eklenir (böylece bunu hem executable hem de files öğesine aktarmanız gerekmez). Ayrıca, runfiles'e dolaylı olarak eklenir:

def _example_binary_impl(ctx):
    executable = ctx.actions.declare_file(ctx.label.name)
    ...
    return [
        DefaultInfo(executable = executable, ...),
        ...
    ]

Bu dosyayı oluşturan işlem, dosyadaki yürütülebilir biti ayarlamalıdır. ctx.actions.run veya ctx.actions.run_shell işlemi için bu işlem, işlem tarafından çağrılan temel araç tarafından yapılmalıdır. ctx.actions.write işlemi için is_executable=True değerini geçin.

Eski davranış olarak, yürütülebilir kuralların önceden beyan edilmiş özel bir ctx.outputs.executable çıkışı vardır. Bu dosya, DefaultInfo kullanarak bir dosya belirtmezseniz varsayılan yürütülebilir dosya olarak sunulur, aksi takdirde kullanılmamalıdır. Bu çıkış mekanizması, yürütülebilir dosyanın adının analiz sırasında özelleştirilmesini desteklemediği için kullanımdan kaldırılmıştır.

Yürütülebilir kural ve test kuralı örneklerine göz atın.

Tüm kurallar için eklenenlere ek olarak, yürütülebilir kurallar ve test kuralları da dolaylı olarak tanımlanmış ek özelliklere sahiptir. Dolaylı olarak eklenen özelliklerin varsayılanları değiştirilemez ancak bu durum, özel bir kuralın varsayılan değeri değiştiren bir Starlark makrosu içine sarmalanmasıyla çözülebilir:

def example_test(size="small", **kwargs):
  _example_test(size=size, **kwargs)

_example_test = rule(
 ...
)

Runfiles konumu

bazel run (veya test) ile yürütülebilir bir hedef çalıştırıldığında, Runfiles dizininin kökü, yürütülebilir dosyanın bitişiğinde olur. Yollar aşağıdaki gibi ilişkilidir:

# Given launcher_path and runfile_file:
runfiles_root = launcher_path.path + ".runfiles"
workspace_name = ctx.workspace_name
runfile_path = runfile_file.short_path
execution_root_relative_path = "%s/%s/%s" % (
    runfiles_root, workspace_name, runfile_path)

Runfiles dizinindeki File yolu File.short_path değerine karşılık gelir.

Doğrudan bazel tarafından yürütülen ikili program, runfiles dizininin köküne bitişiktir. Ancak çalıştırma dosyalarından from olarak adlandırılan ikili programlar aynı varsayımı yapamaz. Bu sorunu azaltmak için her ikili program, bir ortam veya komut satırı bağımsız değişkeni/işaretleme kullanarak çalıştırma dosyası kökünü parametre olarak kabul edebileceğimiz bir yöntem sağlamalıdır. Bu, ikili programların doğru standart çalıştırma dosyası kökünü çağırdığı ikili programlara iletmesini sağlar. Bu ayar belirtilmezse ikili program, çağrılan ilk ikili program olduğunu tahmin edip bitişik bir runfiles dizinini arayabilir.

İleri düzey konular

Çıkış dosyalarını isteme

Tek bir hedefin birden fazla çıkış dosyası olabilir. Bir bazel build komutu çalıştırıldığında, komuta verilen hedeflerin bazı çıkışlarının istendiği kabul edilir. Bazel yalnızca bu istenen dosyaları ve bunların doğrudan veya dolaylı olarak bağımlı olduğu dosyaları oluşturur. (Eylem grafiği söz konusu olduğunda Bazel, yalnızca istenen dosyaların geçişli bağımlılıkları olarak erişilebilen işlemleri yürütür.)

Varsayılan çıkışlara ek olarak önceden bildirilmiş herhangi bir çıkış, komut satırında açıkça istenebilir. Kurallar, çıktı özellikleri aracılığıyla önceden bildirilen çıkışları belirtebilir. Bu durumda, kullanıcı kuralı örneklendirirken çıkışlar için açıkça etiketler seçer. Çıkış özellikleri için File nesnelerini almak amacıyla ilgili ctx.outputs özelliğini kullanın. Kurallar, hedef ada göre de önceden bildirilmiş çıkışları dolaylı olarak tanımlayabilir ancak bu özelliğin desteği sonlandırılmıştır.

Varsayılan çıkışlara ek olarak, birlikte istenebilecek çıkış dosyası koleksiyonları olan çıkış grupları da vardır. Bunları --output_groups ile talep edebilirsiniz. Örneğin, hedef //pkg:mytarget, debug_files çıkış grubuna sahip bir kural türündeyse bu dosyalar bazel build //pkg:mytarget --output_groups=debug_files çalıştırılarak oluşturulabilir. Önceden bildirilmemiş çıkışların etiketleri bulunmadığından yalnızca varsayılan çıkışlarda veya bir çıkış grubunda görünerek istenebilir.

Çıkış grupları OutputGroupInfo sağlayıcısıyla belirtilebilir. Birçok yerleşik sağlayıcıdan farklı olarak OutputGroupInfo, rastgele adlara sahip parametreleri alarak bu ada sahip çıkış gruplarını tanımlar:

def _example_library_impl(ctx):
    ...
    debug_file = ctx.actions.declare_file(name + ".pdb")
    ...
    return [
        DefaultInfo(files = depset([output_file]), ...),
        OutputGroupInfo(
            debug_files = depset([debug_file]),
            all_files = depset([output_file, debug_file]),
        ),
        ...
    ]

Ayrıca çoğu sağlayıcının aksine OutputGroupInfo, aynı çıkış gruplarını tanımlamadıkları sürece hem bir en boy hem de bu özelliğin uygulandığı kural hedefi tarafından döndürülebilir. Bu durumda, sonuçta ortaya çıkan sağlayıcılar birleştirilir.

OutputGroupInfo etiketinin genellikle belirli dosya türlerini bir hedeften tüketicilerin eylemlerine aktarmak için kullanılmaması gerektiğini unutmayın. Bunun yerine bunun için kurala özel sağlayıcılar tanımlayın.

Yapılandırmalar

Farklı bir mimari için C++ ikili programı derlemek istediğinizi düşünün. Derleme karmaşık olabilir ve birkaç adımdan oluşabilir. Derleyiciler ve kod oluşturucular gibi bazı ara ikili programların, yürütme platformunda (ana makineniz veya bir uzak yürütücü olabilir) çalışması gerekir. Nihai çıkış gibi bazı ikili programlar hedef mimariye göre derlenmelidir.

Bu nedenle, Bazel'in "yapılandırmalar" ve geçişler kavramı vardır. En üstteki hedefler (komut satırında istenenler) "hedef" yapılandırmasında oluşturulurken yürütme platformunda çalışması gereken araçlar ise "exec" yapılandırmasında oluşturulur. Kurallar, yapılandırmaya bağlı olarak farklı işlemler (ör. derleyiciye iletilen cpu mimarisini değiştirmek) oluşturabilir. Bazı durumlarda farklı yapılandırmalar için aynı kitaplık gerekli olabilir. Böyle bir durumda analiz yapılacak ve muhtemelen birden fazla kez derlenecektir.

Varsayılan olarak Bazel, hedef bağımlılıklarını hedefin kendisiyle aynı yapılandırmada, yani geçiş olmadan oluşturur. Bağımlılık, hedefin oluşturulmasına yardımcı olması için gereken bir araç olduğunda ilgili özellik, yönetici yapılandırmasına geçişi belirtmelidir. Bu, aracın ve tüm bağımlılıklarının yürütme platformu için derleme yapmasına neden olur.

Her bir bağımlılık özelliği için bağımlılıkların aynı yapılandırmada derlenmesi veya bir yönetici yapılandırmasına geçiş yapılması gerekip gerekmediğine karar vermek için cfg kullanabilirsiniz. Bir bağımlılık özelliği executable=True işaretine sahipse cfg açıkça ayarlanmalıdır. Bunun amacı, yanlış yapılandırmaya yönelik bir aracın yanlışlıkla oluşturulmasını önlemektir. Örneği inceleyin

Genel olarak, çalışma zamanında gerekli olacak kaynaklar, bağımlı kitaplıklar ve yürütülebilir dosyalar aynı yapılandırmayı kullanabilir.

Derlemenin bir parçası olarak yürütülen araçlar (derleyiciler veya kod oluşturucular gibi) bir yönetici yapılandırması için derlenmelidir. Bu durumda, özellikte cfg="exec" değerini belirtin.

Aksi takdirde, çalışma zamanında kullanılan (örneğin bir testin parçası olarak) yürütülebilir dosyalar hedef yapılandırma için derlenmelidir. Bu durumda, özellikte cfg="target" değerini belirtin.

cfg="target" aslında hiçbir şey yapmaz. Kural tasarımcılarının amaçları konusunda açık olmalarına yardımcı olan bir kolaylık değeridir. cfg değerinin isteğe bağlı olduğu executable=False durumunda, yalnızca okunabilirliğe gerçekten yardımcı olduğu durumlarda bunu ayarlayın.

cfg=my_transition'yi kullanıcı tanımlı geçişleri kullanmak için de kullanabilirsiniz. Bu geçişler, derleme grafiğini daha geniş ve daha az anlaşılır hale getirme dezavantajı sayesinde kural yazarlarına yapılandırma değiştirme konusunda büyük esneklik sağlar.

Not: Geçmişte Bazel'de yürütme platformu kavramı yoktu. Bunun yerine, tüm derleme işlemlerinin ana makinede çalıştırılacağı kabul ediliyordu. 6.0'dan önceki Bazel sürümleri, bunu temsil etmek için ayrı bir "ana makine" yapılandırması oluşturuyordu. Kodda veya eski belgelerde "barındırıcı" ifadelerinin kullanılması buna işaret eder. Kavramsal oluşan bu ek yükten kaçınmak için Bazel 6.0 veya daha yeni bir sürüm kullanmanızı öneririz.

Yapılandırma parçaları

Kurallar cpp, java ve jvm gibi yapılandırma parçalarına erişebilir. Ancak erişim hatalarını önlemek için gerekli tüm parçaların bildirilmesi gerekir:

def _impl(ctx):
    # Using ctx.fragments.cpp leads to an error since it was not declared.
    x = ctx.fragments.java
    ...

my_rule = rule(
    implementation = _impl,
    fragments = ["java"],      # Required fragments of the target configuration
    host_fragments = ["java"], # Required fragments of the host configuration
    ...
)

Normalde çalıştırma dosyaları ağacındaki bir dosyanın göreli yolu, kaynak ağacındaki veya oluşturulan çıkış ağacındaki ilgili dosyanın göreli yoluyla aynıdır. Herhangi bir nedenle bunların farklı olması gerekiyorsa root_symlinks veya symlinks bağımsız değişkenlerini belirtebilirsiniz. root_symlinks, dosyalarla sözlükleri eşleyen bir sözlüktür. Burada yollar, Runfiles dizininin köküne göre belirlenir. symlinks sözlüğü aynıdır ancak yollara örtülü olarak ana çalışma alanının adı eklenir (geçerli hedefi içeren deponun adı değil).

    ...
    runfiles = ctx.runfiles(
        root_symlinks = {"some/path/here.foo": ctx.file.some_data_file2}
        symlinks = {"some/path/here.bar": ctx.file.some_data_file3}
    )
    # Creates something like:
    # sometarget.runfiles/
    #     some/
    #         path/
    #             here.foo -> some_data_file2
    #     <workspace_name>/
    #         some/
    #             path/
    #                 here.bar -> some_data_file3

symlinks veya root_symlinks kullanılıyorsa iki farklı dosyayı Runfiles ağacındaki aynı yola eşlememeye dikkat edin. Bu, derlemenin çakışmayı açıklayan bir hatayla başarısız olmasına neden olur. Bunu düzeltmek için çakışmayı kaldırmak için ctx.runfiles bağımsız değişkenlerinizi değiştirmeniz gerekir. Bu kontrol, kuralınızı kullanan tüm hedefler ve bu hedeflere bağlı olan her tür hedef için yapılır. Aracınızın başka bir araç tarafından geçişli olarak kullanılması ihtimali yüksekse bu durum özellikle risklidir. Sembolik bağlantı adlarının bir aracın çalışma dosyaları ve tüm bağımlılıkları genelinde benzersiz olması gerekir.

Kod kapsamı

coverage komutu çalıştırıldığında derlemenin belirli hedefler için kapsam araçları eklemesi gerekebilir. Derleme, gerekli kılınan kaynak dosyaların listesini de toplar. Değerlendirmeye alınan hedeflerin alt kümesi, --instrumentation_filter işaretiyle kontrol edilir. --instrument_test_targets belirtilmediği sürece test hedefleri hariç tutulur.

Bir kural uygulaması, derleme sırasında kapsam araçları eklerse bunu uygulama işlevinde dikkate alması gerekir. Bir hedefin kaynaklarının enstrümantasyonu olması gerekiyorsa ctx.coverage_instrumented, kapsam modunda doğru değerini döndürür:

# Are this rule's sources instrumented?
if ctx.coverage_instrumented():
  # Do something to turn on coverage for this compile action

Kapsam modunda her zaman açık olması gereken mantık (hedef kaynaklarının özellikle gerekli olup olmadığına bakılmaksızın), ctx.configuration.coverage_enabled

Kural, derlemeden önce bağımlılıklarından gelen kaynakları doğrudan içeriyorsa (başlık dosyaları gibi) ve bağımlılıkların kaynaklarının izlenmesi gerektiğinde derleme süresi enstrümantasyonunu da etkinleştirmesi gerekebilir:

# Are this rule's sources or any of the sources for its direct dependencies
# in deps instrumented?
if (ctx.configuration.coverage_enabled and
    (ctx.coverage_instrumented() or
     any([ctx.coverage_instrumented(dep) for dep in ctx.attr.deps]))):
    # Do something to turn on coverage for this compile action

Kurallar, hangi özelliklerin coverage_common.instrumented_files_info kullanılarak oluşturulan InstrumentedFilesInfo sağlayıcısının kapsamıyla ilgili olduğu hakkında da bilgi sağlamalıdır. instrumented_files_info öğesinin dependency_attributes parametresi, deps gibi kod bağımlılıkları ve data gibi veri bağımlılıkları dahil olmak üzere tüm çalışma zamanı bağımlılık özelliklerini listelemelidir. Kapsam araçları eklenebilecekse source_attributes parametresi, kuralın kaynak dosyaları özelliklerini listelemelidir:

def _example_library_impl(ctx):
    ...
    return [
        ...
        coverage_common.instrumented_files_info(
            ctx,
            dependency_attributes = ["deps", "data"],
            # Omitted if coverage is not supported for this rule:
            source_attributes = ["srcs", "hdrs"],
        )
        ...
    ]

InstrumentedFilesInfo döndürülmezse dependency_attributes içinde cfg, özellik şemasında "host" veya "exec" olarak ayarlanmayan her bir araç dışı bağımlılık özelliği ile varsayılan bir öğe oluşturulur. (Bu ideal bir davranış değildir çünkü source_attributes yerine dependency_attributes içinde srcs gibi özellikler koyar, ancak bağımlılık zincirindeki tüm kurallar için açık kapsam yapılandırmasına ihtiyaç duyulmaz.)

Doğrulama İşlemleri

Bazen derlemeyle ilgili bir şeyi doğrulamanız gerekir ve bu doğrulamayı yapmak için gereken bilgiler yalnızca yapılarda (kaynak dosyalar veya oluşturulan dosyalar) bulunur. Bu bilgiler yapılarda bulunduğundan, kurallar dosyaları okuyamadığından kurallar analiz zamanında bu doğrulamayı yapamaz. Bunun yerine, yürütme sırasında bu doğrulamayı yapması gerekir. Doğrulama başarısız olursa işlem ve dolayısıyla derleme başarısız olur.

Çalıştırılabilecek doğrulamalara örnek olarak statik analiz, hata analizi, bağımlılık ve tutarlılık kontrolleri ve stil kontrolleri verilebilir.

Doğrulama işlemleri, yapıları oluşturmak için gerekli olmayan işlem bölümlerini ayrı işlemlere taşıyarak derleme performansının iyileştirilmesine de yardımcı olabilir. Örneğin, derleme ve hata analizi yapan tek bir işlem bir derleme işlemine ve bir hata analizi işlemine ayrılabiliyorsa hata analizi işlemi, doğrulama işlemi olarak çalıştırılabilir ve diğer işlemlerle paralel olarak çalıştırılabilir.

Bu "doğrulama işlemleri" yalnızca girişleriyle ilgili bir şeyler belirtmeleri gerektiğinden genellikle derlemenin başka bir yerinde kullanılan herhangi bir şey üretmez. Ancak bu durum bir sorun teşkil eder: Bir doğrulama işlemi derlemenin başka bir yerinde kullanılan hiçbir şey üretmezse kural, işlemin çalıştırılmasını nasıl sağlar? Eskiden yaklaşım, doğrulama işleminin boş bir dosya çıktısını sağlamak ve bu çıktıyı derlemedeki diğer önemli bir işlemin girişlerine yapay olarak eklemekti:

Bu yöntem işe yarar çünkü Bazel, derleme işlemi çalıştırıldığında her zaman doğrulama işlemini çalıştıracak olsa da bunun önemli dezavantajları da vardır:

  1. Doğrulama işlemi, derlemenin kritik yolundadır. Bazel, derleme işlemini çalıştırmak için boş çıkışın gerekli olduğunu düşündüğünden, derleme işlemi girişi yoksaysa bile önce doğrulama işlemini çalıştırır. Bu, paralelliği azaltır ve derlemeleri yavaşlatır.

  2. Derlemedeki diğer işlemler derleme işlemi yerine çalıştırılabilirse doğrulama işlemlerinin boş çıkışlarının da bu işlemlere eklenmesi gerekir (ör. java_library kaynak jar çıkışı). Bu durum ayrıca, derleme işlemi yerine çalıştırılabilecek yeni işlemlerin daha sonra eklenmesi ve boş doğrulama çıkışının yanlışlıkla bırakılması durumunda da soruna neden olur.

Bu sorunların çözümü Doğrulamalar Çıktı Grubu'nu kullanmaktır.

Doğrulamalar Çıkış Grubu

Doğrulamalar Çıkış Grubu, doğrulama işlemlerinin aksi halde kullanılmayan çıkışlarını, diğer işlemlerin girişlerine yapay olarak eklenmeleri gerekmeyecek şekilde tutmak için tasarlanmış bir çıkış grubudur.

Bu grup, --output_groups işaretinin değerinden bağımsız olarak ve hedefin nasıl bağlı olduğuna bakılmaksızın (örneğin, komut satırında, bağımlılık olarak veya hedefin örtülü çıkışları aracılığıyla) çıkışlarının her zaman istenmesi açısından özeldir. Normal önbelleğe alma ve artımlılığın geçerli olmaya devam ettiğini unutmayın: Doğrulama işlemi için yapılan girişler değişmediyse ve doğrulama işlemi daha önce başarılıysa doğrulama işlemi çalıştırılmaz.

Bu çıkış grubunun kullanılması, doğrulama işlemlerinin boş olsa bile bazı dosyalar üretmesini gerektirir. Bu işlem, bir dosyanın oluşturulması için normalde çıkış oluşturmayan bazı araçların sarmalanmasını gerektirebilir.

Bir hedefin doğrulama işlemleri üç durumda çalıştırılmaz:

  • Hedefe bir araç olarak ihtiyaç duyulduğunda
  • Hedefe örtülü bir bağımlılık yapıldığında (örneğin, "_" ile başlayan bir özellik)
  • Hedef, ana makine veya yönetici yapılandırmasında oluşturulduğunda.

Bu hedeflerin, doğrulama hatalarını ortaya çıkaracak kendi ayrı derleme ve testlerine sahip olduğu varsayılır.

Doğrulamalar Çıkış Grubunu Kullanma

Doğrulamalar Çıkış Grubu _validation olarak adlandırılır ve diğer çıkış grupları gibi kullanılır:

def _rule_with_validation_impl(ctx):

  ctx.actions.write(ctx.outputs.main, "main output\n")

  ctx.actions.write(ctx.outputs.implicit, "implicit output\n")

  validation_output = ctx.actions.declare_file(ctx.attr.name + ".validation")
  ctx.actions.run(
      outputs = [validation_output],
      executable = ctx.executable._validation_tool,
      arguments = [validation_output.path])

  return [
    DefaultInfo(files = depset([ctx.outputs.main])),
    OutputGroupInfo(_validation = depset([validation_output])),
  ]


rule_with_validation = rule(
  implementation = _rule_with_validation_impl,
  outputs = {
    "main": "%{name}.main",
    "implicit": "%{name}.implicit",
  },
  attrs = {
    "_validation_tool": attr.label(
        default = Label("//validation_actions:validation_tool"),
        executable = True,
        cfg = "exec"),
  }
)

Doğrulama çıkış dosyasının DefaultInfo bölümüne veya başka bir işlemin girişlerine eklenmediğine dikkat edin. Hedefin etikete bağlı olduğu veya hedefin örtülü çıkışlarından herhangi birine doğrudan ya da dolaylı olarak bağımlıysa bu kural türündeki bir hedefle ilgili doğrulama işlemi çalışmaya devam eder.

Genellikle doğrulama işlemleri çıktılarının yalnızca doğrulama çıkış grubuna gitmesi ve diğer işlemlerin girişlerine eklenmemesi önemlidir. Çünkü bu, paralellik kazançlarını geçersiz kılabilir. Ancak, Bazel'in şu anda bunu uygulamak için özel bir denetimi olmadığını unutmayın. Bu nedenle, Starlark kurallarının testlerindeki hiçbir işlemin girişlerine doğrulama işlemi çıkışlarının eklenip eklenmediğini test etmeniz gerekir. Örneğin:

load("@bazel_skylib//lib:unittest.bzl", "analysistest")

def _validation_outputs_test_impl(ctx):
  env = analysistest.begin(ctx)

  actions = analysistest.target_actions(env)
  target = analysistest.target_under_test(env)
  validation_outputs = target.output_groups._validation.to_list()
  for action in actions:
    for validation_output in validation_outputs:
      if validation_output in action.inputs.to_list():
        analysistest.fail(env,
            "%s is a validation action output, but is an input to action %s" % (
                validation_output, action))

  return analysistest.end(env)

validation_outputs_test = analysistest.make(_validation_outputs_test_impl)

Doğrulama İşlemleri İşareti

Doğrulama işlemlerinin çalıştırılması, varsayılan olarak "doğru" değerini alan --run_validations komut satırı işaretiyle kontrol edilir.

Desteği sonlandırılan özellikler

Desteği sonlandırılan önceden beyan edilmiş çıkışlar

Önceden beyan edilmiş çıkışları kullanmanın desteği sonlandırılmış iki yöntemi vardır:

  • rule öğesinin outputs parametresi, önceden beyan edilmiş çıkış etiketleri oluşturmak için çıkış özelliği adları ile dize şablonları arasında bir eşleme belirtir. Önceden bildirilmemiş çıkışları kullanmayı ve çıkışları açıkça DefaultInfo.files öğesine eklemeyi tercih edin. Önceden beyan edilmiş bir çıkışın etiketi yerine çıkışı tüketen kurallar için giriş olarak kural hedefinin etiketini kullanın.

  • Yürütülebilir kurallar için ctx.outputs.executable, kural hedefiyle aynı ada sahip önceden bildirilmiş yürütülebilir bir çıkışı ifade eder. Çıkışı açıkça (örneğin, ctx.actions.declare_file(ctx.label.name) ile) beyan etmeyi tercih edin ve yürütülebilir dosyayı oluşturan komutun izinleri yürütülebilecek şekilde ayarladığından emin olun. Yürütülebilir çıkışı açık bir şekilde DefaultInfo öğesinin executable parametresine iletin.

Kaçınılması gereken Runfiles özellikleri

ctx.runfiles ve runfiles türü, birçoğu eski nedenlerle saklanan bir dizi karmaşık özelliğe sahiptir. Aşağıdaki öneriler karmaşıklığı azaltmaya yardımcı olur:

  • ctx.runfiles collect_data ve collect_default modlarını kullanmaktan kaçının. Bu modlar, çalışma dosyalarını sabit kodlu belirli bağımlılık kenarlarında karmaşık yöntemlerle, dolaylı olarak toplar. Bunun yerine, ctx.runfiles öğesinin files veya transitive_files parametrelerini kullanarak ya da bağımlılardan gelen çalıştırma dosyalarını runfiles = runfiles.merge(dep[DefaultInfo].default_runfiles) ile birleştirerek dosya ekleyin.

  • DefaultInfo oluşturucunun data_runfiles ve default_runfiles kullanımından kaçının. Bunun yerine DefaultInfo(runfiles = ...) değerini belirtin. "Varsayılan" ve "veri" çalıştırma dosyaları arasındaki ayrım, eski nedenlerle korunur. Örneğin, bazı kurallar varsayılan çıkışlarını data_runfiles konumuna yerleştirir, ancak default_runfiles içine koymaz. Kuralların data_runfiles kullanmak yerine varsayılan çıkışları içermesi ve çalıştırma dosyaları sağlayan özelliklerden default_runfiles içinde birleştirilmesi gerekir (genellikle data).

  • DefaultInfo öğesinden runfiles alırken (genellikle yalnızca geçerli kural ve bağımlılıkları arasında çalıştırma dosyalarını birleştirmek için) DefaultInfo.data_runfiles değerini kullanmayın. DefaultInfo.default_runfiles kullanın.

Eski sağlayıcılardan taşıma

Geçmişte Bazel sağlayıcıları Target nesnesindeki basit alanlardı. Bu uygulamalara nokta operatörü kullanılarak erişildi ve alan, kuralın uygulama işlevi tarafından döndürülen bir struct'ın içine yerleştirilerek oluşturuldu.

Bu stil kullanımdan kaldırıldı ve yeni kodda kullanılmamalıdır. Geçiş yapmanıza yardımcı olabilecek bilgiler için aşağıya bakın. Yeni sağlayıcı mekanizması, ad çakışmalarını önler. Ayrıca, sağlayıcı örneğine erişen kodların sağlayıcı sembolünü kullanarak almasını zorunlu kılarak veri gizlemeyi de destekler.

Şu an için, eski sağlayıcılar desteklenmektedir. Bir kural, hem eski hem de modern sağlayıcıları aşağıdaki gibi döndürebilir:

def _old_rule_impl(ctx):
  ...
  legacy_data = struct(x="foo", ...)
  modern_data = MyInfo(y="bar", ...)
  # When any legacy providers are returned, the top-level returned value is a
  # struct.
  return struct(
      # One key = value entry for each legacy provider.
      legacy_info = legacy_data,
      ...
      # Additional modern providers:
      providers = [modern_data, ...])

dep, bu kuralın bir örneği için sonuçta ortaya çıkan Target nesnesiyse sağlayıcılar ve içerikleri dep.legacy_info.x ve dep[MyInfo].y olarak alınabilir.

Döndürülen struct, providers'e ek olarak özel anlamı olan birkaç başka alanı da alabilir (dolayısıyla buna karşılık gelen bir eski sağlayıcı oluşturmaz):

  • files, runfiles, data_runfiles, default_runfiles ve executable alanları, DefaultInfo öğesinin aynı adlı alanlarına karşılık gelir. Bir DefaultInfo sağlayıcısı döndürürken bu alanlardan herhangi birinin belirtilmesine izin verilmez.

  • output_groups alanı bir struct değeri alır ve bir OutputGroupInfo değerine karşılık gelir.

provides kural bildirimlerinde ve providers bağımlılık özelliği bildirimlerinde, eski sağlayıcılar dize olarak, modern sağlayıcılar ise *Info sembolleri ile aktarılır. Taşıma sırasında dizeleri semboller olarak değiştirdiğinizden emin olun. Tüm kuralların atomik olarak güncellenmesinin zor olduğu karmaşık veya büyük kural kümeleri için aşağıdaki adım sırasını uygulamanız daha kolay olabilir:

  1. Yukarıdaki söz dizimini kullanarak eski sağlayıcıyı oluşturan kuralları hem eski hem de modern sağlayıcıları oluşturacak şekilde değiştirin. Eski sağlayıcıyı iade ettiğini belirten kurallar için bu beyanı hem eski hem de modern sağlayıcıları içerecek şekilde güncelleyin.

  2. Eski sağlayıcıyı kullanan kuralları, bunun yerine modern sağlayıcıyı kullanacak şekilde değiştirin. Herhangi bir özellik bildirimleri için eski sağlayıcı gerekiyorsa bunları da modern sağlayıcıyı gerektirecek şekilde güncelleyin. İsteğe bağlı olarak, tüketicilerin hasattr(target, 'foo') kullanarak eski sağlayıcının varlığını test etme veya FooInfo in target kullanarak yeni sağlayıcıdan birini kabul etmesini/zorunlu kılmasını sağlayarak bu çalışmayı 1. adıma ekleyebilirsiniz.

  3. Eski sağlayıcıyı tüm kurallardan tamamen kaldırın.