Özellikler

Sorun bildir Kaynağı göster

Bu sayfada, görünümleri kullanmanın temelleri ve avantajları açıklanmakta ve basit ve gelişmiş örnekler sağlanmaktadır.

Özellikler, ek bilgi ve işlemlerle bağımlılık grafiklerinin geliştirilmesini sağlar. Özelliklerin yararlı olabileceği bazı yaygın senaryolar:

  • Bazel'i entegre eden IDE'ler, proje hakkında bilgi toplamak için unsurları kullanabilir.
  • Kod oluşturma araçları, girdilerini hedeften bağımsız ş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 işlemler eklemek üzere yönleri kullanabilir.

En boy oranıyla ilgili temel bilgiler

BUILD dosyaları, bir projenin kaynak kodunun açıklamasını sağlar: Hangi kaynak dosyaların projenin parçası olduğunu, bu dosyalardan hangi yapıların (hedefler) oluşturulması gerektiği, bu dosyalar arasındaki bağımlılıkların neler olduğu vb. Bazel, bir derleme gerçekleştirmek için bu bilgileri kullanır. Bazel, yapıları (ör. derleyici veya bağlayıcıyı çalıştıran) üretmek için gereken işlem kümesini belirler. Bazel, hedefler arasında bir bağımlılık grafiği oluşturarak ve bu işlemleri toplamak için bu grafiği ziyaret ederek bunu 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ımlıyor:

Grafik oluştur

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

Bazel, yukarıdaki örnekte yer alan 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ı ve adları gibi bilgileri sağlayıcılardaki bu hedeflerin ters bağımlılıklarına iletir.

Özellikler, işlem oluşturan ve sağlayıcı döndüren bir uygulama işlevine sahip olması açısından kurallara benzer. Ancak güç, bağımlılık grafiğinin onlar için oluşturulma biçiminden kaynaklanır. Bir özelliğin bir uygulaması ve birlikte yaydığı tüm özelliklerin bir listesi vardır. "Deps" adlı öznitelikler boyunca yayılan bir A yönünü düşünün. Bu özellik, hedef X'e uygulanabilir ve böylece bir en boy uygulama düğümü A(X) elde edilir. A yönü, A yönü, X'in "deps" özelliğinde atıfta bulunduğu tüm hedeflere (A'nın yayılım listesindeki tüm özellikler) yinelemeli olarak uygulanır.

Dolayısıyla, A yönünün bir hedef X'e uygulanmasıyla ilgili 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 Boy Grafik Oluşturma

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

Gölgelendirilen tek kenarlar, yayılım grubundaki özellikler boyunca uzanan kenarlardır. Dolayısıyla runtime_deps kenarı bu örnekte 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 yinelemeli olarak nasıl yazdırılacağı ve deps özelliğine sahip tüm bağımlılıklarının nasıl yazdırılacağı gösterilmektedir. Burada 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ö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 parçalara 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 yönün uygulama işlevi şu şekildedir: _print_aspect_impl.

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

attr_aspects için bir diğer yaygın bağımsız değişken de ['*'], 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: En boy özelliğinin 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 sorgu, uygulandığı hedef tarafından sağlanan sağlayıcıları inceleyebilir (target bağımsız değişkeni aracılığıyla).

Sağlayıcı listesi döndürülmesi için özellikler gereklidir. Bu örnekte, en boy oranı hiçbir şey sağlamadığından boş bir liste döndürür.

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

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 şu şekilde print.bzl adlı bir dosyada tanımlandığı varsayıldığında:

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

print_aspect öğesini hedef example öğesine 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şkeni alır. Bu, <extension file label>%<aspect top-level name> biçimindeki en boy özelliğinin spesifikasyonudur.

Gelişmiş örnek

Aşağıdaki örnek, hedeflerdeki dosyaları sayan ve bunları uzantıya göre filtreleyen bir hedef kuraldan özelliğin kullanımını göstermektedir. Değer döndürmek için sağlayıcının nasıl kullanılacağını, bir bağımsız değişkeni en boy uygulamasına geçirmek için parametrelerin nasıl kullanılacağını ve bir kuraldan bir özelliğin nasıl çağrılacağını gösterir.

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 örnek, en boy oranının deps özelliği aracılığıyla nasıl yayıldığını gösterir.

attrs, bir özellik grubunu tanımlar. Herkese açık en boy özellikleri, parametreleri tanımlar ve yalnızca bool, int veya string türlerinde olabilir. Kuralla çoğaltılan yönler için int ve string parametrelerinin üzerinde values belirtilmiş olmalıdır. Bu örnekte extension adlı bir parametrenin değer olarak "*", "h" veya "cc" olmasına izin veriliyor.

Kuralla çoğaltılan yönler için parametre değerleri, aynı ada ve türe sahip kuralın özelliği kullanılarak, en boy bilgisini isteyen kuraldan alınır. (file_count_rule tanımına bakın).

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

Ayrıca, boyutların label veya label_list türünde gizli özelliklere sahip olmasına da izin verilir. Özel etiket özellikleri, yönler tarafından oluşturulan işlemler için gereken araçlar veya kitaplıklardaki bağımlılıkları belirtmek için kullanılabilir. Bu örnekte tanımlı özel 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, en boy uygulama işlevi de bağımlılıkları tarafından erişilebilen sağlayıcıların bir struct'ını döndürür.

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

Bir boy uygulaması A(X) için sağlayıcılar grubu, hedef X için bir kuralın uygulanmasından ve A yönünün uygulanmasından gelen sağlayıcıların birleşimidir. Bir kural uygulamasının yaydığı sağlayıcılar, en boy oranları uygulanmadan önce oluşturulup dondurulur ve belirli bir açıdan değiştirilemez. Bir hedef ve ona uygulanan bir özelliğin her biri aynı türde bir sağlayıcı sağlarsa OutputGroupInfo (kural ve en boy farklı çıkış grupları belirttiği sürece birleştirilir) ve InstrumentedFilesInfo (bu öğeden alınır) istisnaları olması hatadır. Bu, yön uygulamalarının hiçbir zaman DefaultInfo döndürmeyebileceği anlamına gelir.

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

Geri gelen sağlayıcılar için, en boy özelliğinin yayıldığı özelliklerin değerleri (attr_aspects listesinden), ilgili özelliğin kendilerine uygulandığı bir uygulamanın sonuçlarıyla değiştirilir. Örneğin, X hedefinin atamasında Y ve Z varsa A(X) için ctx.rule.attr.deps [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 nesneleridir.

Bu örnekte en boy, 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ğırın

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ı, 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 varsayılan değerin (*) nasıl verileceğini gösterir. "cc", "h" veya "*" değerlerinden biri olmayan bir varsayılan değere sahip olmanın, en boy tanımında parametreye yerleştirilen kısıtlamalar nedeniyle hata olabileceğini unutmayın.

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

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

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

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

Bu şekilde, extension parametresinin kural aracılığıyla ilgili öğeye nasıl iletileceği gösterilmektedir. 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 yinelemeli olarak erişilebilir.

Referanslar