Kurallar

Sorun bildir Kaynağı göster

Kural, Bazel'ın bir dizi çıkış oluşturmak için girişler üzerinde gerçekleştirdiği ve kuralın uygulama işlevi tarafından döndürülen sağlayıcılarda başvurulan işlemleri tanımlar. Örneğin, bir C++ ikili kuralı şu şekilde çalışır:

  1. Bir .cpp kaynak dosyası (girişler) grubu alın.
  2. Kaynak dosyalarda g++ komutunu çalıştırın (işlem).
  3. Çalışma zamanında kullanılabilir olması için yürütülebilir çıkış ve diğer dosyalarla birlikte DefaultInfo sağlayıcısını döndürün.
  4. CcInfo sağlayıcısını, hedeften ve bağımlılıklarından toplanan C++'ya özgü bilgilerle döndürün.

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

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

Bazel'ın kendisinde birkaç kural bulunur. genrule ve filegroup gibi bu yerel kurallar, bir miktar temel destek sağlar. Kendi kurallarınızı tanımlayarak Bazel'ın yerel olarak desteklemediği diller ve araçlar için destek ekleyebilirsiniz.

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

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

Kuralın implementation işlevi, analiz aşamasında tam davranışını tanımlar. Bu işlev herhangi bir harici komut çalıştırmaz. Bunun yerine, gerekliyse kural çıkışlarını oluşturmak için yürütme aşamasında daha sonra kullanılacak eylemleri 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.

Ayrıca, rule çağrısı, kuralın yürütülebilir çıkış (executable = True ile) veya özellikle yürütülebilir bir test (test = True ile) oluşturup oluşturmadığını belirtmelidir. İkinci kural, bir test kuralı olup kuralın adı _test ile bitmelidir.

Hedef örneklendirme

Kurallar BUILD dosyalarında yüklenebilir ve ç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ı değer döndürmez ancak hedef tanımlama gibi bir yan etkiye sahiptir. Bu işleme, kuralı hazırlatma adı verilir. Bu, yeni hedef için bir ad ve hedefin özellikleri için değerleri belirtir.

Kurallar, Starlark işlevlerinden de çağrılabilir ve .bzl dosyalarına yüklenebilir. Kural çağıran Starlark işlevlerine Starlark makroları adı verilir. Starlark makroları en sonunda BUILD dosyalarından çağrılmalıdır ve yalnızca yükleme aşamasında, BUILD dosyaları hedefleri örneklendirmek için değerlendirildiğinde çağrılabilir.

Özellikler

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

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

Bağımlılık özellikleri

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

  • srcs, bir hedefin işlemleri tarafından işlenen kaynak dosyaları belirtir. Çoğunlukla özellik şeması, kuralın işlediği kaynak dosya türü için hangi dosya uzantılarının beklendiğini belirtir. Başlık dosyası içeren dil kuralları, bir hedef ve tüketicileri tarafından işlenen başlıklar için genellikle ayrı bir hdrs özelliği belirtir.
  • deps, 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, çalışma sırasında bir hedefe bağlı olan yürütülebilir herhangi bir yürütülebilir dosya için kullanılabilir hale getirilecek dosyaları belirtir. Bu, rastgele dosyaların belirtilmesine olanak tanır.
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 örneklerdir. Giriş etiketi belirten tüm özellikler (attr.label_list, attr.label veya attr.label_keyed_string_dict ile tanımlananlar), bir hedef ile hedef tanımlandığında etiketleri (veya karşılık gelen Label nesneleri) söz konusu öznitelikte listelenen hedefler arasındaki belirli türde 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 için bir bağımlılık olduğundan ö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. Bu, hedef grafiğin bir parçası olduğu ve kullanıcının bir BUILD dosyasında belirtmediği bir parça olduğu için örtülüdür. Kullanıcı çoğu zaman kuralın kullandığı aracı belirtmekle ilgilenmediğinden, örtülü bağımlılıklar bir kural ve araç arasındaki ilişkiyi (derleyici gibi bir derleme süresi bağımlılığı) sabit kodlamak için yararlıdır. Bu, kuralın uygulama işlevinde diğer bağımlılıklarla aynı şekilde değerlendirilir.

