Özellikler

Sorun bildirin Kaynağı göster

Bu sayfada, yönleri kullanmanın temelleri ve faydaları ile basit ve ileri düzey örnekler verilmiştir.

Görünümler, ek bilgi ve eylemlerle birlikte bağımlılık grafiklerinin artırılmasına olanak tanır. Özelliklerin yararlı olabileceği bazı tipik senaryolar:

  • Bazel'i entegre eden IDE'ler, proje hakkında bilgi toplamak için farklı yönleri kullanabilir.
  • Kod oluşturma araçları, girdilerini hedeften bağımsız şekilde yürütmek için özelliklerden yararlanabilir. Örneğin, BUILD dosyaları protobuf kitaplık tanımları hiyerarşisini belirtebilir ve dile özgü kurallar, belirli bir dil için protobuf destek kodu oluşturan işlemleri eklemek için hususları kullanabilir.

Aspect ile ilgili temel bilgiler

BUILD dosyaları, projenin kaynak koduyla ilgili açıklama sunar: Hangi kaynak dosyaların projenin parçası olduğu, hangi yapıların (hedefler) bu dosyalardan oluşturulması gerektiği, bu dosyalar arasındaki bağımlılıkların neler olduğu vb. Bazel, derlemeyi gerçekleştirmek için bu bilgileri kullanır. Yani yapıları oluşturmak için gereken işlem kümesini (derleyici veya bağlayıcı işlemleri çalıştırmak gibi) belirler. Bazel bunu hedefler arasında bir bağımlılık grafiği oluşturup bu işlemleri toplamak için bu grafiği ziyaret ederek başarabilir.

Şu BUILD dosyasını göz önünde bulundurun:

java_library(name = 'W', ...)
java_library(name = 'Y', deps = [':W'], ...)
java_library(name = 'Z', deps = [':W'], ...)
java_library(name = 'Q', ...)
java_library(name = 'T', deps = [':Q'], ...)
java_library(name = 'X', deps = [':Y',':Z'], runtime_deps = [':T'], ...)

Bu BUILD dosyası, aşağıdaki şekilde gösterilen bir bağımlılık grafiğini tanımlar:

Grafik oluştur

Şekil 1. BUILD dosya bağımlılık grafiği.

Bazel, yukarıdaki örnekte her hedef için karşılık gelen kuralın (bu örnekte "java_library") uygulama işlevini çağırarak bu bağımlılık grafiğini analiz eder. Kural uygulama işlevleri, .jar dosyaları gibi yapılar oluşturan ve bu yapıların konumları ve adları gibi bilgileri sağlayıcılardaki hedeflerin ters bağımlılıklarına ileten işlemler oluşturur.

