Özellikler

Bu sayfada, özellikler kullanımının temelleri ve faydaları açıklanmakta ve basit ve gelişmiş örnekler sağlanmaktadır.

Yönler, ek bilgi ve işlemlerle derleme bağımlılığı grafiklerinin genişletilmesine olanak tanır. Özelliklerin yararlı olabileceği bazı yaygın senaryolar:

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

En boy ile ilgili temel bilgiler

BUILD dosyaları, projenin kaynak kodu için bir açıklama sağlar: Projenin parçası olan kaynak dosyaları, bu dosyalardan hangi yapıların (hedefler) derlenmesi gerektiği, bu dosyalar arasındaki bağımlılıkların ne olduğu gibi. Bazel, bu bilgileri kullanarak derleme gerçekleştirir. Bir başka deyişle, yapıları (derleyici veya bağlayıcıyı çalıştıran işlemler gibi) üretmek için gereken işlem kümesini belirler. Bazel, bunu hedefler arasında bir bağımlılık grafiği oluşturarak ve bu işlemleri toplamak için bu grafiği ziyaret ederek yapar.

Şu BUILD dosyasını inceleyin:

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:

Derleme grafiği

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

Bazel, yukarıdaki örnekte bulunan 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 işlemler oluşturur ve bu yapıların konumları ile adları gibi bilgileri sağlayıcılardaki hedeflerin tersine bağımlılıklarına iletir.