Kullanıcının bu değeri geçersiz kılmasına izin vermeden dolaylı bir bağımlılık sağlamak isterseniz, özelliğe alt çizgi (_) ile başlayan bir ad vererek gizli özelliğini ayarlayabilirsiniz. Ö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 bir örtülü bağımlılığa sahiptir. Bu işlem, example_library uygulama işlevinin, kullanıcı etiketini giriş olarak iletmese bile derleyiciyi çağıran işlemler oluşturmasına olanak tanır. _compiler gizli bir özellik olduğundan, ctx.attr._compiler kuralının bu kural türünün tüm hedeflerinde her zaman //tools:example_compiler öğesine işaret edeceğini izler. Alternatif olarak, compiler özelliğini alt çizgi olmadan adlandırabilir ve varsayılan değeri koruyabilirsiniz. Bu, gerektiğinde kullanıcıların farklı bir derleyiciyi değiştirmesine izin verir ancak derleyicinin etiketine ilişkin bilgi sahibi olmayı gerektirmez.

Örtülü 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, bu aracı bir araç zincirinden almalıdır.

Çıkış özellikleri

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

  • Başka bir yerde tanımlanan hedeflere başvurmak yerine çıkış dosyası hedeflerini tanımlarlar.
  • Çıkış dosyası hedefleri, örneklenen kural hedefine değil, oluşturulan kural hedefine bağlıdır.

Genelde, çıkış özellikleri yalnızca bir kuralın hedef ada dayalı olamayacak kullanıcı tanımlı adlarla çıkışlar oluşturması gerektiğinde kullanılır. Kuralın bir çıkış özelliği varsa bu özellik genellikle out veya outs olarak adlandırılır.

Çıkış özellikleri, özellikle bağımlı olabilecek veya komut satırında istenebilen önceden tanımlanmış çıkışlar oluşturmanın tercih edilen yöntemidir.

Uygulama işlevi

Her kural için bir implementation işlevi gerekir. Bu işlevler kesinlikle 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 bir eylem grafiğine dönüştürür. Dolayısıyla, uygulama işlevleri gerçekte dosya okuyamaz veya yazamaz.

Kural uygulama işlevleri genellikle gizlidir (başta bir alt çizgiyle adlandırılır). Geleneksel olarak bu kurallar, kurallarıyla aynı şekilde adlandırılır ancak son eki _impl olur.

Uygulama işlevleri tam olarak tek bir parametre alır: normalde ctx olarak adlandırılan bir kural bağlamı. Bunlar, bir sağlayıcı listesi döndürür.

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 sağlayıcıları içerir.

ctx.attr, her bağımlılık özelliğinin adına karşılık gelen alanlara sahiptir ve bu özelliği kullanan her doğrudan bağımlılığı temsil eden Target nesnelerini içerir. label_list özellikleri için bu listede Targets yer alır. label özellikleri için bu tek bir Target veya None olur.

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

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

Bunlara dizin gösterimi ([]) kullanılarak ve sağlayıcı türü anahtar 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 hdrs özelliğini kullanarak başlık dosyalarını alır ve bu dosyaları hedefin ve tüketicilerinin derleme işlemlerine sağlarsa şu şekilde toplayabilir:

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

Eski bir struct stili vardır. Bu stil kesinlikle önerilmez ve kuralların bu stilden taşınması gerekir.

Files

Dosyalar File nesneleriyle temsil edilir. Bazel, analiz aşamasında dosya G/Ç'si 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 yayınlayan işlevlere (bkz. ctx.actions) geçirilir.

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 çıktısı olamaz.

Her bağımlılık özelliğinde, ilgili ctx.files alanı, bu özelliği kullanan 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, özellikleri allow_single_file = True olan 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 teknik özellikleri executable = True olan bağımlılık özellikleri için alanları içerir.

Çıkışları bildirme

Analiz aşamasında, bir kuralın uygulama işlevi çıktı 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 nesneleri 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 tanımlanmış çıkışlarda, bunun yerine karşılık gelen ctx.outputs alanlarından File nesneleri alınabilir.

İşlemler

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

İşlem oluşturan genel amaçlı işlevler ctx.actions içinde tanımlanmıştır:

ctx.actions.args, işlemlere ilişkin bağımsız değişkenleri verimli bir şekilde toplamak için kullanılabilir. Yürütme zamanına kadar düzleşmeyi ö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 yerlerini kaldırı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ıkların sağlayıcıları dahil olmak üzere özelliklerin değerine bağlı olabilir ancak yürütmenin sonucuna bağlı olamaz. Örneğin, işleminiz sıkıştırma açma komutunu çalıştırıyorsa hangi dosyaların şişirilmesini beklediğinizi (zip açma işlemi çalıştırmadan önce) belirtmeniz gerekir. Dahili olarak değişken sayıda dosya oluşturan işlemler, bu dosyaları tek bir dosyada (ör. zip, tar veya başka bir arşiv biçimi) sarmalayabilir.

İşlemlerde tüm girişler listelenmelidir. Kullanılmayan girişlerin listelenmesine izin verilir ancak verimsizdir.

İşlemler, tüm çıktılarını oluşturmalıdır. Başka dosyalar yazabilirler ancak çıktılarda olmayanlar tüketiciler tarafından kullanılamaz. Bildirilen tüm çıkışların bir işlem tarafından yazılması gerekir.

İşlemler, salt işlevlere benzer: Yalnızca sağlanan girişlere bağlı olmalı ve bilgisayar bilgilerine, kullanıcı adı, saat, ağ veya G/Ç cihazlarına (girişleri okuma ve yazma çıkışları hariç) erişmekten kaçınmalıdırlar. Çıkış önbelleğe alınıp yeniden kullanılacağından bu önemli bir husustur.

Bağımlılıklar, hangi işlemlerin yürütüleceğine karar veren Bazel tarafından çözümlenir. Bağımlılık grafiğinde bir döngü varsa bu bir hatadır. Bir işlemin oluşturulması işlemin yürütüleceğini garanti etmez. Bu, çıkış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 ona bağlı başka kurallara sunduğu bilgi parçalarıdır. Bu veriler arasında çıkış dosyaları, kitaplıklar, bir aracın komut satırına aktarılacak parametreler veya hedef tüketicilerinin bilmesi gereken diğer her şey bulunabilir.

Bir kuralın uygulama işlevi, sağlayıcıları yalnızca örneklenen hedefin anlık bağımlılıklarından okuyabildiğinden, kuralların hedefin tüketicileri tarafından bilinmesi gereken, genellikle bu bilgileri bir depset içinde toplayarak hedef bağımlılıklarındaki tüm bilgileri yönlendirmesi gerekir.

Hedefin sağlayıcıları, uygulama işlevi tarafından döndürülen sağlayıcı nesneleri 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 bu stilden taşınmalıdır.

Varsayılan çıkışlar

Hedeflerin 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 oluşturulur.

Varsayılan çıkışlar, DefaultInfo öğesinin 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 tanımlanmış çıkışlara (genellikle çıkış özellikleri tarafından oluşturulanlar) ayarlanır.

Eylem gerçekleştiren kurallar, doğrudan kullanılması beklenmese bile varsayılan çıkışlar sağlamalıdır. İstenen çıktıların grafiğinde yer almayan işlemler kısaltılır. Çıktı yalnızca hedefin tüketicileri tarafından kullanılıyorsa hedef ayrı olarak oluşturulduğunda bu işlemler gerçekleştirilmez. Sadece başarısız olan hedefin yeniden oluşturulması hataya neden olmayacağı için bu, hata ayıklamayı daha zor hale getirir.

Çalıştırma dosyaları

Çalıştırma dosyaları, çalışma zamanında (derleme zamanı yerine) hedef tarafından kullanılan bir dosya grubudur. 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ın çalışma zamanı sırasında çalıştırma dosyalarına erişebilmesi için ortamı aşamaz.

Çalıştırma dosyaları, kural oluşturma işlemi sırasında manuel olarak eklenebilir. runfiles nesneleri, ctx.runfiles kural bağlamında runfiles yöntemiyle oluşturulabilir ve DefaultInfo üzerinde runfiles parametresine aktarılabilir. Yürütülebilir kuralların yürütülebilir çıkışı, çalıştırma dosyalarına dolaylı olarak eklenir.