Yönler, işlem oluşturan ve sağlayıcı döndüren bir uygulama işlevine sahip olmaları açısından kurallara benzer. Ancak güçleri, bağımlılık grafiğinin onlar için oluşturulma biçiminden gelir. Bir özelliğin bir uygulaması ve birlikte yayıldığı tüm özelliklerin listesi vardır. "Deps" adı verilen özelliklerde yayan bir özellik olan A özelliğini düşünün. Bu yön, hedef X'e uygulanarak bir en boy uygulama düğümü A(X) elde edilir. Uygulama sırasında A noktası, X'in "deps" özelliğinde (A'nın yayılım listesindeki tüm özellikler) belirttiği tüm hedeflere yinelemeli olarak uygulanır.

Bu yüzden, A niteliğinin bir hedef X'e uygulanması tek bir eylem, aşağıdaki şekilde gösterilen hedeflerin orijinal bağımlılık grafiğinin bir "gölge grafiğini" oluşturur:

En Boy Oranıyla Grafik Oluşturma

Şekil 2. Farklı yönleri olan bir grafik oluşturun.

Gölgelendirilen yalnızca kenarlar, yayılım kümesindeki özellikler boyunca kenarlar olduğundan, bu örnekte runtime_deps kenarı gölgelendirilmez. Daha sonra, orijinal grafiğin düğümlerinde kural uygulamalarının çağrılmasına benzer şekilde, gölge grafiğindeki tüm düğümlerde bir en boy uygulama işlevi çağrılır.

Basit örnek

Bu örnekte, bir kural için kaynak dosyalarının ve deps özelliğine sahip tüm bağımlılıklarının tekrarlı olarak nasıl yazdırılacağı gösterilmektedir. Burada bir özellik uygulaması, bir en boy tanımı ve Bazel komut satırından özelliğin nasıl çağrılacağı gösterilmektedir.

def _print_aspect_impl(target, ctx):
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the files that make up the sources and
        # print their paths.
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                print(f.path)
    return []

print_aspect = aspect(
    implementation = _print_aspect_impl,
    attr_aspects = ['deps'],
)

Örneği bölümlere ayıralım ve her birini ayrı ayrı inceleyelim.

En boy tanımı

print_aspect = aspect(
    implementation = _print_aspect_impl,
    attr_aspects = ['deps'],
)

En boy tanımları, kural tanımlarına benzer ve aspect işlevi kullanılarak tanımlanır.

Kuralda olduğu gibi, bir özelliğin de bir uygulama işlevi vardır. Bu örnekte _print_aspect_impl olan öğedir.

attr_aspects, özelliğin yayıldığı kural özelliklerinin listesidir. Bu durumda, özellik, uygulandığı kuralların deps özelliği boyunca yayılır.

attr_aspects için yaygın olarak kullanılan bir diğer bağımsız değişken ['*'], bu bağımsız değişkeni bir kuralın tüm özelliklerine yayar.

Aspect uygulaması

def _print_aspect_impl(target, ctx):
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the files that make up the sources and
        # print their paths.
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                print(f.path)
    return []

Aspect uygulama işlevleri, kural uygulama işlevlerine benzer. Sağlayıcılar döndürürler, işlemler oluşturabilirler ve iki bağımsız değişken alırlar:

  • target: Özelliğin uygulandığı hedef.
  • ctx: Özelliklere erişmek ve çıkışlar ile işlemler oluşturmak için kullanılabilecek ctx nesnesi.

Uygulama işlevi, hedef kuralın özelliklerine ctx.rule.attr üzerinden erişebilir. Uygulandığı hedef tarafından sağlanan sağlayıcıları inceleyebilir (target bağımsız değişkeni aracılığıyla).

Sağlayıcı listesini döndürmek için özellikler gerekir. Bu örnekte, özellik hiçbir şey sağlamadığından boş bir liste döndürür.

Komut satırını kullanarak detayı çağırma

Bir özelliği uygulamanın en basit yolu, --aspects bağımsız değişkenini kullanarak komut satırını kullanmaktır. Yukarıdaki özelliğin print.bzl adlı bir dosyada tanımlandığı varsayıldığında:

bazel build //MyExample:example --aspects print.bzl%print_aspect

print_aspect öğesini, example hedefine ve deps özelliği aracılığıyla yinelenen olarak erişilebilen tüm hedef kurallarına uygular.

--aspects işareti bir bağımsız değişken alır. Bu bağımsız değişken, özelliğin <extension file label>%<aspect top-level name> biçimindeki spesifikasyonudur.

Gelişmiş örnek

Aşağıdaki örnekte, hedeflerdeki dosyaları sayan ve potansiyel olarak uzantıya göre filtreleyen hedef kuralındaki bir özelliğin kullanıldığı gösterilmektedir. Değer döndürmek için sağlayıcının nasıl kullanılacağı, en boy uygulamasına bağımsız değişken iletmek için parametrelerin nasıl kullanılacağı ve kuraldan bir özelliğin nasıl çağrılacağı gösterilmektedir.

file_count.bzl dosyası:

FileCountInfo = provider(
    fields = {
        'count' : 'number of files'
    }
)

def _file_count_aspect_impl(target, ctx):
    count = 0
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the sources counting files
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
                    count = count + 1
    # Get the counts from our dependencies.
    for dep in ctx.rule.attr.deps:
        count = count + dep[FileCountInfo].count
    return [FileCountInfo(count = count)]

file_count_aspect = aspect(
    implementation = _file_count_aspect_impl,
    attr_aspects = ['deps'],
    attrs = {
        'extension' : attr.string(values = ['*', 'h', 'cc']),
    }
)

def _file_count_rule_impl(ctx):
    for dep in ctx.attr.deps:
        print(dep[FileCountInfo].count)

file_count_rule = rule(
    implementation = _file_count_rule_impl,
    attrs = {
        'deps' : attr.label_list(aspects = [file_count_aspect]),
        'extension' : attr.string(default = '*'),
    },
)

BUILD.bazel dosyası:

load('//:file_count.bzl', 'file_count_rule')

cc_library(
    name = 'lib',
    srcs = [
        'lib.h',
        'lib.cc',
    ],
)

cc_binary(
    name = 'app',
    srcs = [
        'app.h',
        'app.cc',
        'main.cc',
    ],
    deps = ['lib'],
)

file_count_rule(
    name = 'file_count',
    deps = ['app'],
    extension = 'h',
)

En boy tanımı

file_count_aspect = aspect(
    implementation = _file_count_aspect_impl,
    attr_aspects = ['deps'],
    attrs = {
        'extension' : attr.string(values = ['*', 'h', 'cc']),
    }
)

Bu örnekte, en boy oranının deps özelliği aracılığıyla nasıl yayıldığı gösterilmektedir.

attrs, bir yönle ilgili özellik grubunu tanımlar. Herkese açık en boy özellikleri, parametreleri tanımlar ve yalnızca bool, int veya string türünde olabilir. Kural yayılımlı özellikler için int ve string parametrelerinde values belirtilmiş olmalıdır. Bu örnekte, "*", "h" veya "cc" değerine sahip olmasına izin verilen extension adlı bir parametre var.

Kural yayılımlı yönler için parametre değerleri, özelliği isteyen kuraldan aynı ada ve türe sahip kural özelliği kullanılarak alınır. (file_count_rule kelimesinin tanımına bakın).

Komut satırı özellikleri için parametre değerleri --aspects_parameters işareti kullanılarak iletilebilir. int ve string parametrelerinin values kısıtlaması atlanabilir.

Boyların label veya label_list türünde gizli özelliklere de sahip olmasına izin verilir. Özel etiket özellikleri, unsurlar tarafından oluşturulan işlemler için gerekli olan araç veya kitaplıklardaki bağımlılıkları belirtmek için kullanılabilir. Bu örnekte tanımlanmış bir gizli özellik yoktur ancak aşağıdaki kod snippet'i, bir araçta belirli bir unsura nasıl iletebileceğiniz gösterilmektedir:

...
    attrs = {
        '_protoc' : attr.label(
            default = Label('//tools:protoc'),
            executable = True,
            cfg = "exec"
        )
    }
...

Aspect uygulaması

FileCountInfo = provider(
    fields = {
        'count' : 'number of files'
    }
)

def _file_count_aspect_impl(target, ctx):
    count = 0
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the sources counting files
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
                    count = count + 1
    # Get the counts from our dependencies.
    for dep in ctx.rule.attr.deps:
        count = count + dep[FileCountInfo].count
    return [FileCountInfo(count = count)]

Kural uygulama işlevinde olduğu gibi, bir özellik uygulama işlevi de bağımlılıkları tarafından erişilebilen bir sağlayıcı yapısı döndürür.

Bu örnekte, FileCountInfo tek bir alanı count olan bir sağlayıcı olarak tanımlanmıştır. En iyi uygulama, fields özelliğini kullanarak bir sağlayıcının alanlarını açıkça tanımlamaktır.

A(X) en boy uygulaması için sağlayıcı grubu, hedef X için bir kuralın uygulanmasından ve A özelliğinin uygulanmasından gelen sağlayıcıların birleşimidir. Bir kural uygulamasının yaydığı sağlayıcılar, unsurlar uygulanmadan önce oluşturulur ve dondurulur ve bir açıdan değiştirilemez. Bir hedef ve ona uygulanan bir özelliğin her biri aynı türden bir sağlayıcı sağlarsa bu durum bir hatadır. Ancak OutputGroupInfo (kural ve detay farklı çıkış gruplarını belirttiği sürece birleştirilir) ve InstrumentedFilesInfo (boyuttan alınır). Bu, en boy uygulamalarının DefaultInfo değerini hiçbir zaman döndürmeyeceği anlamına gelir.

Parametreler ve gizli özellikler, ctx özelliklerinde aktarılır. Bu örnekte extension parametresi referans alınmıştır ve hangi dosyaların sayılacağını belirler.

Geri gelen sağlayıcılar için, özelliğin yayıldığı özelliklerin değerleri (attr_aspects listesinden), söz konusu özelliğin uygulanmasından elde edilen sonuçlarla değiştirilir. Örneğin, X hedefinin noktalarında Y ve Z varsa A(X) için ctx.rule.attr.deps değeri [A(Y), A(Z)] olur. Bu örnekte ctx.rule.attr.deps, özelliğin uygulandığı orijinal hedefin "dep'lerine" özelliğin uygulanması sonucunda ortaya çıkan hedef nesnelerdir.

Bu örnekte, en boy oranı toplam geçişli dosya sayısını toplamak için hedefin bağımlılıklarından FileCountInfo sağlayıcısına erişiyor.

Bir kuraldan özellik çağrılıyor

def _file_count_rule_impl(ctx):
    for dep in ctx.attr.deps:
        print(dep[FileCountInfo].count)

file_count_rule = rule(
    implementation = _file_count_rule_impl,
    attrs = {
        'deps' : attr.label_list(aspects = [file_count_aspect]),
        'extension' : attr.string(default = '*'),
    },
)

Kural uygulamasında, FileCountInfo öğesine ctx.attr.deps aracılığıyla nasıl erişileceği gösterilmektedir.

Kural tanımı, bir parametrenin (extension) nasıl tanımlanacağını ve varsayılan bir değer (*) verileceğini gösterir. Varsayılan değerin "cc", "h" veya "*" dışında bir değere ayarlanması, en boy tanımındaki parametreye uygulanan kısıtlamalar nedeniyle hata teşkil eder.

Hedef kuralı aracılığıyla bir özelliği çağırma

load('//:file_count.bzl', 'file_count_rule')

cc_binary(
    name = 'app',
...
)

file_count_rule(
    name = 'file_count',
    deps = ['app'],
    extension = 'h',
)

Bu görselde, extension parametresinin kural aracılığıyla özelliğe nasıl iletileceği gösterilmektedir. extension parametresi, kuralın uygulanmasında varsayılan bir değere sahip olduğundan extension, isteğe bağlı bir parametre olarak kabul edilir.

file_count hedefi oluşturulduğunda, yönümüz kendimiz için değerlendirilecek ve tüm hedeflere deps üzerinden tekrarlı olarak erişilebilecek.

Referanslar