Özellikler

Sorun bildirme Kaynağı görüntüleme Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Bu sayfada, yönleri kullanmanın temel özellikleri ve avantajları açıklanmakta, basit ve ileri düzey örnekler sunulmaktadır.

Yönler, derleme bağımlılık graflarını ek bilgiler ve işlemlerle genişletmenize olanak tanır. Yönleri kullanabileceğiniz bazı tipik senaryolar:

  • Bazel'i entegre eden IDE'ler, proje hakkında bilgi toplamak için yönleri kullanabilir.
  • Kod oluşturma araçları, girişlerinde hedeften bağımsız bir şekilde yürütmek için yönlerden 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ğlılık grafiği oluşturarak ve bu işlemleri toplamak için bu grafiği ziyaret ederek yapar.

Aşağıdaki BUILD dosyasını ele alalım:

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şturma

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

Bazel, yukarıdaki örnekteki her hedef için ilgili kuralın (bu durumda "java_library") bir 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ılar içindeki bu hedeflerin ters bağımlılıklarına ileten işlemler oluşturur.

Yönleri, işlemler oluşturan ve sağlayıcıları döndüren bir uygulama işlevine sahip olmaları açısından kurallara benzer. Ancak bu modellerin gücü, bağımlılıkları için oluşturulan bağımlılıklar grafiğinden gelir. Bir görünümün bir uygulaması ve dağıttığı tüm özelliklerin listesi vardır. "deps" adlı özelliklerde dağıtılan bir A yönü olduğunu varsayalım. Bu görünüm, bir X hedefine uygulanarak A(X) görünümü uygulama düğümü oluşturulabilir. 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 nedenle, A yönü X hedefine tek bir kez uygulandığında, aşağıdaki şekilde gösterilen hedeflerin orijinal bağımlılık grafiğinin "gölge grafiği" elde edilir:

En boy oranıyla grafik oluşturma

Şekil 2. Boyutlarla grafik oluşturma.

Gölgelenen tek kenarlar, dağıtım grubundaki özellikler boyunca uzanan kenarlardır. Bu nedenle, bu örnekte runtime_deps kenarı gölgelenmez. Ardından, gölge grafiğin tüm düğümlerinde bir görünüm uygulama işlevi çağrılır. Bu, orijinal grafiğin düğümlerinde kural uygulamalarının çağrılmasına benzer.

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. Bu örnekte, bir özellik uygulaması, bir özellik 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 parçalarına ayırıp her birini ayrı ayrı inceleyelim.

Aspect tanımı

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

Yön özellik 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, görünümün yayıldığı kural özelliklerinin listesidir. Bu durumda, görünüm 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 uygulama

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

Yön özellik uygulama işlevleri, kural uygulama işlevlerine benzer. Sağlayıcılar döndürür, işlemler oluşturabilir ve iki bağımsız değişken alır:

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

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

Servis sağlayıcıların listesini döndürmek için yönler gereklidir. Bu örnekte, özellik hiçbir şey sağlamadığından boş bir liste döndürür.

Komut satırını kullanarak yönü çağırma

Bir yönü uygulamanın en kolay yolu, komut satırında --aspects bağımsız değişkenini kullanmaktır. Yukarıdaki en boy oranının print.bzl adlı bir dosyada tanımlandığını varsayalım. Bu durumda:

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, <extension file label>%<aspect top-level name> biçiminde boyutun bir spesifikasyonu olan bir bağımsız değişken alır.

İleri düzey örnek

Aşağıdaki örnekte, hedef kuraldaki bir özelliğin kullanılması gösterilmektedir. Bu özellik, hedeflerdeki dosyaları sayar ve bunları uzantıya göre filtreleyebilir. Bu örnekte, değerleri döndürmek için sağlayıcının nasıl kullanılacağı, bir bağımsız değişkeni bir özellik uygulamasına iletmek için parametrelerin nasıl kullanılacağı ve bir özelliği kuraldan nasıl çağıracağınız 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 yön özellikleri parametreleri tanımlar ve yalnızca bool, int veya string türünde olabilir. Kural tarafından dağıtılan özellikler için int ve string parametrelerinde values belirtilmelidir. Bu örnekte, değeri "*", "h" veya "cc" olan extension adlı bir parametre vardır.

Kuralla dağıtılan görünümler için parametre değerleri, görünümü isteyen kuraldan alınır. Bu işlemde, kuralın aynı ada ve türe sahip özelliği kullanılı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 parametrelerinin values kısıtlaması atlanabilir.

Ayrıca, label veya label_list türünde özel özelliklere sahip olmaları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 özel bir özellik tanımlanmamıştır ancak aşağıdaki kod snippet'inde bir aracı bir görünüme nasıl iletebileceğiniz gösterilmektedir:

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

Aspect uygulama

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

Bir kural uygulama işlevi gibi bir özellik uygulama işlevi de bağımlılıklarına erişilebilen bir sağlayıcı yapısı döndürür.

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

Bir özellik uygulaması A(X) için sağlayıcı grubu, hedef X için bir kuralın uygulanması ve özellik A'nın uygulanması sonucunda ortaya çıkan sağlayıcıların birleşimidir. Bir kural uygulamasının dağıttığı sağlayıcılar, yönler uygulanmadan önce oluşturulur ve dondurulur ve bir yönden değiştirilemez. Bir hedef ve ona uygulanan bir özellik, OutputGroupInfo (kural ve özellik farklı çıkış grupları belirttiği sürece birleştirilir) ve InstrumentedFilesInfo (özellikten alınır) hariç olmak üzere her biri aynı türde bir sağlayıcı sağlıyorsa bu bir hatadır. Bu, yön uygulamalarının hiçbir zaman DefaultInfo döndürmeyeceği anlamına gelir.

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

Döndürülen sağlayıcılar için, özelliğin dağıtıldığı özelliklerin (attr_aspects listesinden) değerleri, özelliğin bunlara uygulanmasının sonuçlarıyla değiştirilir. Örneğin, hedef X'in bağımlılıkları arasında Y ve Z varsa A(X) için ctx.rule.attr.deps [A(Y), A(Z)] olur. Bu örnekte ctx.rule.attr.deps, özelliğin uygulandığı orijinal hedefin "deps"ine özelliğin uygulanmasının sonucu olan hedef nesnelerdir.

Örnekte, görünüm, 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 görünümü ç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 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östermektedir. "cc", "h" veya "*" dışında bir varsayılan değere sahip olmanın, en boy tanımındaki parametreye uygulanan kısıtlamalar nedeniyle hata olacağını unutmayın.

Hedef kural aracılığıyla bir yönü çağırma

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

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

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

Bu örnekte, extension parametresinin kural aracılığıyla görünüme nasıl geçirileceğ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 kendi başına ve deps aracılığıyla yinelemeli olarak erişilebilen tüm hedefler için değerlendirilir.

Referanslar