Bazı kurallar, çıkışları bir hedeflerin çalıştırma dosyalarına eklenen, genellikle data adlı özellikleri belirtir. Çalıştırma dosyaları hem data hem de nihai yürütme için kod sağlayabilecek tüm özelliklerden (genellikle srcs (ilişkili data ile filegroup hedefleri içerebilir) ve deps) 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 özgü 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.",
    },
)

Kural uygulama işlevleri, sağlayıcı örnekleri derleyip 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ı özel başlatma

Özel ön işleme ve doğrulama mantığıyla bir sağlayıcının örneklenmesini korumak mümkündür. Bu, tüm sağlayıcı örneklerinin belirli sabit değerleri karşılamasını sağlamak veya kullanıcılara örnek elde etmeleri için daha temiz bir API sunmak amacıyla kullanılabilir.

Bu, provider işlevine bir init geri çağırması geçirilerek gerçekleştirilir. Bu geri çağırma sağlanırsa provider() döndürülme türü, iki değerden oluşan bir unsur olacak şekilde değişir: init kullanılmadığında normal döndürülen değer olan sağlayıcı simgesi ve "ham kurucu".

Bu durumda, sağlayıcı 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ürdüğü değer, alan adlarını (dizeler) değerlerle eşleştiren bir dikte biçiminde olmalıdır. Bu değer, yeni örneğin alanlarını başlatmak için kullanılır. Geri çağırmanın 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ğırma işlemini atlar.

Aşağıdaki örnekte, bağımsız değişkenlerinin ön işlenmesi ve doğrulanması için init kullanılmaktadır:

# //pkg:exampleinfo.bzl

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

# 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(
    fields = ["files_to_link", "headers"],
    init = _exampleinfo_init,
)

Bir kural uygulaması, sağlayıcıyı aşağıdaki gibi ö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 genel fabrika işlevleri tanımlamak için kullanılabilir. Örneğin, exampleinfo.bzl dosyası şunları tanımlayabilir:

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 bunu yükleyemez ve rastgele sağlayıcı örnekleri oluşturamaz.

init işlevinin bir başka kullanımı da kullanıcının sağlayıcı simgesini tamamen çağırmasını engellemek ve 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, bazel run komutuyla çağrılabilecek hedefleri tanımlar. Test kuralları, hedefleri bazel test komutuyla da başlatılabilen özel bir yürütülebilir 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 bitmelidir. (Test hedef adları da genellikle kural gereği _test ile biter, ancak bu zorunlu değildir.) Test dışı kurallarda bu son ek bulunmamalıdır.

Her iki kural türü de run veya test komutları ile çağrılacak yürütülebilir bir çıkış dosyası (önceden bildirilmiş olabilir veya olmayabilir) üretmelidir. Bazel'a kural çıkışlarından hangisinin bu yürütülebilir dosya olarak kullanılacağını bildirmek için bunu, döndürülen bir 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 (bunu hem executable hem de files için iletmeniz gerekmez). Ayrıca dolaylı olarak runfiles'e 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. Bir ctx.actions.run veya ctx.actions.run_shell işlemi için bu, 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ış nedeniyle yürütülebilir kuralların önceden tanımlanmış özel bir ctx.outputs.executable çıkışı vardır. Bu dosya, DefaultInfo kullanarak bir dosya belirtmezseniz varsayılan yürütülebilir dosya olarak hizmet verir; aksi halde kullanılmamalıdır. Bu çıkış mekanizması, analiz sırasında yürütülebilir dosyanın adının özelleştirilmesini desteklemediğinden kullanımdan kaldırılmıştır.

Yürütülebilir kural ve test kuralı örneklerine bakın.

Yürütülebilir kurallar ve test kuralları, tüm kurallar için eklenenlerin yanı sıra dolaylı olarak tanımlanmış ek özelliklere sahiptir. Dolaylı olarak eklenen özelliklerin varsayılanları değiştirilemez, ancak bir Starlark makrosu içine varsayılan ayarı değiştiren özel bir kural sarmalanarak bu sorun çö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 hemen yanında olur. Yollar şu şekilde 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 dizininin bitişiğindedir. Ancak, çalıştırma dosyalarından gelen ikili programlar aynı varsayımda bulunamaz. Bu tür durumların hafifletilmesi için her ikili program, bir ortam, komut satırı bağımsız değişkeni veya işaret kullanarak parametre olarak runfiles kökünü kabul edecek bir yol sağlamalıdır. Bu, ikili programların doğru standart çalıştırma dosyaları kökünü çağırdığı ikili programlara geçirmesine olanak tanır. Bu politika ayarlanmazsa ikili program, çağrılan ilk ikili program olduğunu tahmin edebilir ve bitişik bir runfiles dizini arayabilir.

