Alet Zincirleri

Sorun bildir Kaynağı göster Gece · 7,3 · 7,2 · 7,1 · 7,0 · 6,5

Bu sayfada, kural yazarlarının kural mantığını platform tabanlı araç seçiminden ayırmaktadır. Evet kuralları ve platformları okumanız önerilir sayfaları inceleyin. Bu sayfada, araç zincirlerinin neden gerekli olduğu, bunları tanımlayıp kullanmalarını ve Bazel'in bu verilere bağlı olarak platform kısıtlamalarını artırır.

Motivasyon

Öncelikle araç zincirlerinin çözmek için tasarlandığı soruna bakalım. Diyelim ki "çubuk"u destekleyecek kurallar yazan programlama dili. Size ait bar_binary kuralı, *.bar dosyalarını derleyici olan barc derleyiciyi kullanarak derler. Bu araç çalışma alanınızda başka bir hedef olarak oluşturulduğundan emin olun. bar_binary yazan kullanıcılardan beri hedeflerin derleyiciye bir bağımlılık belirtmesi gerekmez; bunu örtülü bağımlılığı ifade eder.

bar_binary = rule(
    implementation = _bar_binary_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        ...
        "_compiler": attr.label(
            default = "//bar_tools:barc_linux",  # the compiler running on linux
            providers = [BarcInfo],
        ),
    },
)

//bar_tools:barc_linux artık her bar_binary hedefi için bir bağımlılık. Bu nedenle herhangi bir bar_binary hedefinden önce oluşturulur. Kurala göre, diğer özellikler gibi bir uygulama işlevine de sahip olursunuz:

BarcInfo = provider(
    doc = "Information about how to invoke the barc compiler.",
    # In the real world, compiler_path and system_lib might hold File objects,
    # but for simplicity they are strings for this example. arch_flags is a list
    # of strings.
    fields = ["compiler_path", "system_lib", "arch_flags"],
)

def _bar_binary_impl(ctx):
    ...
    info = ctx.attr._compiler[BarcInfo]
    command = "%s -l %s %s" % (
        info.compiler_path,
        info.system_lib,
        " ".join(info.arch_flags),
    )
    ...

Buradaki sorun, derleyici etiketinin bar_binary koduna gömülmüş olmasıdır. farklı hedefler, kullandıkları platforma bağlı olarak farklı derleyicilere ihtiyaç duyabilir oluşturulmakta olduklarına ve hangi platform üzerinde geliştirildiklerine hedef platform ve yürütme platformu. Ayrıca kural, mevcut tüm araçları ve platformları bile bilmediğinden, bunları kuralın tanımına gömmek mümkün değildir.

İdeal olmayan bir çözüm, bir görevi yerine getirerek birtakım _compiler özelliği gizli değil. Bu durumda ayrı ayrı hedefler koda gömüldüğünden emin olun.

bar_binary(
    name = "myprog_on_linux",
    srcs = ["mysrc.bar"],
    compiler = "//bar_tools:barc_linux",
)

bar_binary(
    name = "myprog_on_windows",
    srcs = ["mysrc.bar"],
    compiler = "//bar_tools:barc_windows",
)

compiler seçmek için select kullanarak bu çözümü iyileştirebilirsiniz platforma göre:

config_setting(
    name = "on_linux",
    constraint_values = [
        "@platforms//os:linux",
    ],
)

config_setting(
    name = "on_windows",
    constraint_values = [
        "@platforms//os:windows",
    ],
)

bar_binary(
    name = "myprog",
    srcs = ["mysrc.bar"],
    compiler = select({
        ":on_linux": "//bar_tools:barc_linux",
        ":on_windows": "//bar_tools:barc_windows",
    }),
)

Ancak bu çok yorucu ve her bar_binary kullanıcısından sormanız gereken biraz fazla bir şey var. Bu stil çalışma alanı boyunca tutarlı bir şekilde kullanılmazsa tek bir platformda sorunsuz çalışan ancak senaryoları inceleyelim. Destek ekleme ve .

