Bağımlılık Yönetimi

Sorun bildirme Kaynağı görüntüleme Nightly · 7.4 . 7.3 · 7,2 · 7,1 · 7,0 · 6,5

Önceki sayfalara bakarken bir tema tekrar tekrar geçiyor: "yönetmek" kodunuz oldukça basittir, ancak onun bağımlılıklarını yönetmek daha zor olabilir. Çeşitli bağımlılık türleri vardır: Bazen bir göreve (ör. "Sürümün tamamlandığını işaretlemeden önce dokümanları gönderin") bazen de bir yapıya (ör. "Kodu derlemek için bilgisayar görüşü kitaplığının en son sürümüne sahip olmam gerekiyor") bağımlılık vardır. Bazen kod tabanınızın başka bir bölümüne, bazen de başka bir ekibin (kuruluşunuzda veya üçüncü taraf) sahip olduğu kod veya verilere harici bağımlılıklarınız vardır. Ancak her durumda, "Buna sahip olmak için önce şuna ihtiyacım var" fikri, derleme sistemlerinin tasarımında tekrar tekrar ortaya çıkan bir şeydir ve bağımlılıkları yönetmek, belki de bir derleme sisteminin en temel işlevidir.

Modüller ve Bağımlılıklarla Başa Çıkma

Bazel gibi yapıya dayalı derleme sistemleri kullanan projeler, bir dizi modüle ayrılır. Bu modüller, BUILD dosyaları aracılığıyla birbirlerine olan bağımlılıkları ifade eder. Bu modüllerin ve bağımlılıkların doğru şekilde düzenlenmesi, hem de derleme işlemi için gereken çalışma miktarına sağlar.

Ayrıntılı Modülleri ve 1:1:1 Kuralını Kullanma

Yapıya dayalı bir derleme oluştururken ilk soru, tek bir modülün ne kadar işlev içermesi gerektiğine karar vermektir. Bazel'de, modül, aşağıdaki gibi derlenebilir bir birim belirten bir hedefle temsil edilir: java_library veya go_binary. Bir uç noktada, köke bir BUILD dosyası yerleştirilerek ve söz konusu projenin tüm kaynak dosyaları yinelemeli olarak gruplandırılarak projenin tamamı tek bir modüle dahil edilebilir. Diğer uçta ise neredeyse her kaynak dosya kendi modülüne dönüştürülebilir. Bu durumda, her dosyanın bir BUILD dosyasında bağımlı olduğu diğer tüm dosyaları listelemesi gerekir.

Çoğu proje bu uç noktalar arasında bir yerdedir ve seçim, arasındaki dengeyi konuşacağız. Projenin tamamı için tek bir modül kullanmak, harici bağımlılık eklediğiniz durumlar dışında BUILD dosyasına hiç dokunmanız gerekmediği anlamına gelebilir. Ancak bu durumda, derleme sisteminin her zaman projenin tamamını tek seferde oluşturması gerekir. Bu, derlemenin bazı bölümlerini paralelleştiremeyeceği veya dağıtamayacağı, ayrıca daha önce derlenmiş bölümleri önbelleğe alamayacak anlamına gelir. Dosya başına bir modülde ise durum tam tersidir: Derleme sistemi, derlemenin önbelleğe alma ve planlama adımlarında maksimum esnekliğe sahiptir ancak mühendislerin, hangi dosyaların hangisine referans verdiğini değiştirdiklerinde bağımlılık listelerini korumak için daha fazla çaba göstermesi gerekir.