İleri düzey konular

Çıkış dosyalarını isteme

Tek bir hedefin birkaç çı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 istenen bu dosyaları ve bunların doğrudan veya dolaylı olarak bağımlı olduğu dosyaları oluşturur. (Eylem grafiğine bakacak olursak, Bazel yalnızca istenen dosyaların geçişli bağımlılıkları olarak erişilebilecek işlemleri yürütür.)

Varsayılan çıkışlara ek olarak önceden tanımlanmış çıkışlar da komut satırında açıkça istenebilir. Kurallar, çıkış özelliklerini kullanarak önceden tanımlanmış çıkışları belirtebilir. Bu durumda kullanıcı, kuralı somutlaştırırken çıkışlar için etiketleri açık bir şekilde seçer. Çıkış özellikleri için File nesneleri elde etmek amacıyla ctx.outputs özelliğinin karşılık gelen özelliğini kullanın. Kurallar, hedef adına göre önceden tanımlanmış çıkışları da dolaylı olarak tanımlayabilir, ancak bu özellik kullanımdan kaldı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 istenebilir. Ö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 etiketi olmadığından, bunlar yalnızca varsayılan çıkışlarda veya bir çıkış grubunda görünerek istenebilir.

Çıkış grupları, OutputGroupInfo sağlayıcısı ile belirtilebilir. Birçok yerleşik sağlayıcının aksine OutputGroupInfo, bu ada sahip çıkış gruplarını tanımlamak için rastgele adlara sahip parametreleri alabilir:

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 elde edilen sağlayıcılar birleştirilir.

Belirli dosya türlerini bir hedeften tüketicilerin işlemlerine aktarmak için genellikle OutputGroupInfo kullanılmamalıdır. Bunun yerine bunun için kurala özel sağlayıcılar tanımlayın.

Yapılandırmalar

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

Bu nedenle, Bazel'da "yapılandırmalar" ve geçişler kavramı vardır. En üstteki hedefler (komut satırında istenenler) "hedef" yapılandırmasında yerleşiktir. Yürütme platformunda çalışması gereken araçlar ise yerleşik bir "exec" yapılandırmasıdır. Kurallar, yapılandırmaya bağlı olarak örneğin derleyiciye iletilen CPU mimarisini değiştirmek gibi farklı işlemler oluşturabilir. Bazı durumlarda, farklı yapılandırmalar için aynı kitaplık gerekebilir. Böyle bir durumda, analiz edilir ve birden çok kez oluşturulabilir.

Varsayılan olarak Bazel, hedefin bağımlılıklarını hedefin kendisiyle aynı yapılandırmada, diğer bir deyişle geçiş olmadan oluşturur. Bağımlılık, hedefin oluşturulmasına yardımcı olmak için ihtiyaç duyulan bir araç olduğunda, karşılık gelen özellik, exec 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 derlemesine neden olur.

Her bir bağımlılık özelliğinde bağımlılıkların aynı yapılandırmada derlenmesi veya bir exec yapılandırmasına geçiş yapması 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. Bu, bir aracın yanlışlıkla yanlış yapılandırmaya yönelik oluşturulmasını önlemek içindir. Örneği inceleyin

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

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

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

cfg = "target" aslında hiçbir şey yapmaz. Sadece kural tasarımcılarının niyetlerini açıkça ifade etmelerine yardımcı olmak amacıyla sunulan bir kolaylık değeridir. executable = False (yani cfg) isteğe bağlı olduğunda bunu yalnızca okunabilirliğe gerçekten yardımcı olduğunda ayarlayın.

