Kural, Bazel'in girişlerde gerçekleştirdiği ve bir dizi çıkış üreten bir dizi işlem tanımlar. Bu çıkışlara, kuralın uygulama işlevi tarafından döndürülen sağlayıcılarda referans verilir. Örneğin, bir C++ ikili kuralı şu şekilde olabilir:
.cpp
kaynak dosyası (giriş) alın.- Kaynak dosyalarda
g++
'ü çalıştırın (işlem). - Çalışma zamanında kullanılabilir hale getirmek için, yürütülebilir çıktıyı ve diğer dosyaları içeren
DefaultInfo
sağlayıcısını iade edin. - Hedeften ve bağımlılıklarına ait toplanan C++'ya özgü bilgileri içeren
CcInfo
sağlayıcısını döndürme
Bazel'in bakış açısından g++
ve standart C++ kitaplıkları da bu kuralın girişleridir. Kural yazarı olarak, yalnızca bir kurala kullanıcı tarafından sağlanan girişleri değil, aynı zamanda işlemleri yürütmek için gereken tüm araçları ve kitaplıkları da dikkate almanız gerekir.
Herhangi bir kuralı oluşturmadan veya değiştirmeden önce Bazel'ın derleme aşamaları hakkında bilgi sahibi olduğunuzdan emin olun. Derlemenin üç aşamasını (yükleme, analiz ve yürütme) anlamanız önemlidir. Kurallar ve makrolar arasındaki farkı anlamak için makrolar hakkında bilgi edinmek de faydalı olur. Başlamak için önce Kurallar Eğitimi'ni inceleyin. Ardından, bu sayfayı referans olarak kullanın.
Bazel'in kendisine yerleştirilmiş birkaç kural vardır. cc_library
ve java_binary
gibi bu yerel kurallar, belirli diller için bazı temel destek sağlar.
Kendi kurallarınızı tanımlayarak Bazel'in yerel olarak desteklemediği diller ve araçlar için benzer bir destek ekleyebilirsiniz.
Bazel, Starlark dilini kullanarak kurallar yazmak için bir genişletilebilirlik modeli sağlar. Bu kurallar, doğrudan BUILD
dosyadan yüklenebilen .bzl
dosyalarında yazılır.
Kendi kuralınızı tanımlarken hangi özellikleri destekleyeceğine ve çıkışlarını nasıl oluşturacağına siz karar verirsiniz.
Kuralın implementation
işlevi, analiz aşaması sırasındaki tam davranışını tanımlar. Bu işlev harici komut çalıştırmaz. Bunun yerine, gerektiğinde kuralın çıktılarını oluşturmak için yürütme aşamasının sonlarında kullanılacak işlemleri kaydeder.
Kural oluşturma
Bir .bzl
dosyasında, yeni bir kural tanımlamak için rule işlevini kullanın ve sonucu, genel değişkende depolayın. rule
çağrısı, özellikleri ve bir uygulama işlevini belirtir:
example_library = rule(
implementation = _example_library_impl,
attrs = {
"deps": attr.label_list(),
...
},
)
Bu, example_library
adlı bir kural türünü tanımlar.
rule
çağrısı, kuralın executable=True
ile bir yürütülebilir çıktı mı (executable=True
ile) yoksa özellikle test=True
ile yürütülebilir bir test mi oluşturacağını da belirtmelidir. İkincisi ise kural bir test kuralı olur ve kuralın adı _test
ile bitmelidir.
Hedef örneklendirme
Kurallar BUILD
dosyalarında yüklenebilir ve çağrılabilir:
load('//some/pkg:rules.bzl', 'example_library')
example_library(
name = "example_target",
deps = [":another_target"],
...
)
Bir derleme kuralına yapılan her çağrı, hiçbir değer döndürmez ancak bunun bir hedef tanımlama gibi yan etkisi vardır. Buna, kuralın tetiklenmesi denir. Bu, yeni hedef için bir ad ve hedefin özelliklerinin değerlerini belirtir.
Kurallar, Starlark işlevlerinden çağrılabilir ve .bzl
dosyalarına yüklenebilir.
Kuralları çağıran Starlark işlevleri Starlark makroları olarak adlandırılır.
Starlark makroları nihayetinde BUILD
dosyalarından çağrılmalıdır ve yalnızca BUILD
dosyalarının hedefleri örneklemek için değerlendirildiği yükleme aşamasında çağrılabilir.
Özellikler
Özellik, bir kural bağımsız değişkenidir. Özellikler, bir hedefin uygulanması için belirli değerler sağlayabilir veya diğer hedeflere atıfta bulunarak bağımlılık grafiği oluşturabilir.
srcs
veya deps
gibi kurala özgü özellikler, bir eşlemenin özellik adlarından şemalara (attr
modülü kullanılarak oluşturulan) rule
attrs
parametresine geçirilmesiyle tanımlanır.
name
ve visibility
gibi ortak özellikler tüm kurallara dolaylı olarak eklenir. Ek özellikler özellikle yürütülebilir ve test kurallarına dolaylı olarak eklenir. Bir kurala dolaylı olarak eklenen özellikler, attrs
parametresine iletilen sözlüğe dahil edilemez.
Bağımlılık özellikleri
Kaynak kodu işleyen kurallar, genellikle çeşitli bağımlılık türlerini yönetmek için aşağıdaki özellikleri tanımlar:
srcs
, bir hedefin işlemleri tarafından işlenen kaynak dosyaları belirtir. Özellik şeması genellikle, kuralın işlediği kaynak dosya türü için hangi dosya uzantılarının beklendiğini belirtir. Başlık dosyası içeren diller için kurallar genellikle bir hedef ve tüketicileri tarafından işlenen üstbilgiler için ayrı birhdrs
özelliği belirtir.deps
, bir hedef için kod bağımlılıklarını belirtir. Özellik şeması, bu bağımlılıkların hangi sağlayıcıları sağlaması gerektiğini belirtmelidir. (Örneğin,cc_library
,CcInfo
değerini sağlar.)data
, bir hedefe bağlı olan tüm yürütülebilir dosyalara çalışma zamanında kullanıma sunulacak dosyaları belirtir. Bu sayede, istediğiniz dosyaları belirtebilirsiniz.
example_library = rule(
implementation = _example_library_impl,
attrs = {
"srcs": attr.label_list(allow_files = [".example"]),
"hdrs": attr.label_list(allow_files = [".header"]),
"deps": attr.label_list(providers = [ExampleInfo]),
"data": attr.label_list(allow_files = True),
...
},
)
Bunlar, bağımlılık özelliklerine örnektir. Giriş etiketi belirten tüm özellikler (attr.label_list
, attr.label
veya attr.label_keyed_string_dict
ile tanımlananlar), hedef tanımlandığında bir hedef ile etiketleri (veya karşılık gelen Label
nesneleri) listelenen hedefler arasındaki belirli bir türdeki bağımlılıkları belirtir. Bu etiketlerin depolama alanı ve muhtemelen yolu, tanımlanan hedefe göre çözülür.
example_library(
name = "my_target",
deps = [":other_target"],
)
example_library(
name = "other_target",
...
)
Bu örnekte other_target
, my_target
'un bir bağımlısıdır ve bu nedenle önce other_target
analiz edilir. Hedeflerin bağımlılık grafiğinde döngü varsa hata oluşur.
Özel özellikler ve örtülü bağımlılıklar
Varsayılan değeri olan bir bağımlılık özelliği örtülü bağımlılık oluşturur. Kullanıcının BUILD
dosyasında belirtmediği hedef grafiğin bir parçası olduğu için örtülüdür. Örtülü bağımlılıklar, bir kural ile araç (derleyici gibi bir derleme zamanı bağımlılığı) arasındaki ilişkinin sabit bir şekilde kodlanması için yararlıdır. Çünkü kullanıcının çoğu, kuralın hangi aracı kullandığını belirtmekle ilgilenmez. Kuralın uygulama işlevinde bu durum, diğer bağımlılıklarla aynı şekilde ele alınır.
Kullanıcının bu değeri geçersiz kılmasına izin vermeden örtülü bir bağımlılık sağlamak istiyorsanız alt çizgi (_
) ile başlayan bir ad vererek özelliği gizli hale getirebilirsiniz. Özel özelliklerin varsayılan değerleri olmalıdır. Özel özelliklerin yalnızca örtülü bağımlılıklar için kullanılması genellikle anlamlıdır.
example_library = rule(
implementation = _example_library_impl,
attrs = {
...
"_compiler": attr.label(
default = Label("//tools:example_compiler"),
allow_single_file = True,
executable = True,
cfg = "exec",
),
},
)
Bu örnekte, example_library
türündeki her hedef //tools:example_compiler
derleyicisine dolaylı bir bağımlılığa sahiptir. Bu sayede, kullanıcı etiketini giriş olarak iletmemiş olsa bile example_library
uygulama işlevi, derleyiciyi çağıran işlemler oluşturabilir. _compiler
gizli bir özellik olduğundan, ctx.attr._compiler
bu kural türünün tüm hedeflerinde her zaman //tools:example_compiler
öğesini gösterir. Alternatif olarak, özelliği alt çizgi olmadan compiler
olarak adlandırabilir ve varsayılan değeri koruyabilirsiniz. Bu, kullanıcıların gerekirse farklı bir derleyici yerine koymasına olanak tanır, ancak derleyici etiketi hakkında bilgi sahibi olmayı gerektirmez.
Örtük bağımlılıklar genellikle kural uygulamasıyla aynı depoda bulunan araçlar için kullanılır. Araç çalışma platformundan veya farklı bir depodan geliyorsa kural, bu aracı bir araç zincirinden almalıdır.
Çıkış özellikleri
attr.output
ve attr.output_list
gibi çıkış özellikleri, hedefin oluşturduğu bir çıkış dosyasını bildirir. Bu özellikler, bağımlılık özelliklerinden iki şekilde farklıdır:
- Bu uzantılar, başka bir yerde tanımlanan hedeflere başvurmak yerine çıkış dosyası hedeflerini tanımlar.
- Çıkış dosyası hedefleri, bunun tam tersi yerine oluşturulan kural hedefine bağlıdır.
Genel olarak çıkış özellikleri, yalnızca bir kuralın hedef ada dayalı olamayan kullanıcı tanımlı adlara sahip çıkışlar oluşturması gerektiğinde kullanılır. Bir kuralın bir çıkış özelliği varsa kural genellikle out
veya outs
olarak adlandırılır.
Önceden tanımlanmış çıkışlar oluşturmanın tercih edilen yöntemi çıkış özellikleridir. Bu çıkışlara özellikle bağlı olabilir veya komut satırında istenebilir.
Uygulama işlevi
Her kural için bir implementation
işlevi gerekir. Bu işlevler, sıkı bir şekilde analiz aşamasında yürütülür ve yükleme aşamasında oluşturulan hedeflerin grafiğini, yürütme aşamasında gerçekleştirilecek işlemlerin grafiğine dönüştürür. Dolayısıyla, uygulama işlevleri aslında dosyaları okuyamaz veya yazamaz.
Kural uygulama işlevleri genellikle özeldir (başında alt çizgi bulunan bir adla adlandırılır). Kurallarla aynı şekilde adlandırılırlar ancak sonlarına _impl
eklenir.
Uygulama işlevleri tam olarak bir parametre alır: Geleneksel olarak ctx
olarak adlandırılan bir kural bağlamı. Sağlayıcıların listesini döndürürler.
Hedefler
Bağımlılıklar analiz zamanında Target
nesneleri olarak temsil edilir. Bu nesneler, hedefin uygulama işlevi yürütüldüğünde oluşturulan sağlayıcıları içerir.
ctx.attr
, her bağımlılık özelliğinin adlarına karşılık gelen alanlara sahiptir. Bu alanlar, ilgili özellik aracılığıyla her doğrudan bağımlılığı temsil eden Target
nesneleri içerir. label_list
özellikleri için bu, Targets
öğelerinin bir listesidir. label
özellikleri için bu tek bir Target
veya None
olmalıdır.
Bir hedefin uygulama işlevi, sağlayıcı nesnelerinin listesini döndürür:
return [ExampleInfo(headers = depset(...))]
Bunlara, anahtar olarak sağlayıcı türü ile dizin gösterimi ([]
) kullanılarak erişilebilir. Bunlar, Starlark'ta tanımlanan özel sağlayıcılar veya Starlark genel değişkenleri olarak kullanılabilen yerel kurallar için sağlayıcılar olabilir.
Örneğin, bir kural hdrs
özelliği aracılığıyla üstbilgi dosyalarını alır ve bunları hedefin ve tüketicilerinin derleme işlemlerine sağlarsa bunları aşağıdaki gibi toplayabilir:
def _example_library_impl(ctx):
...
transitive_headers = [hdr[ExampleInfo].headers for hdr in ctx.attr.hdrs]
Bir hedefin uygulama işlevinden struct
öğesinin, sağlayıcı nesneleri listesi yerine döndürüldüğü eski stil için:
return struct(example_info = struct(headers = depset(...)))
Sağlayıcılar, Target
nesnesinin ilgili alanından alınabilir:
transitive_headers = [hdr.example_info.headers for hdr in ctx.attr.hdrs]
Bu stil kesinlikle önerilmez ve kuralların kendisinden taşınması gerekir.
Dosyalar
Dosyalar File
nesneleriyle temsil edilir. Bazel analiz aşamasında dosya G/Ç işlemi gerçekleştirmediğinden bu nesneler, dosya içeriğini doğrudan okumak veya yazmak için kullanılamaz. Bunun yerine, eylem grafiğinin parçalarını oluşturmak için işlem yayan işlevlere (bkz. ctx.actions
) yönlendirilirler.
File
, bir kaynak dosya veya oluşturulmuş bir dosya olabilir. Oluşturulan her dosya tam olarak bir işlemin sonucu olmalıdır. Kaynak dosyalar, herhangi bir işlemin sonucu olamaz.
Her bağımlılık özelliği için karşılık gelen ctx.files
alanı, bu özellik aracılığıyla tüm bağımlılıkların varsayılan çıkışlarının bir listesini içerir:
def _example_library_impl(ctx):
...
headers = depset(ctx.files.hdrs, transitive=transitive_headers)
srcs = ctx.files.srcs
...
ctx.file
, spesifikasyonları allow_single_file=True
olarak ayarlanmış bağımlılık özellikleri için tek bir File
veya None
içerir.
ctx.executable
, ctx.file
ile aynı şekilde davranır ancak yalnızca özellikleri executable=True
olarak ayarlanmış bağımlılık özellikleri için alanlar içerir.
Çıktıları beyan etme
Analiz aşamasında, bir kuralın uygulama işlevi çıkışlar oluşturabilir.
Yükleme aşamasında tüm etiketlerin bilinmesi gerektiğinden bu ek çıkışların etiketi yoktur. Çıkışlar için File
nesneleri ctx.actions.declare_file
ve ctx.actions.declare_directory
kullanılarak oluşturulabilir. Çıkışların adları genellikle hedefin adına (ctx.label.name
) dayanır:
def _example_library_impl(ctx):
...
output_file = ctx.actions.declare_file(ctx.label.name + ".output")
...
Çıkış özellikleri için oluşturulanlar gibi önceden bildirilmiş çıkışlar için ctx.outputs
'nin ilgili alanlarından File
nesneleri alınabilir.
İşlemler
İşlem, bir giriş grubundan nasıl çıkış kümesi oluşturulacağını açıklar. Örneğin, "hello.c'de gcc'yi çalıştır ve hello.o'yu al". Bazel, bir işlem oluşturulduğunda komutu hemen çalıştırmaz. Eylemi bağımlılıklar grafiğine kaydeder, çünkü bir eylem başka bir eylemin sonucuna bağlı olabilir. Örneğin, C'de bağlayıcı, derleyiciden sonra çağrılmalıdır.
İşlem oluşturan genel amaçlı işlevler ctx.actions
bölümünde tanımlanmıştır:
ctx.actions.run
, yürütülebilir bir dosyayı çalıştırmak için.ctx.actions.run_shell
: Kabuk komutu çalıştırmak için.ctx.actions.write
, bir dosyaya dize yazmak için kullanılır.ctx.actions.expand_template
: Şablondan dosya oluşturmak için
ctx.actions.args
, eylemler için bağımsız değişkenleri verimli
bir şekilde toplamak amacıyla kullanılabilir. Yürütme zamanına kadar boşlukların birleştirilmesinden kaçınılır:
def _example_library_impl(ctx):
...
transitive_headers = [dep[ExampleInfo].headers for dep in ctx.attr.deps]
headers = depset(ctx.files.hdrs, transitive=transitive_headers)
srcs = ctx.files.srcs
inputs = depset(srcs, transitive=[headers])
output_file = ctx.actions.declare_file(ctx.label.name + ".output")
args = ctx.actions.args()
args.add_joined("-h", headers, join_with=",")
args.add_joined("-s", srcs, join_with=",")
args.add("-o", output_file)
ctx.actions.run(
mnemonic = "ExampleCompile",
executable = ctx.executable._compiler,
arguments = [args],
inputs = inputs,
outputs = [output_file],
)
...
İşlemler, giriş dosyaları listesini veya depset'ini alır ve çıkış dosyaları listesini (boş olmayan) oluşturur. Giriş ve çıkış dosyaları grubu analiz aşamasında bilinmelidir. Bağımlılıklardan gelen sağlayıcılar da dahil olmak üzere özelliklerin değerine bağlı olabilir ancak yürütmenin sonucuna bağlı olamaz. Örneğin, işleminiz sıkıştırılmış dosya açma komutunu çalıştırıyorsa, hangi dosyaların şişirilmesini beklediğinizi (sıkıştırma açmayı çalıştırmadan önce) belirtmeniz gerekir. Dahili olarak değişken sayıda dosya oluşturan işlemler, bunları tek bir dosyaya (zip, tar veya başka bir arşiv biçimi gibi) sarmalayabilir.
İşlemler, tüm girişlerini listelemelidir. Kullanılmayan girişlerin listelenmesine izin verilir ancak bu durum verimsizdir.
İşlemler tüm çıkışlarını oluşturmalıdır. Başka dosyalar yazabilirler ama çıktılarda olmayan hiçbir şey tüketicilerin kullanımına sunulmaz. Beyan edilen tüm çıkışlar bir işlemle yazılmalıdır.
Eylemler salt işlevlerle karşılaştırılabilir: Yalnızca sağlanan girişlere bağlı olmalı ve bilgisayar bilgilerine, kullanıcı adına, saate, ağa veya G/Ç cihazlarına (okuma girişleri ve yazma çıkışları hariç) erişilmemelidir. Çıkış önbelleğe alınacağı ve yeniden kullanılacağı için bu önemlidir.
Bağımlılıklar Bazel tarafından çözülür. Bazel, hangi işlemlerin yürütüleceğine karar verir. Bağımlılık grafiğinde bir döngü varsa bu bir hatadır. Bir eylemin oluşturulması, yürütüleceğini garanti etmez. Bu, çıktılarının derleme için gerekli olup olmadığına bağlıdır.
Sağlayıcılar
Sağlayıcılar, bir kuralın diğer kurallara bağlı olduğu bilgilerdir. Bu veriler arasında çıkış dosyaları, kitaplıklar, bir aracın komut satırında iletilecek parametreler veya hedef tüketicilerinin bilmesi gereken diğer her şey yer alabilir.
Bir kuralın uygulama işlevi yalnızca örneklendirilmiş hedefin doğrudan bağımlılıklarındaki sağlayıcıları okuyabildiğinden, kuralların hedefin bağımlılıkları arasından hedefin tüketicileri tarafından bilinmesi gereken tüm bilgileri genellikle bir depset
içinde toplayarak iletmesi gerekir.
Bir hedefin sağlayıcıları, uygulama işlevi tarafından döndürülen Provider
nesnelerinin listesi ile belirtilir.
Eski uygulama işlevleri, uygulama işlevinin sağlayıcı nesneleri listesi yerine struct
döndürdüğü eski bir tarzda da yazılabilir. Bu stilin kullanılması önerilmez ve kurallar bu stilde taşınmamalıdır.
Varsayılan çıkışlar
Bir hedefin varsayılan çıkışları, komut satırında derleme için hedef istendiğinde varsayılan olarak istenen çıkışlardır. Örneğin, java_library
hedefi //pkg:foo
varsayılan çıkış olarak foo.jar
değerine sahiptir. Bu nedenle, bazel build //pkg:foo
komutu tarafından oluşturulur.
Varsayılan çıkışlar, DefaultInfo
öğesinin files
parametresi tarafından belirtilir:
def _example_library_impl(ctx):
...
return [
DefaultInfo(files = depset([output_file]), ...),
...
]
DefaultInfo
bir kural uygulaması tarafından döndürülmezse veya files
parametresi belirtilmezse DefaultInfo.files
varsayılan olarak tüm önceden tanımlanmış çıkışlara (genellikle çıkış özellikleri tarafından oluşturulanlar) ayarlanır.
İşlem gerçekleştiren kurallar, doğrudan kullanılması beklenmese bile varsayılan çıkışlar sağlamalıdır. İstenen çıkışların grafiğinde bulunmayan işlemler budanır. Bir çıkış yalnızca hedefin tüketicileri tarafından kullanılıyorsa hedef bağımsız olarak oluşturulduğunda bu işlemler gerçekleştirilmez. Bu da başarısız olan hedefi yeniden oluşturmak hatayı yeniden oluşturmayacağı için hata ayıklamayı da zorlaştırır.
Çalıştırma dosyaları
Çalıştırma dosyaları, bir hedef tarafından çalışma zamanında (derleme zamanı yerine) kullanılan dosya kümesidir. Bazel, yürütme aşamasında çalışma dosyalarına işaret eden sembolik bağlantılar içeren bir dizin ağacı oluşturur. Bu işlem, çalışma zamanında çalışma dosyalarına erişebilmesi için ortamda ikili dosyayı hazırlar.
Çalıştırma dosyaları, kural oluşturma sırasında manuel olarak eklenebilir.
runfiles
nesneleri, kural bağlamında ctx.runfiles
üzerinde runfiles
yöntemi ile oluşturulabilir ve DefaultInfo
tarihinde runfiles
parametresine iletilebilir. Yürütülebilir kuralların yürütülebilir çıktısı, çalıştırma dosyalarına dolaylı olarak eklenir.
Bazı kurallar, genellikle data
olarak adlandırılan ve çıkışları hedeflerin çalışma dosyalarına eklenen özellikleri belirtir. Runfile'ler, data
'ten ve genellikle srcs
(ilişkili data
'ye sahip filegroup
hedefler içerebilir) ve deps
'tan da birleştirilmelidir.
def _example_library_impl(ctx):
...
runfiles = ctx.runfiles(files = ctx.files.data)
transitive_runfiles = []
for runfiles_attr in (
ctx.attr.srcs,
ctx.attr.hdrs,
ctx.attr.deps,
ctx.attr.data,
):
for target in runfiles_attr:
transitive_runfiles.append(target[DefaultInfo].default_runfiles)
runfiles = runfiles.merge_all(transitive_runfiles)
return [
DefaultInfo(..., runfiles = runfiles),
...
]
Özel sağlayıcılar
Sağlayıcılar, kurala özel bilgileri aktarmak için provider
işlevi kullanılarak tanımlanabilir:
ExampleInfo = provider(
"Info needed to compile/link Example code.",
fields={
"headers": "depset of header Files from transitive dependencies.",
"files_to_link": "depset of Files from compilation.",
})
Kural uygulama işlevleri daha sonra sağlayıcı örneklerini oluşturup döndürebilir:
def _example_library_impl(ctx):
...
return [
...
ExampleInfo(
headers = headers,
files_to_link = depset(
[output_file],
transitive = [
dep[ExampleInfo].files_to_link for dep in ctx.attr.deps
],
),
)
]
Sağlayıcıların özel olarak başlatılması
Özel ön işleme ve doğrulama mantığıyla bir sağlayıcının oluşturulmasını korumak mümkündür. Bu, tüm sağlayıcı örneklerinin belirli değişken değerlere uymasını sağlamak veya kullanıcılara örnek almaları için daha net bir API sunmak amacıyla kullanılabilir.
Bu işlem, provider
işlevine bir init
geri çağırma işlevi geçirilerek yapılır. Bu geri çağırma verilirse provider()
işleminin dönüş türü, iki değerin toplamı olacak şekilde değişir: init
kullanılmadığında normal döndürülen değer olan sağlayıcı simgesi ve "ham oluşturucu".
Bu durumda, sağlayıcı sembolü çağrıldığında doğrudan yeni bir örnek döndürmek yerine bağımsız değişkenleri init
geri çağırma işlevine iletir. Geri çağırmanın döndürülen değeri, değerler ile alan adları (dizeler) eşleştirilen bir dize olmalıdır. Bu, yeni örneğin alanlarını başlatmak için kullanılır. Geri çağırma işlevinin herhangi bir imzaya sahip olabileceğini ve bağımsız değişkenler imzayla eşleşmezse geri çağırma işlevi doğrudan çağrılmış gibi bir hata bildirildiğini unutmayın.
Buna karşılık ham oluşturucu, init
geri çağırma işlevini atlar.
Aşağıdaki örnekte, bağımsız değişkenlerini ön işleme almak ve doğrulamak için init
kullanılır:
# //pkg:exampleinfo.bzl
_core_headers = [...] # private constant representing standard library files
# It's possible to define an init accepting positional arguments, but
# keyword-only arguments are preferred.
def _exampleinfo_init(*, files_to_link, headers = None, allow_empty_files_to_link = False):
if not files_to_link and not allow_empty_files_to_link:
fail("files_to_link may not be empty")
all_headers = depset(_core_headers, transitive = headers)
return {'files_to_link': files_to_link, 'headers': all_headers}
ExampleInfo, _new_exampleinfo = provider(
...
init = _exampleinfo_init)
export ExampleInfo
Daha sonra bir kural uygulaması, sağlayıcıyı şu şekilde örneklendirebilir:
ExampleInfo(
files_to_link=my_files_to_link, # may not be empty
headers = my_headers, # will automatically include the core headers
)
Ham oluşturucu, init
mantığından geçmeyen alternatif kamu fabrikası işlevlerini tanımlamak için kullanılabilir. Örneğin, exampleinfo.bzl'de:
def make_barebones_exampleinfo(headers):
"""Returns an ExampleInfo with no files_to_link and only the specified headers."""
return _new_exampleinfo(files_to_link = depset(), headers = all_headers)
Ham kurucu genellikle adı alt çizgiyle başlayan bir değişkene (yukarıdaki _new_exampleinfo
) bağlanır. Böylece kullanıcı kodu, kurucuyu yükleyemez ve rastgele sağlayıcı örnekleri oluşturamaz.
init
'ün başka bir kullanımı, kullanıcının sağlayıcı simgesini çağırmasını tamamen engellemek ve bunun yerine kullanıcıyı fabrika işlevini kullanmaya zorlamaktır:
def _exampleinfo_init_banned(*args, **kwargs):
fail("Do not call ExampleInfo(). Use make_exampleinfo() instead.")
ExampleInfo, _new_exampleinfo = provider(
...
init = _exampleinfo_init_banned)
def make_exampleinfo(...):
...
return _new_exampleinfo(...)
Yürütülebilir kurallar ve test kuralları
Yürütülebilir kurallar, bir bazel run
komutu tarafından çağrılabilecek hedefleri tanımlar.
Test kuralları, hedefleri bazel test
komutuyla da çağrılabilecek özel bir tür çalıştırılabilir kuraldır. Yürütülebilir ve test kuralları, rule
çağrısında ilgili executable
veya test
bağımsız değişkenini True
olarak ayarlayarak oluşturulur:
example_binary = rule(
implementation = _example_binary_impl,
executable = True,
...
)
example_test = rule(
implementation = _example_binary_impl,
test = True,
...
)
Test kurallarının adları _test
ile biten adlara sahip olmalıdır. (Test hedef adları da genellikle kurallara göre _test
ile biter, ancak bu zorunlu değildir.) Test dışı kurallarda bu son ek olmamalıdır.
Her iki kural türü de run
veya test
komutları tarafından çağrılacak yürütülebilir bir çıkış dosyası (önceden bildirilmiş veya bildirilmemiş olabilir) oluşturmalıdır. Bazel'e, bir kuralın çıktılarından hangisinin bu yürütülebilir dosya olarak kullanılacağını bildirmek için döndürülen bir DefaultInfo
sağlayıcının executable
bağımsız değişkeni olarak iletin. Bu executable
, kuralın varsayılan çıkışlarına eklenir (bu nedenle bunu hem executable
hem de files
'a iletmeniz gerekmez). Ayrıca çalışma dosyalarına da dolaylı olarak eklenir:
def _example_binary_impl(ctx):
executable = ctx.actions.declare_file(ctx.label.name)
...
return [
DefaultInfo(executable = executable, ...),
...
]
Bu dosyayı oluşturan işlem, dosyada yürütülebilir bitini ayarlamalıdır. ctx.actions.run
veya ctx.actions.run_shell
işlemi için bu işlem, işlem tarafından çağrılan temel araç tarafından yapılmalıdır. ctx.actions.write
işlemi için is_executable=True
değerini geçin.
Eski davranış olarak, yürütülebilir kuralların önceden beyan edilmiş özel bir ctx.outputs.executable
çıkışı vardır. Bu dosya, DefaultInfo
kullanarak bir dosya belirtmezseniz varsayılan yürütülebilir dosya olur; aksi takdirde kullanılmamalıdır. Bu çıkış mekanizması, yürütülebilir dosyanın adının analiz sırasında özelleştirilmesini desteklemediği için kullanımdan kaldırılmıştır.
Yürütülebilir kural ve test kuralı örneklerini inceleyin.
Yürütülebilir kurallar ve test kuralları, tüm kurallar için eklenenlere ek olarak, dolaylı olarak tanımlanmış ek özelliklere sahiptir. Anlamlı olarak eklenen özelliklerin varsayılan değerleri değiştirilemez. Ancak bu durum, özel bir kuralı varsayılan değeri değiştiren bir Starlark makrosuna sarmalayarak atlatılabilir:
def example_test(size="small", **kwargs):
_example_test(size=size, **kwargs)
_example_test = rule(
...
)
Çalışma dosyası konumu
bazel run
(veya test
) ile yürütülebilir bir hedef çalıştırıldığında, Runfiles dizininin kökü, yürütülebilir dosyanın bitişiğinde olur. Yollar aşağıdaki gibi ilişkilidir:
# Given launcher_path and runfile_file:
runfiles_root = launcher_path.path + ".runfiles"
workspace_name = ctx.workspace_name
runfile_path = runfile_file.short_path
execution_root_relative_path = "%s/%s/%s" % (
runfiles_root, workspace_name, runfile_path)
Runfiles dizinindeki File
yolu File.short_path
değerine karşılık gelir.
Doğrudan bazel
tarafından yürütülen ikili dosya, runfiles
dizininin köküne bitişiktir. Ancak çalıştırma dosyalarından from olarak adlandırılan ikili programlar aynı varsayımı yapamaz. Bu sorunu azaltmak için her ikili, bir ortam veya komut satırı bağımsız değişkeni/işareti kullanarak çalıştırma dosyası kökünü parametre olarak kabul etmenin bir yolunu sağlamalıdır. Bu sayede ikili dosyalar, çağırdığı ikili dosyalara doğru standart çalışma dosyası kökünü iletebilir. Bu ayar belirtilmezse ikili program, çağrılan ilk ikili program olduğunu tahmin edip bitişik bir runfiles dizinini arayabilir.
İleri düzey konular
Çıkış dosyalarını isteme
Tek bir hedefin birden fazla çıkış dosyası olabilir. Bir bazel build
komutu çalıştırıldığında, komuta verilen hedeflerin bazı çıkışlarının istendiği kabul edilir. Bazel yalnızca istenen bu dosyaları ve doğrudan veya dolaylı olarak bu dosyalara bağlı olan dosyaları derleyebilir. (Eylem grafiği söz konusu olduğunda Bazel, yalnızca istenen dosyaların geçişli bağımlılıkları olarak erişilebilen işlemleri yürütür.)
Varsayılan çıkışlara ek olarak, komut satırında açıkça istenebilecek herhangi bir önceden bildirilmiş çıkış vardır. Kurallar, çıktı özellikleri aracılığıyla önceden tanımlanmış çıkışları belirtebilir. Bu durumda, kullanıcı kuralı örneklendirirken çıkışlar için açıkça etiketler seçer. Çıkış özellikleri için File
nesnelerini almak amacıyla ilgili ctx.outputs
özelliğini kullanın. Kurallar, hedef ada göre de önceden bildirilmiş çıkışları dolaylı olarak tanımlayabilir ancak bu özelliğin desteği sonlandırılmıştır.
Varsayılan çıkışlara ek olarak, birlikte istenebilecek çıkış dosyası koleksiyonları olan çıkış grupları da vardır. Bu bilgiler --output_groups
ile istenebilir. Örneğin, bir hedef //pkg:mytarget
, debug_files
çıkış grubuna sahip bir kural türüne sahipse bu dosyalar bazel build //pkg:mytarget
--output_groups=debug_files
çalıştırılarak oluşturulabilir. Önceden bildirilmemiş çıkışların etiketleri bulunmadığından yalnızca varsayılan çıkışlarda veya bir çıkış grubunda görünerek istenebilir.
Çıkış grupları OutputGroupInfo
sağlayıcısıyla belirtilebilir. Birçok yerleşik sağlayıcının aksine OutputGroupInfo
'ün, çıkış gruplarını tanımlamak için rastgele adlara sahip parametreler alabileceğini unutmayın:
def _example_library_impl(ctx):
...
debug_file = ctx.actions.declare_file(name + ".pdb")
...
return [
DefaultInfo(files = depset([output_file]), ...),
OutputGroupInfo(
debug_files = depset([debug_file]),
all_files = depset([output_file, debug_file]),
),
...
]
Ayrıca çoğu sağlayıcının aksine OutputGroupInfo
, aynı çıkış gruplarını tanımlamadıkları sürece hem bir en boy hem de bu özelliğin uygulandığı kural hedefi tarafından döndürülebilir. Bu durumda, sonuçta ortaya çıkan sağlayıcılar birleştirilir.
OutputGroupInfo
etiketinin genellikle belirli dosya türlerini bir hedeften tüketicilerin eylemlerine aktarmak için kullanılmaması gerektiğini unutmayın. Bunun yerine kurala özgü sağlayıcılar tanımlayın.
Yapılandırmalar
Farklı bir mimari için C++ ikili dosyası oluşturmak istediğinizi varsayalım. Derleme karmaşık olabilir ve birkaç adımdan oluşabilir. Derleyiciler ve kod oluşturucular gibi bazı ara ikili programların, yürütme platformunda (ana makineniz veya bir uzak yürütücü olabilir) çalışması gerekir. Nihai çıkış gibi bazı ikili programlar hedef mimariye göre derlenmelidir.
Bu nedenle Bazel'de "yapılandırmalar" ve geçişler kavramı vardır. En üstteki hedefler (komut satırında istenenler) "hedef" yapılandırmasında oluşturulurken yürütme platformunda çalışması gereken araçlar ise "exec" yapılandırmasında oluşturulur. Kurallar, yapılandırmaya bağlı olarak farklı işlemler oluşturabilir (ör. derleyiciye iletilen CPU mimarisini değiştirmek için). Bazı durumlarda farklı yapılandırmalar için aynı kitaplık gerekli olabilir. Bu durumda, analiz edilir ve muhtemelen birden çok kez oluşturulur.
Varsayılan olarak Bazel, hedef bağımlılıklarını hedefin kendisiyle aynı yapılandırmada, yani geçiş olmadan oluşturur. Bir bağımlılık, hedefin oluşturulmasına yardımcı olmak için gereken bir araç olduğunda, ilgili özellik bir yürütme yapılandırmasına geçiş belirtmelidir. Bu, aracın ve tüm bağımlılıklarının yürütme platformu için derleme yapmasına neden olur.
Her bir bağımlılık özelliği için bağımlılıkların aynı yapılandırmada derlenmesi veya bir yönetici yapılandırmasına geçiş yapılması gerekip gerekmediğine karar vermek için cfg
kullanabilirsiniz.
Bir bağımlılık özelliğinde executable=True
işareti varsa cfg
açıkça ayarlanmalıdır. Bunun amacı, yanlış yapılandırmaya yönelik bir aracın yanlışlıkla oluşturulmasını önlemektir.
Örneği inceleyin
Genel olarak, çalışma zamanında ihtiyaç duyulacak kaynaklar, bağımlı kitaplıklar ve yürütülebilir dosyalar aynı yapılandırmayı kullanabilir.
Derleme işleminin bir parçası olarak çalıştırılan araçlar (ör. derleyiciler veya kod oluşturucular) bir exec yapılandırması için derlenmelidir. Bu durumda, özellikte cfg="exec"
değerini belirtin.
Aksi takdirde, çalışma zamanında kullanılan yürütülebilir dosyalar (ör. bir testin parçası olarak) hedef yapılandırma için derlenmelidir. Bu durumda, özellikte cfg="target"
değerini belirtin.
cfg="target"
aslında hiçbir şey yapmaz. Kural tasarımcılarının amaçları konusunda açık olmalarına yardımcı
olan bir kolaylık değeridir. executable=False
olduğunda (cfg
isteğe bağlıdır), bu seçeneği yalnızca okunabilirliği gerçekten artırdığı durumlarda ayarlayın.
cfg=my_transition
simgesini kullanarak kullanıcı tanımlı geçişler de kullanabilirsiniz. Bu geçişler, kural yazarlarına yapılandırmaları değiştirme konusunda çok fazla esneklik sağlar. Bunun dezavantajı ise derleme grafiğinin daha büyük ve daha az anlaşılır hale gelmesidir.
Not: Geçmişte Bazel'de yürütme platformu kavramı yoktu. Bunun yerine, tüm derleme işlemlerinin ana makinede çalıştırılacağı kabul ediliyordu. 6.0'dan önceki Bazel sürümleri, bunu temsil etmek için ayrı bir "ana makine" yapılandırması oluşturuyordu. Kodda veya eski dokümanlarda "ana makine"ye yapılan referanslar görürseniz bu terim ana makineyi ifade eder. Bu ek kavramsal yükü önlemek için Bazel 6.0 veya daha yeni bir sürümü kullanmanızı öneririz.
Yapılandırma parçaları
Kurallar cpp
, java
ve jvm
gibi yapılandırma parçalarına erişebilir. Ancak erişim hatalarını önlemek için gerekli tüm parçalar bildirilmelidir:
def _impl(ctx):
# Using ctx.fragments.cpp leads to an error since it was not declared.
x = ctx.fragments.java
...
my_rule = rule(
implementation = _impl,
fragments = ["java"], # Required fragments of the target configuration
host_fragments = ["java"], # Required fragments of the host configuration
...
)
Runfile sembolik bağlantıları
Normalde, bir dosyanın runfiles ağacındaki göreli yolu, söz konusu dosyanın kaynak ağaç veya oluşturulan çıkış ağacındaki göreli yolu ile aynıdır. Bu değerlerin bir nedenle farklı olması gerekiyorsa root_symlinks
veya symlinks
bağımsız değişkenlerini belirtebilirsiniz. root_symlinks
, yolları dosyalarla eşleyen bir sözlüktür. Bu yolların referans noktası, runfiles dizininin köküdür. symlinks
sözlüğü aynıdır ancak yollara örtülü olarak ana çalışma alanının adı eklenir (geçerli hedefi içeren deponun adı değil).
...
runfiles = ctx.runfiles(
root_symlinks = {"some/path/here.foo": ctx.file.some_data_file2}
symlinks = {"some/path/here.bar": ctx.file.some_data_file3}
)
# Creates something like:
# sometarget.runfiles/
# some/
# path/
# here.foo -> some_data_file2
# <workspace_name>/
# some/
# path/
# here.bar -> some_data_file3
symlinks
veya root_symlinks
kullanılıyorsa iki farklı dosyayı Runfiles ağacındaki aynı yola eşlememeye dikkat edin. Bu, derlemenin çakışmayı açıklayan bir hatayla başarısız olmasına neden olur. Bunu düzeltmek için çakışmayı kaldırmak için ctx.runfiles
bağımsız değişkenlerinizi değiştirmeniz gerekir. Bu kontrol, kuralınızı kullanan tüm hedeflerin yanı sıra bu hedeflere bağlı her tür hedef için yapılır. Bu durum, aracınızın başka bir araç tarafından aktarmalı olarak kullanılması muhtemelse özellikle risklidir. Simge bağlantısı adları, bir aracın çalışma dosyalarında ve tüm bağımlılıklarında benzersiz olmalıdır.
Kod kapsamı
coverage
komutu çalıştırıldığında derlemenin belirli hedefler için kapsam araçları eklemesi gerekebilir. Derleme, enstrümante edilen kaynak dosyaların listesini de toplar. Değerlendirmeye alınan hedeflerin alt kümesi, --instrumentation_filter
işaretiyle kontrol edilir.
--instrument_test_targets
belirtilmediği sürece test hedefleri hariç tutulur.
Bir kural uygulaması derleme sırasında kapsam enstrümasyonu ekliyorsa bunu uygulama işlevinde hesaba katması gerekir. Bir hedefin kaynaklarının enstrümasyona tabi tutulması gerekiyorsa ctx.coverage_instrumented kapsam modunda true değerini döndürür:
# Are this rule's sources instrumented?
if ctx.coverage_instrumented():
# Do something to turn on coverage for this compile action
Kapsam modunda her zaman açık olması gereken mantık (hedef kaynaklarının özellikle gerekli olup olmadığına bakılmaksızın), ctx.configuration.coverage_enabled
Kural, derlemeden önce doğrudan bağımlılıklarından kaynak içeriyorsa (ör. başlık dosyaları) bağımlılıkların kaynaklarının da enstrümante edilmesi gerekiyorsa derleme zamanı enstrümantasyonunu da etkinleştirmesi gerekebilir:
# Are this rule's sources or any of the sources for its direct dependencies
# in deps instrumented?
if (ctx.configuration.coverage_enabled and
(ctx.coverage_instrumented() or
any([ctx.coverage_instrumented(dep) for dep in ctx.attr.deps]))):
# Do something to turn on coverage for this compile action
Kurallar, hangi özelliklerin coverage_common.instrumented_files_info
kullanılarak oluşturulan InstrumentedFilesInfo
sağlayıcısının kapsamıyla ilgili olduğu hakkında da bilgi sağlamalıdır.
instrumented_files_info
öğesinin dependency_attributes
parametresi, deps
gibi kod bağımlılıkları ve data
gibi veri bağımlılıkları dahil olmak üzere tüm çalışma zamanı bağımlılık özelliklerini listelemelidir. Kapsam araçları eklenebilecekse source_attributes
parametresi, kuralın kaynak dosyaları özelliklerini listelemelidir:
def _example_library_impl(ctx):
...
return [
...
coverage_common.instrumented_files_info(
ctx,
dependency_attributes = ["deps", "data"],
# Omitted if coverage is not supported for this rule:
source_attributes = ["srcs", "hdrs"],
)
...
]
InstrumentedFilesInfo
döndürülmezse dependency_attributes
içinde, cfg
özelliği "host"
veya "exec"
olarak ayarlanmayan her araç dışı bağımlılık özelliği için varsayılan bir özellik oluşturulur. (srcs
gibi özellikleri source_attributes
yerine dependency_attributes
içine yerleştirdiği için bu ideal bir davranış değildir ancak bağımlılık zincirindeki tüm kurallar için açık kapsam yapılandırması gerekmesini önler.)
Doğrulama İşlemleri
Bazen derlemeyle ilgili bir şeyi doğrulamanız gerekir ve bu doğrulamayı yapmak için gereken bilgiler yalnızca yapılarda (kaynak dosyalar veya oluşturulan dosyalar) bulunur. Bu bilgiler yapılarda olduğu için kurallar dosyaları okuyamadığından analiz sırasında bu doğrulamayı yapamaz. Bunun yerine, işlemlerin bu doğrulamayı yürütme sırasında yapması gerekir. Doğrulama başarısız olduğunda işlem de başarısız olur ve dolayısıyla derleme de başarısız olur.
Çalıştırılabilecek doğrulamalara örnek olarak statik analiz, linting, bağımlılık ve tutarlılık kontrolleri ve stil kontrolleri verilebilir.
Doğrulama işlemleri, yapıları oluşturmak için gerekli olmayan işlem bölümlerini ayrı işlemlere taşıyarak derleme performansının iyileştirilmesine de yardımcı olabilir. Örneğin, derleme ve linting yapan tek bir işlem, derleme işlemi ve linting işlemi olarak ayrılabilirse linting işlemi, doğrulama işlemi olarak ve diğer işlemlerle paralel olarak çalıştırılabilir.
Bu "doğrulama işlemleri" yalnızca girişleriyle ilgili bir şeyler belirtmeleri gerektiğinden genellikle derlemenin başka bir yerinde kullanılan herhangi bir şey üretmez. Ancak bu durum bir sorun teşkil eder: Bir doğrulama işlemi, derlemede başka bir yerde kullanılan bir şey üretmiyorsa kural, işlemi nasıl çalıştırır? Eskiden yaklaşım, doğrulama işleminin boş bir dosya çıktısını sağlamak ve bu çıktıyı derlemedeki başka bir önemli işlemin girişlerine yapay olarak eklemekti:
Bazel, derleme işlemi çalıştırıldığında doğrulama işlemini her zaman çalıştıracağı için bu yöntem işe yarar. Ancak bu yöntemin önemli dezavantajları vardır:
Doğrulama işlemi, derlemenin kritik yolundadır. Bazel, derleme işlemini çalıştırmak için boş çıkışın gerekli olduğunu düşündüğünden, derleme işlemi girişi yoksaysa bile önce doğrulama işlemini çalıştırır. Bu, paralelliği azaltır ve derlemeleri yavaşlatır.
Derlemedeki diğer işlemler derleme işlemi yerine çalıştırılabilirse doğrulama işlemlerinin boş çıkışlarının da bu işlemlere eklenmesi gerekir (örneğin,
java_library
kaynak jar çıkışı). Bu durum ayrıca, derleme işlemi yerine çalıştırılabilecek yeni işlemlerin daha sonra eklenmesi ve boş doğrulama çıkışının yanlışlıkla bırakılması durumunda da soruna neden olur.
Bu sorunların çözümü Doğrulamalar Çıktı Grubu'nu kullanmaktır.
Doğrulamalar Çıkış Grubu
Doğrulamalar Çıkış Grubu, doğrulama işlemlerinin aksi halde kullanılmayan çıkışlarını, diğer işlemlerin girişlerine yapay olarak eklenmeleri gerekmeyecek şekilde tutmak için tasarlanmış bir çıkış grubudur.
Bu grup, --output_groups
işaretinin değerinden ve hedefin nasıl bağımlı olduğundan (örneğin, komut satırında, bağımlılık olarak veya hedefin gizli çıkışları aracılığıyla) bağımsız olarak çıkışlarının her zaman istenmesi açısından özeldir. Normal önbelleğe alma ve artımlılığın geçerli olmaya devam ettiğini unutmayın: Doğrulama işlemi için yapılan girişler değişmediyse ve doğrulama işlemi daha önce başarılıysa doğrulama işlemi çalıştırılmaz.
Bu çıkış grubunun kullanılması, doğrulama işlemlerinin boş olsa bile bazı dosyalar üretmesini gerektirir. Bu işlem, bir dosyanın oluşturulması için normalde çıkış oluşturmayan bazı araçların sarmalanmasını gerektirebilir.
Bir hedefin doğrulama işlemleri üç durumda çalıştırılmaz:
- Hedef bir araç olarak kullanıldığında
- Hedefe örtülü bir bağımlılık yapıldığında (örneğin, "_" ile başlayan bir özellik)
- Hedef, ana makine veya yönetici yapılandırmasında oluşturulduğunda.
Bu hedeflerin, doğrulama hatalarını ortaya çıkaracak kendi ayrı derlemelerinin ve testlerinin olduğu varsayılır.
Doğrulamalar Çıkış Grubu'nu kullanma
Doğrulamalar Çıkış Grubu _validation
olarak adlandırılır ve diğer çıkış grupları gibi kullanılır:
def _rule_with_validation_impl(ctx):
ctx.actions.write(ctx.outputs.main, "main output\n")
ctx.actions.write(ctx.outputs.implicit, "implicit output\n")
validation_output = ctx.actions.declare_file(ctx.attr.name + ".validation")
ctx.actions.run(
outputs = [validation_output],
executable = ctx.executable._validation_tool,
arguments = [validation_output.path])
return [
DefaultInfo(files = depset([ctx.outputs.main])),
OutputGroupInfo(_validation = depset([validation_output])),
]
rule_with_validation = rule(
implementation = _rule_with_validation_impl,
outputs = {
"main": "%{name}.main",
"implicit": "%{name}.implicit",
},
attrs = {
"_validation_tool": attr.label(
default = Label("//validation_actions:validation_tool"),
executable = True,
cfg = "exec"),
}
)
Doğrulama çıkış dosyasının DefaultInfo
bölümüne veya başka bir işlemin girişlerine eklenmediğine dikkat edin. Hedefin etikete bağlı olduğu veya hedefin örtülü çıkışlarından herhangi birine doğrudan ya da dolaylı olarak bağımlıysa bu kural türündeki bir hedefle ilgili doğrulama işlemi çalışmaya devam eder.
Genellikle doğrulama işlemleri çıktılarının yalnızca doğrulama çıkış grubuna gitmesi ve diğer işlemlerin girişlerine eklenmemesi önemlidir. Aksi takdirde, paralellik kazanımları geçersiz kılınabilir. Ancak, Bazel'in şu anda bunu uygulamak için özel bir denetimi olmadığını unutmayın. Bu nedenle, Starlark kurallarının testlerindeki hiçbir işlemin girişlerine doğrulama işlemi çıkışlarının eklenip eklenmediğini test etmeniz gerekir. Örneğin:
load("@bazel_skylib//lib:unittest.bzl", "analysistest")
def _validation_outputs_test_impl(ctx):
env = analysistest.begin(ctx)
actions = analysistest.target_actions(env)
target = analysistest.target_under_test(env)
validation_outputs = target.output_groups._validation.to_list()
for action in actions:
for validation_output in validation_outputs:
if validation_output in action.inputs.to_list():
analysistest.fail(env,
"%s is a validation action output, but is an input to action %s" % (
validation_output, action))
return analysistest.end(env)
validation_outputs_test = analysistest.make(_validation_outputs_test_impl)
Doğrulama İşlemleri İşareti
Doğrulama işlemlerinin çalıştırılması, varsayılan olarak "doğru" değerini alan --run_validations
komut satırı işaretiyle kontrol edilir.
Kullanımdan kaldırılan özellikler
Desteği sonlandırılan önceden beyan edilmiş çıkışlar
Önceden beyan edilmiş çıkışları kullanmanın desteği sonlandırılmış iki yöntemi vardır:
rule
öğesininoutputs
parametresi, önceden beyan edilmiş çıkış etiketleri oluşturmak için çıkış özelliği adları ile dize şablonları arasında bir eşleme belirtir. Önceden bildirilmemiş çıkışları kullanmayı ve çıkışları açıkçaDefaultInfo.files
öğesine eklemeyi tercih edin. Önceden beyan edilmiş bir çıkışın etiketi yerine çıkışı tüketen kurallar için giriş olarak kural hedefinin etiketini kullanın.Yürütülebilir kurallar için
ctx.outputs.executable
, kural hedefiyle aynı ada sahip önceden bildirilmiş yürütülebilir bir çıkışı ifade eder. Çıktıyı açıkça belirtmeyi tercih edin (ör.ctx.actions.declare_file(ctx.label.name)
ile) ve yürütülebilir dosyayı oluşturan komutun izinlerini yürütmeye izin verecek şekilde ayarladığından emin olun. Yürütülebilir çıkışı açık bir şekildeDefaultInfo
öğesininexecutable
parametresine iletin.
Kullanılmaması gereken çalışma dosyası özellikleri
ctx.runfiles
ve runfiles
türü, çoğu eski nedenlerle korunan karmaşık bir özellik grubuna sahiptir.
Aşağıdaki öneriler karmaşıklığı azaltmaya yardımcı olur:
ctx.runfiles
collect_data
vecollect_default
modlarını kullanmaktan kaçının. Bu modlar, çalışma dosyalarını sabit kodlu belirli bağımlılık kenarlarında karmaşık yöntemlerle, dolaylı olarak toplar. Bunun yerine,ctx.runfiles
öğesininfiles
veyatransitive_files
parametrelerini kullanarak ya da bağımlılardan gelen çalıştırma dosyalarınırunfiles = runfiles.merge(dep[DefaultInfo].default_runfiles)
ile birleştirerek dosya ekleyin.DefaultInfo
kurucusunundata_runfiles
vedefault_runfiles
özelliklerini kullanmaktan kaçının. Bunun yerineDefaultInfo(runfiles = ...)
değerini belirtin. "Varsayılan" ve "veri" çalışma dosyaları arasındaki ayrım, eski nedenlerden dolayı korunur. Örneğin, bazı kurallar varsayılan çıkışlarınıdata_runfiles
konumuna yerleştirir, ancakdefault_runfiles
içine koymaz. Kurallar,data_runfiles
yerine hem varsayılan çıkışları içermeli hem de çalıştırma dosyaları sağlayan özelliklerden (genellikledata
)default_runfiles
özelliğini birleştirmelidir.runfiles
'üDefaultInfo
'dan alırken (genellikle yalnızca mevcut kural ile bağımlılıkları arasındaki çalıştırma dosyalarını birleştirmek için)DefaultInfo.data_runfiles
'i değilDefaultInfo.default_runfiles
kullanın.
Eski sağlayıcılardan taşıma
Geçmişte Bazel sağlayıcıları Target
nesnesindeki basit alanlardı. Bu uygulamalara nokta operatörü kullanılarak erişildi ve alan, kuralın uygulama işlevi tarafından döndürülen bir struct'ın içine yerleştirilerek oluşturuldu.
Bu stilin desteği sonlandırılmıştır ve yeni kodlarda kullanılmamalıdır; taşıma işleminde size yardımcı olabilecek bilgiler için aşağıya bakın. Yeni sağlayıcı mekanizması, ad çakışmalarını önler. Ayrıca, sağlayıcı örneğine erişen kodların sağlayıcı sembolünü kullanarak almasını zorunlu kılarak veri gizlemeyi de destekler.
Eski sağlayıcılar şu anda hâlâ desteklenmektedir. Bir kural, aşağıdaki gibi hem eski hem de modern sağlayıcıları döndürebilir:
def _old_rule_impl(ctx):
...
legacy_data = struct(x="foo", ...)
modern_data = MyInfo(y="bar", ...)
# When any legacy providers are returned, the top-level returned value is a
# struct.
return struct(
# One key = value entry for each legacy provider.
legacy_info = legacy_data,
...
# Additional modern providers:
providers = [modern_data, ...])
dep
, bu kuralın bir örneği için sonuçta ortaya çıkan Target
nesnesiyse sağlayıcılar ve içerikleri dep.legacy_info.x
ve dep[MyInfo].y
olarak alınabilir.
Döndürülen struct, providers
'e ek olarak özel anlamı olan birkaç başka alanı da alabilir (dolayısıyla buna karşılık gelen bir eski sağlayıcı oluşturmaz):
files
,runfiles
,data_runfiles
,default_runfiles
veexecutable
alanları,DefaultInfo
'teki aynı adlı alanlara karşılık gelir. BirDefaultInfo
sağlayıcısı döndürürken bu alanlardan herhangi birinin belirtilmesine izin verilmez.output_groups
alanı bir struct değeri alır ve birOutputGroupInfo
değerine karşılık gelir.
Kuralların provides
bildirimlerinde ve bağımlılık özelliklerinin providers
bildirimlerinde eski sağlayıcılar dize olarak, modern sağlayıcılar ise *Info
sembolleriyle iletilir. Taşıma sırasında dizeleri semboller
olarak değiştirdiğinizden emin olun. Tüm kuralların atomik olarak güncellenmesinin zor olduğu karmaşık veya büyük kural kümeleri için aşağıdaki adım sırasını uygulamanız daha kolay olabilir:
Yukarıdaki söz dizimini kullanarak eski sağlayıcıyı oluşturan kuralları hem eski hem de modern sağlayıcıları oluşturacak şekilde değiştirin. Eski sağlayıcıyı döndürdüğünü belirten kurallarda, bu beyanı hem eski hem de modern sağlayıcıları içerecek şekilde güncelleyin.
Eski sağlayıcıyı kullanan kuralları, modern sağlayıcıyı kullanacak şekilde değiştirin. Herhangi bir özellik beyanı eski sağlayıcıyı gerektiriyorsa bunları modern sağlayıcıyı gerektirecek şekilde de güncelleyin. İsterseniz bu çalışmayı 1. adımla birlikte yürütebilirsiniz. Bunun için tüketicilerin sağlayıcılardan birini kabul etmesini/zorunlu tutmasını sağlayın:
hasattr(target, 'foo')
kullanarak eski sağlayıcının varlığını veyaFooInfo in target
kullanarak yeni sağlayıcının varlığını test edin.Eski sağlayıcıyı tüm kurallardan tamamen kaldırın.