Araç zinciri çerçevesi bu sorunu çözmek için fazladan bir düzey dolaylı da olabilir. Özetle, kuralınızın soyut bir bağımlılığı olduğunu hedef ailesinin (araç zinciri türü) bazı üyelerinde ve Bazel'de buna göre belirli bir hedefe (araç zinciri) otomatik olarak geçerli platform kısıtlamalarına tabidir. Kuralın yazarı da hedef yazarı da değil mevcut platformların ve araç zincirlerinin tamamını bilmeniz gerekir.

Araç zincirleri kullanan kurallar yazma

Araç zinciri çerçevesi kapsamında, kuralların doğrudan araçlara bağlı olması yerine, araç zinciri türlerine bağlıdır. Araç zinciri türü, basit bir hedeftir Bu bir dizi, farklı anahtar kelimeler için aynı rolü platformlar. Örneğin, çubuğu temsil eden bir tür tanımlayabilirsiniz. derleyici:

# By convention, toolchain_type targets are named "toolchain_type" and
# distinguished by their package path. So the full path for this would be
# //bar_tools:toolchain_type.
toolchain_type(name = "toolchain_type")

Önceki bölümdeki kural tanımı, kod, derleyiciyi bir özellik olarak aldığında, //bar_tools:toolchain_type araç zincirini kullanmaya devam edebilirsiniz.

bar_binary = rule(
    implementation = _bar_binary_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        ...
        # No `_compiler` attribute anymore.
    },
    toolchains = ["//bar_tools:toolchain_type"],
)

Uygulama işlevi artık bu bağımlılığa ctx.toolchains altından erişiyor yerine ctx.attr yerine araç zinciri türünü kullanın.

def _bar_binary_impl(ctx):
    ...
    info = ctx.toolchains["//bar_tools:toolchain_type"].barcinfo
    # The rest is unchanged.
    command = "%s -l %s %s" % (
        info.compiler_path,
        info.system_lib,
        " ".join(info.arch_flags),
    )
    ...

ctx.toolchains["//bar_tools:toolchain_type"] şunu döndürür: ToolchainInfo sağlayıcı Bazel'ın araç zinciri bağımlılığını çözdüğü her hedefin bir örneğidir. ToolchainInfo nesnesi temel aracın kuralı tarafından ayarlanır; sonraki bölümünde, bu kural sarmalanan bir barcinfo alanı olacak şekilde tanımlanır BarcInfo nesnesini ifade eder.

Bazel'in araç zincirlerini hedeflere çözme prosedürü açıklanıyor burada bulabilirsiniz. Çözümlenmiş araç zinciri hedefi aslında tüm aday alanına değil, bar_binary hedefine bağımlı hale geldi araç zincirleri olarak düşünebilirsiniz.

Zorunlu ve İsteğe Bağlı Araç Zincirleri

Varsayılan olarak, bir kural çıplak etiket kullanarak araç zinciri türü bağımlılığı ifade ettiğinde (yukarıda gösterildiği gibi) araç zinciri türünün zorunlu olduğu kabul edilir. Bazel eşleşen bir araç zinciri bulamıyorsa (bkz. Araç zinciri çözünürlüğünü kullanın) bu bir hatadır ve analiz durdurulur.

Bunun yerine isteğe bağlı bir araç zinciri türü bağımlılığı tanımlamak mümkündür. şöyle olur:

bar_binary = rule(
    ...
    toolchains = [
        config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
    ],
)

İsteğe bağlı bir araç zinciri türü çözümlenemediğinde analiz devam eder ve ctx.toolchains["//bar_tools:toolchain_type"] işlevinin sonucu None.

config_common.toolchain_type işlevinin varsayılan olarak ayarlanması zorunludur.

Aşağıdaki formlar kullanılabilir:

  • Zorunlu araç zinciri türleri:
    • toolchains = ["//bar_tools:toolchain_type"]
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type")]
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = True)]
  • İsteğe bağlı araç zinciri türleri:
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False)]
bar_binary = rule(
    ...
    toolchains = [
        "//foo_tools:toolchain_type",
        config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
    ],
)

