Bu belgede, kod tabanı ve Bazel'ın nasıl yapılandırıldığı açıklanmaktadır. Google , son kullanıcıları değil, Bazel'a katkıda bulunmak isteyen kişiler için tasarlanmıştır.
Giriş
Bazel'in kod tabanı büyük (~350KLOC üretim kodu ve ~260 KLOC testi) kimse o işin geneline hakim değil: Herkes kendi işlerini çok iyidir, ama her bölgede tepelerin üstünde neler olduğunu çok az kişi bilir girin.
Yolculuğun ortasında olan insanların kendilerini bir engelin içinde yalın bir patikanın kaybolduğu, karanlık bir ormana bakan bu belgede, kod tabanına ilişkin genel bir bakış sunacaktır. Böylece, anlamaya başladım.
Bazel'ın kaynak kodunun herkese açık sürümü, GitHub'da şu adreste bulunmaktadır: github.com/bazelbuild/bazel adresine gidin. Bu değil "bilgi kaynağı"; Google'ın dahili kaynak ağacından türetilir ve Google dışında yararlı olmayan ek işlevler içeriyor. İlgili içeriği oluşturmak için kullanılan GitHub'ı bilgi kaynağı hâline getirmek.
Katkılar, normal GitHub pull isteği mekanizması üzerinden kabul edilir. ve bir Google çalışanı tarafından manuel olarak dahili kaynak ağacına içe aktarılır, tekrar GitHub'a aktarılmıştır.
İstemci/sunucu mimarisi
Bazel'in büyük kısmı, derlemeler arasında RAM'de kalan bir sunucu işleminde yer alır. Bu, Bazel'in derlemeler arasındaki durumu korumasına olanak tanır.
Bu nedenle Bazel komut satırında iki tür seçenek vardır: başlatma ve komutuna ekleyin. Aşağıdaki gibi bir komut satırında:
bazel --host_jvm_args=-Xmx8G build -c opt //foo:bar
Bazı seçenekler (--host_jvm_args=
), çalıştırılacak komutun adından önce gelir
ve bazıları da sonrasında (-c opt
); önceki türe "başlangıç seçeneği" denir ve
sunucu işlemini bir bütün olarak etkilerken, ikincisinde "komutun
seçenek", yalnızca tek bir komutu etkiler.
Her sunucu örneğinin tek bir ilişkilendirilmiş çalışma alanı (kaynak koleksiyonu) "depolar" olarak bilinir) ve her çalışma alanında genellikle tek bir etkin sunucu örneğidir. Özel bir çıkış tabanı belirterek bu durumu atlatabilirsiniz. (daha fazla bilgi için "Dizin düzeni" bölümüne bakın).
Bazel, aynı zamanda geçerli bir .zip dosyası olan tek bir ELF yürütülebilir dosyası olarak dağıtılır.
bazel
yazdığınızda, yukarıdaki ELF yürütülebilir dosyası C++ (
"client") kontrolü alır. Bu komut dosyasını kullanarak uygun bir sunucu işlemi
şu adımları uygulayın:
- Dosyanın daha önce çıkarılıp çıkarılmadığını kontrol eder. İlişkilendirilmediğinde bu mümkün değildir. Bu sunucu uygulamasının geldiği yerdir.
- Çalışan bir etkin sunucu örneği olup olmadığını kontrol eder: Çalışıyor,
doğru çalışma alanı dizinini kullanır. Google
$OUTPUT_BASE/server
dizinine bakarak çalışan sunucuyu bulur sunucunun dinlediği bağlantı noktasının yer aldığı bir kilit dosyası bulunur. - Gerekirse eski sunucu işlemini sonlandırır
- Gerekirse yeni bir sunucu işlemi başlatır
Uygun bir sunucu işlemi hazır olduğunda, çalıştırılması gereken komut
bir gRPC arayüzü üzerinden iletildikten sonra Bazel çıktısı
dokunun. Aynı anda yalnızca bir komut çalışabilir. Bu
parçalarının C++ ve diğer kısımlarında bulunan özel bir kilitleme mekanizması kullanılarak
Java. Paralel olarak birden fazla komutu çalıştırmak için
bir altyapı vardır.
bazel version
komutunu başka bir komutla paralel olarak çalıştıramamak,
biraz utanç verici. En büyük engel BlazeModule
sn. yaşam döngüsüdür.
ve BlazeRuntime
içinde bazı eyaletler.
Bir komutun sonunda, Bazel sunucusu çıkış kodunu istemciye iletir.
dönmelidir. İlginç bir kırışıklık ise bazel run
'nin uygulanmasıdır:
bu komutun işi, Bazel'ın yeni derlediği bir şeyi çalıştırmak
sunucu işleminden çıkarırız, çünkü bunun bir terminali yoktur. Bunun yerine
hangi ikili programla ujexec()
birlikte çalışacağını ve hangi bağımsız değişkenlerle
Ctrl-C tuşlarına basıldığında, istemci bunu gRPC'de bir İptal çağrısına çevirir komutu mümkün olan en kısa sürede sonlandırmaya çalışır. üçüncü Ctrl-C tuşlarına basarsanız istemci, bunun yerine sunucuya bir SIGKILL gönderir.
İstemcinin kaynak kodu src/main/cpp
altında ve protokol için kullanılan protokol
sunucuyla iletişim kurma src/main/protobuf/command_server.proto
içindedir .
Sunucunun ana giriş noktası BlazeRuntime.main()
ve gRPC çağrılarıdır.
GrpcServerImpl.run()
tarafından işlenir.
Dizin düzeni
Bazel, derleme sırasında biraz karmaşık bir dizin grubu oluşturur. Tam açıklaması Çıkış dizini düzeninde bulunur.
"Ana depo" Bazel'in çalıştırıldığı kaynak ağaçtır. Genellikle karşılık gelen göz atmayı unutmayın. Bu dizinin kökü "çalışma alanı kökü" olarak adlandırılır.
Bazel, tüm verilerini "çıkış kullanıcısı kökü" altına yerleştirir. Bu genelde
$HOME/.cache/bazel/_bazel_${USER}
ancak
--output_user_root
başlatma seçeneği.
"Yükleme tabanı" Bazel'ın çıkarıldığı yerdir. Bu işlem otomatik olarak yapılır.
ve her Bazel sürümü,
önemli bir rol oynar. Varsayılan olarak $OUTPUT_USER_ROOT/install
konumundadır ve değiştirilebilir
--install_base
komut satırı seçeneğini kullanarak.
"Çıkış tabanı" öğesi, Bazel örneğinin belirli bir
çalışma alanı yazar. Her çıkış tabanında en fazla bir Bazel sunucu örneği bulunur
devam edebilir. Normalde saat $OUTPUT_USER_ROOT/<checksum of the path
to the workspace>
. --output_base
başlangıç seçeneği kullanılarak değiştirilebilir.
Bu, başka şeylerin yanı sıra, sınırlı bir etki yaratarak
bir Bazel örneği herhangi bir zamanda herhangi bir çalışma alanında çalışabilir.
Çıkış dizini şunları içerir:
$OUTPUT_BASE/external
itibarıyla getirilen harici depolar.- Tüm kaynağa ilişkin sembolik bağlantıları içeren bir dizin olan exec kökü
kodunu ekleyin. Bulunduğu yer:
$OUTPUT_BASE/execroot
. Etkinlik sırasında çalışma dizini$EXECROOT/<name of main repository>
şeklindedir. Bunu$EXECROOT
olarak değiştirmeyi planlıyoruz. Ancak bu uzun vadeli bir plandır çünkü çok uyumsuz bir değişikliktir. - Derleme sırasında oluşturulan dosyalar.
Komut çalıştırma işlemi
Bazel sunucusu kontrolü ele alıp bu sunucu için şu aşamalar gerçekleşir:
BlazeCommandDispatcher
yeni istek hakkında bilgilendirildi. Karar verir komutun çalışması için bir çalışma alanına ihtiyaç duyup duymadığı (komutun sürüm veya sürüm gibi kaynak koduyla hiçbir ilgisi olmayanlar yardım) ve başka bir komutun çalışıp çalışmadığını kontrol eder.Doğru komut bulundu. Her komut, arayüzü uygulamalıdır
BlazeCommand
ve@Command
ek açıklamasına sahip olmalıdır (bu, bir komutun ihtiyaç duyduğu tüm meta veriler şöyle olsaydıBlazeCommand
alanındaki yöntemler tarafından açıklanmaktadır)Komut satırı seçenekleri ayrıştırılır. Her komutun farklı bir komut satırı vardır
@Command
ek açıklamasında açıklanan seçenekleri sunar.Bir etkinlik yolu oluşturulur. Etkinlik otobüsü, gerçekleşen etkinlikleri yardımcı oluyorum. Bunlardan bazıları Bazel dışına nasıl çalıştığını dünyaya anlatmak için gidilir.
Kontrol, komuta geçer. En ilginç komutlar, bir komut dosyası çalıştırma derleme: derleme, test etme, çalıştırma, kapsam vb.: bu işlev
BuildTool
tarafından uygulandı.Komut satırındaki hedef kalıplar grubu ayrıştırılır ve
//pkg:all
ve//pkg/...
çözümlendi. Uygulandığı yer:AnalysisPhaseRunner.evaluateTargetPatterns()
ve Skyframe'de şu şekilde yeniden düzenlendi:TargetPatternPhaseValue
.Yükleme/analiz aşaması, eylem grafiğini üretmek için çalıştırılır. derleme için yürütülmesi gereken komutların döngüsel grafiğidir).
Yürütme aşaması başlar. Bu, müşteri hizmetleri için gereken her istenen üst düzey hedefleri derlemek için kullanır.
Komut satırı seçenekleri
Bazel çağrısına ilişkin komut satırı seçenekleri
OptionsParsingResult
nesnesi, bu nesne de "option"dan bir harita içeriyor
sınıflar" değerleriyle eşleştirin. Bir "seçenek sınıfı" şunun bir alt sınıfıdır:
OptionsBase
ve her biriyle ilgili komut satırı seçeneklerini birlikte gruplandırır
diğer. Örneğin:
- Bir programlama diliyle (
CppOptions
veyaJavaOptions
) ilgili seçenekler. Bunlar,FragmentOptions
alt sınıfı olmalıdır ve sonunda sarmalanır birBuildOptions
nesnesine dönüştürülebilir. - Bazel'in işlemleri yürütme şekliyle ilgili seçenekler (
ExecutionOptions
)
Bu seçenekler analiz aşamasında tüketilmek üzere tasarlanmıştır ve (
Java'da RuleContext.getFragment()
veya Starlark'ta ctx.fragments
aracılığıyla).
Bazılarının (örneğin, C++ yapılıp yapılmayacağına tarama dahil olup olmaması gerektiği) okunuyor
aşamasında gerçekleşir, ancak bu durumda her zaman tesisat kurulumu gerekir;
BuildConfiguration
o zaman kullanılamaz. Daha fazla bilgi için
"Configurations" (Yapılandırmalar) bölümüne gidin.
UYARI: OptionsBase
örneklerinin sabit olduğunu ve
bu şekilde kullanabilirsiniz (örneğin, SkyKeys
ürününün bir parçası). Böyle bir durum söz konusu değildir ve
Bunlarda değişiklik yapmak, Bazel'ı
daha zorlayıcı yöntemlerle kırmanın iyi bir yoludur.
hata ayıklaması gerekir. Maalesef bunları değiştirilemez hale getirmek büyük bir iştir.
(Bir FragmentOptions
üzerinde, yapım aşamasından hemen sonra değişiklik yapılması gerekir.)
e-posta adresi equals()
veya hashCode()
olmadan önce
sorun yok.)
Bazel, seçenek sınıflarını aşağıdaki yöntemlerle öğrenir:
- Bazıları Bazel'e kabloyla bağlı (
CommonCommandOptions
) - Her Bazel komutundaki
@Command
ek açıklamasından ConfiguredRuleClassProvider
kaynağından (bunlar, ilgili komut satırı seçenekleridir) ayrı programlama dillerine)- Starlark kuralları kendi seçeneklerini de tanımlayabilir ( burada bulabilirsiniz)
Her seçenek (Starlark tarafından tanımlanan seçenekler hariç),
Aşağıdaki gibi bir @Option
ek açıklamasına sahip FragmentOptions
alt sınıfı
komut satırı seçeneğinin adı ve türü ile birlikte yardım metnini belirtin.
Bir komut satırı seçeneği değerinin Java türü genellikle basit bir şeydir
(dize, tam sayı, Boole, etiket vb.). Bununla birlikte,
daha karmaşık türler için seçenekler; Bu örnekte,
komut satırı dizesi,
com.google.devtools.common.options.Converter
Bazel'in gördüğü şekliyle kaynak ağaç
Bazel, yazılım geliştirme işiyle çalışıyor. Bu işleri okuyarak ve kaynak kodu yorumlama. Bazel'in çalıştığı kaynak kodunun toplamı "çalışma alanı" denir. kod depoları, paketler ve kurallar.
Kod depoları
Bir "depo" geliştiricinin üzerinde çalıştığı bir kaynak ağacıdır; genelde tek bir projeyi temsil eder. Bazel'in atası Blaze, bir monorepo yani derlemeyi çalıştırmak için kullanılan tüm kaynak kodunu içeren tek bir kaynak ağacıdır. Buna karşın Bazel, kaynak kodu birden çok sayfaya yayılan projeleri destekler. ekleyebilirsiniz. Bazel'ın çağrıldığı depoya "ana diğerlerine "harici depolar" adı verilir.
Bir depo, kod deposu sınır dosyasıyla (MODULE.bazel
, REPO.bazel
veya
kök dizininde (WORKSPACE
veya WORKSPACE.bazel
) kullandığınızdan emin olun. İlgili içeriği oluşturmak için kullanılan
ana depo, Bazel'ı çağıracağınız kaynak ağacıdır. Harici depolar
çeşitli şekillerde tanımlanır; harici bağımlılıkları görebilirsiniz.
genel bakış makalesini inceleyebilirsiniz.
Harici depoların kodları bağlantılı veya indirilmiş
$OUTPUT_BASE/external
Derlemeyi çalıştırırken tüm kaynak ağacın birleştirilmesi gerekir. bu
ana depodaki her paketin sembolü olan SymlinkForest
tarafından yapılır
$EXECROOT
özelliğine ve her harici depoya $EXECROOT/external
veya
$EXECROOT/..
.
Paketler
Her depo; paketler, ilgili dosyalar ve dosyalar koleksiyonundan oluşur.
ve bağımlılıkların
belirtilmesidir. Bunlar,
BUILD
veya BUILD.bazel
. Her ikisi de mevcutsa Bazel BUILD.bazel
tercih eder; neden
neden BUILD
dosyanın hâlâ kabul edildiği için Bazel'ın atası Blaze, bu dosyayı
dosya adı. Ancak, özellikle dönüşüm hunisinin üst kısmındaki
Windows'da çalışır. Bu sistemde dosya adları büyük/küçük harfe duyarlı değildir.
Paketler birbirinden bağımsızdır: Paketin BUILD
dosyasında yapılan değişiklikler
diğer paketlerin değişmesine neden olamaz. BUILD
dosyanın eklenmesi veya kaldırılması
Yinelenen glob'lar paket sınırlarında durduğu için _can _change diğer paketleri
Böylece bir BUILD
dosyasının varlığı, yinelemeyi durdurur.
BUILD
dosyasının değerlendirilmesine "paket yükleme" adı verilir. Uygulandı
PackageFactory
sınıfında çalışır. Starlark çevirmeni
mevcut kural sınıfları kümesi hakkında bilgi gerektirir. Paketin sonucu
yükleme, Package
nesnesidir. Çoğunlukla bir dizeden (bir
bir hedef) ekleyebilirsiniz.
Paket yükleme sırasındaki karmaşıklığın büyük bir kısmı globbing (karmaşıklık) sorunudur: Bazel
her kaynak dosyanın açıkça listelenmesini gerektirir ve bunun yerine glob'ları çalıştırabilir
(ör. glob(["**/*.java"])
). Kabuktan farklı olarak, aynı işleve sahip yinelemeli
alt dizinlere ayrılmalıdır (ancak alt paketlere değil). Bunun için şunlara erişim gerekir:
hale getirmeyi amaçlıyoruz. Bu yavaş olabileceği için,
paralel ve verimli çalışmasını
sağlamak sizin görevinizdir.
Globbing aşağıdaki sınıflarda uygulanır:
LegacyGlobber
, hızlı ve keyifli bir SkyFrame'in farkında olmayan küresel küreSkyframeHybridGlobber
, Skyframe kullanan ve "Skyframe yeniden başlatılması"ndan kaçınmak için eski globber'ı (aşağıda açıklanmıştır)
Package
sınıfının kendisi, yalnızca şu üyeliklere alışmış bazı üyeleri içerir:
"harici" değerini ayrıştır bağımlılıklarıyla ilgili olan) ve bir maliyeti olmayan
anlam ifade eder. Bu
normal paketleri açıklayan nesnelerin
alanları görebilirsiniz. Bunlardan bazıları:
- Depo eşlemeleri
- Kayıtlı araç zincirleri
- Kayıtlı yürütme platformları
İdeal olarak, "harici" değerini ayrıştırmak arasında daha fazla paket
Package
, normal paketleri ayrıştırmak zorunda kalmayacak şekilde,
gerekiyor. Maalesef bu ikisi bir arada olduğundan bunu yapmak
bir şekilde iç içe geçmiştim.
Etiketler, Hedefler ve Kurallar
Paketler, aşağıdaki türlerde hedeflerden oluşur:
- Dosyalar: Derlemenin girdisi veya çıktısı olan öğeler. İçinde Bazel dilindeki bunlara eserler (başka yerde tartışılan) diyoruz. Bazı taraflar, Derleme sırasında oluşturulan dosyalar hedeftir; genelde Bazel'in ilişkili bir etiketi olmamalıdır.
- Kurallar: Bunlar, girişlerden çıkışları elde etmeye yönelik adımları açıklar. Onlar
genellikle bir programlama diliyle (ör.
cc_library
,java_library
veyapy_library
), ancak dilden bağımsız bazı uzantılar da var (ör.genrule
veyafilegroup
) - Paket grupları: Görünürlük bölümünde tartışılır.
Hedefin adına Etiket adı verilir. Etiketlerin söz dizimi:
@repo//pac/kage:name
; burada repo
, Etiketin bulunduğu deponun adıdır
içinde pac/kage
, BUILD
dosyasının bulunduğu dizin, name
ise dosya yoludur.
dosyasının (etiket bir kaynak dosyaya başvuruda bulunuyorsa), dosyanın
paketinden yararlanın. Komut satırında bir hedefe atıfta bulunurken, etiketin bazı bölümleri
şunlar atlanabilir:
- Depo atlanırsa etiket ana sayfada depodur.
- Paket bölümü atlanırsa (
name
veya:name
gibi) etiket alınır dizin paketine (göreli yollar) eklemeniz gerekir. üst düzey referanslar (..) içeren uygulamalara izin verilmez.
Bir kural türüne ("C++ kitaplığı" gibi) "kural sınıfı" adı verilir. Kural sınıfları
Starlark'ta (rule()
işlevi) veya Java'da (yani
"yerel kurallar", RuleClass
yazın). Uzun vadede, dile özgü tüm
kuralı Starlark'ta uygulanacak, ancak bazı eski kural ailelerinde (ör. Java
veya C++) şu anda hâlâ Java'dadır.
Starlark kural sınıflarının BUILD
dosyanın başında içe aktarılması gerekiyor
load()
ifadesini kullanarak, Java kural sınıfları ise "doğuştan gelen" bilinen
ConfiguredRuleClassProvider
kuruluşunun kayıtlı olması nedeniyle Bazel.
Kural sınıfları aşağıdaki gibi bilgileri içerir:
- Özellikleri (ör.
srcs
,deps
): türleri, varsayılan değerleri, kısıtlamalar vb. - Varsa her bir özelliğe ekli yapılandırma geçişleri ve unsurlar
- Kuralın uygulanması
- Geçişli bilgi sağlayıcılar için "genellikle" kuralı oluşturur
Terminoloji notu: Kod tabanında genellikle "Kural" anlamına gelir.
bir kural sınıfı tarafından oluşturulur. Ancak Starlark'ta ve kullanıcılara yönelik belgelerde
"Kural" Yalnızca kural sınıfına atıfta bulunmak için kullanılmalıdır; hedef
bir "hedef"tir. Ayrıca RuleClass
"class" sınıfına sahip olmasına rağmen kendi
kural sınıfı ile hedefler arasında Java devralma ilişkisi yoktur
tercih edebilirsiniz.
Gökyüzü Çerçevesi
Bazel'in temelini oluşturan değerlendirme çerçevesi Skyframe'dir. Modeli, sisteme yapı sırasında oluşturulması gereken her şey, herhangi bir veri parçasından bağımlılıklarına işaret eden kenarları olan döngüsel grafik yani projeyi oluşturmak için bilinmesi gereken diğer veri parçalarını içerir.
Grafikteki düğümlere SkyValue
denir ve düğümlerin adları
SkyKey
sn. İkisi de tamamen değiştirilemez. Sadece değiştirilemez nesneler
erişilebilir hale getirebilirsiniz. Bu değişkenlik hemen hemen her zaman geçerlidir ve
(örneğin, şunun üyesi olan BuildOptions
bağımsız seçenek sınıfları için:
BuildConfigurationValue
ve SkyKey
gibi) değişiklik yapmamak için
yalnızca dışarıdan gözlemlenemeyecek şekillerde değiştirmelidir.
Bu şekilde, Skyframe içinde hesaplanan her şeyin (örneğin,
yapılandırılmış hedefler) sabit de olmalıdır.
Skyframe grafiğini gözlemlemenin en uygun yolu bazel dump
--skyframe=deps
komutunu çalıştırmaktır. Bu işlem, grafiğin dökümünü her satıra bir SkyValue
olacak şekilde atar. En iyisi
çok büyük olabildiği için bunu küçük
derlemeler için de yapabilirsiniz.
Skyframe, com.google.devtools.build.skyframe
paketinde bulunur. İlgili içeriği oluşturmak için kullanılan
benzer ada sahip com.google.devtools.build.lib.skyframe
paketi
Skyframe'in üzerine Bazel uygulanışı. Skyframe hakkında daha fazla bilgi:
burada bulabilirsiniz.
Skyframe, belirli bir SkyKey
öğesini SkyValue
olarak değerlendirmek için Skyframe
SkyFunction
(anahtarın türüne karşılık gelir). İşlevin
Skyframe'i çağırarak başka bağımlılıklar da isteyebilir
çeşitli SkyFunction.Environment.getValue()
aşırı yüklemeleri. Bu,
bu bağımlılıkları Skyframe’in dahili grafiğine kaydetmenin yan etkisine
ve herhangi bir bağımlılığı ortaya çıktığında Skyframe’in fonksiyonu yeniden değerlendireceği
unutmayın. Başka bir deyişle, Skyframe'in önbelleğe alma ve artımlı hesaplaması
SkyFunction
ve SkyValue
öğelerinin ayrıntı düzeyi.
SkyFunction
, kullanılamayan bir bağımlılık istediğinde, getValue()
null değerini döndürür. Bunun ardından fonksiyon,
kendisi null döndürüyor. Daha sonra Skyframe, Search Ads 360'taki
sonra, işlevi yeniden başlatın (yalnızca bu
getValue()
çağrısının, null olmayan bir sonuçla başarılı olacağı anlamına gelir.
Bunun sonucunda da SkyFunction
işlemi tekrarlamanız gerekir. Ancak bu risk,
proje yöneticiliği
önbelleğe alınan SkyValues
bağımlılığını değerlendirin. Bu nedenle, genellikle
şunları yaparak çözer:
- Bağımlılıkları gruplar halinde bildirmek (
getValuesAndExceptions()
kullanarak) yeniden başlatma sayısını sınırlandırın. SkyValue
, farklı tarafından hesaplanan ayrı parçalara ayrılırSkyFunction
'leri destekler. Böylece bağımsız olarak hesaplanıp önbelleğe alınabilirler. Bu Hafızayı artırma potansiyeline sahip olduğundan stratejik olarak yapılmalıdır. bazı yolları da görmüştük.- Yeniden başlatmalar arasında durumu depolama
SkyFunction.Environment.getState()
veya geçici statik önbellek tutma "Skyframe'in arkasında". Karmaşık SkyFunctions özellikleri ile durum yönetimi yeniden başlatmalar arasında zorlayıcı olabilir,StateMachine
, mantıksal eşzamanlılığa yönelik yapılandırılmış yaklaşım (kancalar, askıya alma veSkyFunction
içindeki hiyerarşik hesaplamaları devam ettirir. Örnek:DependencyResolver#computeDependencies
potansiyel olarak büyük grubu hesaplamak içingetState()
ile birStateMachine
kullanır doğrudan bağımlılıklarına neden olabilir. Aksi takdirde çok fazla maliyete yol açabilir.
Esasen Bazel'in bu tür çözümlere ihtiyacı var çünkü yüzlerce
devam eden binlerce Skyframe düğümü var ve Java'nın desteği,
basit ileti dizilerinin
2023 itibarıyla StateMachine
uygulaması.
Starlark
Starlark, kullanıcıların yapılandırma ve genişletme işlemleri için kullandıkları, alana özgü dildir. Bazel. Python'un çok daha az türü olan kısıtlanmış bir alt kümesi olarak düşünülür; kontrol akışı üzerinde daha fazla kısıtlama ve en önemlisi, güçlü eşzamanlı okuma olanağı sağlar. Bu Turing-complete değil, Bazı kullanıcıları (ancak hepsini değil) genel anlamda bir şeyler yapmaya çalışmaktan caydıran programlama görevlerini öğrenmiş olacaksınız.
Starlark, net.starlark.java
paketinde uygulandı.
Ayrıca bağımsız bir Go uygulaması vardır.
burada bulabilirsiniz. Java
Bazel'de kullanılan uygulama şu anda çevirmendir.
Starlark çeşitli bağlamlarda kullanılır. Örneğin:
BUILD
dosya. Burada yeni derleme hedefleri tanımlanır. Starlark bu bağlamda çalışan kod yalnızcaBUILD
içeriğine erişebilir dosyanın kendisi ve.bzl
dosya tarafından yüklendi.MODULE.bazel
dosyası. Bu noktada dış bağımlılıklar tanımlanmıştır. Bu bağlamda çalışan Starlark kodunun erişimi çok sınırlı yönergeye uygulayabilirsiniz..bzl
dosya. Bu adımda yeni derleme kuralları, depo kuralları, nasıl tanımlanacağını gösterir. Buradaki Starlark kodu yeni fonksiyonları tanımlayabilir ve diğer.bzl
dosyadan.
BUILD
ve .bzl
dosyalarının lehçeleri biraz farklı
ifade eder. Farklılıkların bir listesi mevcut
burada bulabilirsiniz.
Starlark hakkında daha fazla bilgiyi burada bulabilirsiniz.
Yükleme/analiz aşaması
Yükleme/analiz aşamasında Bazel, bir sonraki aşamaya geçmek için bir kural oluşturacağız. Temel birimi "yapılandırılmış hedef"tir. bir (hedef, yapılandırma) çifti seçin.
Buna "yükleme/analiz aşaması" denir çünkü genelde ikiye önceden seri hâlinde olan ayrı bölümler, artık zaman içinde çakışabiliyor:
- Paketler yükleniyor, yani
BUILD
dosyalarıPackage
nesnelere dönüştürülüyor temsil eden - Yapılandırılmış hedefleri analiz etmek, yani işlem grafiğini oluşturmak için
Yapılandırılmış hedeflerin geçişli kapatma işlemindeki yapılandırılmış her hedef komut satırından istenenler aşağıdan yukarıya analiz edilmelidir; yaprak düğümleri ardından komut satırındakilere taşıyın. Bu analizin girdileri tek bir yapılandırılmış hedef vardır:
- Yapılandırma. ("nasıl" kuralının oluşturulacağı; örneğin, kullanıcının gösterilmesini istediği komut satırı seçenekleri gibi ayarlar da C++ derleyicisine geçirilen)
- Doğrudan bağımlılıklar. Geçişli bilgi sağlayıcıları kullanılabilir bağlantı kurulabilir. Böyle isimlendiriliyorlar, çünkü çok sayıda "toplayıcı" kapatma işlemiyle ilgili bilgilerin, tüm .jar dosyaları veya listelenen tüm .o dosyaları gibi bir C++ ikili programına bağlı olması gerekir)
- Hedefin kendisi. Bu, hedef paketin yüklenmesinin sonucudur içinde. Bunlar, kuralların sahip olduğu özellikleri içerir. önemlidir.
- Yapılandırılmış hedefin uygulanması. Kurallarda bu, Starlark'ta veya Java'da olmak. Kuralla yapılandırılmamış tüm hedefler uygulandı yardımcı oldu.
Yapılandırılmış bir hedef analizinin sonucu:
- Buna dayalı hedefleri yapılandıran geçişli bilgi sağlayıcılar, erişim
- Oluşturabileceği eserler ve bunları üreten eylemler.
Java kurallarına sunulan API, RuleContext
API'sinin eşdeğeridir.
Starlark kurallarının ctx
bağımsız değişkeni. API'si daha güçlü ancak aynı zamanda
Örneğin, Bad ThingsTM yapmak daha kolaydır. Örneğin, zamanı veya zamanı
alan karmaşıklığı ikinci dereceden (veya daha kötüsü), Bazel sunucusunun
Java istisnası veya değişken değerleri ihlal etme (örneğin, bir
Options
örneğini kullanarak veya yapılandırılmış bir hedefi değişebilir hale getirerek)
Yapılandırılmış bir hedefin doğrudan bağımlılıklarını belirleyen algoritma
yaşadığı yer: DependencyResolver.dependentNodeMap()
.
Yapılandırmalar
Yapılandırmaları "nasıl" hedefi oluşturma: hangi platform için, komut satırı seçenekleri vb.
Bir derlemedeki birden fazla yapılandırma için aynı hedef oluşturulabilir. Bu Örneğin, aynı kod, geliştirme sırasında çalıştırılan bir araç için derlemeye başlarsak ve derlemeye başlarsak veya kocaman bir Android uygulaması (birden fazla CPU için yerel kod içeren bir uygulama) mimariler)
Kavramsal olarak yapılandırma bir BuildOptions
örneğidir. Ancak,
alıştırma, BuildOptions
şunu sağlayan BuildConfiguration
ile sarmalanır:
ek işlevler sunar. Bu, her bir sayfanın en üstünden
en alta inmek lazım. Değişmesi durumunda, derlemenin
yeniden analiz edildi.
Bu durum, derleme ya da Örneğin, istenen test çalıştırmalarının sayısı değişir (yalnızca test amaçlı etkileyen faktörlerden biri, test hedeflerini etkiler (bunun hazır değil, ancak hazır değil).
Bir kural uygulamasının yapılandırmanın bir parçası olması gerektiğinde
tanımında RuleClass.Builder.requiresConfigurationFragments()
kullanarak
, Bu, hem hatalardan kaçınmak hem de (Java parçasını kullanan Python kuralları gibi) hem de
Böylece Python seçenekleri değişirse C++
hedefin yeniden analiz edilmesi gerekmez.
Bir kuralın yapılandırması, bu kuralın "üst" yapılandırmasıyla aynı değildir kuralı. Bağımlılık ucunda yapılandırmayı değiştirme işlemine "yapılandırma geçişi". Bu durum iki yerde gerçekleşebilir:
- Bağımlılık ucunda. Bu geçişler,
Attribute.Builder.cfg()
ve birRule
(burada geçişi gerçekleşir) veBuildOptions
(orijinal yapılandırma) veya daha fazlaBuildOptions
(çıkış yapılandırması). - Yapılandırılmış bir hedefe gelen herhangi bir uçta. Bunlar,
RuleClass.Builder.cfg()
İlgili sınıflar: TransitionFactory
ve ConfigurationTransition
.
Yapılandırma geçişleri kullanılır. Örneğin:
- Derleme sırasında belirli bir bağımlılığın kullanıldığını ve bu nedenle yürütme mimarisinde
- Birden fazla öğe için belirli bir bağımlılığın oluşturulması gerektiğini mimariler (şişman Android APK'larındaki yerel kod gibi)
Bir yapılandırma geçişi birden fazla yapılandırmayla sonuçlanırsa buna bölünmüş geçiş.
Yapılandırma geçişleri Starlark'ta da uygulanabilir (dokümanlar burada bulabilirsiniz)
Geçiş bilgisi sağlayıcıları
Geçiş bilgisi sağlayıcıları, yapılandırılmış hedeflere yönelik bir yöntemdir (ve _only _way) ve buna bağımlı diğer yapılandırılmış hedefler hakkında bilgi verir. Bunun nedeni "geçişli" bunun bir tür kapsamlı görünümüdür. yapılandırılmış bir hedefin geçişli olarak kapatılması.
Java geçişli bilgi sağlayıcıları arasında genellikle bire bir iletişim vardır.
ile Starlark'ı karşılaştırabilirsiniz (istisna, DefaultInfo
FileProvider
, FilesToRunProvider
ve RunfilesProvider
, çünkü söz konusu API şuydu:
doğrudan Java harf çevirisinden daha Starlark dilindeki gibi kabul edilir).
Bunların temeli aşağıdakilerden biridir:
- Java Sınıfı nesnesi. Bu, yalnızca
Starlark'tan erişilebilir. Bu sağlayıcılar
TransitiveInfoProvider
- Dizedir. Bu, eski bir yapıya sahiptir ve genellikle
çakışmaları yaşanır. Bu tür geçişli bilgi sağlayıcılar,
build.lib.packages.Info
- Sağlayıcı sembolü. Bu öğe,
provider()
kullanılarak Starlark'tan oluşturulabilir işlevi görebilir ve yeni sağlayıcı oluşturmak için önerilen yöntemdir. Sembol Java'da birProvider.Key
örneğiyle temsil edilir.
Java'da uygulanan yeni sağlayıcılar BuiltinProvider
kullanılarak uygulanmalıdır.
NativeProvider
desteği sonlandırıldı (henüz kaldıracak zamanımız olmadı) ve
Starlark'tan TransitiveInfoProvider
alt sınıfına erişilemiyor.
Yapılandırılmış hedefler
Yapılandırılmış hedefler RuleConfiguredTargetFactory
olarak uygulanır. Bir
alt sınıfını kullanır. Starlark yapılandırılmış hedefleri
StarlarkRuleConfiguredTargetUtil.buildRule()
aracılığıyla oluşturulur .
Yapılandırılmış hedef fabrikalar şu işlemleri yapmak için RuleConfiguredTargetBuilder
kullanmalıdır:
getiri değerini oluşturmalıdır. Şunlardan oluşur:
- Onların
filesToBuild
, belirsiz bir kavram olan "bu kural ifade eder." Bunlar, yapılandırılan hedef komut satırında veya bir genrule yönergesinin src'sinde yer almalıdır. - Çalıştırma dosyaları, normal ve veriler.
- Çıkış grupları. Bunlar çeşitli "diğer dosya gruplarıdır" bu kural,
seçeceğiz. Çıkış_grubu özelliği kullanılarak
dosya grubu kuralını BUILD'da yayınlamak ve Java'da
OutputGroupInfo
sağlayıcısını kullanmak anlamına gelir.
Çalıştırma dosyaları
Bazı ikili programların çalışması için veri dosyaları gerekir. Bunun belirgin bir örneği, giriş dosyaları olabilir. Bu, Bazel'de "runfiles" kavramıyla temsil edilir. CEVAP "runfiles ağacı" belirli bir ikili program için veri dosyalarının dizin ağacıdır. Dosya sisteminde tek tek sembolik bağlantılara sahip bir sembolik bağlantı ağacı olarak oluşturulur çıkış ağaçlarının kaynağındaki dosyalara işaret eder.
Çalıştırmalar grubu, Runfiles
örneği olarak temsil edilir. Kavramsal olarak
çalışma dosyaları ağacındaki bir dosyanın yolundan,Artifact
temsil eder. İki kişilik tek bir Map
uygulamasından biraz daha karmaşıktır
nedenler:
- Çoğu zaman, bir dosyanın runfiles yolu, execpath ile aynıdır. Bunu, RAM'den tasarruf etmek için kullanırız.
- Runfiles ağaçlarında çeşitli eski giriş türleri bulunur. temsil edilir.
Çalıştırma dosyaları RunfilesProvider
kullanılarak toplanır: bu sınıfın bir örneği
çalıştırma dosyalarını yapılandırılmış bir hedefi (ör. kitaplık) ve geçişli olarak temsil eder
ve bu ihtiyaçlar iç içe yerleştirilmiş bir küme gibi toplanır (aslında bu,
(kapak altındaki iç içe yerleştirilmiş kümeler kullanılarak uygulanır): her hedef, çalıştırma dosyalarını birleştirir
kendi bağımlılıklarını ekler, kendi bağımlılıklarını ekler ve ardından ortaya çıkan kurulumu
olduğunu görüyoruz. RunfilesProvider
örneği iki Runfiles
içeriyor
Bu örnek, kuralın "veriler" aracılığıyla bağlı olduğu ve
iki tür bir bağımlılık var. Çünkü bir hedef
Bir veri özelliği üzerinden bağlı olduğunda bazen farklı çalıştırma dosyaları sunar
çok daha iyidir. Bu, artık gözden kaçırdığımız, istenmeyen eski davranış
henüz kaldırılmıyor.
İkili programların çalıştırma dosyaları, RunfilesSupport
örneği olarak gösterilir. Bu
RunfilesSupport
şu özelliklere sahip olduğundan Runfiles
ile farklıdır:
aslında inşa edilmekte olan (yalnızca bir eşleme olan Runfiles
'ın aksine). Bu
aşağıdaki ek bileşenleri gerektirir:
- Runfiles manifesti. Bu, Runfiles ağacı. Runfiles ağacının içeriği için proxy olarak kullanılır ve Bazel, Runfiles ağacının yalnızca içerik barındıran bildiriyor.
- Çıkış runfiles manifesti. Bu özellik, şu özelliklere sahip çalışma zamanı kitaplıkları tarafından kullanılır: özellikle de Windows'da çalışma dosyalarınızın yanı sıra sembolik bağlantılardan bahsedelim.
- Runfiles arabulucu. Çalıştırma ağacının var olması için sembolik bağlantı ağacını ve sembollerin işaret ettiği yapıyı oluşturmak için kullanılır. Siparişte etmek isterseniz, runfiles aracısını temsil eder.
- Komut satırı bağımsız değişkenleri
RunfilesSupport
nesnesi temsil eder.
Yönler
Oranlar, "hesaplamayı bağımlılık grafiğinde yaymanın" bir yoludur. Bunlar:
Bazel kullanıcıları için açıklandı
burada bulabilirsiniz. İyi
motive edici bir örnek olarak protokol arabellekleri verilebilir: proto_library
kuralı, bu tamponların
her dil için değil, bir protokolün uygulanmasını
herhangi bir programlamada arabellek mesajı ("protokol arabelleklerinin "temel birimi")
dil proto_library
kuralıyla birleştirilmelidir. Böylece, iki hedefte
aynı dil aynı protokol arabelleğine bağlı olduğundan yalnızca bir kez oluşturulur.
Yapılandırılmış hedefler gibi Skyframe'de SkyValue
olarak gösterilir
ve oluşturulma biçimleri, yapılandırılan hedeflerin yapısına çok benzer.
ConfiguredAspectFactory
adında bir fabrika sınıfı bulunuyor.
bir RuleContext
erişimi sağlar, ancak yapılandırılmış hedef fabrikaların aksine
Eklendiği yapılandırılmış hedef ve sağlayıcıları hakkında.
Bağımlılık grafiğinde aşağı yayılan yönler, her metrik için
özelliğini Attribute.Builder.aspects()
işlevini kullanarak öğrenin. Birkaç tane
sürece katılan ve kafa karıştırıcı şekilde adlandırılmış sınıflar:
AspectClass
, özelliğin uygulanmasıdır. Java'da olabilir. (bu bir alt sınıftır) veya Starlark'ta (bu durumdaStarlarkAspectClass
örneği). Benzer bir şekilde,RuleConfiguredTargetFactory
.AspectDefinition
, özelliğin tanımıdır; şunları içerir: sağlayıcılarını, sağladığı sağlayıcıları ve referanslarını listeleyen (ör. uygunAspectClass
örneği) uygulama. İnsanlarınRuleClass
ile benzerdir.AspectParameters
, aşağı yayılan bir özelliği parametriye etmenin bir yoludur bağımlılık grafiğini öğrenebilirsiniz. Şu anda dize eşleme olarak kullanılıyor. İyi bir örnek faydasına dair bir örnek de protokol arabellekleridir: Bir dilde birden fazla API varsa, protokol arabelleklerinin hangi API için oluşturulması gerektiği alta yayılabilir.Aspect
, benzersiz bir özelliği hesaplamak için gereken tüm verileri bağımlılık grafiğinde aşağı doğru yayılır. En boy oranı sınıfını, tanımını ve parametrelerini içerir.RuleAspect
, belirli bir kuralın hangi yönlerini belirleyen işlevdir olmalıdır. Bu birRule
->Aspect
işlevi.
Bazı yönlerin başka yönlerle bağlantılı olması,
Örneğin, bir Java IDE için sınıf yolunu toplayan bir özellik büyük olasılıkla
sınıf yolundaki tüm .jar dosyaları hakkında bilgi edinmek istiyorsanız, ancak bunların bazıları
protokol arabellekleridir. Bu durumda, IDE (Entegre Geliştirme Ortamı) özelliği
(proto_library
kuralı + Java proto en boy çifti) çifti.
Belirli yönlerin karmaşıklığını sınıfta ele alınır
AspectCollection
Platformlar ve araç zincirleri
Bazel, çoklu platform derlemelerini, yani mümkün olan yerlerde derleme işlemlerinin çalıştırıldığı birden fazla mimari ve yardımcı oluyorum. Bu mimariler Bazel'de platformlar olarak adlandırılır konuşma dili (belgelerin tamamı) burada bulabilirsiniz)
Platform, kısıtlama ayarlarındaki (ör.
"CPU mimarisi") kavramını kısıtlı değerlere (örneğin, belirli bir CPU
x86_64 gibi). Bir "sözlüğümüz" var kısıtlanmasıyla ilgili
@platforms
deposundaki ayarları ve değerleri içerir.
Araç zinciri kavramı, hangi platformlara bağlı olarak ve hangi platformlarda hedeflendiği gibi farklı derleyiciler; Örneğin, belirli bir C++ araç zinciri bir ve başka işletim sistemlerini hedefleyebilmeleri gerekir. Bazel, C++ belirlenen yürütme ve hedef platforma göre kullanılan derleyici (araç zincirleri için dokümanlar burada bulabilirsiniz).
Bunu yapmak için araç zincirlerine yürütme kümesi ve destekleyici hedef platform kısıtlamalarını belirlemenize yardımcı olur. Bunu başarabilmek için de projenin tanımı araç zinciri iki bölüme ayrılır:
- Yürütme ve hedef grubunu açıklayan
toolchain()
kuralı bir araç zincirinin desteklediği kısıtlamaları (ör. C++ veya Java) araç zinciri (toolchain_type()
kuralıyla temsil edilir) - Gerçek araç zincirini açıklayan dile özgü bir kural (ör.
cc_toolchain()
) olarak değiştirildi
Bu da böyle yapılır çünkü her bir müşterinin kısıtlarını bilmemiz gerekir.
yapmak için araç zinciri
aracılığıyla çalışır.
*_toolchain()
kural bundan çok daha fazla bilgi içerdiğinden daha fazla zaman alır.
kalan süreyi ifade eder.
Yürütme platformları aşağıdaki yollardan biriyle belirtilir:
register_execution_platforms()
işlevini kullanan MODULE.bazel dosyasında- Komut satırında --extra_execution_platforms komut satırını kullanarak seçenek
Mevcut yürütme platformları grubu hesaplanırken
RegisteredExecutionPlatformsFunction
Yapılandırılmış bir hedef için hedef platform şuna göre belirlenir:
PlatformOptions.computeTargetPlatform()
Bu bir platform listesi. Çünkü
nihayetinde birden çok hedef platformu desteklemek isteyebilirsiniz, ancak bu destek,
(henüz).
Yapılandırılmış bir hedef için kullanılacak araç zinciri kümesi
ToolchainResolutionFunction
İşlevi:
- Kayıtlı araç zinciri grubu (MODULE.bazel dosyasında ve yapılandırma)
- İstenen yürütme ve hedef platformlar (yapılandırmada)
- Yapılandırılmış hedefin gerektirdiği araç zinciri türleri kümesi (
UnloadedToolchainContextKey)
. - Yapılandırılmış hedefin (
exec_compatible_with
özelliği) ve yapılandırma (--experimental_add_exec_constraints_to_targets
), inçUnloadedToolchainContextKey
Bu sonucun sonucu olan UnloadedToolchainContext
. Bu aslında
öğesinin etiketine araç zinciri türü (ToolchainTypeInfo
örneği olarak gösterilir)
devreye sokmanız gerekir. Buna "unload" denir çünkü bu bölümü
kendi etiketleriyle ilişkilendirmesine yardımcı olur.
Daha sonra araç zincirleri ResolvedToolchainContext.load()
kullanılarak yüklenir.
ve bunları isteyen yapılandırılmış hedefin uygulanması tarafından kullanılır.
Ayrıca, tek bir "ana makine" olmasını gerektiren eski bir sistemimiz de var.
temsil edilen çeşitli yapılandırma ve hedef yapılandırmalarla
--cpu
gibi yapılandırma işaretleri için kullanılır . Kademeli olarak yukarıdaki
bahsedeceğim. Kullanıcıların eski yapılandırmayı kullandığı durumları ele almak için
Google Alışveriş’te
platform eşlemeleri
kullanıma sunduk.
Kodu PlatformMappingFunction
dilinde ve Starlark olmayan "küçük"
dil" olarak adlandırılır.
Sınırlamalar
Bazen bir kullanıcı bir hedefi yalnızca birkaç tanesiyle uyumlu platformlar. Maalesef Bazel bu amaca ulaşmak için birden fazla mekanizmaya sahiptir:
- Kurala özgü kısıtlamalar
environment_group()
/environment()
- Platform kısıtlamaları
Kurala özgü kısıtlamalar, Java kuralları için çoğunlukla Google'da kullanılır; onlar
ve Bazel'da kullanılamazlar, ancak kaynak kodu
buna ilişkin referanslar vardır. Bunu yöneten özelliğe
constraints=
media_group() veenvironment()
Bu kurallar eski bir mekanizmadır ve yaygın olarak kullanılmamaktadır.
Tüm derleme kuralları hangi "ortamları" belirtebilir için geliştirilebilir. Örneğin,
"çevre" environment()
kuralının bir örneğidir.
Bir kural için desteklenen ortamlar çeşitli şekillerde belirtilebilir:
restricted_to=
özelliği aracılığıyla. Bu, proje yöneticisinin spesifikasyon; Kuralın desteklediği ortam grubunu tam olarak bildirir bu grup için.compatible_with=
özelliği aracılığıyla. Bu işlem, ortamlara bir kural bildirir "standard"a ek olarak destekler desteklenen ortamlarda varsayılandır.- Paket düzeyindeki özellikler:
default_restricted_to=
vedefault_compatible_with=
. environment_group()
kurallarındaki varsayılan spesifikasyonlar aracılığıyla. Hepsini ortam, tematik olarak birbiriyle ilişkili bir grup ("CPU" gibi) mimariler", "JDK sürümleri" veya "mobil işletim sistemleri"). İlgili içeriği oluşturmak için kullanılan ortam grubu tanımı, bu ortamlardan hangisinin "default" tarafından desteklenmelidir tarafından aksi belirtilmediği takdirderestricted_to=
/environment()
özellikleri. Böyle bir içermeyen kural özellikleri tüm varsayılanları devralır.- Kural sınıfı varsayılanı aracılığıyla. Bu, tüm cihazlar için genel varsayılanları geçersiz kılar
örneklerinden birini seçin. Bu, örneğin insanların
tüm
*_test
kurallarının, her bir örneğin açıkça test edilmesine gerek kalmadan birlikte açıklanması gerekir.
environment()
normal kural olarak uygulanırken environment_group()
hem Target
öğesinin bir alt sınıfıdır ancak Rule
(EnvironmentGroup
) değildir
Starlark'tan varsayılan olarak sunulan bir işlev
(StarlarkLibrary.environmentGroup()
) gibi bir ad verilir.
hedefi belirleyebilirsiniz. Böylece ortaya çıkabilecek döngüsel bağımlılıkları önleyebilirsiniz. Çünkü
ait olduğu ortam grubunu tanımlaması ve her bir ortamın
ortam grubunun varsayılan ortamlarını bildirmesi gerekir.
Bir derleme,
--target_environment
komut satırı seçeneği.
Kısıtlama kontrolünün uygulanması
RuleContextConstraintSemantics
ve TopLevelConstraintSemantics
.
Platform kısıtlamaları
Şu anki "resmi" hedefin hangi platformlarla uyumlu olduğunu açıklamanın araç zincirlerini ve platformları tanımlamak için kullanılan kısıtlamaların aynısını kullanmaktır. Getirme isteğinde inceleniyor #10945.
Görünürlük
Çok sayıda geliştiriciyle (Google'daki gibi) büyük bir kod tabanı üzerinde çalışıyorsanız kendi isteklerinize bağlı olarak başkalarının istedikleri girin. Aksi takdirde, Hyrum yasası uyarınca kullanıcılar sizin uyguladığınız şekilde davranacak bolca fırsat sunuyor.
Bazel bunu görünürlük adı verilen mekanizmayla destekler: Tanımlayabileceğiniz gibi, belirli bir hedefe yalnızca görünürlük özelliğine sahip olmalıdır. Bu özellik biraz özeldir çünkü bir etiket listesi içermesine rağmen, etiketler, herhangi bir öğeye bir işaretçi yerine paket adları üzerinde bir kalıbı kodlayabilir olduğunu varsayalım. (Evet, bu bir tasarım hatasıdır.)
Bu, aşağıdaki yerlerde uygulanır:
RuleVisibility
arayüzü, görünürlük bildirimini temsil eder. O da Sabit (tamamen herkese açık veya tamamen gizli) ya da bir etiketler listesi olmalıdır.- Etiketler, iki paket grubuna (önceden tanımlanmış paket listesi) veya
doğrudan paketler (
//pkg:__pkg__
) veya paketlerin alt ağaçları (//pkg:__subpackages__
). Bu, komut satırı söz diziminden farklıdır.//pkg:*
veya//pkg/...
kullanır. - Paket grupları kendi hedefleri (
PackageGroup
) olarak uygulanır ve yapılandırılmış hedef (PackageGroupConfiguredTarget
). Muhtemelen bunları basit kurallarla değiştirebiliriz. Mantıklarını uyguladılar yardımcı olacak:PackageSpecification
, toplamda değeri tek kalıp (ör.//pkg/...
);PackageGroupContents
değerine karşılık gelir tek birpackage_group
packages
özelliğine; vePackageSpecificationProvider
, birpackage_group
ve geçişliincludes
. - Görünürlük etiketi listelerinden bağımlılıklara dönüşüm
DependencyResolver.visitTargetVisibility
ve birkaç başka örnek yer. - Asıl kontrol ise
CommonPrerequisiteValidator.validateDirectPrerequisiteVisibility()
.
İç içe yerleştirilmiş setler
Yapılandırılmış bir hedef, çoğu zaman bağımlılarından bir dizi dosya toplar. kendi verilerini ekler ve küme kümesini geçişli bilgi sağlayıcıda birleştirir. aynısını yapabilir. Örnekler:
- Derleme için kullanılan C++ başlık dosyaları
cc_library
öğesinin geçişli kapatılmasını temsil eden nesne dosyaları- Bir Java kuralının çalıştırılması için sınıf yolunda olması gereken .jar dosyaları derleme veya çalıştırma
- Python kuralının geçişli kapanışındaki Python dosyaları kümesi
Bunu, örneğin List
veya Set
kullanarak naif bir şekilde yaparsak
ikinci dereceden bellek kullanımı: N kural zinciri varsa ve her kural kendisine bir
olursa 1+2+...+N koleksiyon üyemiz olur.
Bu sorunu çözmek için
NestedSet
Diğer NestedSet
bileşenlerinden oluşan bir veri yapısıdır
ve kendine ait bazı örnekler oluşturur. Böylece, yönlendirilmiş bir döngüsel grafik oluşturulur.
oluşturuyoruz. Bu kurallar değiştirilemez ve üyeleri için iterasyon yapılabilir. Bizim işimiz,
birden çok yineleme siparişi (NestedSet.Order
): preorder (ön sipariş), postorder (son sipariş), topolojik (topolojik)
(bir düğüm her zaman üst öğelerinden sonra gelir) ve "önemli değil, ancak
her seferinde aynı oluyor" şeklinde görünür.
Aynı veri yapısı, Starlark'ta depset
olarak adlandırılır.
Yapılar ve İşlemler
Gerçek derleme, çalıştırılması gereken bir dizi komuttan oluşur.
istediği çıktıyı vermelidir. Komutlar, tablodaki komut dosyaları
Action
sınıfı ve dosyalar sınıfın örnekleri olarak gösterilir
Artifact
. Bunlar iki parçalı, yönlendirilmiş, dairesel bir grafikle düzenlenir.
"eylem grafiği".
Yapılar iki tür olabilir: kaynak yapılar (kullanılabilir olanlar ve türetilmiş yapıları (bunların yeniden yürütülmesi gereken oluşturulur). Türetilmiş yapılar birden çok tür olabilir:
- **Normal yapılar. **Bunların güncellik durumu bilgisayar tarafından kontrol edilir onların sağlama toplamı, kısayol olarak mtime uygulaması ile; eğer varsa dosyayı saat değişmedi.
- Çözümlenmemiş sembolik bağlantı yapıları. Bu bilgilerin güncelliği Readlink() çağrısında bulunun. Normal yapılardan farklı olarak bu yapılar sembolik bağlantılardır. Genellikle, bir kullanıcının bazı dosyaları tek bir yerde topladığı bir çeşit arşiv dosyası olabilir.
- Ağaç yapıları. Bunlar tek dosyalar değil, dizin ağaçlarıdır. Onlar
içindeki dosya seti ve bu dosyaların güncellikleri kontrol edilerek
içerik. Bunlar
TreeArtifact
olarak gösterilir. - Sabit meta veri yapıları. Bu yapılarda yapılan değişiklikler, yeniden inşa etmeliyiz. Bu, yalnızca derleme damgası bilgileri için kullanılır: şu anki zaman değişti diye yeniden oluşturmak.
Kaynak yapıların ağaç eserleri oluşamamasının veya
ancak bu, henüz uygulamadığımız bir simgedir (ör.
bir BUILD
dosyasında kaynak dizine referans vermek
Bazel ile ilgili uzun süredir var olan hatalarla ilgili sorunlar; bir
olanak sağlayan bu tür bir uygulama
BAZEL_TRACK_SOURCE_DIRECTORIES=1
JVM mülkü)
Kayda değer bir Artifact
türü aracıdır. Artifact
ile gösterilir.
Bunlar, MiddlemanAction
çıktılarıdır. Bu özellikler
bazı durumlar vardır:
- Toplanan aracılar, yapıları birlikte gruplandırmak için kullanılır. Böylece, birçok işlem aynı büyük giriş grubunu kullanıyorsa N*M'miz yoktur. bağımlılık kenarları, yalnızca N+M (iç içe yerleştirilmiş kümelerle değiştiriliyor)
- Bağımlılık aracılarını programlamak, bir işlemin diğerinden önce çalışmasını sağlar.
Bunlar, çoğunlukla hata analizi için değil, aynı zamanda C++ derlemesi için de kullanılır (bkz.
Açıklama için
CcCompilationContext.createMiddleman()
) - Runfiles aracıları, bir Runfiles ağacının bulunduğundan emin olmak için çıkış manifestine bağlı olmasının gerekmediğini ve her bir tek bir yapı tarafından yürütülür.
İşlemler en iyi, çalıştırılması gereken bir komut olarak kabul edilir; ve ürettiği çıktıları seçmelidir. Aşağıda belirtilen temel hususlar bileşenleri hakkında daha fazla bilgi edinin:
- Çalıştırılması gereken komut satırı
- İhtiyaç duyduğu giriş yapıları
- Ayarlanması gereken ortam değişkenleri
- Çalışması gereken ortamı (ör. platform) tanımlayan notlar \
Bazı özel durumlar da vardır. Örneğin, içeriği tam olarak aynı olan
bazı ipuçları vereceğim. Bunlar, AbstractAction
dersinin alt sınıfıdır. İşlemlerin çoğu
SpawnAction
veya StarlarkAction
(aynı şekilde, muhtemelen
ayrı sınıflarda yapabilir) ancak Java ve C++ kendi işlem türlerine sahiptir
(JavaCompileAction
, CppCompileAction
ve CppLinkAction
).
Sonunda her şeyi SpawnAction
hedefine taşımak istiyoruz. JavaCompileAction
oldukça yakındır ancak C++, .d dosya ayrıştırması ve
taramayı da içerir.
İşlem grafiği çoğunlukla "yerleştirilmiş" durumdadır kavramsal olarak
bir eylemin yürütülmesi,
ActionExecutionFunction
Bir eylem grafiği bağımlılık kenarından bir
Gökyüzü çerçevesi bağımlılık kenarı
ActionExecutionFunction.getInputDeps()
ve Artifact.key()
, birkaç tane var
kullanarak Skyframe kenarlarının sayısını düşük tutun:
- Türetilen yapıların kendi
SkyValue
'leri yoktur. Bunun yerineArtifact.getGeneratingActionKey()
, dokümanın anahtarını bulmak için kullanılır bu işlemi oluşturan - İç içe yerleştirilmiş setlerin kendi Skyframe anahtarı vardır.
Paylaşılan işlemler
Bazı işlemler, birden çok yapılandırılmış hedef tarafından oluşturulur; Starlark kuralları yalnızca türetilen eylemlerini bir toplu e-tabloya yerleştirmelerine izin yapılandırmalarına ve paketlerine göre belirlenen bir dizindir (ancak aynı zamanda kuralları çakışabilir), ancak Java'da uygulanan kurallar, ortaya çıkarmanıza yardımcı olabilir.
Bunun yanlış bir özellik olduğu düşünülse de bundan kurtulmak gerçekten zor. Çünkü örneğin bir kullanıcı (ör. bir kullanıcı) çalışırken yürütme süresinde dosyanın bir şekilde işlenmesi gerektiğini ve bu dosyaya (el dalgası-el dalgaları) kullanır. Bu işlem için RAM maliyeti geçerlidir: paylaşılan bir eylem örneği bellekte ayrı olarak saklanabilir.
İki işlem aynı çıkış dosyasını oluşturuyorsa tam olarak aynı olmalıdır:
aynı girişlere ve aynı çıkışlara sahip olmalı ve aynı komut satırını çalıştırmalıdır. Bu
denklik ilişkisi Actions.canBeShared()
işlevinde uygulanmış ve
ve yürütme aşamaları arasında doğrulandığından emin olun.
SkyframeActionExecutor.findAndStoreArtifactConflicts()
üzerinde uygulanır
ve Bazel'de "global" öğe gerektiren az sayıda görünümü
seçeceğiz.
Yürütme aşaması
Bu aşamada Bazel, aşağıdakileri yapmak için gereken komutlar gibi derleme işlemlerini ve çıktılar üretir.
Analiz aşamasından sonra Bazel'in yaptığı ilk şey, o ana kadar
Yapıların oluşturulması gerekir. Bunun mantığı aşağıdaki gibi kodlanmıştır:
TopLevelArtifactHelper
; genel olarak, filesToBuild
.
komut satırındaki yapılandırılmış hedefler ve özel bir çıkışın içeriği
"bu hedef komuttaysa" ifadesi için bir grup oluşturun
bu eserleri inşa edin".
Bir sonraki adım, yürütme kökünü oluşturmaktır. Bazel, okuma kabiliyetini
dosya sisteminde farklı konumlardan gelen kaynak paketleri (--package_path
),
tam kaynak ağacıyla yerel olarak gerçekleştirilen işlemler sağlaması gerekir. Bu
SymlinkForest
sınıfı tarafından ele alınır ve her hedefi not ederek çalışır
ve bu kümelere sembolik bağlantılar sunan tek bir dizin ağacı oluşturmak için
her paket için tek bir hedef kullanabilirsiniz. Alternatif bir yöntem de
(--package_path
dikkate alınarak) komutlara giden doğru yolların iletilmesini gerektirir.
Bu istenmeyen bir durumdur çünkü:
- Bir paket, paket yolundan taşındığında işlem komut satırlarını değiştirir başka bir kullanıcıya giriş (eskiden yaygın olarak karşılaşılan bir durumdu)
- Bir işlem, uzaktan çalıştırıldığında farklı komut satırlarına neden olur. yerel olarak çalıştırılıyor
- Kullanılmakta olan araca özel bir komut satırı dönüşümü gerektirir. (Java sınıf yolları ile C++ içeren yollar arasındaki farkı göz önünde bulundurun)
- Bir işlemin komut satırı değiştirildiğinde işlem önbelleği girişi geçersiz kılınır
--package_path
yavaş yavaş ve sürekli olarak kullanımdan kaldırılıyor
Sonra Bazel eylem grafiğinde (iki taraflı, yönlendirilmiş grafik)
işlemler ile bunların giriş ve çıkış yapıları) ve çalışan işlemlerden oluşur.
Her işlemin yürütülmesi SkyValue
öğesinin bir örneğiyle gösterilir
ActionExecutionValue
sınıfı.
Bir işlemi çalıştırmak pahalı olduğundan, işlemi gerçekleştirebilecek birkaç önbelleğe alma katmanımız Skyframe'in arkasına vurulacak:
ActionExecutionFunction.stateMap
, Skyframe'in yeniden başlatılmasını sağlayacak veriler içeriyor /ActionExecutionFunction
ucuz- Yerel işlem önbelleği, dosya sisteminin durumuyla ilgili verileri içerir
- Uzaktan yürütme sistemleri genellikle kendi önbelleklerini de içerir.
Yerel işlem önbelleği
Bu önbellek, Skyframe'in arkasında bulunan başka bir katmandır; bir eylem de tekrar çalıştırıldığında yerel işlem önbelleğinde isabet olabilir. Google temsil eder. Bu, yerel dosya sisteminin durumunu gösterir ve yeni bir Bazel sunucusu başlatıldığında yerel işlem önbelleği isabetler içeren bir ekran görüntüsüdür.
Bu önbellek, yöntem kullanılarak isabetler açısından kontrol edilir
ActionCacheChecker.getTokenIfNeedToExecute()
Adının aksine, türetilmiş bir eserin izlediği yoldan harekete geçiriyor. İşlem şu şekilde açıklanır:
- Giriş ve çıkış dosyaları ile bunların sağlama toplamı
- Bu genellikle yürütülen komut satırı olan "işlem tuşu"dur ancak
genel olarak,
giriş dosyaları (örneğin
FileWriteAction
için, verilerin sağlama toplamıdır) yazın)
Ayrıca oldukça deneysel bir "yukarıdan aşağıya işlem önbelleği" vardır hâlâ altında gibi geçişli karmalar kullanan geliştirme yöntemidir. kez.
Giriş keşfi ve giriş ayıklama
Bazı işlemler sadece bir dizi girdiye sahip olmaktan daha karmaşıktır. Şu değişiklikler var: bir eyleme yönelik girdiler kümesi iki biçimde olur:
- Bir eylem, yürütmeden önce yeni girişler keşfedebilir veya
ve girdilere ihtiyacı yoktur. Standart örnek C++,
C++ içeren üstbilgi dosyalarına ilişkin bilgiye dayalı bir tahminde bulunmanın daha iyi olduğu
dosyanın geçişli kapanmasından yararlanır. Böylece, her
iletebilir; Bu nedenle, her bir müşterinin
başlık dosyasını "giriş" olarak görüntüler ancak kaynak dosyayı geçişli olarak tarama
ve bu üstbilgi dosyalarını yalnızca
#include
ifadelerinde bahsedilen ( tam C ön işlemcisi kullanmanız gerekir) Bu seçenek şu an için "yanlış" olarak kullanılır ve yalnızca Google'da kullanılmaktadır. - Bir işlem, yürütülürken bazı dosyaların kullanılmadığını fark edebilir. İçinde buna ".d dosyaları" adı verilir; derleyici, hangi başlık dosyalarının ve daha kötüsü artımlılığından bahsediyor. Bazel bu gerçeği kullanıyor. Bu sayede, tahmine dahil edilir çünkü derleyiciye dayanır.
Bunlar, İşlem yöntemleri kullanılarak uygulanır:
Action.discoverInputs()
çağrıldı. İç içe yerleştirilmiş Gerekli olduğu belirlenen yapılar. Bunlar kaynak yapılar olmalıdır Böylece eylem grafiğinde eşdeğerdir.- İşlem,
Action.execute()
çağrısı yapılarak gerçekleştirilir. Action.execute()
sonunda, işlem şunu çağırabilir: Bazel'a tüm girişlerinin olmadığını bildirmek içinAction.updateInputs()
gerekir. Bu durum, kullanılan giriş kullanılmadığı bildirildi.
Bir işlem önbelleği yeni bir Action örneğinde (ör. oluşturulan
yeniden başlatıldıktan sonra) Bazel updateInputs()
öğesini çağırır. Böylece
girdileri daha önce yapılan keşif ve ayıklama işlemlerinin sonucunu yansıtır.
Starlark eylemleri, bazı girişleri kullanılmadığını bildirmek için bu olanağı kullanabilir
şunun unused_inputs_list=
bağımsız değişkeni kullanılıyor:
ctx.actions.run()
.
İşlem yürütmenin çeşitli yolları: Stratejiler/ActionContexts
Bazı işlemler farklı şekillerde çalıştırılabilir. Örneğin, bir komut satırı
yerel olarak, ancak çeşitli korumalı alanlarda veya uzaktan yürütülür. İlgili içeriği oluşturmak için kullanılan
içeren bir kavrama ActionContext
(ya da Strategy
olarak adlandırılır)
yeniden adlandırma işleminin yalnızca yarısına kadar tamamlandı...)
Bir işlem bağlamının yaşam döngüsü aşağıdaki gibidir:
- Yürütme aşaması başladığında
BlazeModule
örneğe ne sorulur? bağlamları hakkında bilgi edindiniz. Bu,ExecutionTool
İşlem bağlamı türleri JavaClass
ile tanımlanırActionContext
alt arayüzünü ifade eden ve aynı zamanda işlem bağlamının uygulaması gereken arayüze bağlıdır. - Uygun işlem bağlamı, mevcut olanlardan seçilir ve
ActionExecutionContext
veBlazeExecutor
adreslerine yönlendirildi . - İşlemler,
ActionExecutionContext.getContext()
ve kullanarak bağlamları ister.BlazeExecutor.getStrategy()
(arama yaparken bu...)
Stratejiler, görevlerini yerine getirmek için başka stratejilerden yararlanabilir. bunun için, Eylemleri hem yerel olarak hem de uzaktan başlatan dinamik stratejide ve ardından hangisi önce biteni kullanır.
Kayda değer stratejilerden biri de kalıcı işçi süreçlerinin uygulanmasıdır.
(WorkerSpawnStrategy
). Burada bazı araçlarda başlangıç süresinin uzun olduğu düşünülür.
Bu nedenle, aynı işlem için yeniden bir işlem yapmak yerine işlemler arasında yeniden
her işlem (Bu durum, olası bir doğruluk sorununu temsil eder, çünkü Bazel
işçi sürecinin gözlemlenebilir öğeler içermemesi yönündeki taahhüdüne dayanır
aynı durumu ifade eder)
Araç değişirse çalışan işleminin yeniden başlatılması gerekir. Çalışan olup olmadığı
değeri, kullanılan araç için sağlama
toplamı hesaplanarak belirlenir.
WorkerFilesHash
Bu, eylemin hangi girişlerinin temsil ettiğini bilmek
ve girişleri temsil eden bir bölümü vardır. içerik üretici tarafından belirlenir.
Spawn.getToolFiles()
işlemi ve Spawn
çalıştırma dosyaları
bir araç olarak görür.
Stratejiler (veya işlem bağlamları) hakkında daha fazla bilgi edinin:
- İşlem çalıştırmaya yönelik çeşitli stratejilerle ilgili bilgiler mevcuttur burada bulabilirsiniz.
- Hem işlem gerçekleştirdiğimiz dinamik stratejiyle ilgili bilgiler hangisinin önce gerçekleştiğini görmek için yerel olarak ve uzaktan burada bulabilirsiniz.
- İşlemleri yerel olarak yürütmenin zorlukları hakkında bilgi edinebilirsiniz burada bulabilirsiniz.
Yerel kaynak yöneticisi
Bazel birçok işlemi paralel olarak çalıştırabilir. Gerçekleştirdiğiniz yerel işlemlerin sayısı, paralel olarak çalıştırılması gerekir, işlemden eyleme farklılık gösterir: bir proje için ne kadar fazla daha az örneğin aynı anda çalışması gerekir. Aksi takdirde, yerel makineye aşırı yüklenmeye başlar.
Bu, ResourceManager
sınıfında uygulanır: Her işlem
ihtiyaç duyulan yerel kaynakların bir tahminini içeren
ResourceSet
örneği (CPU ve RAM). Sonra işlem bağlamları bir şey yaptığında
yerel kaynaklar gerektiren işletmeler için ResourceManager.acquireResources()
ve gerekli kaynaklar mevcut olana kadar engellenir.
Yerel kaynak yönetimiyle ilgili daha ayrıntılı bir açıklama mevcut burada bulabilirsiniz.
Çıkış dizininin yapısı
Her işlem, çıkış dizininde, ve çıkarımlarda bulunması gerekir. Türetilen yapıların konumu genellikle şu şekildedir:
$EXECROOT/bazel-out/<configuration>/bin/<package>/<artifact name>
Belirli bir web sitesiyle ilişkilendirilen dizinin adı nasıl belirlenir? Çakışan istenen iki özellik vardır:
- Aynı derlemede iki yapılandırmanın oluşabileceği durumlarda, Böylece her iki dizinin de kendi sürümüne sahip olması gerekir eylem; aksi takdirde, iki yapılandırma arasında hemfikir değilse (örneğin, işlemini oluşturan bir eylem satırı oluşturmak için, Bazel hangi seçilecek işlem ("eylem çakışması")
- İki yapılandırma "kabaca" ifadesini temsil ediyorsa aynı şeyi düşünmeleri aynı ada sahip olduğundan, birinde yürütülen işlemlerin diğeri için yeniden kullanılabilmesi komutu satırlarıyla eşleşir: örneğin, komut satırı seçeneklerinde yapılan değişiklikler Java derleyicisi, C++ derleme işlemlerinin yeniden çalıştırılmasına neden olmamalıdır.
Şu ana kadar bu sorunu çözmek için prensipli bir yöntem bulamadık. yapılandırma kırpma sorunuyla benzerliklere sahiptir. Daha uzun bir tartışma kullanılabilir bir seçenek var burada bulabilirsiniz. Başlıca sorunlu alanlar Starlark kurallarıdır (yazarlar genellikle (Bazel hakkında bilgi sahibi olan) ve yönleriyle "aynı" şeyi üretebilen şeylerin alanı çıktı dosyası olarak kaydedebilirsiniz.
Mevcut yaklaşım, yapılandırmanın yol segmentinin
Yapılandırmanın düzgün çalışması için çeşitli sonekler eklenmiş <CPU>-<compilation mode>
geçişler, işlem çakışmalarına neden olmaz. Ayrıca,
Starlark yapılandırma geçişleri kümesinin sağlama toplamı, kullanıcıların
işlem çakışmalarına neden olamaz. Bu yöntem mükemmel değildir. Uygulandığı yer:
OutputDirectories.buildMnemonic()
ve her bir yapılandırma parçasına dayanır
çıkış dizininin adına kendi parçasını ekleyerek.
Testler
Bazel, test çalıştırmak için zengin desteğe sahiptir. Şunları destekler:
- Testleri uzaktan çalıştırma (uzaktan yürütme arka ucu varsa)
- Testleri paralel olarak birden çok kez çalıştırmak (detay oluşturmak veya zamanlamayı toplamak için) verileri)
- Testleri parçalama (aynı testteki test durumlarını birden çok işlem üzerinde bölme) için)
- Güvenilir olmayan testleri yeniden çalıştırma
- Testleri test paketleri halinde gruplandırma
Testler, bir TestProvider'ı içeren normal yapılandırılmış hedeflerdir. testin nasıl çalıştırılması gerektiği:
- Yapısı sonucunda testin çalıştırıldığı yapılar. Bu bir "önbellek"tir
durum" serileştirilmiş
TestResultData
mesajı içeren dosya - Testin çalıştırılma sayısı
- Testin bölünmesi gereken parça sayısı
- Testin nasıl çalıştırılması gerektiğiyle ilgili bazı parametreler (test zaman aşımı gibi)
Hangi testlerin çalıştırılacağını belirleme
Hangi testlerin gerçekleştirileceğini belirlemek ayrıntılı bir süreçtir.
İlk olarak, hedef kalıp ayrıştırması sırasında test paketleri yinelemeli bir şekilde genişletilir. İlgili içeriği oluşturmak için kullanılan
genişletme TestsForTargetPatternFunction
uygulamasında uygulandı. Biraz
şaşırtıcı kırışıklık ise bir test paketinin test olmadığını beyan etmesi durumunda,
her testi kendi paketine ekler. Bu, Package.beforeBuild()
ürününde şunun tarafından uygulanır:
paket kurallarını test etmek için $implicit_tests
adlı örtülü bir özellik ekleyerek.
Ardından, testler
komut satırı seçenekleri. Bu, TestFilter
üzerinde uygulandı ve
ve hedef ayrıştırma sırasında TargetPatternPhaseFunction.determineTests()
sonuç TargetPatternPhaseValue.getTestsToRunLabels()
içine yerleştirilir. Neden
filtrelenebilecek kural özelliklerinin yapılandırılamamasının nedeni,
aşamasından önce gerçekleştiğinden yapılandırma,
kullanılabilir.
Bu daha sonra BuildView.createResult()
içinde daha fazla işlenir:
başarısız olan analizler filtrelenir ve testler özel ve
münhasır olmayan testler olabilir. Daha sonra AnalysisResult
bölümüne yerleştiriliyor.
ExecutionTool
hangi testlerin çalıştırılacağını biliyor.
tests()
, bu ayrıntılı sürece biraz şeffaflık kazandırmak için
sorgu operatörü (TestsFunction
dilinde uygulanır) hangi testlerin
komut satırında belirli bir hedef belirtildiğinde çalıştırılır. İnsanların
bu nedenle de büyük olasılıkla yukarıdakilerden farklı bir uygulamadır.
pek çok farklı yöntem vardır.
Testler yapılıyor
Testlerin çalıştırılma şekli, önbellek durumu yapıları istemektir. Bu ardından
bir TestRunnerAction
yürütülmesiyle sonuçlanır ve sonuçta
--test_strategy
komut satırı seçeneği tarafından seçilen TestActionContext
testi istenen şekilde çalıştırır.
Testler, ortam değişkenlerini kullanan ayrıntılı bir protokole göre yürütülür ve testlere onlardan ne beklendiğini anlatmayı unutmayın. Bazel'in ne yaptığını ayrıntılı şekilde kullanılabilirliği hakkında daha fazla bilgi için burada bulabilirsiniz. Şurada: en basit olanı, 0 çıkış kodunun başarılı olduğu, diğer her şey ise başarısız olduğu anlamına gelir.
Önbellek durum dosyasına ek olarak, her test işlemi bir dizi başka örnek
dosyası olarak da kaydedebilir. Bu dosyalar "test günlüğü dizinine" yerleştirilir. "Bu sayfa, Google'ın açık kaynak
Hedef yapılandırmanın çıkış dizininin testlogs
kadarı:
test.xml
, her test durumunu ayrıntılı bir şekilde belirten JUnit stili bir XML dosyasıdır test kırığıtest.log
, testin konsol çıktısı. stdout ve stderr değildir olabilir.test.outputs
, "bildirilmemiş çıkışlar dizini"; bu, testler tarafından terminalde yazdırdıklarına ek olarak dosyaların çıkışını almak isteyen kullanıcılar içindir.
Test yürütülürken gerçekleşebilecek iki şey vardır. düzenli hedefler oluşturma: özel test yürütme ve çıkış akışı.
Bazı testlerin özel modda (örneğin,
başka testler de var. Bu,tags=["exclusive"]
test kuralını uygulayın veya testi --test_strategy=exclusive
ile çalıştırın . Her biri özel
test,
"ana" koddan sonra test etmek seçeceğiz. Uygulandığı yer:
SkyframeExecutor.runExclusiveTest()
İşlem sırasında terminal çıkışı dökümü alınan normal işlemlerin aksine
kullanıcı testlerin sonucunun akış olarak akmasını isteyebilir. Böylece,
ve uzun süreli bir testin ilerleme durumu hakkında bilgilendirilecek. Bu,
--test_output=streamed
komut satırı seçeneği ve özel test anlamına gelir
Böylece, farklı testlerin çıktıları karışıklığa neden olmaz.
Bu, uygun şekilde adlandırılmış StreamedTestOutput
sınıfında uygulanır ve
söz konusu testin test.log
dosyasında yapılan yoklama değişiklikleri ve yeni döküm
bayt olarak aktarması gerekir.
Yürütülen testlerin sonuçları
çeşitli etkinlikler (TestAttempt
, TestResult
veya TestingCompleteEvent
gibi).
Bunlar, Derleme Etkinliği Protokolü'ne atanır ve konsola iletilir
AggregatingTestListener
tarafından.
Kapsam koleksiyonu
Kapsam, testler tarafından dosyalardaki LCOV biçiminde bildirilir.
bazel-testlogs/$PACKAGE/$TARGET/coverage.dat
Kapsamın toplanması için her test yürütmesi,
collect_coverage.sh
Bu komut dosyası, test ortamını kapsam toplamayı etkinleştirecek şekilde ayarlar ve kapsam dosyalarının kapsam çalışma zamanlarına göre nereye yazıldığını belirler. Daha sonra testi çalıştırır. Bir testin kendisi birden fazla alt işlem yürütebilir ve birden fazla programlama dilinde yazılmış bölümlerin sayısı (ayrı ayrı kapsam koleksiyonu çalışma zamanları) gösterilir. Sarmalayıcı komut dosyası, oluşturulan bu dosyaları, gerekirse LCOV biçiminde olur ve tek bir dosyası olarak kaydedebilirsiniz.
collect_coverage.sh
arasındaki konum, test stratejileri tarafından yapılır ve
collect_coverage.sh
öğesinin test girişlerinde olmasını gerektirir. Bu
,:coverage_support
--coverage_support
yapılandırma işaretinin değeri (bkz.
TestConfiguration.TestOptions.coverageSupport
)
Bazı diller çevrimdışı enstrümantasyonu yapar, yani kapsam enstrümantasyon, derleme sırasında eklenir (ör. C++) araçlar, yani kapsam araçları yürütme sırasında eklenir gerekir.
Diğer bir temel kavram da referans kapsamdır. Bu, bir kütüphanenin kapsamı,
veya içinde hiç kod çalıştırılıp çalıştırılmadığını test edebilirsiniz. Çözdüğü sorun, herhangi bir sorun
test kapsamını hesaplamak istiyorsanız
tüm testlere dahildir çünkü ikili dosyada
teste bağlı değildir. Dolayısıyla, yaptığımız her bir trafik için bir kapsam dosyası
yalnızca kapsama dahil ettiğimiz dosyaları içeren ikili program
satırlarda ilerleyin. Bir hedef için referans kapsam dosyası şu değerdedir:
bazel-testlogs/$PACKAGE/$TARGET/baseline_coverage.dat
Ayrıca,
testlere ek olarak ikili programlar ve kitaplıklar için
Bazel'e --nobuild_tests_only
işareti.
Temel kapsam şu anda bozuk.
Her kural için kapsam koleksiyonu amacıyla iki dosya grubu izleriz: araç meta veri dosyaları kümesi için de geçerlidir.
Enstrümantasyonlu dosyalar grubu da adından da anlaşılacağı üzere bir dizi dosyadan ibarettir. Örneğin, bu değer, çalışma zamanında hangi dosyaların gösterileceğine karar vermek için enstrümanı kullanabilirsiniz. Ayrıca temel kapsamı uygulamak için de kullanılır.
Araç meta veri dosyaları grubu, testin ihtiyaç duyduğu ekstra dosya kümesidir kullanarak, Bazel'ın gerektirdiği LCOV dosyalarını oluşturabilirsiniz. Pratikte bu, çalışma zamanına özel dosyalar; örneğin, gcc, derleme sırasında .gcno dosyaları yayar. Kapsam modu etkin.
Kapsamın toplanıp toplanmadığı
BuildConfiguration
Bu, testi değiştirmenin kolay bir yolu olduğu için yararlıdır
bağlı olarak değişiklik gösterir, ancak aynı zamanda
tüm hedeflerin yeniden analiz edilmesi gerekir (örneğin,
C++, kapsam toplayabilecek kod yayınlamak için farklı derleyici seçenekleri gerektirir,
Bu da sorunun bir miktar azalmasına neden olur, çünkü yeniden analiz yapılması gerekir).
Kapsam destek dosyaları, örtülü bağımlılığı, böylece bunların geçersiz kılınabilmesi için çağrı politikası ve Bazel'in farklı versiyonları arasında farklılık göstermeliyiz. İdeal koşullarda bu ve bunlardan birini standartlaştırdık.
Ayrıca bir "kapsam raporu" ve bu veriler için toplanan
kapsamı birleştirerek
her test için kullanılamaz. Bu işlem,
CoverageReportActionFactory
ve BuildView.createResult()
üzerinden çağrılıyor . Google
:coverage_report_generator
kontrol ederek ihtiyacı olan araçlara erişebilir.
özelliğinin değerini döndürür.
Sorgu motoru
Bazel, küçük dil çeşitli grafiklerle ilgili çeşitli sorular sormak için kullanılır. Aşağıdaki sorgu türleri şunlar sağlanır:
- Hedef grafiği incelemek için
bazel query
kullanılır bazel cquery
, yapılandırılmış hedef grafiğini incelemek için kullanılırbazel aquery
, işlem grafiğini incelemek için kullanılır.
Bunların her biri, AbstractBlazeQueryEnvironment
alt sınıfıyla uygulanır.
QueryFunction
alt sınıflandırması yapılarak ek sorgu işlevleri yapılabilir.
, Sorgu sonuçlarının akış şeklinde gösterilmesi için, sonuçları belirli
QueryFunction
veri yapısına bir query2.engine.Callback
aktarılır ve
istediği sonuçlar için çağrıyı yapar.
Sorgunun sonucu çeşitli şekillerde yayınlanabilir: etiketler, etiketler ve kural
temel işlemlerdir. Bunlar,
OutputFormatter
Bazı sorgu çıkış biçimlerinin (proto, kesinlikle) bir gereksinimi olan Bazel'in, paket yüklemenin sağladığı _tüm _bilgileri yayması gerekir. çıktıyı farklılık gösterebilir ve belirli bir hedefin değişip değişmediğini belirleyebilir. Sonuç olarak, özellik değerlerinin seri hale getirilebilir olması gerekir. Bu nedenle, hiçbir özelliği karmaşık Starlark'a sahip olmayan çok az sayıda özellik türü olduğundan değerler. Genellikle çözüm, bir etiket kullanmak ve karmaşıklığı bilgisi de ekler. Pek tatmin edici bir çözüm değil bu şartın kaldırılması çok faydalı olur.
Modül sistemi
Bazel, üzerine modül eklenerek genişletilebilir. Her modülün alt sınıfları
BlazeModule
(ad, eskiden Bazel'in tarihine ait bir emanettir.
adı verilen ve Blaze) yürütülürken çeşitli olaylar hakkında bilgi edinir
komut verebilirsiniz.
Çoğunlukla "temel olmayan" çeşitli öğelerin uygulanması için kullanılırlar işlevsellik yalnızca bazı Bazel sürümlerinde (Google'da kullandığımız sürüm gibi) şunlara ihtiyaç vardır:
- Uzaktan yürütme sistemlerine yönelik arayüzler
- Yeni komutlar
BlazeModule
tarafından sunulan uzantı noktaları grubu rastgele. Şunları Yapmayın:
iyi tasarım ilkelerine örnek olarak gösterilebilir.
Etkinlik otobüsü
BlazeModules'un Bazel'in geri kalanıyla iletişim kurmak için kullandığı ana yöntem etkinlik otobüsü
(EventBus
): Bazel'in çeşitli bölümleri olan her derleme için yeni bir örnek oluşturulur
bu bölümde etkinlik yayınlayabilir ve modüller, dinleyicileri
emin olabilirsiniz. Örneğin, aşağıdakiler etkinlik olarak temsil edilir:
- Oluşturulacak derleme hedeflerinin listesi belirlendi
(
TargetParsingCompleteEvent
) - Üst düzey yapılandırmalar belirlendi
(
BuildConfigurationEvent
) - Başarıyla oluşturulmuş bir hedefin oluşturulup oluşturulmadığı (
TargetCompleteEvent
) - Bir test çalıştırıldı (
TestAttempt
,TestSummary
)
Bu etkinliklerden bazıları Bazel dışındaki
Etkinlik Protokolü Oluşturma
(bunlar BuildEvent
). Bu sadece BlazeModule
değil, aynı zamanda şeylere de izin verir.
Bazel sürecinin dışında kalan kod bulunur. Bunlara
veya Bazel'in bir sunucuya bağlanabildiğini (ör.
(Derleme Etkinliği Hizmeti) oluşturun.
Bu, build.lib.buildeventservice
ve
build.lib.buildeventstream
Java paketleri.
Harici depolar
Bazel aslen bir monorepoda (tek bir kaynak olarak) bir ağaç kıyafeti için gereken her şeyi içeren bir ağaç varsa, Bazel bu her zaman doğru değildir. "Harici depolar" bir soyutlama olan arasında bağlantı oluşturur. Bunlar derleme için gerekli olan kodu temsil eder, ana kaynak ağacında değildir.
WORKSPACE dosyası
Harici depo grubu, WORKSPACE dosyasının ayrıştırılmasıyla belirlenir. Örneğin, şöyle bir beyan:
local_repository(name="foo", path="/foo/bar")
@foo
adlı depodaki sonuçlar kullanılabilir. Bu ne anlama geliyor?
Starlark dosyalarında yeni depo kuralları tanımlanabilmesi,
sonra yeni Starlark kodunu yüklemek üzere kullanılabilir. Bu kod,
depo kuralları ve benzerleri...
Bu durumu ele almak için WORKSPACE dosyasının ayrıştırılması (
WorkspaceFileFunction
), load()
ile açıklanan parçalara ayrılır
açıklamalarına dikkat edin. Parça dizini WorkspaceFileKey.getIndex()
ve
X dizinine kadar WorkspaceFileFunction
hesaplama, o zamana kadar
X. load()
ifadesi.
Kod depoları getiriliyor
Kod deposunun kodu Bazel'in kullanımına sunulmadan önce,
getirildi. Bu, Bazel'in
$OUTPUT_BASE/external/<repository name>
Kod deposunu getirme işlemi aşağıdaki adımlarla gerçekleşir:
PackageLookupFunction
, bir depoya ihtiyacı olduğunu anlar ve birRepositoryLoaderFunction
çağıranSkyKey
olarakRepositoryName
RepositoryLoaderFunction
, isteği şu adrese yönlendirir: Belirsiz nedenlerleRepositoryDelegatorFunction
(kodda yeniden indirilmemesi durumunda bir şeyleri yeniden indirmekten kaçının ancak bu, gerçekten çok sağlam bir gerekçe)RepositoryDelegatorFunction
, istenen depo kuralını öğrenir istenene kadar WORKSPACE dosyasının parçaları üzerinde yineleme yaparak getirme depo bulundu- Depoyu uygulayan uygun
RepositoryFunction
bulundu fetching; deponun Starlark uygulaması veya bir için sabit kodlu bir harita oluşturur.
Depo getirmek çok zor olabileceği için, önbelleğe almanın çeşitli katmanları vardır pahalı:
- İndirilen dosyalar için sağlama toplamıyla girilen bir önbellek vardır
(
RepositoryCache
). Bu, sağlama toplamının WORKSPACE dosyasıdır, ancak bu hermetik özellik için iyidir. Bu, şu kullanıcı tarafından paylaşılıyor: aynı iş istasyonundaki her Bazel sunucu örneği; çalışma alanı veya çıktı tabanını oluşturur. - "İşaretçi dosyası"
$OUTPUT_BASE/external
altındaki her depo için yazılır kuralının sağlama toplamını içeren bir veri kümesi içerir. Bazel sunucu yeniden başlatılır, ancak sağlama toplamı değişmez ve yeniden getirilmez. BuRepositoryDelegatorFunction.DigestWriter
dilinde uygulandı . --distdir
komut satırı seçeneği, indirilecek yapıları arayın. Bu özellik, kurumsal ayarlarda kullanışlıdır Burada Bazel'in internetten rastgele öğeler getirmemesi gerekir. BuDownloadManager
tarafından uygulandı .
Depo indirildikten sonra içindeki yapılar kaynak olarak değerlendirilir
olabilir. Bazel genellikle güncelleme olup olmadığını kontrol ettiğinden bu durum bir sorun teşkil eder.
stat() çağrısı yaparak kaynak yapıların üzerinde çalışır. Bu yapılar ayrıca,
deponun tanımı değiştiğinde geçersiz kılınır. Böylece,
Harici bir depodaki bir yapı için FileStateValue
öğeleri
depolarına gönderebilirsiniz. Bu işlem ExternalFilesHelper
tarafından işlenecek.
Depo eşlemeleri
Birden fazla depo aynı depoya bağımlı olabilir.
Ancak farklı versiyonlarda (bu, "elmas bağımlılığının
sorun"). Örneğin, derlemede ayrı depolardaki iki ikili program
ikisi de muhtemelen Gava'yı işaret eden plak şirketiyle
@guava//
tarihinden itibaren geçerli olacak ve bu metriğin farklı sürümleri anlamına gelmesini bekleyebilirsiniz.
Bu nedenle Bazel, harici depo etiketlerinin yeniden eşlenmesine izin verir. Böylece,
@guava//
dizesi,@guava1//
bir ikili veri deposu ve başka bir Guava deposu (@guava2//
gibi)
diğerinin depoları olabilir.
Alternatif olarak bu, elmasları birleştirmek için de kullanılabilir. Depo,
@guava1//
veri deposuna bağlıdır, diğeri ise @guava2//
veri deposu eşlemesine bağlıdır
bir standart @guava//
deposu kullanmak için her iki deponun da yeniden eşlenmesine olanak tanır.
Eşleme, WORKSPACE dosyasında repo_mapping
özelliği olarak belirtilir
tanımlarını öğreneceksiniz. Ardından Skyframe'de şunun üyesi olarak görünür:
WorkspaceFileValue
, enerjinin kaynaklandığı yer:
- Etiket değerli dönüşümünü dönüştürmek için kullanılan
Package.Builder.repositoryMapping
özellikleriniRuleClass.populateRuleAttributeValues()
- Analiz aşamasında kullanılan
Package.repositoryMapping
( yükleme sırasında ayrıştırılmayan$(location)
gibi sorunları çözümleme aşamasında) - load() ifadelerindeki etiketleri çözümlemek için
BzlLoadFunction
JNI bitleri
Bazel'in sunucusu çoğunlukla Java'da yazılmıştır. Ancak bir istisna olan Java kendi başına yapamadığı gibi, uygulandığı zaman da kendi başına yapamıyor. Bu çoğu zaman dosya sistemi, işlem denetimi ve süreç düşük seviyeli başka birçok şey olabilir.
C++ kodu, src/main/native, yerel kod içeren Java sınıfları altında bulunur. yöntemleri şunlardır:
NativePosixFiles
veNativePosixFileSystem
ProcessUtils
WindowsFileOperations
veWindowsFileProcesses
com.google.devtools.build.lib.platform
Konsol çıkışı
Konsoldan çıkış yapma gibi basit bir iş gibi görünse de birden fazla işlem (bazen uzaktan), ayrıntılı önbelleğe alma, güzel ve renkli bir terminal çıkışı vardır ve uzun süreli bir sunucuya sahip olmak sıradan bir iş değildir.
İstemciden gelen TBG çağrısının hemen ardından iki RpcOutputStream
stdout ve stderr için, örneği hazırlayıp
müşteriye anlatır. Bunlar daha sonra bir OutErr
(stdout, stderr) içine alınır.
çifti) ekleyebilirsiniz. Konsolda yazdırılması gereken her şey bu belgelerden geçer
akışlar. Ardından bu canlı yayınlar,
BlazeCommandDispatcher.execExclusively()
Çıktı, varsayılan olarak ANSI çıkış sıralarıyla yazdırılır. Bunlar ne zaman
--color=no
) AnsiStrippingOutputStream
ile çıkarılır. İçinde
Ayrıca System.out
ve System.err
bu çıkış akışlarına yönlendiriliyor.
Böylece hata ayıklama bilgileri
System.err.println()
ve hâlâ istemcinin terminal çıkışında yer alıyor
(sunucudan farklıdır). Bir işlemin
ikili çıktı (bazel query --output=proto
gibi) üretir, stdout ile uğraşmaz
yaşandığını söylüyor.
Kısa mesajlar (hatalar, uyarılar vb.)
EventHandler
arayüzü. Bunların yayınlardan farklı
EventBus
(bu kafa karıştırıcıdır). Her Event
için bir EventKind
var (hata,
uyarı, bilgi ve birkaç kişi daha) varsa Location
(Şuradaki yer)
(etkinliğin gerçekleştirilmesine neden olan kaynak kodu).
Bazı EventHandler
uygulamaları, aldıkları etkinlikleri depolar. Kullanılan
çeşitli önbellek işleme yöntemlerinin neden olduğu kullanıcı arayüzünde bilgileri tekrar oynatmak için
örneğin, önbelleğe alınmış yapılandırılmış bir hedef tarafından yayınlanan uyarılar.
Bazı EventHandler
'lar, sonunda yolu olan etkinliklerin yayınlanmasına da olanak tanır.
etkinlik otobüsü (normal Event
'ler orada _görünmez _gibi) görünür. Bunlar:
ExtendedEventHandler
uygulamaları ve bunların temel kullanımı, önbelleğe alınan içeriği tekrar oynatmaktır
EventBus
etkinlik. Bu EventBus
etkinliklerinin tümü Postable
uygular ancak uygulamaz
EventBus
adresine gönderilen her şey, mutlaka bu arayüzü uygular;
yalnızca bir ExtendedEventHandler
tarafından önbelleğe alınanlar (kullanılabilir
çoğu şeyi yapıyor; Ancak bu yaptırım uygulanmaz)
Terminal çıkışı çoğunlukla UiEventHandler
üzerinden yayınlanır.
tüm gösterişli çıktı biçimlendirmesi ve ilerleme raporlamasından sorumlu Bazel.
yapıyor. İki girişi vardır:
- Etkinlik otobüsü
- Rapor eden aracılığıyla eklenen etkinlik akışı
Komut çalıştırma makinelerinin tek doğrudan bağlantısıdır (örneğin,
Bazel), istemciye giden RPC akışını Reporter.getOutErr()
üzerinden yapıyor.
Bu da söz konusu akışlara doğrudan erişim sağlar. Yalnızca bir komut için
büyük miktarlarda olası ikili veri (bazel query
gibi) dökümünü almak için kullanılır.
Bazel Profil Oluşturma
Bazel hızlı. Bazel de yavaştır çünkü derlemeler yalnızca
her açıdan bir göze çarpar. Bu nedenle, Bazel
profil binaları ve Bazel'in kendisi için kullanılır. Uygulanmış olan sınıfa
uygun şekilde Profiler
olarak adlandırıldı. Varsayılan olarak açıktır, ancak yalnızca kayıt
kısaltılmış olmalıdır. Komut satırı
--record_full_profiler_data
, yapabildiği her şeyi kaydetmesini sağlar.
Chrome profili biçiminde bir profil oluşturur; en iyi şekilde Chrome'da görüntülenir. Veri modeli görev yığınları şeklindedir: Görevleri başlatıp bitirebilir ve birbirlerine düzgün bir şekilde yerleştirilmeleri gerekir. Her bir Java iş parçacığı görevi görebilir. YAPILACAKLAR: Bu özellik, işlemleri ve nasıl oluşturabilirsiniz?
Profil oluşturucu BlazeRuntime.initProfiler()
içinde başlatıldı ve durduruldu
Sırasıyla BlazeRuntime.afterCommand()
ve süre boyunca yayında kalmaya çalışır
Böylece her şeyin profilini çıkarabiliriz. Profile öğe eklemek için:
Profiler.instance().profile()
numaralı telefonu arayın. Kapanışı olan bir Closeable
döndürüyor
görevin sonunu temsil eder. Deneme amaçlı kaynaklarla kullanılması daha uygundur
açıklamalarına dikkat edin.
MemoryProfiler
ürününde de temel bellek profili oluşturma işlemi gerçekleştiriyoruz. Ayrıca her zaman açık
ve çoğunlukla maksimum yığın boyutlarını ve GC davranışını kaydeder.
Bazel'i test etme
Bazel'in iki ana tür testi vardır: Bazel'in "kara kutu" olduğunu gösteren testler ve yalnızca analiz aşamasını gerçekleştirenler. Eski adıyla "entegrasyon testleri" "birim testleri" olarak tanımlar, ancak bu testler daha çok çok daha az entegredir. Ayrıca, kullanılan birim testlerinde gerekir.
Entegrasyon testlerinin iki türü vardır:
-
src/test/shell
. - Java'da uygulananlar. Bunlar,
BuildIntegrationTestCase
.
BuildIntegrationTestCase
, entegrasyon testi çerçevesi
olduğundan tercih edilen
çoğu test senaryosuna uygun donanıma sahiptir. Bu bir Java çerçevesi olduğundan
birçok yaygın geliştirme ile hata ayıklanabilirlik ve sorunsuz entegrasyon sağlar
araçlar. BuildIntegrationTestCase
sınıfına ilişkin birçok örnek
Bazel deposu.
Analiz testleri, BuildViewTestCase
alt sınıfları olarak uygulanır. Bir
BUILD
dosyalarını yazmak için kullanabileceğiniz ortak çalışma dosya sistemi ve ardından çeşitli yardımcılar
yöntemlerin yapılandırılmış hedefleri isteme, yapılandırmayı değiştirme ve onaylama
çeşitli şeylerle açıklanabilir.