Sbt #2: Proje yonetimi
Postun basligi iki yillik bolum adi gibi oldu ama idare edelim.
IntelliJ Yeni projesi
Oncelikle IntelliJ ile bir sbt projesi olsuturuyoruz. (IntelliJ community edition ucretsiz ve scala plugini de kurulabiliyor). Sbt'nin de sisteminizde yuklu oldugunu varsayarak (onceki postta bahsetmistik), yeni proje'ye tikliyoruz ve sbt'yi seciyoruz. Projeye bir isim verim tamam'a tikliyoruz. Akabinde IntelliJ hemen build.sbt'yi yuklemeye baslayacak ve karsimiza su sekilde bir klasor yapisi cikacaktir:
build.sbt'ye goz atarsak, 3 adet key tanimlanmis oldugunu goruyoruz. Ek olarak IntelliJ spesifik dosyalari ve de projeyi build ettikten sonra olusan artifactlari source repository'e gondermemek icin bir .gitignore dosyasi olusutup su iki satiri ekliyoruz:
.idea/
target/
Sub-project yapisi
Genisletilebilir (extensible) bir proje yapisi elde etmek icin proje icerisinde sub-proje'ler olsuturmak yaygin bir yaklasim. Bu sayede buyuk bir projenin alt sistemlerini izole veya birbirine depend eden kisimlari belirleyerek maintain edebiliyoruz. Simdi bir ana proje (root) ve bir de sub-project olmak uzere iki proje tanimlamasi yapalim:
val root = project.in(file("."))
val sayisalLoto = project
burada project keywordu ile iki adet sub-project tanimlamasi yaptik. Ilki root isimli ve ana projeyi temsil ediyor. Bunu da root klasor uzerinde tanimlanmasi icin file(".") seklinde yaptik. Diger sub-project ise sayisalLoto adinda, klasor tanimlamasi yapmadigimiz icin, build.sbt reload edildiginde sayisalLoto isimli bir klasor olusturacak. build.sbt'yi reload edip yaptigimiz degisikliklerin gecerli olmasi icin IntelliJ'in sbt plugini uzerinde reload'a tiklayabilir ya da komut satirindan sbt reload calistirabiliriz.
Reload ettikten sonra, sayisalLoto isimli bir klasor olusturulacaktir. Hemen ilk dosyasimizi olusturmak icin bu klasore sag tiklayip, new/file seceneginden soyle bir path giriyoruz:
src/main/scala/SayisalTahmin.scala
Sayisal loto tahmin programimizi impente edelim:
object SayisalTahmin extends App {
println((0 to 5).map(_ => scala.util.Random.nextInt(50)).mkString(","))
}
Bu sayede IntelliJ, ayni zamanda ara klasorleri de src/main/scala olsuturacaktir. Simdi InteeliJ icerisinden terminal acarak projemizi derleyelim:
> sbt
> ;reload ;compile
Ama o da ne? Projede bir degisiklik yapilmadigi goruluyor ama biz bir dosya ekledik oysa ki? Bunun sebebi suan iki farkli sub-project olmasi ve root'un aktif projec olarak secili olmasidir. Bunu projects komutu ile dogrulayabiliriz:
sbt:hello_sbt> projects
[info] In file:/hello_sbt/
[info] * root
[info] sayisalLoto
.aggregate(sayisalLoto)
val sayisalLoto = project
Haha ingilizce run kelimesinin bu contexte "kosmak" olarak cevrilmesine asiri uyuz oldugum icin bu basligi sectim. Neyse, simdilik test calistirmayi test etmek icin 3rd party bir testi framework (scala-test gibi) kullanmayacagiz. Hersey hardcore... Simdi src klasorunun icerisine su path uzerinden bir test dosyasi ekliyoruz: test/main/SayisalTahminSpec.scala.
Bu noktada test classini calistirmaya calisir isek, sayilsalTahmin/run komutu ile, src/main/scala 'daki implementasyon caliscaktir, test degil. Buna ek olarak test komutu da sadece bir 3rd party test framework integrasyonu varsa calisacaktir. Peki bu durumda nasil src/test/scala 'daki bir class'i nasil calistiracagiz? Bunun icin compile scope'undan test scope'una gecmemiz gerekiyor.
Sbt birseyi calistirirken su uc degere gore calistirir:
project_axis/config_axis/task_key
Biz bir onceki adimda sayisalLoto/run komutunu calistitiken aslinda config_axis degerini bos geciyoruz ve sbt default deger olarak Compile degerini aliyor. Hatta ilk bastaki project_axis degerini de bos gectigimizde (sadece run calistirinca) bu sefer de project_axis degerini de default degeri olarak mevcut aktif projeyi aliyor. Hatta detalari incelemek icin onceki postta inspect komutundan bahsetmistik.
sbt:hello_sbt> inspect sayisalLoto/run
[info] Input task: Unit
[info] Description:
[info] Runs a main class, passing along arguments provided on the command line.
[info] Provided by:
[info] ProjectRef(uri("file:/hello_sbt/"), "sayisalLoto") / Compile / run
Simdi yapmamiz gereken de bu config_axis degerini elle Test olarak set etmek ki, src/test/scala 'daki bir class uzerinde run calissin:
sayisalLoto/Test/run
Bu sefer test class'inin calistigini goruyoruz.
Yeni bir class
Simdi projemize yeni bir class ekliyoruz. Bu sefer verilen at listesi icerisinden birinci gelecek ati tahmin eden bir class gelistiriyoruz. SayisalLoto projesine bunu eklemek biraz sacma olacak ama idare edelim, ileride proje adini SansOyunlari seklinde degistiririz belki.
object AtYarisiTahmin extends App {
def tahminEt(atlar: Array[String]): String = {
atlar(scala.util.Random.nextInt(atlar.length)
}
override def main(args: Array[String]): Unit = {
println(tahminEt(args))
}
}
compile calistirdigimizda dikkat ederseniz sadece 1 dosyanin derlendigini goruyoruz. Bu da sbt'nin incremental compilation yapmasindan kaynaklaniyor. Yani sadece derlenmeye ihtiyaci olan dosyalar derleniyor. Class'i calistirirken at listesini de arguman olarak vermemiz gerekiyor:
sbt:hello_sbt> sayisalLoto/runMain AtYarisiTahmin "sahbatur" "baturalp" "baturmert"
[warn] multiple main classes detected: run 'show discoveredMainClasses' to see the list
[info] running AtYarisiTahmin evren uzay deniz
baturalp
Evet bu kosuda baturalp geldi. Burada runMain komutu ile verilen proje icerisinden verilen class'in main metodunu calistirmis olduk.
Cok asiri ozet:
- sub-project yapisi ve aggregator proje
- sub-proje olusturma
- scope'lar nelerdir (Test ve Compile)
Diger bir sbt postunda gorusmek uzere.
Yorumlar
Yorum Gönder