Ayrıca, aynı kuralda formları karıştırıp eşleştirebilirsiniz. Ancak aynı araç zinciri türü birden çok kez listeleniyorsa en katı olanı alır. daha katı olduğu durumlar için bunu yapabiliriz.

Araç zincirlerini kullanan özellikleri yazma

Boyutların kurallarla aynı araç zinciri API'sine erişimi vardır: Gerekli araç zinciri türlerini, bağlam yoluyla araç zincirlerine erişme ve bunları kullanarak yeni işlemleri nasıl yapacağınızla ilgilidir.

bar_aspect = aspect(
    implementation = _bar_aspect_impl,
    attrs = {},
    toolchains = ['//bar_tools:toolchain_type'],
)

def _bar_aspect_impl(target, ctx):
  toolchain = ctx.toolchains['//bar_tools:toolchain_type']
  # Use the toolchain provider like in a rule.
  return []

Araç zincirlerini tanımlama

Belirli bir araç zinciri türü için araç zinciri tanımlamak üzere üç şeye ihtiyacınız vardır:

  1. Aracın veya araç paketinin türünü temsil eden dile özgü bir kural. Ölçüt bu kural adının sonunda "_toolchain" bulunur.

    1. Not: \_toolchain kuralı derleme işlemi oluşturamaz. Bunun yerine, diğer kurallardan eserleri toplayıp ilkesini ele alalım. Bu kural, tüm sorumluluklarınızı derleme işlemleri için de geçerlidir.
  2. Bu kural türündeki birkaç hedef (araç veya aracın sürümlerini temsil eder) yardımcı olan araçlardır.

  3. Bu tür her bir hedef için genel toolchain kuralıyla birlikte araç zinciri çerçevesi tarafından kullanılan meta verileri sağlar. Bu toolchain hedef aynı zamanda bu araç zinciriyle ilişkilendirilmiş toolchain_type anlamına gelir. Bu, belirli bir _toolchain kuralının herhangi bir reklamverenle toolchain_type ve bu yalnızcatoolchain toolchain_type ile ilişkili olduğunu belirten bu _toolchain kuralını uygulayın.

Çalışan örneğimizde, bar_toolchain kuralının tanımını aşağıda bulabilirsiniz. Bizim örnekte yalnızca bir derleyici bulunur, ancak bağlayıcı gibi diğer araçlar da altında gruplandırılır.

def _bar_toolchain_impl(ctx):
    toolchain_info = platform_common.ToolchainInfo(
        barcinfo = BarcInfo(
            compiler_path = ctx.attr.compiler_path,
            system_lib = ctx.attr.system_lib,
            arch_flags = ctx.attr.arch_flags,
        ),
    )
    return [toolchain_info]

bar_toolchain = rule(
    implementation = _bar_toolchain_impl,
    attrs = {
        "compiler_path": attr.string(),
        "system_lib": attr.string(),
        "arch_flags": attr.string_list(),
    },
)

Kural, ToolchainInfo sağlayıcısı döndürmelidir. Bu sağlayıcı, alıcı kuralı, ctx.toolchains ve araç zinciri türünü seçin. ToolchainInfo (struct gibi) rastgele alan değeri içerebilir çiftler. ToolchainInfo öğesine tam olarak hangi alanların ekleneceğine ilişkin spesifikasyon araç zinciri türünde açıkça belgelenmelidir. Bu örnekte, değerler yukarıda tanımlanan şemayı yeniden kullanmak için bir BarcInfo nesnesi içine sarmalanmış daima; bu stil, doğrulama ve kodun yeniden kullanımı için yararlı olabilir.

Artık belirli barc derleyicileri için hedefler tanımlayabilirsiniz.

