Go 1.11 ile gelen yeni Module özelliği — 1. Bölüm
Module kavramını anlatmadan önce, Module ile ne çözülmek isteniyor sorusuna cevap verebilmek adına biraz genel kavramlardan başlamak lazım. Ama bu kısımları biliyorsanız, o zaman background kısmını direk atlayabilirsiniz. Bu makaleyi anlamak için aşağıda Go bilgilerini bilmeniz gerekiyor: Workspace kavramı, Go paketleri, Go CLI. Kısacası Go dilini hiç kullanmadıysanız, o zaman bu makaleyi çokta anlamayacaksınız.
Background
Başkalarının geliştirdikleri kütüphaneleri kullanmadan en basit uygulamaları bile neredeyse yazamadığımız zamanlardan geçiyoruz. Bundan dolayı her programlama dilinin belkide en önemli özelliği olarak karşımıza çıkıyor paketler. Paket (Package) ya da diğer bir ismi ile yeniden kullanılabilir kod üniteleri, örneğin kütüphaneler, hızlı ve güvenilir kod yazmak ve gelişen endüstride iş değeri olan ürünler çıkarmak için fazlasıyla dominant bir konsept. Farklı diller bunu farklı isim ve formatlarda sunuyorlar. Mesela, Java da .jar
dosyaları, .NET ortamında .dll
dosyaları, ve asıl konumuz olan Go programlama dilinde ise go get library-name
demek suretiyle indirilen ve kendi kodunuza import ettiğiniz paketler. Çoğu zaman derlenmiş halde indirir ve kullanırsınız bu paketleri. Ama, Go diğer dillerden farklı olarak, derlenmiş kodu değil, kaynak kodları paket olarak indirir, indirdiği bu dosyalar src
klasörü altına koyar. Derlemeyi hızlandırmak içinde bu kaynak kodları pkg klasörü altında object dosyaları olarak saklar.
Yazılım çoğu zaman iterative bir süreçtir ve bundan dolayı projelerimiz devamlı değişir. Bu hem kendi uygulamalarınız hem de uygulamalarınızın kullandığı kütüphaneler için geçerlidir. Bu devamlı geliştirme sürecinde amaç daha kaliteli ürünler çıkarmak olsa da, ciddi bazı sorunlarında beraberinde getirir. Güncellediğiniz kütüphanelerin API’larında büyük değişimler, kodunuzu büyük bir ihtimalle bozacaktır. Versiyonlama bundan dolayı çok önemlidir. Hangi versiyonlar ile çalıştığınızı bilmek zorundasınız. Projeniz de kullandığınız kütüphanelerin versiyonları değişmediği sürece beklenmedik bir problem ile karşılaşma riskiniz ciddi oranda azalacaktır.
Kullandığınız kütüphaneler de bu versiyon sorunlarından kurtulmanın en kolay yöntemi, onları da kodunuzla beraber Git (ya da başka bir VCS) reponuz içinde saklamak olacaktır. Tabi bu kesin çözüm olmayabilir.
Ama sundukları önemli artılardan dolayı package managers (NPM, NuGet, vs) kullanıyorsanız, o zaman package.json
gibi dosyalar içinde bir kütüphanenin hangi versiyonunu kullandığınızı belirtmek daha bir önem kazanıyor. Aksi halde kütüphaneleri her download edişte, en yeni versiyonları indirileceği için API’lar arasında ki uyumsuzluktan dolayı kodunuz her an break edebilir. Bu ciddi bir sorun olduğu için, bazı package manager uygulamaları lock
isminde, versiyonları birebir saklayan dosyaları projelere eklemeye başladılar.
Paket kavramlarına ve versiyonlamanın önemine kısaca değindikten sonra şimdi Go da Module kavramını anlatabiliriz.
Modules in Go
Module, versiyonlanmış kaynak kodları kümesi. .DLL ya da .JAR gibi düşünülebilir. Tabi farklılıkları var. Bu farklılıkların ne olduğunu ise bu yazı dizimde anlatmaya çalışacağım.
Problem
Go ilk çıktığından beri paketleri için native olarak versiyonlama desteği vermedi. Bu soruna cevap olması için araçlar geliştirenler oldu ama herkes tarafından kullanılan ortak çözümler olmadı. Sonrasında Go tarafından dep diye bir araç geliştirildi. Go 1.11 ile dep yerini vgo ya bırakmaya başladı. Bunların dışında Go içinde vendor
özelliği vardı. Yani go get
ile getirmek istemediğiniz paketleri, vendor
klasörü altında saklıyordunuz. Bu da farklı vendorların kaynak kodlarının sizi kodunuz ile birlikte kaynak kod kontrol sistemlerinde saklanması demekti. Bu sayede versiyonlama sorunlarından bir nebze kurtulabiliyordunuz.
dep
gibi araçlar kullanmadığınızda, Go package meselesinde sorunlu olan bir dil. Diyelim ki, kullandığınız uygulama aşağıdakine benzer bir dependency durumu mevcut:
YourApp 3.0 > HelloMars 2.0 > HelloWorld 1.0
Yani sizin YourApp uygulamanız, HelloMars kütüphanesine bağımlı. HelloMars kütüphanesi de HelloWorld kütüphanesini kullanıyor. Bir zaman sonra yeni bir kütüphane daha eklediniz ve o da HelloWorld 2.4 kütüphanesini kullanıyor:
YourApp 3.0 > HelloJupiter 2.1 > HelloWorld 2.4
Bu arada Go da bu şekilde versiyonlama anlayışı yok ama kütüphaneyi develop eden arkadaş bu şekilde versiyonlama yapıyor diyelim. Paketler de workspace’de ki src
klasöründe saklandığı için, go get
ile HelloJupiter kütüphanesi getirdiğinizde, önceden HelloWorld paketi indirilmiş olduğu için o zaman yeniden indirmeyecek ve dolayısıyla HelloJupiter paketi, HelloWorld 1.0 versiyonu kullanıyor olacak. Sonuç? 2.4 versiyonu nu bekleyen bir kütüphaneye 1.0 versiyonu verirseniz, olacak şey, kodun bozulması olacaktır. İşte bugüne kadar Go bu şekilde versiyonları ayırt etmediği için, başınıza buna benzer problemler geliyordu. Eğer go get -u
derseniz, o zaman bu paketler güncellenecek ve HelloWorld 2.4 versiyonu indirilecek. Sonrasında? Sonrasında ise HelloMars, HelloWorld 1.0 kullanmak yerine HelloWorld 2.4 versiyonunu kullanıyor olacağından kodunuz yine break etme riski ile karşı karşıya kalacaktır. Kısacası yukarı tükürsen bıyık, aşağı tükürsen sakal.
Çözüm
Versiyonlama. Go 1.11 ile Go CLI içinde destek verildi. Bir süredir vgo
ile prototip olarak test ediliyordu. vgo
, command line üzerinde çalıştırabilecek ayrı bir app. Go dan ayrı olarak indirip kullanmanız gerekiyordu. Artık onda ki özellikler go CLI içine entegre edildi ve dolayısıyla ayrı bir şekilde download edip kullanmanıza gerek yok. Ama unutmadan söyleyeyim, Go yazılımcıları hala bu modules sistemi üzerine çalışıyorlar ve ileride ufak bazı değişikler olabilir ama genel konseptin değişeceğini düşünmüyorum.
Go eksi sisteme verdiği desteği kaldırmadı. Dolayısıyla, modules özelliğini kullanacağınızı açık bir şekilde belirtmeniz gerekiyor. Bunun için aşağıda ki yöntemleri kullanabilirsiniz:
go
komutunu$GOPATH/src
klasörü dışında kullanın vego
projenizingo.mod
dosyasına sahip olduğuna emin olun.Command line içinde
GO111Module
ortam değişkenineon
değerini atayın.vgo
uygulamasını indirip kullanın.
Yukarıda ki yöntemlerden her biri modules özelliğini kullanmanızı sağlayacaktır.
Yeni modules özelliğini kullandığınızda artık paketlerinizi import ederken, aşağıda ki koda benzer şekilde import edeceksiniz:
import “example.com/my/module/v2/pkg/foo”
Bu şu demek, foo
paketini v2
den getir.
Aynı zamanda NPM de kipackage.json
dosyasına benzer olarak ayrı bir dosya geldi go.mod
isminde. İçerisinde ki kod ise Go yazılımcılarına familiar gelecektir:
module github.com/my/module/v3
require (
github.com/some/dependency v1.2.3
github.com/another/dependency v0.1.0
github.com/additional/dependency/v4 v4.0.0
)
Eğer var olan bir projeye module desteği eklemek isterseniz, o zaman go mod init
demekle go.mod
dosyasının projenize otomatik olarak eklenmesini sağlayabilirsiniz. Sonrasında go build
demekle ya da bu komutu çağıran başka bir komut kullanmak ile kaynak kodlarınızın otomatik olarak incelenmesini ve kullandığınız kütüphanelerin versiyonları ile birlikte go.mod
dosyasına eklenmesini sağlayabilirsiniz.
Modüller seçilirken, eğer versiyonları belirtilmemiş ise, o zaman en yüksek versiyonları ile go.mod
dosyasına eklenecekler.
Peki nereden geliyor bu versiyon numaraları?
go get
dediğiniz zaman paketler GitHub gibi yerlerden geliyor. Git reponuzda tag
kullanarak istediğiniz commitlere versiyon verebiliyorsunuz. İşte, yeni versiyon numaraları buralardan geliyor.
Devamı Sonra
Buraya kadarki kısım Go modülleri hakkında kısa bir bilgi vermek içindi. Bundan sonra ki yazılarımda detaylara inerek yeni Modules sisteminin farklılıklarını ve nasıl çalıştığını daha derinlemesine incelemeye çalışacağız.