Yönler, işlem oluşturan ve sağlayıcı döndüren bir uygulama işlevine sahip olması bakımından kurallara benzer. Fakat gücün kaynağı, bağımlılık grafiğinin onlar için oluşturulma şeklidir. Bir unsurun bir uygulaması ve yaydığı tüm özelliklerin listesi vardır. "deps" adlı öznitelikler boyunca yayılan bir A yönünü düşünün. Bu özellik, bir hedef X'e uygulanarak bir en boy uygulama düğümü A(X) elde edilir. A yönü, uygulama sırasında X'in "deps" özelliğinde (A'nın yayılım listesindeki tüm özellikler) atıfta bulunduğu tüm hedeflere yinelemeli olarak uygulanır.

Dolayısıyla, A özelliğini bir hedef X'e uygulamak için yapılan tek bir işlem, aşağıdaki şekilde gösterilen hedeflerin orijinal bağımlılık grafiğinin bir "gölge grafiğini" verir:

En Boydan Resim Derleme

2. Şekil. Farklı yönleriyle grafik oluşturun.

Gölgelendirilen tek kenarlar, yayılım kümesindeki özellikler boyunca olan kenarlardır. Dolayısıyla, bu örnekte runtime_deps kenarı gölgelendirilmez. Daha sonra, kural uygulamalarının orijinal grafiğin düğümlerinde çağrılmasına benzer şekilde, gölge grafikteki 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 ve deps özelliğine sahip tüm bağımlılıklarının yinelemeli olarak nasıl yazdırılacağı gösterilmektedir. Bir en boy uygulaması, en boy tanımı ve söz konusu özelliğin Bazel komut satırından nasıl çağrılacağı gösterilmiştir.

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.

Kurallarda olduğu gibi, bir unsurun da bir uygulama işlevi vardır. Bu uygulama işlevi şudur: _print_aspect_impl.

attr_aspects, en boy öğesinin yayıldığı kural özelliklerinin listesidir. Bu durumda, en boy öğesi, uygulandığı kuralların deps özelliği boyunca yayılır.

attr_aspects için yaygın olarak kullanılan bir başka bağımsız değişken de ['*'] kuralıdır. Bu bağımsız değişken, bir kuralın tüm özelliklerine yayılır.

En boy 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 []

En boy uygulama işlevleri, kural uygulama işlevlerine benzer. Bunlar sağlayıcılar döndürür, eylemler oluşturabilir ve iki bağımsız değişken alabilir:

  • 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. Bu uzantı, uygulandığı hedef tarafından sağlanan sağlayıcıları inceleyebilir (target bağımsız değişkeni aracılığıyla).

Sağlayıcıların listesini döndürmek için bu özelliklerin özellikleri gereklidir. Bu örnekte, en boy hiçbir şey sağlamadığından boş bir liste döndürür.

Komut satırını kullanarak öğe çağırma

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

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

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

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

Gelişmiş örnek

Aşağıdaki örnek, hedeflerde dosyaları sayan ve bunları uzantıya göre filtreleyen bir hedef kuraldan en boy özelliğinin kullanımını göstermektedir. Bu kılavuzda, değerleri döndürmek için sağlayıcının nasıl kullanılacağı, bir en boy uygulamasına bir bağımsız değişkeni aktarmak için parametrelerin nasıl kullanılacağı ve bir özelliğin bir kuraldan 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 değerinin deps özelliği aracılığıyla nasıl yayıldığı gösterilmektedir.

attrs, bir özellik grubunu tanımlar. Herkese açık özellik özellikleri string türündedir ve parametre olarak adlandırılır. Parametreler üzerinde birvalues özelliği belirtilmelidir. Bu örnekte, "*", "h" veya "cc" değerine sahip olmasına izin verilen extension adlı bir parametre bulunmaktadır.

En boy için parametre değerleri, özelliği isteyen kuralın adıyla aynı ada sahip dize özelliğinden alınır (file_count_rule tanımına bakın). Parametreleri tanımlamak için söz dizimi bulunmadığından, parametre içeren özellikler komut satırı üzerinden kullanılamaz.

Özelliklerin label veya label_list türünde özel özelliklere de sahip olmasına izin verilir. Özel etiket özellikleri, yönler tarafından oluşturulan işlemler için gereken araçlara veya kitaplıklara olan bağımlılıkları belirtmek için kullanılabilir. Bu örnekte tanımlanmış gizli bir özellik yoktur, ancak aşağıdaki kod snippet'i bir aracı bir özelliğe nasıl geçirebileceğinizi göstermektedir:

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

En boy 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, ö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, bir adet count alanına sahip bir sağlayıcı olarak tanımlanmıştır. En iyi uygulama, fields özelliğini kullanarak sağlayıcının alanlarını açıkça tanımlamaktır.

Bir en boy uygulaması A(X) için sağlayıcılar grubu, hedef X için bir kuralın uygulanmasından ve A öğesinin uygulanmasından gelen sağlayıcıların birleşimidir. Bir kural uygulamasının yaydığı sağlayıcılar, özellikler uygulanmadan önce oluşturulur ve dondurulur ve belirli bir açıdan değiştirilemez. OutputGroupInfo (kural ve en boy farklı çıkış grupları belirttiği sürece birleştirilir) ve InstrumentedFilesInfo (bu öğeden alınır) hariç olmak üzere, her bir hedef ve ona uygulanan bir özelliğin her biri aynı türe sahip bir sağlayıcı sağlarsa hatadır. Yani, özellik uygulamaları hiçbir zaman DefaultInfo döndürmeyebilir.

Parametreler ve özel özellikler, ctx özelliklerinde iletilir. Bu örnekte extension parametresine referansta bulunulur ve hangi dosyaların sayılacağı belirlenir.

Geri gelen sağlayıcılarda, en boy öğesinin yayıldığı özelliklerin değerleri (attr_aspects listesinden), söz konusu özelliğin uygulamasının sonuçlarıyla değiştirilir. Örneğin, X hedefinin dikey çizgilerinde 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, en boy özelliğinin uygulandığı orijinal hedefin "dep'lerine" uygulanmasının sonuçları olan 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şir.

Bir kuraldan öğe çağırma

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 uygulama, ctx.attr.deps aracılığıyla FileCountInfo öğesine nasıl erişileceğini gösterir.

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

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örsel, kural aracılığıyla extension parametresinin ilgili öğeye nasıl iletileceğini göstermektedir. Kural uygulamasında extension parametresi varsayılan bir değere sahip olduğundan extension isteğe bağlı bir parametre olarak kabul edilir.

file_count hedefi oluşturulduğunda, özelliğimiz kendi başına değerlendirilir ve tüm hedefler, deps üzerinden tekrar tekrar erişilebilir.