Kesin ayrıntı düzeyi dile göre değişse de (ve genellikle Google, diğerlerine kıyasla çok daha küçük modülleri tercih ediyor. göreve dayalı derleme sistemleri kullanır. Google'daki tipik bir üretim ikili dosyası genellikle on binlerce hedefe bağlıdır ve orta büyüklükte bir ekip bile kod tabanında birkaç yüz hedefe sahip olabilir. Örneğin, Güçlü bir yerleşik paketleme kavramına sahip Java'da, her dizin genellikle tek bir paket, hedef ve BUILD dosyası (Pantolon, başka bir derleme sistemi) kullanıyorsanız bunu 1:1:1 kuralı olarak adlandırabilirsiniz. Paketleme kuralları daha zayıf olan diller genellikle BUILD dosyası başına birden fazla hedef tanımlar.

Daha küçük yapı hedeflerinin avantajları büyük ölçekte görülmeye başlar çünkü Bu durum, daha hızlı dağıtılan derlemelerin ve hedeflerin yeniden oluşturulma sıklığının daha az duyulmasını sağlar. Daha ayrıntılı hedefler, derleme sisteminin yalnızca belirli bir değişiklikten etkilenebilecek sınırlı sayıda test çalıştırması anlamına geldiğinden, test devreye girdikten sonra avantajlar daha da ilgi çekici hale gelir. Google, daha küçük boyutlu reklam öğeleri kullanmanın sistemli olmakla birlikte, şu hedeflere ulaşılacak şekilde yatırımlar yaparak olumsuz tarafları araçlarını kullanarak BUILD dosyalarını otomatik olarak yönetirsiniz.

buildifier ve buildozer gibi bu araçlardan bazıları, Bazel'de buildtools dizininde kullanılabilir.

Modül görünürlüğünü en aza indirme

Bazel ve diğer derleme sistemleri, her bir hedefin bir görünürlük belirtmesine olanak tanır. ona bağlı olabilecek başka hedefleri belirleyen bir mülk. Gizli hedeflere yalnızca kendi BUILD dosyalarında referans verilebilir. Bir hedef, açıkça tanımlanmış bir BUILD dosyası listesinin hedeflerine veya herkese açık görünürlük durumunda, çalışma alanındaki her hedefe daha geniş görünürlük verebilir.

Çoğu programlama dilinde olduğu gibi, görünürlüğü olabildiğince en aza indirmek genellikle en iyi seçenektir. Genellikle Google'daki ekipler, hedefleri yalnızca Google'daki tüm ekiplerin kullanabileceği yaygın olarak kullanılan kitaplıkları temsil ediyorsa herkese açık hale getirir. Kodlarını kullanmadan önce başkalarının kendileriyle koordine çalışmasını gerektiren ekipler, hedeflerinin görünürlüğü olarak müşteri hedeflerinin izin verilenler listesini tutmalıdır. Her biri Ekibin dahili uygulama hedefleri yalnızca dizinlerle sınırlandırılacaktır ve çoğu BUILD dosyanın sahibi olmayan tek bir hedefi olur özel.

Bağımlılıkları Yönetme

Modüllerin birbirine referans vermesi gerekir. Kod tabanını ayrıntılı modüllere ayırmanın dezavantajı, bu modüller arasındaki bağımlılıkları yönetmeniz gerekmesidir (ancak araçlar bu işlemi otomatikleştirmenize yardımcı olabilir). Bu bağımlılıkların ifade edilmesi, genellikle bir BUILD dosyasındaki içeriğin büyük kısmını oluşturur.

Dahili bağımlılıklar

Ayrıntılı modüllere ayrılmış büyük bir projede çoğu bağımlılık dahili olması muhtemeldir; yani aynı dilde tanımlanan ve oluşturulan başka bir hedefte kaynak depodur. İç bağımlılıklar, derleme işlemi sırasında önceden derlenmiş bir yapı olarak indirilmek yerine kaynaktan derlendikleri için harici bağımlılıklardan farklıdır. Bu aynı zamanda, bir dönüşüm için ”sürüm” kavramının İç bağımlılıklar vardır. Bir hedef ve tüm iç bağımlılıkları aynı kayıt/düzeltme üzerinde oluşturulur. Dahili bağımlılıklarla ilgili dikkatli bir şekilde ele alınması gereken konulardan biri, geçişli bağımlılıkların nasıl ele alınacağıdır (Şekil 1). Hedef A'nın, her bir dönüşüm işleminde ortak kitaplık hedef C'ye bağlıdır. A hedefi, C hedefinde tanımlanan sınıfları kullanabilmeli mi?

Geçiş bağımlılıkları

Şekil 1. Geçiş bağımlılıkları

Temel araçlar açısından bu konuda bir sorun yoktur. Hem B hem de C, derlendiğinde hedef A'ya bağlanır. Bu nedenle, C'de tanımlanan tüm semboller A tarafından bilinir. Bazel yıllarca buna izin verdi ancak Google büyüdükçe sorunlar görmeye başladı. B'nin, artık C'ye bağımlı olması gerekmeyecek şekilde yeniden yapılandırıldığını varsayalım. Ardından B'nin C'ye olan bağımlılığı kaldırılırsa A ve B'ye olan bağımlılığı aracılığıyla C'yi kullanan diğer tüm hedefler bozulur. Bir hedefin kamu sözleşmesinin bir parçası haline geldi ve güvensiz bir şekilde değiştirildi. Bu durum, zaman içinde bağımlılıkların birikmesine ve Google'daki derlemelerin yavaşlamaya başlamasına neden oldu.

Google, sonunda bu sorunu “katı geçişli geçişli bir şarta bağımlılık modu"nu gönderin. Bu modda Bazel, bir hedefin bir sembole doğrudan bağımlı olmadan referans verip vermediğini algılar ve bu durumda bir hata ve bağımlılığı otomatik olarak eklemek için kullanılabilecek bir kabuk komutu ile başarısız olur. Bu değişikliği Google'ın tüm kod tabanında kullanıma sunmak ve milyonlarca derleme hedefimizin her birini bağımlılıkları açıkça listeleyecek şekilde yeniden yapılandırmak yıllar süren bir çalışmaydı ancak buna değdi. Yapılarımız hedeflerin artık daha az gereksiz bağımlılıklara sahip olması ve ihtiyaç duymadıkları bağımlılıkları, bu bağımlılığı hakkında daha fazla bilgi edineceksiniz.

Her zaman olduğu gibi, katı geçişli bağımlılıkları zorunlu kılmak da bir denge gerektiriyordu. Sık kullanılan kitaplıkların artık tesadüfen dahil edilmek yerine birçok yerde açıkça listelenmesinin gerekmesi ve mühendislerin BUILD dosyalarına bağımlılıklar eklemek için daha fazla çaba harcaması gerektiğinden, derleme dosyaları daha ayrıntılı hale geldi. O zamandan beri, birçok eksik bağımlılık otomatik olarak algılayıp geliştirici müdahalesi olmadan BUILD dosyalarına ekleyerek bu zahmeti azaltan araçlar geliştirdik. Ancak bu tür araçlar olmadan bile dengenin iyi olduğunu gördük. kod tabanı ölçeklendirildikçe buna değer: BUILD dosyasına açıkça bir bağımlılık ekleme tek seferlik bir maliyettir, ancak örtülü geçişli bağımlılıklarla başa çıkmak, devam eden sorunları çözebilir. Bazel, varsayılan olarak Java kodunda katı geçişli bağımlılıkları zorunlu kılar.

Harici bağımlılıklar

Bir bağımlılık dahili değilse harici olmalıdır. Dış bağımlılıklar, derleme sistemi dışında oluşturulup depolanan yapılardır. Bağımlılık doğrudan bir yapı deposundan (genellikle internet üzerinden erişilir) içe aktarılır ve kaynaktan derlenmek yerine olduğu gibi kullanılır. Şunlardan biri: dış ve iç bağımlılıklar arasındaki en büyük fark, dış bağımlılıkların sürümleri vardır ve bu sürümler projenin kaynak kodundan kaynaklanabilir.

Otomatik ve manuel bağımlılık yönetimi

Derleme sistemleri, dış bağımlılıkların sürümlerinin yönetilmesine izin verebilir manuel ya da otomatik olarak yapabilirsiniz. Manuel olarak yönetildiğinde derleme dosyası yapı deposundan indirmek istediği sürümü açıkça listelediğinde çoğu zaman anlamsal sürüm dizesi 1.1.4 olarak. Otomatik olarak yönetildiğinde, kaynak dosya ve derleme sistemi her zaman en yeni sürümü indirir. Örneğin, Gradle, ana sürüm 1 olduğu sürece bağımlılığın tüm küçük veya yama sürümlerinin kabul edilebilir olduğunu belirtmek için bağımlılığın sürümünün "1.+" olarak tanımlanmasına olanak tanır.

Otomatik olarak yönetilen bağımlılıklar küçük projeler için uygun olabilir ancak genellikle önemli boyutlarda veya birden fazla mühendisin üzerinde çalıştığı projelerde felaketin habercisi olur. Otomatik olarak yönetilen bağımlılıklarla ilgili sorun, sürümün ne zaman güncelleneceği üzerinde hiçbir kontrole sahip olmamanızdır. Harici tarafların, anlamsal sürüm kontrolünü kullandıklarını iddia etseler bile çalışmayı durduran güncellemeler yapmayacaklarını garanti etmek mümkün değildir. Bu nedenle, bir gün çalışan bir derleme ertesi gün çalışmayı durdurabilir ve neyin değiştiğini tespit etmenin veya derlemeyi çalışan bir duruma geri döndürmenin kolay bir yolu yoktur. Derleme bozulmasa bile, tespit edilmesi imkansız olan küçük davranış veya performans değişiklikleri olabilir.

Aksine, manuel olarak yönetilen bağımlılıklar için kaynakta ve bunların kolayca bulunması ve geri çekilebilmesi sayesinde eski bağımlılıklarla derlemek için deponun daha eski bir sürümüne göz atın. Bazel, tüm bağımlılıkların sürümlerinin manuel olarak belirtilmesini gerektirir. Orta ölçekte bile manuel sürüm yönetiminin sağladığı kararlılık için bu işleme harcanan zamana değer.

Tek Sürüm Kuralı

Bir kütüphanenin farklı sürümleri genellikle farklı yapı taşlarıyla temsil edilir. Bu nedenle, teorik olarak aynı harici bağımlılık için farklı sürümlerin derleme sisteminde farklı adlar altında tanımlanmasının bir sakıncası yoktur. Böylece her hedef, bağımlılığın hangi sürümünü pek de iyi olmadığını unutmayın. Bu durum uygulamada birçok soruna neden olduğundan Google, kod tabanımızdaki tüm üçüncü taraf bağımlılıkları için katı bir Tek Sürüm Kuralı uyguluyor.

Birden fazla sürüme izin vermenin en büyük sorunu elmas bağımlılığıdır . Hedef A'nın hedef B'ye ve harici bir reklamın v1'e kitaplığını açar. Hedef B daha sonra aynı öğenin v2'sine bağımlılık eklemek için yeniden düzenlenirse artık dolaylı olarak iki kaynağa bağlı olduğundan, A hedefi bozulur emin olmanız gerekir. Etkili bir sisteme bir hedeften birden fazla sürümü olan üçüncü taraf kitaplığına yeni bağımlılıklar, çünkü söz konusu hedefin kullanıcılarından herhangi biri zaten farklı bir sürümünü değil. Tek Sürüm Kuralını izlemek bu çakışmayı hedef, üçüncü taraf kitaplığına, mevcut bağımlılara zaten aynı sürümde olurlar, böylece mutlu bir şekilde bir arada olabilirler.

Geçişli harici bağımlılıklar

Bir dış bağımlılığın geçişli bağımlılıklarıyla başa çıkmak için zor oluyor. Maven Central gibi birçok yapı deposu, ve diğer yapıların belirli sürümlerindeki bağımlılıkları belirlemek için kullanılan seçeceğim. Maven veya Gradle gibi derleme araçları genellikle varsayılan olarak her geçişli bağımlılığı yinelemeli olarak indirir. Bu, projenize tek bir bağımlılık eklemenin toplamda düzinelerce yapının indirilmesine neden olabileceği anlamına gelir.

Bu çok kullanışlıdır: Yeni bir kitaplığa bağımlılık eklerken, söz konusu kitaplığın geçişli bağımlılıklarının her birini bulup hepsini manuel olarak eklemek çok zor olur. Ancak bunun büyük bir dezavantajı da vardır: Farklı kitaplıklar aynı üçüncü taraf kitaplığının farklı sürümlerine bağlı olabileceğinden bu strateji, bir sürüm kuralını ihlal eder ve elmas bağımlılığı sorununa yol açar. Hedefiniz, aynı bağımlılığın farklı versiyonları varsa, hangi sürümü alın. Bu aynı zamanda bir dış bağımlılığı güncellemenin, yeni sürüm uyumlu olmaya başlarsa kod tabanı boyunca ve bazı bağımlılıklarının çakışan versiyonları olabilir.

Bu nedenle Bazel, geçişli bağımlılıkları otomatik olarak indirmez. Maalesef sihirli bir değnek de yok. Bazel'in alternatifi, depodaki tüm harici verileri listeleyen bir global dosya bağımlılıkları ve bu bağımlılık için kullanılan açık depodur. Neyse ki Bazel, bir Maven yapı grubuna ait geçişli bağımlılıkları içeren bu tür bir dosyayı otomatik olarak oluşturabilen araçlar sağlar. Bu araç, bir projenin ilk WORKSPACE dosyasını oluşturmak için bir kez çalıştırılabilir. Ardından, her bağımlılığın sürümlerini ayarlamak için bu dosya manuel olarak güncellenebilir.

Burada da seçim, kolaylık ve ölçeklenebilirlik arasındadır. Küçük projelerde, geçişli bağımlılıkları yönetmek zorunda kalmamak tercih edilebilir ve otomatik geçişli bağımlılıklar kullanılabilir. Kuruluş, bu stratejiye giderek daha az cazip gelir. ve kod tabanı büyür, çakışmalar ve beklenmedik sonuçlar giderek daha da artar. sıklıkla görebilirsiniz. Büyük ölçeklerde bağımlılıkları manuel olarak yönetmenin maliyeti otomatik bağımlılığın neden olduğu sorunlarla ilgilenmenin maliyetinden daha düşüktür. üzerine konuşalım.

Harici bağımlılıkları kullanarak derleme sonuçlarını önbelleğe alma

Dış bağımlılıklar çoğu zaman bir şeyi serbest bırakan üçüncü taraflar kaynak kodu sağlamadan kitaplıkların kararlı sürümlerini kullanır. Bazı kuruluşlar, kendi kodlarının bir kısmını yapı olarak sunmayı da seçebilir. Bu sayede diğer kod parçaları, şirket içi bağımlılıklar yerine üçüncü taraf olarak bu yapılara bağımlı olabilir. Bu, teorik olarak yapıların oluşturmaları yavaş ancak indirmeleri hızlıdır.

Ancak bu, çok fazla ek yük ve karmaşıklık da getirir: Bu yapıların her birini oluşturmaktan ve yapı deposuna yüklemekten birinin sorumlu olması gerekir. Ayrıca istemcilerin en son sürümle güncel kalmasını sağlaması gerekir. Çeşitlilik nedeniyle hata ayıklama da çok daha zor hale gelir. ve ekibin farklı noktalarından geliştirilmiştir. ve artık kaynak ağacın tutarlı bir görünümü yoktur.

İnşaatı uzun süren eserler sorununu çözmenin daha iyi bir yolu, daha önce açıklandığı gibi uzaktan önbelleğe almayı destekleyen bir derleme sistemi kullanın. Bu tür bir derleme sistemi, her derlemeden elde edilen yapıları mühendisler arasında paylaşılan bir konuma kaydeder. Bu nedenle, bir geliştirici yakın zamanda başka biri tarafından oluşturulmuş bir yapıya ihtiyaç duyarsa derleme sistemi, yapıyı oluşturmak yerine otomatik olarak indirir. Bu, kampanyalarınızı geliştirerek Aynı zamanda derlemelerin gerektiği gibi olduğundan emin olurken her zaman aynı kaynaktan oluşturulmuş gibi tutarlı olur. Bu, Google tarafından şirket içinde kullanılan stratejidir ve Bazel, uzak önbelleği kullanacak şekilde yapılandırılabilir.

Harici bağımlılıkların güvenliği ve güvenilirliği

Üçüncü taraf kaynaklarından gelen yapıların kullanılması doğal olarak risklidir. Üçüncü taraf kaynağı (ör. yapı deposu) devre dışı kalırsa kullanılabilirlik riski vardır. Çünkü harici bir bağımlılık indiremezse derlemenizin tamamı durabilir. Bir de güvenlik riski vardır: üçüncü taraf sistemin, güvenliği bir saldırgan tarafından ele geçirildiğinde, saldırgan, referans verilen rastgele kod yerleştirmelerine olanak tanıyan bu yapı, kendi tasarımlarından biriyle içine ekleyebilirsiniz. Hem bu hem de diğer sorun, bağımlı olduğunuz tüm yapıları kontrol ettiğiniz sunuculara yansıtarak ve derleme sisteminizin Maven Central gibi üçüncü taraf yapı depolarına erişmesini engelleyerek azaltılabilir. Bununla birlikte, bu yansıtıcıların bakımı için çaba ve kaynak gerekir. Bu nedenle, bunları kullanıp kullanmama kararı genellikle projenin ölçeğine bağlıdır. Güvenlik sorunu, her üçüncü taraf yapının karmasının kaynak depoda belirtilmesini zorunlu kılarak da çok az ek yük ile tamamen önlenebilir. Bu durumda, yapıda değişiklik yapılması derlemenin başarısız olmasına neden olur. Tamamen yeni bir alternatif en önemli iki yanı da projenizin bağımlılıklarını tedarik etmektir. Bir proje, bağımlılıkları tedarikçiye gönderirken bunları projenin kaynak koduyla birlikte kaynak veya ikili kod olarak kaynak denetimine gönderir. Bu, projenin tüm harici bağımlılıklarının dahili bağımlılıklara dönüştürüldüğü anlamına gelir. Google bu yaklaşımı dahili olarak kullanır ve tüm üçüncü taraf Google genelinde, kökte bir third_party dizinine referans verilen kitaplık temel alandır. Ancak, bu yalnızca Google'da işe yarar çünkü Google'ın kaynak kontrol sistemi, aşırı büyük monorepo'yu işleyecek şekilde özel olarak tasarlanmıştır. tedarikçi bulma tüm kurumlar için bir seçenek olmayabilir.