Kullanıcı tanımlı geçişleri kullanmak için de cfg = my_transition kullanabilirsiniz. Bu geçişler, oluşturma grafiğini daha büyük ve daha az anlaşılır hale getirme geriliğiyle birlikte kural yazarlarına yapılandırma değiştirme konusunda büyük oranda esneklik sağlar.

Not: Geçmişte, Bazel'da yürütme platformu kavramı yoktu. Bunun yerine, tüm derleme işlemleri ana makinede çalışacak şekilde değerlendiriliyordu. 6.0'dan önceki Bazel sürümleri bunu temsil edecek ayrı bir "ana makine" yapılandırması oluşturuyordu. Kodda veya eski belgelerde "host" (ana makine) terimine referanslar görüyorsanız bu durum bu anlama gelir. Bu kavramsal 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 ve java gibi yapılandırma parçalarına erişebilir. Bununla birlikte, erişim hatalarını önlemek için gerekli tüm parçalar bildirilmelidir:

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
    ...
)

Normalde çalıştırma dosyaları ağacındaki bir dosyanın göreli yolu, kaynak ağaçtaki veya oluşturulan çıkış ağacındaki ilgili dosyanın yoluyla aynıdır. Herhangi bir nedenle bunların farklı olması gerekirse root_symlinks veya symlinks bağımsız değişkenlerini belirtebilirsiniz. root_symlinks, yolların dosyalara eşlendiği bir sözlüktür. Yollar, runfiles dizininin köküne göredir. symlinks sözlüğü aynıdır ancak yolların ön ekine dolaylı 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ı çalıştırma dosyaları ağacında aynı yola eşlememeye dikkat edin. Bu durumda derleme, çakışmayı açıklayan bir hata mesajıyla başarısız olur. Sorunu düzeltmek için ctx.runfiles bağımsız değişkenlerini değiştirerek çakışmayı ortadan kaldırmanız gerekir. Bu kontrol, kuralınızı kullanan tüm hedeflerin yanı sıra bu hedeflere bağlı olan tüm hedefler için yapılır. Bu, özellikle aracınızın başka bir araç tarafından geçişli olarak kullanılması ihtimali varsa risklidir. Simgesel bağlantı adları, bir aracın çalıştırma dosyaları ve tüm bağımlılıkları genelinde benzersiz olmalıdır.

Kod kapsamı

coverage komutu çalıştırıldığında, derlemenin belirli hedefler için kapsam araçları eklemesi gerekebilir. Derleme, ayrıca donatılmış kaynak dosyaların listesini de toplar. Dikkate alınan hedeflerin alt kümesi, işaret --instrumentation_filter tarafından 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 hesaba katması gerekir. Bir hedefin kaynaklarının ayarlanması gerekiyorsa ctx.coverage_instrumented, kapsam modunda True 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 (bir hedefin kaynaklarının özellikle donatılmış olup olmamasından bağımsız olarak) ctx.configuration.coverage_enabled öğesine koşullandırılabilir.

Kural, derlemeden önce bağımlılıklarındaki kaynakları doğrudan dahil ediyorsa (başlık dosyaları gibi) bağımlılıkların kaynaklarının ayarlanması gerekiyorsa derleme zamanı enstrümantasyonunun da etkinleştirilmesi 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, coverage_common.instrumented_files_info kullanılarak oluşturulan ve InstrumentedFilesInfo sağlayıcısının kapsamıyla ilgili olan özellikler 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 enstrümantasyonu 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çindeki özellik şemasında cfg özelliğini "exec" olarak ayarlamayan araç dışı her bağımlılık özelliğiyle bir varsayılan değer oluşturulur. (Bu, srcs gibi özellikleri source_attributes yerine dependency_attributes öğesine yerleştirdiği için ideal bir davranış değildir ancak bağımlılık zincirindeki tüm kurallar için açık kapsam yapılandırması ihtiyacını ortadan kaldırır.)

Doğrulama İşlemleri

Bazen derlemeyle ilgili bir şeyi doğrulamanız gerekir ve bu doğrulama için gereken bilgiler yalnızca yapılarda (kaynak dosyalar veya oluşturulan dosyalar) bulunur. Bu bilgiler yapılarda bulunduğundan, kurallar dosyaları okuyamadığı için kurallar analiz sırasında bu doğrulamayı yapamaz. Bunun yerine, işlemlerin bu doğrulamayı yürütme sırasında yapması gerekir. Doğrulama başarısız olduğunda işlem başarısız olur ve dolayısıyla derleme de 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 ile stil kontrolleri verilebilir.