bar_toolchain(
    name = "barc_linux",
    arch_flags = [
        "--arch=Linux",
        "--debug_everything",
    ],
    compiler_path = "/path/to/barc/on/linux",
    system_lib = "/usr/lib/libbarc.so",
)

bar_toolchain(
    name = "barc_windows",
    arch_flags = [
        "--arch=Windows",
        # Different flags, no debug support on windows.
    ],
    compiler_path = "C:\\path\\on\\windows\\barc.exe",
    system_lib = "C:\\path\\on\\windows\\barclib.dll",
)

Son olarak, iki bar_toolchain hedefi için toolchain tanım oluşturursunuz. Bu tanımlar, dile özgü hedefleri araç zinciri türüne bağlar ve araç zincirinin ne zaman çalışacağını Bazel'a bildiren kısıtlama bilgilerini bir şablon görevi görür.

toolchain(
    name = "barc_linux_toolchain",
    exec_compatible_with = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
    target_compatible_with = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
    toolchain = ":barc_linux",
    toolchain_type = ":toolchain_type",
)

toolchain(
    name = "barc_windows_toolchain",
    exec_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:x86_64",
    ],
    target_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:x86_64",
    ],
    toolchain = ":barc_windows",
    toolchain_type = ":toolchain_type",
)

Yukarıdaki göreli yol söz diziminin kullanımı, bu tanımların tümünün eder. Ancak bunun için araç zinciri türünün, dile özgü araç zinciri hedefleri ve toolchain tanım hedeflerinin hepsi ayrı olamaz pakettir.

go_toolchain sayfasını inceleyin. örneği inceleyelim.

Araç zincirleri ve yapılandırmalar

Kural yazarları için önemli bir soru şudur: bar_toolchain hedefi hangi yapılandırmayı gördüğü ve hangi geçişlerin bağımlılıklar için nasıl kullanılmalıdır? Yukarıdaki örnekte dize özellikleri kullanılmıştır, ancak diğer hedeflere dayalı daha karmaşık bir araç zincirine hangi yöntemi kullanır?

bar_toolchain öğesinin daha karmaşık bir sürümünü inceleyelim:

def _bar_toolchain_impl(ctx):
    # The implementation is mostly the same as above, so skipping.
    pass

bar_toolchain = rule(
    implementation = _bar_toolchain_impl,
    attrs = {
        "compiler": attr.label(
            executable = True,
            mandatory = True,
            cfg = "exec",
        ),
        "system_lib": attr.label(
            mandatory = True,
            cfg = "target",
        ),
        "arch_flags": attr.string_list(),
    },
)

attr.label kullanımı standart bir kuralla aynıdır. ancak cfg parametresinin anlamı biraz farklıdır.

Araç zinciri aracılığıyla bir hedeften ("üst öğe" denir) araç zincirine bağımlılık Çözünürlüğü "araç zinciri" adı verilen özel bir yapılandırma geçişi kullanır geçiş" gibi bir ifade kullanabilirsiniz. Araç zinciri geçişi, aşağıdaki hariç yapılandırma işlemleri için aynı kalır: yürütme platformunu, araç zinciri için (aksi takdirde, araç zincirinin araç zinciri çözünürlüğünü yürütme platformundakilerle aynı olmayabilir). Bu araç zincirinin tüm exec bağımlılıklarının üst öğe derleme işlemlerini içerebilir. Araç zincirinin cfg = "target" kullanan (veya "target" varsayılan olduğu için cfg belirtmeyen) bağımlılıklarından herhangi biri ana platformla aynı hedef platform için oluşturulmuş olmalıdır. Bu şekilde, araç zinciri kurallarının hem kitaplıklara (yukarıdaki system_lib özelliği) hem de araçlara ( compiler özelliği) ekleyin. Sistem kitaplıkları ve nihai eserle bağlantılı olduğu için aynı yapı için platformunda çağrılan derleyici ise derleme sırasında çağrılan bir araçtır ve yürütme platformunda çalıştırılabilir.

Araç zincirleriyle kaydolma ve derleme