Doğrulama işlemleri, yapıları derlemek için gerekli olmayan işlemlerin parçalarını 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 ve hata ayıklama işlemine ayrılabiliyorsa bu işlem, 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 hiçbir şey üretmez. Ancak bu durum bir sorun teşkil eder: 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? Geçmişte yaklaşım, doğrulama işlemi sonucunda boş bir dosya oluşturmak ve bu çıkışı yapay olarak derlemedeki başka bir önemli işlemin girişlerine eklemekti:

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

  1. Doğrulama işlemi, derlemenin kritik yolunda. 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 ilk olarak doğrulama işlemini çalıştırır. Bu, paralelliği azaltır ve derlemeleri yavaşlatır.

  2. Derleme işlemi yerine derleme işlemi yerine başka işlemler çalıştırılabiliyorsa doğrulama işlemlerinin boş çıkışlarının de bu işlemlere (örneğin, java_library öğesinin kaynak jar çıkışı) eklenmesi gerekir. Bu durum, daha sonra derleme işlemi yerine çalıştırılabilecek yeni işlemlerin eklenmesi ve boş doğrulama çıkışının yanlışlıkla bırakılması durumunda da bir sorun teşkil eder.

Bu sorunların çözümü, Doğrulama Çıkış Grubu'nu kullanmaktır.

Doğrulamalar Çıktı Grubu

Doğrulama Çıkışları Grubu, doğrulama işlemlerinin normalde kullanılmayan çıkışlarını tutmak ve bu sayede diğer işlemlerin girişlerine yapay olarak eklenmesine gerek kalmaması 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ğundan bağımsız olarak (ö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 hâlâ geçerli olduğunu unutmayın: Doğrulama işlemine yönelik girişler değişmediyse ve doğrulama işlemi daha önce başarılı olduysa doğrulama işlemi çalıştırılmaz.

Bu çıkış grubunu kullanmak için doğrulama işlemlerinin, boş bir dosya da dahil olmak üzere bazı dosyalar çıkarması gerekir. Bu, normalde çıkış oluşturmayan bazı araçların bir dosyanın oluşturulması için sarmalanmasını gerektirebilir.

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

  • Hedefin bir araç olarak kullanılması gerektiğinde
  • Hedef, örtülü bağımlılık olarak (ör. "_" ile başlayan bir özellik) bağımlı olduğunda
  • Hedef, yönetici yapılandırmasında derlendiğinde.

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

Doğrulamalar Çıkış Grubunu Kullanma

Doğrulama Çıkış Grubu, _validation olarak adlandırılmıştır ve diğer tüm çı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 öğesine veya girişlerin başka herhangi bir işleme eklenmediğine dikkat edin. Bu kural türündeki bir hedef için doğrulama işlemi, hedefe etikete göre bağımlıysa veya hedefin örtülü çıkışlarından herhangi birine doğrudan ya da dolaylı olarak bağımlıysa yine de çalışır.

Paralellik kazançlarını ortadan kaldırabileceğinden, doğrulama işlemlerinin çıkışlarının yalnızca doğrulama çıkış grubuna gitmesi ve diğer işlemlerin girişlerine eklenmemesi genellikle önemlidir. Ancak, Bazel'in bunu uygulamak için herhangi bir özel kontrolü yoktur. Bu nedenle, Starlark kuralları testlerindeki hiçbir işlemin girişlerine doğrulama işlemi çıkışlarının 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 true şeklinde ayarlanan --run_validations komut satırı işareti ile kontrol edilir.

Desteği sonlandırılan özellikler

Kullanımdan kaldırılmış önceden tanımlanmış çıkışlar

Önceden tanımlanmış çıkışları kullanmanın kullanımdan kaldırılan iki yolu vardır:

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

  • Yürütülebilir kurallar söz konusu olduğunda ctx.outputs.executable, kural hedefiyle aynı ada sahip, önceden tanımlanmış yürütülebilir bir çıkış anlamına gelir. Çıkışı, örneğin ctx.actions.declare_file(ctx.label.name) ile açıkça belirtmeyi tercih edin ve yürütülebilir dosyayı oluşturan komutun izinlerini yürütülebilir şekilde ayarladığından emin olun. Yürütülebilir çıkışı açıkça DefaultInfo öğesinin executable parametresine iletin.

Kaçınılması gereken Runfiles özellikleri

ctx.runfiles ve runfiles türü, çoğu eski nedenlerden dolayı saklanan karmaşık bir özellik grubuna sahiptir. Aşağıdaki öneriler karmaşıklığı azaltmaya yardımcı olur:

  • ctx.runfiles özelliğinin collect_data ve collect_default modlarını kullanmayın. Bu modlar, sabit kodlu belirli bağımlılık kenarlarında çalıştırma dosyaları üzerinde kafa karıştırıcı bir şekilde, dolaylı yoldan toplanır. Bunun yerine, ctx.runfiles öğesinin files veya transitive_files parametrelerini kullanarak ya da bağımlılıklardaki ç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 öğelerini kullanmayın. Bunun yerine DefaultInfo(runfiles = ...) değerini belirtin. "Varsayılan" ve "veri" çalıştırma dosyaları arasındaki ayrım, eski nedenlerden dolayı korunur. Örneğin, bazı kurallar varsayılan çıkışlarını data_runfiles öğesine yerleştirir ancak default_runfiles öğesine yerleştirmez. Kurallar data_runfiles kullanmak yerine, varsayılan çıkışların her ikisini de içermeli ve runfiles (genellikle data) sağlayan özelliklerden default_runfiles ile birleştirilmelidir.

  • DefaultInfo öğesinden runfiles alırken (genellikle yalnızca mevcut kural ile bağımlılıkları arasındaki çalıştırma dosyalarını birleştirmek için) DefaultInfo.data_runfiles kullanmayın, DefaultInfo.default_runfiles kullanın.

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

Önceden, Bazel sağlayıcıları Target nesnesindeki basit alanlardı. Bunlara nokta operatörü kullanılarak erişiliyordu ve alan, sağlayıcı nesneleri listesi yerine kuralın uygulama işlevi tarafından döndürülen bir struct içine yerleştirilerek oluşturuldu:

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

Bu tür 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 kullanımdan kaldırılmıştır 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, bir sağlayıcı örneğine erişen herhangi bir kodun sağlayıcı sembolünü kullanarak getirmesini gerektirerek veri gizlemeyi de destekler.

Şu an için eski sağlayıcılar desteklenmeye devam etmektedir. 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, ...])

Bu kuralın bir örneğinde dep sonucunda Target nesnesi oluşursa sağlayıcılar ve bunların içerikleri, dep.legacy_info.x ve dep[MyInfo].y olarak alınabilir.

providers'a ek olarak, döndürülen struct özel anlamı olan diğer alanları da alabilir (dolayısıyla bunlara karşılık gelen bir eski sağlayıcı oluşturmaz):

  • files, runfiles, data_runfiles, default_runfiles ve executable alanları DefaultInfo ile aynı adlı alanlara karşılık gelir. Bu alanlardan herhangi birinin belirtilmesine ve aynı zamanda bir DefaultInfo sağlayıcısının döndürülmesine izin verilmez.

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

provides kural bildirimlerinde ve providers bağımlılık özellikleri bildirimlerinde, eski sağlayıcılar dize olarak, modern sağlayıcılar ise Info sembolüyle geçirilir. Taşıma sırasında dizelerden simgelere geçtiğinizden emin olun. Tüm kuralların anatomik 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ı uygulayarak süreci daha kolay halledebilirsiniz:

  1. Eski sağlayıcıyı üreten kuralları, önceki söz dizimini kullanarak hem eski hem de modern sağlayıcıları oluşturacak şekilde değiştirin. Eski sağlayıcıyı döndürdüklerini bildiren 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 şu sağlayıcıyı kabul etmesini veya zorunlu kılmasını sağlayarak bu çalışmayı 1. adımla ekleyebilirsiniz: hasattr(target, 'foo') kullanarak eski sağlayıcının veya FooInfo in target ile yeni sağlayıcının varlığını test etme.

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