Bu noktada tüm yapı taşları birleştirilir ve sadece birkaç küçük yapı içinde Bazel'in çözümleme prosedüründe kullanılabilecek araç zincirleridir. Bu, kullanarak bir MODULE.bazel dosyasına veya araç zincirini register_toolchains() veya araç zincirlerinin komuttaki etiketler satırında --extra_toolchains işaretini kullanabilirsiniz.

register_toolchains(
    "//bar_tools:barc_linux_toolchain",
    "//bar_tools:barc_windows_toolchain",
    # Target patterns are also permitted, so you could have also written:
    # "//bar_tools:all",
    # or even
    # "//bar_tools/...",
)

Araç zincirlerini kaydetmek için hedef kalıpları kullanırken, her birinin kaydedilmesi aşağıdaki kurallara göre belirlenir:

  • Bir paketin alt paketinde tanımlanan araç zincirleri, araç zincirleri bulunur.
  • Paket içinde araç zincirleri; terimler sözlüğü sırasına göre adlarını ekleyebilirsiniz.

Artık bir araç zinciri türüne bağlı olan bir hedef oluşturduğunuzda araç zinciri, hedefe ve yürütme platformlarına göre seçilecek.

# my_pkg/BUILD

platform(
    name = "my_target_platform",
    constraint_values = [
        "@platforms//os:linux",
    ],
)

bar_binary(
    name = "my_bar_binary",
    ...
)
bazel build //my_pkg:my_bar_binary --platforms=//my_pkg:my_target_platform

Bazel, //my_pkg:my_bar_binary öğesinin @platforms//os:linux içeriyor ve bu nedenle //bar_tools:barc_linux_toolchain için //bar_tools:toolchain_type referansı. Bu işlem //bar_tools:barc_linux uzantısıyla sonuçlanacaktır //bar_tools:barc_windows.

Araç zinciri çözünürlüğü

Araç zincirlerini kullanan her hedef için Bazel'in araç zinciri çözüm prosedürü hedefin somut araç zinciri bağımlılıklarını belirler. Prosedür bir dizi gerekli araç zinciri türünü, hedef platformun, dönüşüm hunisinin mevcut yürütme platformlarını ve kullanılabilir araç zincirlerinin listesini içerir. Çıktıları her bir araç zinciri türü için seçilmiş bir araç zinciri ve aynı zamanda seçili yürütme platformu belirleyin.

Mevcut yürütme platformları ve araç zincirleri, dış bağımlılık grafiği register_execution_platforms ve register_toolchains arama şurada: MODULE.bazel dosya. Ayrıca, komut satırı üzerinden --extra_execution_platforms ve --extra_toolchains. Ana makine platformu da otomatik olarak kullanılabilir bir yürütme platformu arasına eklenir. Kullanılabilir platformlar ve araç zincirleri, deterministikçi göstermek amacıyla sıralı listeler olarak takip edilir. listenin önceki öğelerine verilen tercihle.

Mevcut araç zincirleri grubu, öncelik sırasına göre şuradan oluşturulur: --extra_toolchains ve register_toolchains:

  1. --extra_toolchains kullanılarak kaydedilen araç zincirleri önce eklenir. ( son araç zinciri en yüksek önceliğe sahiptir.)
  2. Geçişli harici modda register_toolchains kullanılarak kaydedilen araç zincirleri bağımlılık grafiğine aşağıdaki sıralamayla ulaşabilirsiniz: (Bunların içinde, birinci belirtilen araç zincirinin en yüksek önceliğe sahip olduğunu unutmayın.)
    1. Kök modül tarafından kaydedilen araç zincirleri (ör. MODULE.bazel workspace root);
    2. Kullanıcının WORKSPACE dosyasına kayıtlı araç zincirleri. buradan çağrılan makrolar;
    3. Kök olmayan modüller tarafından kaydedilen araç zincirleri ( kök modül, bunların bağımlılıkları vb.).
    4. "WORKSPACE son ekinde" kayıtlı araç zincirleri; yalnızca tarafından kullanılan Bazel yüklemesiyle paket halinde sunulan belirli yerel kurallar.

NOT: :all, :* ve gibi sözde hedefler /..., Bazel'in paketine göre sıralanıyor kullanılan yükleme mekanizmasıdır.

Çözüm adımları aşağıda açıklanmıştır.

  1. target_compatible_with veya exec_compatible_with ifade şununla eşleşir: a platform, listesindeki her constraint_value için aynı zamanda bu constraint_value (açıkça veya varsayılan olarak).

    Platformda constraint_setting yerine constraint_value varsa tarafından referansta bulunulması durumunda eşleşmeler etkilenmez.

  2. Oluşturulmakta olan hedef exec_compatible_with özelliği (veya kural tanımı exec_compatible_with bağımsız değişken), Mevcut yürütme platformlarının listesi filtrelenerek kaldırılarak yürütülme kısıtlamalarıyla eşleşmeyen tüm anahtar kelimeler.

  3. Mevcut araç zincirleri listesi, tüm araç zincirlerini kaldıracak şekilde filtrelenir Mevcut yapılandırmayla eşleşmeyen target_settings belirtiliyor.

  4. Mevcut her yürütme platformu için her araç zinciri türünü bununla uyumlu ilk araç zinciri (varsa), ve hedef platforma göre hazırlanır.

  5. Uyumlu bir zorunlu araç zinciri bulamayan yürütme platformları devre dışı bırakıldığını görebilirsiniz. Diğer platformlar arasından ilki mevcut hedefin yürütme platformu olur ve ilişkilendirilmiş araç zincirleri (varsa) hedefin bağımlılıkları haline gelir.

Seçilen yürütme platformu, hedefin gerçekleştireceği tüm işlemleri çalıştırmak için üretir.

Aynı hedefin birden fazla yapılandırmada oluşturulabildiği (örneğin, aynı derlemede) çözümleme prosedürü uygulanırsa her sürümü için ayrı ayrı düzenleyebilirsiniz.

Kural yürütme grupları kullanıyorsa her yürütme grubu, araç zinciri çözümlemesini ayrı ayrı gerçekleştirir ve her grubun kendi yürütmesi vardır birçok farklı araç bulunur.

Hata ayıklama araç zincirleri

Mevcut bir kurala araç zinciri desteği ekliyorsanız --toolchain_resolution_debug=regex işareti. Araç zinciri çözümlemesi sırasında normal ifade değişkeniyle eşleşen araç zinciri türleri veya hedef adları için ayrıntılı çıkış sağlar. Siz .* öğesini kullanarak tüm bilgilerin çıkışını sağlayabilir. Bazel, oluşturulan araç zincirlerinin adlarını çıkarır kontroller ve atlamalar yaşar.

Hangi cquery bağımlılıklarının araç zincirinden geldiğini görmek istiyorsanız cquery çözünürlüğü için --transitions işaretini kullanın:

# Find all direct dependencies of //cc:my_cc_lib. This includes explicitly
# declared dependencies, implicit dependencies, and toolchain dependencies.
$ bazel cquery 'deps(//cc:my_cc_lib, 1)'
//cc:my_cc_lib (96d6638)
@bazel_tools//tools/cpp:toolchain (96d6638)
@bazel_tools//tools/def_parser:def_parser (HOST)
//cc:my_cc_dep (96d6638)
@local_config_platform//:host (96d6638)
@bazel_tools//tools/cpp:toolchain_type (96d6638)
//:default_host_platform (96d6638)
@local_config_cc//:cc-compiler-k8 (HOST)
//cc:my_cc_lib.cc (null)
@bazel_tools//tools/cpp:grep-includes (HOST)

# Which of these are from toolchain resolution?
$ bazel cquery 'deps(//cc:my_cc_lib, 1)' --transitions=lite | grep "toolchain dependency"
  [toolchain dependency]#@local_config_cc//:cc-compiler-k8#HostTransition -> b6df211